Skip to content

Commit af59e6c

Browse files
authored
Merge pull request #960 from mprib/fix/lightweight-thumbnails
Replace FrameSource with lightweight PyAV decode for thumbnails
2 parents 9776ea2 + 12699d4 commit af59e6c

1 file changed

Lines changed: 22 additions & 13 deletions

File tree

src/caliscope/core/process_synchronized_recording.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ def get_initial_thumbnails(
125125
) -> dict[int, NDArray[np.uint8]]:
126126
"""Extract first frame from each camera for thumbnail display.
127127
128-
Uses same FrameSource mechanism as processing, just reads frame 0.
128+
Opens each video briefly with PyAV to decode the first frame,
129+
then closes immediately. No keyframe scanning or frame index
130+
construction -- much faster than FrameSource for this use case.
129131
130132
Args:
131133
recording_dir: Directory containing cam_N.mp4 files
@@ -134,22 +136,29 @@ def get_initial_thumbnails(
134136
Returns:
135137
Mapping of cam_id -> first frame (BGR image)
136138
"""
139+
import av
140+
137141
thumbnails: dict[int, NDArray[np.uint8]] = {}
138142

139143
for cam_id in cameras:
140-
try:
141-
source = FrameSource(recording_dir, cam_id)
142-
frame = source.get_frame(0)
143-
source.close()
144-
145-
if frame is not None:
146-
thumbnails[cam_id] = frame
147-
else:
148-
logger.warning(f"Could not read first frame for cam_id {cam_id}")
149-
except FileNotFoundError:
144+
video_path = recording_dir / f"cam_{cam_id}.mp4"
145+
if not video_path.exists():
150146
logger.warning(f"Video file not found for cam_id {cam_id}")
151-
except ValueError as e:
152-
logger.warning(f"Error opening video for cam_id {cam_id}: {e}")
147+
continue
148+
149+
try:
150+
container = av.open(str(video_path))
151+
try:
152+
stream = container.streams.video[0]
153+
for frame in container.decode(stream):
154+
# bgr24 always produces uint8; PyAV stubs don't narrow the type
155+
arr: NDArray[np.uint8] = frame.to_ndarray(format="bgr24") # type: ignore[assignment]
156+
thumbnails[cam_id] = arr
157+
break
158+
finally:
159+
container.close()
160+
except Exception as e:
161+
logger.warning(f"Error reading first frame for cam_id {cam_id}: {e}")
153162

154163
return thumbnails
155164

0 commit comments

Comments
 (0)