Video I/O¶
Video reading utilities with support for raw H.264 elementary streams (common from Raspberry Pi cameras) where OpenCV metadata is unreliable.
Handles frame counting, FPS detection via ffprobe, and sequential reading for non-seekable streams.
video_io ¶
Video I/O helpers for media extraction.
VideoMetadata
dataclass
¶
Basic metadata for a video file.
VideoSegment
dataclass
¶
VideoSegment(path: Path, frame_count: int, fps: float, width: int, height: int, start_frame: int, seekable: bool)
Metadata for one video file within a multi-video sequence.
MultiVideoReader ¶
Unified read interface across N ordered video files.
Provides a single virtual frame index space across N video files. Video 0 has frames [0, N0), video 1 has frames [N0, N0+N1), etc.
For single-video sequences, accepts a single Path and works as a thin wrapper with minimal overhead.
Parameters¶
video_paths : list[Path] | Path | str One or more video file paths, in playback order.
Source code in src/mosaic/media/video_io.py
segment_for_frame ¶
Return (segment_index, local_frame) for a global frame index.
Source code in src/mosaic/media/video_io.py
seek ¶
Seek to a global frame position.
For seekable videos, uses CAP_PROP_POS_FRAMES. For non-seekable streams (raw H.264), reopens the segment and skips sequentially.
Source code in src/mosaic/media/video_io.py
read ¶
Read the next frame, automatically transitioning between videos.
Returns (success, frame) like cv2.VideoCapture.read().
Source code in src/mosaic/media/video_io.py
FFmpegFrameReader ¶
FFmpegFrameReader(video_path: Path | str, start_frame: int = 0, end_frame: int | None = None, frame_step: int = 1, resize: tuple[int, int] | None = None, hwaccel: bool = False)
Sequential frame reader using ffmpeg subprocess pipe.
Spawns an ffmpeg process that decodes video and pipes raw BGR frames to stdout. Supports decode-time resize, frame stepping, and optional NVDEC hardware acceleration.
This reader is optimised for high-throughput sequential access (e.g.
batched inference). It does not support random seeking — frame
selection is handled by ffmpeg's select filter at spawn time.
Parameters¶
video_path : path
Path to input video file.
start_frame : int
First frame to output (0-based).
end_frame : int, optional
Stop before this frame (exclusive). None = to end of video.
frame_step : int
Output every Nth frame (relative to start_frame).
resize : (width, height), optional
Resize frames during decode. None = original resolution.
hwaccel : bool
Attempt NVDEC hardware decoding if available.
Source code in src/mosaic/media/video_io.py
read ¶
Read the next frame.
Returns (True, frame) or (False, None) at EOF.
Frame is BGR uint8 with shape (height, width, 3).
Source code in src/mosaic/media/video_io.py
read_batch ¶
Read up to batch_size frames.
Returns¶
indices : ndarray, shape (N,)
Global frame indices for each frame in the batch.
frames : ndarray, shape (N, H, W, 3)
BGR uint8 frames. N may be less than batch_size at EOF.
Source code in src/mosaic/media/video_io.py
__iter__ ¶
Yield (frame_index, frame) tuples.
close ¶
Kill the ffmpeg subprocess and close pipes.
Source code in src/mosaic/media/video_io.py
FFmpegVideoWriter ¶
FFmpegVideoWriter(output_path: Path | str, width: int, height: int, fps: float = 30.0, crf: int = 23, preset: str = 'medium', hwaccel: bool = False)
Write BGR frames to an H.264 MP4 file via an ffmpeg subprocess pipe.
This is the write counterpart to :class:FFmpegFrameReader. Frames are
piped as raw BGR24 to ffmpeg's stdin, which encodes them with libx264
(or h264_nvenc when hwaccel=True and hardware encoding is available).
Parameters¶
output_path : Path or str
Destination MP4 file. Parent directory is created if needed.
width, height : int
Frame dimensions (must match every frame passed to :meth:write).
fps : float
Output frame rate.
crf : int
Constant Rate Factor for libx264 (0–51, lower = higher quality).
Ignored when NVENC is used (replaced by -cq).
preset : str
Encoding speed/quality preset (e.g. "ultrafast", "medium").
hwaccel : bool
If True and h264_nvenc is available, use GPU encoding.
Raises¶
RuntimeError
If ffmpeg is not found on PATH.
Examples¶
with FFmpegVideoWriter("out.mp4", 640, 480, fps=30) as writer: ... for frame in frames: ... writer.write(frame)
Source code in src/mosaic/media/video_io.py
write ¶
Write a single BGR uint8 frame.
Parameters¶
frame : ndarray, shape (H, W, 3), dtype uint8 BGR frame matching the writer's width and height.
Source code in src/mosaic/media/video_io.py
close ¶
Flush and close the ffmpeg encoder.
Source code in src/mosaic/media/video_io.py
get_video_metadata ¶
Read basic metadata from a video file.
For containerless streams (e.g. raw .h264) where OpenCV cannot determine fps or frame count from headers, falls back to ffprobe for fps and decode-counting for frame count.
Source code in src/mosaic/media/video_io.py
normalize_frame_range ¶
normalize_frame_range(frame_count: int, start_frame: Optional[int], end_frame: Optional[int]) -> tuple[int, int]
Clamp and validate the inclusive frame extraction range.
Source code in src/mosaic/media/video_io.py
normalize_crop_rect ¶
normalize_crop_rect(crop: Optional[tuple[int, int, int, int] | dict[str, Any]], frame_width: int, frame_height: int) -> Optional[tuple[int, int, int, int]]
Normalize crop input to an in-bounds (x, y, w, h) rectangle.
Source code in src/mosaic/media/video_io.py
apply_crop ¶
Apply an optional (x, y, w, h) crop rectangle.
Source code in src/mosaic/media/video_io.py
extract_candidate_features ¶
extract_candidate_features(video_path: Path | str, start_frame: int, end_frame: int, candidate_step: int, resize: tuple[int, int], grayscale: bool, crop_rect: Optional[tuple[int, int, int, int]], max_candidates: Optional[int] = None) -> tuple[np.ndarray, np.ndarray]
Decode candidate frames and return
- candidate frame indices (N,)
- flattened feature vectors (N, D)
Source code in src/mosaic/media/video_io.py
save_frames_as_png ¶
save_frames_as_png(video_path: Path | str, frame_indices: ndarray, output_dir: Path | str, crop_rect: Optional[tuple[int, int, int, int]]) -> list[dict[str, Any]]
Decode selected frames and save each as PNG.
For non-seekable streams (e.g. raw .h264), reads sequentially and captures target frames as they are reached.
Source code in src/mosaic/media/video_io.py
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | |
extract_candidate_features_multi ¶
extract_candidate_features_multi(reader: MultiVideoReader, start_frame: int, end_frame: int, candidate_step: int, resize: tuple[int, int], grayscale: bool, crop_rect: Optional[tuple[int, int, int, int]], max_candidates: Optional[int] = None) -> tuple[np.ndarray, np.ndarray]
Extract candidate features across a MultiVideoReader.
Same as extract_candidate_features but reads from multiple videos
via the unified reader. Frame indices are global (virtual).
Source code in src/mosaic/media/video_io.py
save_frames_as_png_multi ¶
save_frames_as_png_multi(reader: MultiVideoReader, frame_indices: ndarray, output_dir: Path | str, crop_rect: Optional[tuple[int, int, int, int]]) -> list[dict[str, Any]]
Save selected frames from a MultiVideoReader as PNG files.
For seekable sequences, seeks to each frame. For non-seekable, reads sequentially and captures target frames.
Source code in src/mosaic/media/video_io.py
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 | |