Skip to content

Commit e107a37

Browse files
wysaidclaudeCopilot
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>
1 parent 77ec01b commit e107a37

40 files changed

Lines changed: 3485 additions & 129 deletions

.github/workflows/macos-build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ jobs:
132132
133133
# Run video file playback tests (Release only)
134134
./run_tests.sh --video --skip-build
135+
136+
# Run video writer tests (Release only, macOS/Windows)
137+
./run_tests.sh --writer --skip-build
135138
fi
136139
137140
- name: Upload artifacts

.github/workflows/windows-build.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ jobs:
207207
208208
# Run video file playback tests (Release only)
209209
./run_tests.sh --video --skip-build
210+
211+
# Run video writer tests (Release only, macOS/Windows)
212+
./run_tests.sh --writer --skip-build
210213
fi
211214
212215
- name: Upload artifacts
@@ -468,6 +471,9 @@ jobs:
468471
469472
# Run video file playback tests (Release only)
470473
./run_tests.sh --video --skip-build
474+
475+
# Run video writer tests (Release only, macOS/Windows)
476+
./run_tests.sh --writer --skip-build
471477
fi
472478
473479
- name: Upload artifacts
@@ -631,6 +637,9 @@ jobs:
631637
632638
# Run video file playback tests (Release only)
633639
./run_tests.sh --video --skip-build
640+
641+
# Run video writer tests (Release only, macOS/Windows)
642+
./run_tests.sh --writer --skip-build
634643
fi
635644
636645
- name: Upload artifacts

.vscode/tasks.json

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,58 @@
11361136
"problemMatcher": "$msCompile"
11371137
}
11381138
},
1139+
{
1140+
"label": "Run 6-record_video (Debug)",
1141+
"type": "shell",
1142+
"command": "bash",
1143+
"args": [
1144+
"-l",
1145+
"-c",
1146+
"( if [[ $(pwd) =~ ^/mnt ]]; then ./6-record_video.exe; else ./6-record_video; fi )"
1147+
],
1148+
"options": {
1149+
"cwd": "${workspaceFolder}/build/Debug"
1150+
},
1151+
"group": "build",
1152+
"problemMatcher": "$gcc",
1153+
"dependsOn": [
1154+
"Build Project (Debug)"
1155+
],
1156+
"windows": {
1157+
"command": ".\\6-record_video.exe",
1158+
"args": [],
1159+
"options": {
1160+
"cwd": "${workspaceFolder}/build/Debug"
1161+
},
1162+
"problemMatcher": "$msCompile"
1163+
}
1164+
},
1165+
{
1166+
"label": "Run 6-record_video (Release)",
1167+
"type": "shell",
1168+
"command": "bash",
1169+
"args": [
1170+
"-l",
1171+
"-c",
1172+
"( if [[ $(pwd) =~ ^/mnt ]]; then ./6-record_video.exe; else ./6-record_video; fi )"
1173+
],
1174+
"options": {
1175+
"cwd": "${workspaceFolder}/build/Release"
1176+
},
1177+
"group": "build",
1178+
"problemMatcher": "$gcc",
1179+
"dependsOn": [
1180+
"Build Project (Release)"
1181+
],
1182+
"windows": {
1183+
"command": ".\\6-record_video.exe",
1184+
"args": [],
1185+
"options": {
1186+
"cwd": "${workspaceFolder}/build/Release"
1187+
},
1188+
"problemMatcher": "$msCompile"
1189+
}
1190+
},
11391191
{
11401192
"label": "Run ccap CLI --help (Debug)",
11411193
"type": "shell",
@@ -1547,6 +1599,62 @@
15471599
},
15481600
"problemMatcher": "$msCompile"
15491601
}
1602+
},
1603+
{
1604+
"label": "Run ccap CLI --record camera (Debug)",
1605+
"type": "shell",
1606+
"command": "bash",
1607+
"args": [
1608+
"-l",
1609+
"-c",
1610+
"( if [[ $(pwd) =~ ^/mnt ]]; then ./ccap.exe -w 1280 -H 720 --record ./camera_capture.mp4 --timeout 5 --preview; else ./ccap --record ./camera_capture.mp4 -w 1280 -H 720 --timeout 5 --preview; fi )"
1611+
],
1612+
"options": {
1613+
"cwd": "${workspaceFolder}/build/Debug"
1614+
},
1615+
"group": "build",
1616+
"problemMatcher": "$gcc",
1617+
"dependsOn": [
1618+
"Config: Enable CLI Tool",
1619+
"Build Project (Debug)"
1620+
],
1621+
"dependsOrder": "sequence",
1622+
"windows": {
1623+
"command": ".\\ccap.exe",
1624+
"args": ["--record", ".\\camera_capture.mp4", "-w", "1280", "-H", "720", "--timeout", "5", "--preview"],
1625+
"options": {
1626+
"cwd": "${workspaceFolder}/build/Debug"
1627+
},
1628+
"problemMatcher": "$msCompile"
1629+
}
1630+
},
1631+
{
1632+
"label": "Run ccap CLI --record camera (Release)",
1633+
"type": "shell",
1634+
"command": "bash",
1635+
"args": [
1636+
"-l",
1637+
"-c",
1638+
"( if [[ $(pwd) =~ ^/mnt ]]; then ./ccap.exe -w 1280 -H 720 --record ./camera_capture.mp4 --timeout 5 --preview; else ./ccap --record ./camera_capture.mp4 -w 1280 -H 720 --timeout 5 --preview; fi )"
1639+
],
1640+
"options": {
1641+
"cwd": "${workspaceFolder}/build/Release"
1642+
},
1643+
"group": "build",
1644+
"problemMatcher": "$gcc",
1645+
"dependsOn": [
1646+
"Config: Enable CLI Tool",
1647+
"Build Project (Release)"
1648+
],
1649+
"dependsOrder": "sequence",
1650+
"windows": {
1651+
"command": ".\\ccap.exe",
1652+
"args": ["--record", ".\\camera_capture.mp4", "-w", "1280", "-H", "720", "--timeout", "5", "--preview"],
1653+
"options": {
1654+
"cwd": "${workspaceFolder}/build/Release"
1655+
},
1656+
"problemMatcher": "$msCompile"
1657+
}
15501658
}
15511659
]
15521660
}

BUILD_AND_INSTALL.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ make install
5555
- `CCAP_BUILD_EXAMPLES`: Build example applications (default: ON for root project)
5656
- `CCAP_BUILD_TESTS`: Build unit tests (default: OFF)
5757
- `CCAP_NO_LOG`: Disable logging functionality (default: OFF)
58+
- `CCAP_ENABLE_VIDEO_WRITER`: Enable video writer support (`ccap::VideoWriter`, C writer API, CLI `--record`) on Windows/macOS (default: ON)
5859

5960
### macOS Universal Binary Build
6061

@@ -205,6 +206,7 @@ build/universal/ # Contains x86_64 + arm64 universal binary
205206
- `CCAP_INSTALL`: Enable install target (default: ON)
206207
- `CCAP_BUILD_EXAMPLES`: Build examples (default: OFF when used as subproject)
207208
- `CCAP_BUILD_TESTS`: Build tests (default: OFF when used as subproject)
209+
- `CCAP_ENABLE_VIDEO_WRITER`: Enable video writing support (Windows/macOS only, default: ON)
208210

209211
### Advanced Usage
210212

@@ -250,6 +252,15 @@ git clean -fdx install/
250252

251253
**Note**: Video file playback is currently supported on Windows and macOS only. Linux video playback support may be added in a future release.
252254

255+
### Video Writer Support Matrix
256+
257+
- ✅ Windows: supported (`CCAP_ENABLE_VIDEO_WRITER=ON`)
258+
- ✅ macOS: supported (`CCAP_ENABLE_VIDEO_WRITER=ON`)
259+
- ❌ Linux: not supported
260+
- ❌ iOS: not supported
261+
262+
`CCAP_ENABLE_VIDEO_WRITER` is independent from `CCAP_ENABLE_FILE_PLAYBACK`.
263+
253264
## Version Information
254265

255266
Current version: 1.7.2

CMakeLists.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,31 @@ if (NOT CCAP_ENABLE_FILE_PLAYBACK)
132132
message(STATUS "ccap: Video file playback support disabled")
133133
endif ()
134134

135+
# Video writer sources (Windows and macOS only)
136+
option(CCAP_ENABLE_VIDEO_WRITER "Enable video file writing support (Windows/macOS)" ON)
137+
if (CCAP_ENABLE_VIDEO_WRITER AND (APPLE OR WIN32))
138+
# Exclude writer sources from main glob to avoid double-compilation
139+
list(FILTER LIB_SOURCE EXCLUDE REGEX ".*ccap_writer_apple.*")
140+
list(FILTER LIB_SOURCE EXCLUDE REGEX ".*ccap_writer_windows.*")
141+
list(FILTER LIB_SOURCE EXCLUDE REGEX ".*ccap_writer_c\..*$")
142+
list(FILTER LIB_SOURCE EXCLUDE REGEX ".*ccap_writer\..*$")
143+
list(APPEND LIB_SOURCE
144+
${CMAKE_CURRENT_SOURCE_DIR}/src/ccap_writer.cpp
145+
${CMAKE_CURRENT_SOURCE_DIR}/src/ccap_writer_c.cpp
146+
)
147+
if (APPLE)
148+
list(APPEND LIB_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/ccap_writer_apple.mm)
149+
elseif (WIN32)
150+
list(APPEND LIB_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/ccap_writer_windows.cpp)
151+
endif ()
152+
message(STATUS "ccap: Video file writing support enabled")
153+
else ()
154+
message(STATUS "ccap: Video file writing support disabled (unsupported platform or disabled)")
155+
endif ()
156+
135157
if (APPLE)
136158
file(GLOB LIB_SOURCE_MAC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.mm)
159+
list(FILTER LIB_SOURCE_MAC EXCLUDE REGEX ".*ccap_writer_apple.*")
137160
message(STATUS "ccap: Using Objective-C++ for macOS: ${LIB_SOURCE_MAC}")
138161
list(APPEND LIB_SOURCE ${LIB_SOURCE_MAC})
139162
endif ()
@@ -207,6 +230,10 @@ else ()
207230
message(STATUS "ccap: Video file playback support disabled")
208231
endif ()
209232

233+
if (CCAP_ENABLE_VIDEO_WRITER AND (APPLE OR WIN32))
234+
target_compile_definitions(ccap PUBLIC CCAP_ENABLE_VIDEO_WRITER=1)
235+
endif ()
236+
210237
# Configure shared library export definitions
211238
if (CCAP_BUILD_SHARED)
212239
target_compile_definitions(ccap PUBLIC CCAP_SHARED=1)

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ A high-performance, lightweight cross-platform camera capture library with hardw
3838
- **Multiple Formats**: RGB, BGR, YUV (NV12/I420) with automatic conversion
3939
- **Dual Language APIs**: ✨ **Complete Pure C Interface** - Both modern C++ API and traditional C99 interface for various project integration and language bindings
4040
- **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`)
4142
- **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))
4243
- **Production Ready**: Comprehensive test suite with 95%+ accuracy validation
4344
- **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
235236
236237
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.
237238
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+
238241
- Pass `extraInfo` as `"auto"`, `"msmf"`, `"dshow"`, or `"backend=<value>"` in the C++/C constructors that accept it.
239242
- Set the environment variable `CCAP_WINDOWS_BACKEND=auto|msmf|dshow` to affect the whole process, including the CLI and Rust bindings.
240243
@@ -297,6 +300,9 @@ cmake --build .
297300
298301
# Video preview with playback controls
299302
./ccap -i video.mp4 --preview --speed 1.0
303+
304+
# Record camera stream to MP4 (Windows/macOS)
305+
./ccap -d 0 --record ./camera_capture.mp4 --timeout 5
300306
```
301307
302308
**Key Features:**
@@ -305,6 +311,7 @@ cmake --build .
305311
- 🎯 Capture single or multiple images
306312
- 👁️ Real-time preview window (with GLFW)
307313
- 🎬 Video file playback and frame extraction
314+
- 🎥 Record camera stream to MP4/MOV (`--record`)
308315
- ⚙️ Configure resolution, format, and frame rate
309316
- 💾 Save images in various formats (JPEG, PNG, BMP, etc.)
310317
- ⏱️ Duration-based or count-based capture modes
@@ -343,6 +350,7 @@ For complete CLI documentation, see [CLI Tool Guide](./docs/content/cli.md).
343350
| [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 |
344351
| [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 |
345352
| [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 |
346354
| [iOS Demo](./examples/) | iOS application | Objective-C++ | iOS |
347355
348356
### Build and Run Examples
@@ -460,6 +468,41 @@ enum class PixelFormat : uint32_t {
460468
};
461469
```
462470
471+
### Video Writing (Windows/macOS)
472+
473+
Video writing is available on Windows and macOS when `CCAP_ENABLE_VIDEO_WRITER=ON`.
474+
475+
```cpp
476+
#include <ccap.h>
477+
#include <ccap_writer.h>
478+
479+
ccap::Provider provider;
480+
ccap::VideoWriter writer;
481+
482+
if (provider.open("", true)) {
483+
ccap::WriterConfig cfg;
484+
cfg.width = 1280;
485+
cfg.height = 720;
486+
cfg.frameRate = 30.0;
487+
cfg.codec = ccap::VideoCodec::H264;
488+
cfg.container = ccap::VideoFormat::MP4;
489+
490+
if (writer.open("camera_record.mp4", cfg)) {
491+
while (auto frame = provider.grab(3000)) {
492+
// timestampNs == 0 means auto timestamp generation from frameRate.
493+
writer.writeFrame(*frame, 0);
494+
}
495+
writer.close();
496+
}
497+
}
498+
```
499+
500+
Notes:
501+
502+
- Writer input supports `NV12`, `I420`, `BGR24`, and `BGRA32`.
503+
- `VideoFrame::orientation` is honored by the writer path (including `BottomToTop` frames common on Windows RGB capture).
504+
- `CCAP_ENABLE_VIDEO_WRITER` is independent from `CCAP_ENABLE_FILE_PLAYBACK`.
505+
463506
### Utility Functions
464507
465508
```cpp
@@ -575,6 +618,20 @@ void ccap_provider_stop(CcapProvider* provider);
575618
bool ccap_provider_is_started(CcapProvider* provider);
576619
```
577620
621+
##### Video Writer API (C)
622+
623+
```c
624+
CcapVideoWriter* ccap_video_writer_create(void);
625+
void ccap_video_writer_destroy(CcapVideoWriter* writer);
626+
bool ccap_video_writer_open(CcapVideoWriter* writer, const char* filePath, const CcapWriterConfig* config);
627+
bool ccap_video_writer_write_frame(CcapVideoWriter* writer, const CcapVideoFrameInfo* frameInfo, uint64_t timestampNs);
628+
void ccap_video_writer_close(CcapVideoWriter* writer);
629+
bool ccap_video_writer_is_opened(const CcapVideoWriter* writer);
630+
CcapVideoCodec ccap_video_writer_actual_codec(const CcapVideoWriter* writer);
631+
```
632+
633+
`timestampNs == 0` is treated as an auto-timestamp sentinel (derived from configured frame rate), not a literal timeline timestamp.
634+
578635
##### Frame Capture and Processing
579636
580637
```c
@@ -729,6 +786,7 @@ Comprehensive test suite with 50+ test cases covering all functionality:
729786
- Multi-backend testing (CPU, AVX2, Apple Accelerate, NEON)
730787
- Performance benchmarks and accuracy validation
731788
- 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
732790
733791
```bash
734792
./scripts/run_tests.sh

0 commit comments

Comments
 (0)