You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
authored
feat: add video file writing support for macOS and Windows (#55)
* feat: add video file writing support for macOS and Windows
Add VideoWriter class to encode frames into MP4/MOV video files using
system frameworks only (AVAssetWriter on macOS, IMFMediaSink on Windows).
- C++ API (VideoWriter) and pure C API (ccap_video_writer_*)
- HEVC codec with automatic H.264 fallback
- Supports BGR24, BGRA32, I420, NV12 pixel formats
- New CMake option CCAP_ENABLE_VIDEO_WRITER (ON by default)
- 15 unit tests covering lifecycle, frame writing, codec fallback
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: restructure video writer to fix Windows build and address review comments
Major changes:
- Replace ccap_writer.mm with ccap_writer.cpp (pure C++ dispatch layer)
using factory function pattern instead of #include of .mm/.cpp files
- Compile platform implementations as separate source files (Apple-only
.mm, Windows-only .cpp) matching the existing file reader pattern
- Rewrite Windows implementation to use MFCreateSinkWriterFromURL
(correct API) instead of non-existent MFCreateMediaSinkForURL
- Guard #pragma comment(lib) behind _MSC_VER for MinGW compatibility
Review comment fixes:
- Respect WriterConfig::codec preference (try requested codec first)
- Replace usleep() with std::this_thread::sleep_for (portability)
- Use high-precision timescale (600000) for CMTime instead of truncating
to integer fps
- Add reportError() calls throughout (not just CCAP_LOG_E)
- Validate frame dimensions match configured output in writeFrame()
- Require even dimensions in open() for NV12 encoding
- Check m_mfInitialized in Windows open() before calling MF APIs
- Fix C API timestamp: pass resolved timestamp to writeFrame()
- Share NV12 conversion code via inline helpers in ccap_writer_imp.h
- Support I420/BGRA32 conversion on Windows (was BGR24/NV12 only)
- Guard playback validation in tests behind CCAP_ENABLE_FILE_PLAYBACK
- Zero-initialize VideoFrame structs in tests
- Use std::error_code overloads in test TearDown() for robustness
- Update frameRate docs to reflect actual behavior (default 30fps)
- Set sample duration on Windows for better seek/playback behavior
- Run format_all.sh
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: remove codecapi.h include to fix MinGW build
MinGW's codecapi.h has UUID template issues that cause compilation
errors. The header was unused - no codec API types are needed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add camera-to-video recording demo and CLI --record support
- examples/desktop/6-record_video.cpp: new demo that opens camera and
records ~5 seconds of frames to an MP4 file using ccap::VideoWriter.
Guarded with #ifdef CCAP_ENABLE_VIDEO_WRITER for non-supported platforms.
- cli: add --record <file> option to record camera frames to a video file.
Integrated into captureFrames() guarded by #ifdef CCAP_ENABLE_VIDEO_WRITER.
Warns if used without -c/--count or --timeout (would run indefinitely).
Warns if used with --video mode (not supported).
- .vscode/tasks.json: add 'Run ccap CLI --record camera (Debug/Release)'
tasks that record 5 seconds from the default camera to camera_capture.mp4.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: add Run 6-record_video tasks for Debug and Release
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: use steady_clock for video writer timestamps to avoid first-frame pause
Camera hardware PTS can have large startup gaps, causing the first frame
to display for ~1 second. Use std::chrono::steady_clock wall-clock time
instead of camera timestamps to generate PTS, ensuring frame intervals
reflect real grab timing. Also add transcode duration verification tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: respect VideoFrame::orientation in shared video writer NV12 conversion
- Shared convertFrameToNv12 helper now honors frame.orientation (TopToBottom/BottomToTop)
- DirectShow commonly yields BottomToTop RGB frames; writer was ignoring orientation and producing vertically flipped video
- Added comprehensive regression tests:
- SharedNv12ConversionRespectsBottomToTopOrientation (unit test for conversion helper)
- BottomToTopFramesRoundTripUpright (C++ API end-to-end test)
- BottomToTopFramesRoundTripUpright (C API end-to-end test)
- Fixed Windows writer close() to clear configuration state (fixes GetPropertiesAfterOpen test)
- Validated with live Windows camera capture: recorded video now matches original frame orientation
Files changed:
- src/ccap_writer_imp.h: orientation-aware row indexing for all frame formats (BGR24, BGRA32, NV12, I420)
- src/ccap_writer_windows.cpp: reset config on close()
- tests/test_video_writer.cpp: new orientation regression tests
- tests/CMakeLists.txt: expose src headers to writer tests
* fix: finalize writer PR hardening and docs alignment
* fix: address review feedback for video writer and CLI parsing
* docs/tests: address follow-up review comments
* cli: ensure --record triggers capture mode (include recordVideoPath in hasAction)
* fix(cli): propagate frame timestamps to VideoWriter; add preview+record support in runPreview
* test/cli: add regression tests and update CLI help and VSCode tasks for preview+record
* fix: remove unreachable null check on getFreeFrame() and document non-null guarantee
getFreeFrame() always allocates a new frame if the pool is empty, so it
can never return null. Remove the dead null-check branch in
FileReaderWindows::readLoop() and add a doc comment on the declaration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat: add C interface video recording example
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(writer): align auto bitrate with YouTube official recommended settings
Replace the simplistic pixels*4 formula with YouTube's official H.264
bitrate recommendations (support.google.com/youtube/answer/2853702).
Uses linear interpolation by pixel count between reference points
(720p, 1080p, 1440p, 4K), extrapolation above 4K, and 720p floor
for lower resolutions. HEVC targets ~60% of H.264 bitrate.
Also:
- Change default codec to H264 (better compatibility and performance)
- Change default bitRate to 0 (auto) so YouTube-based auto takes effect
- Remove unreachable null check on getFreeFrame() and document the
non-null guarantee on its declaration
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* ci: add video writer test execution to macOS and Windows CI
- Add --writer flag to run_tests.sh for running ccap_video_writer_test
- Add writer test step to macOS CI workflow (Release builds)
- Add writer test step to Windows CI workflow (VS2022/VS2026/MinGW Release)
- On Linux the writer test binary is not built (unsupported platform),
so the script gracefully skips without error
- Apply code formatting fixes from format_all.sh
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: README.md
+58Lines changed: 58 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -38,6 +38,7 @@ A high-performance, lightweight cross-platform camera capture library with hardw
38
38
-**Multiple Formats**: RGB, BGR, YUV (NV12/I420) with automatic conversion
39
39
-**Dual Language APIs**: ✨ **Complete Pure C Interface** - Both modern C++ API and traditional C99 interface for various project integration and language bindings
40
40
-**Video File Playback**: 🎬 Play video files (MP4, AVI, MOV, etc.) using the same API as camera capture - supports Windows and macOS
41
+
-**Video Writing / Recording**: 🎥 Write MP4/MOV files from camera frames via `ccap::VideoWriter`, `ccap_video_writer_*`, or CLI `--record` (Windows/macOS, `CCAP_ENABLE_VIDEO_WRITER=ON`)
41
42
-**CLI Tool**: Ready-to-use command-line tool for quick camera operations and video processing - list devices, capture images, real-time preview, video playback ([Documentation](./docs/content/cli.md))
42
43
-**Production Ready**: Comprehensive test suite with 95%+ accuracy validation
43
44
-**Virtual Camera Support**: Compatible with OBS Virtual Camera and similar tools through the default DirectShow path on Windows
@@ -235,6 +236,8 @@ On Windows, camera capture now uses DirectShow by default. This keeps OBS Virtua
235
236
236
237
For most Windows applications, staying in`auto` mode is recommended. ccap normalizes the public capture API, frame orientation handling, and output pixel-format conversion across both backends so callers usually do not need backend-specific code.
237
238
239
+
For video writing, backend selection is a separate axis: on Windows, `VideoWriter` uses Media Foundation's writer stack regardless of camera capture backend (`auto` / `dshow` / `msmf`).
240
+
238
241
- Pass `extraInfo` as `"auto"`, `"msmf"`, `"dshow"`, or `"backend=<value>"` in the C++/C constructors that accept it.
239
242
- Set the environment variable `CCAP_WINDOWS_BACKEND=auto|msmf|dshow` to affect the whole process, including the CLI and Rust bindings.
- 💾 Save images in various formats (JPEG, PNG, BMP, etc.)
310
317
- ⏱️ Duration-based or count-based capture modes
@@ -343,6 +350,7 @@ For complete CLI documentation, see [CLI Tool Guide](./docs/content/cli.md).
343
350
| [3-capture_callback](./examples/desktop/3-capture_callback.cpp) / [3-capture_callback_c](./examples/desktop/3-capture_callback_c.c) | Callback-based capture | C++ / C | Desktop |
344
351
| [4-example_with_glfw](./examples/desktop/4-example_with_glfw.cpp) / [4-example_with_glfw_c](./examples/desktop/4-example_with_glfw_c.c) | OpenGL rendering | C++ / C | Desktop |
345
352
| [5-play_video](./examples/desktop/5-play_video.cpp) / [5-play_video_c](./examples/desktop/5-play_video_c.c) | Video file playback | C++ / C | Windows/macOS |
353
+
| [6-record_video](./examples/desktop/6-record_video.cpp) | Video recording with `VideoWriter` | C++ | Windows/macOS |
`timestampNs == 0` is treated as an auto-timestamp sentinel (derived from configured frame rate), not a literal timeline timestamp.
634
+
578
635
##### Frame Capture and Processing
579
636
580
637
```c
@@ -729,6 +786,7 @@ Comprehensive test suite with 50+ test cases covering all functionality:
729
786
- Multi-backend testing (CPU, AVX2, Apple Accelerate, NEON)
730
787
- Performance benchmarks and accuracy validation
731
788
- 95%+ precision for pixel format conversions
789
+
- Video writer regression tests (`ccap_video_writer_test`) covering C++ and C APIs, codec fallback, MOV container, `BottomToTop` orientation, and transcode duration checks
0 commit comments