Skip to content

Commit 3a116a1

Browse files
committed
fix: finalize writer PR hardening and docs alignment
1 parent 2bb278b commit 3a116a1

18 files changed

Lines changed: 447 additions & 115 deletions

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

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

README.zh-CN.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
- **多种格式**:RGB、BGR、YUV(NV12/I420)及自动转换
3939
- **双语言接口**:✨ **新增完整纯 C 接口**,同时提供现代化 C++ API 和传统 C99 接口,支持各种项目集成和语言绑定
4040
- **视频文件播放**:🎬 使用与相机相同的 API 播放视频文件(MP4、AVI、MOV 等)- 支持 Windows 和 macOS
41+
- **视频写入 / 录制**:🎥 通过 `ccap::VideoWriter``ccap_video_writer_*` 或 CLI `--record` 将相机帧写入 MP4/MOV(Windows/macOS,需 `CCAP_ENABLE_VIDEO_WRITER=ON`
4142
- **命令行工具**:开箱即用的命令行工具,快速实现相机操作和视频处理 - 列出设备、捕获图像、实时预览、视频播放([文档](./docs/content/cli.zh.md)
4243
- **生产就绪**:完整测试套件,95%+ 精度验证
4344
- **虚拟相机支持**:在 Windows 上通过默认 DirectShow 路径兼容 OBS Virtual Camera 等工具
@@ -198,6 +199,8 @@ Windows 上现在默认使用 DirectShow。这样做的主要原因是 DirectSho
198199
199200
对大多数 Windows 应用来说,建议直接使用 `auto` 模式。ccap 会在两个后端之上统一公开的采集 API、帧朝向处理和输出像素格式转换,所以调用方通常不需要编写后端分支逻辑。
200201
202+
对于视频写入,后端选择是另一条独立维度:在 Windows 上,`VideoWriter` 固定使用 Media Foundation 写入链路,不受相机采集后端(`auto` / `dshow` / `msmf`)切换影响。
203+
201204
- 在支持 `extraInfo` 的 C++ / C 构造接口中传入 `"auto"``"msmf"``"dshow"``"backend=<value>"`
202205
- 设置环境变量 `CCAP_WINDOWS_BACKEND=auto|msmf|dshow`,对整个进程生效,包括 CLI 和 Rust 绑定。
203206
@@ -266,13 +269,17 @@ cmake --build .
266269
267270
# 视频预览并控制播放
268271
./ccap -i video.mp4 --preview --speed 1.0
272+
273+
# 将相机流录制为 MP4(Windows/macOS)
274+
./ccap -d 0 --record ./camera_capture.mp4 --timeout 5
269275
```
270276
271277
**主要功能:**
272278
- 📷 列出和选择相机设备
273279
- 🎯 捕获单张或多张图像
274280
- 👁️ 实时预览窗口(需要 GLFW)
275281
- 🎬 视频文件播放和帧提取
282+
- 🎥 将相机流录制为 MP4/MOV(`--record`
276283
- ⚙️ 配置分辨率、格式和帧率
277284
- 💾 保存为多种图像格式(JPEG、PNG、BMP 等)
278285
- ⏱️ 基于时长或数量的捕获模式
@@ -310,6 +317,7 @@ cmake --build .
310317
| [3-capture_callback](./examples/desktop/3-capture_callback.cpp) / [3-capture_callback_c](./examples/desktop/3-capture_callback_c.c) | 回调式捕获 | C++ / C | 桌面端 |
311318
| [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 渲染 | C++ / C | 桌面端 |
312319
| [5-play_video](./examples/desktop/5-play_video.cpp) / [5-play_video_c](./examples/desktop/5-play_video_c.c) | 视频文件播放 | C++ / C | Windows/macOS |
320+
| [6-record_video](./examples/desktop/6-record_video.cpp) | 使用 `VideoWriter` 录制视频 | C++ | Windows/macOS |
313321
| [iOS Demo](./examples/) | iOS 应用程序 | Objective-C++ | iOS |
314322
315323
### 构建和运行示例
@@ -429,6 +437,41 @@ enum class PixelFormat : uint32_t {
429437
};
430438
```
431439
440+
### 视频写入(Windows/macOS)
441+
442+
`CCAP_ENABLE_VIDEO_WRITER=ON` 时,可在 Windows/macOS 使用视频写入能力。
443+
444+
```cpp
445+
#include <ccap.h>
446+
#include <ccap_writer.h>
447+
448+
ccap::Provider provider;
449+
ccap::VideoWriter writer;
450+
451+
if (provider.open("", true)) {
452+
ccap::WriterConfig cfg;
453+
cfg.width = 1280;
454+
cfg.height = 720;
455+
cfg.frameRate = 30.0;
456+
cfg.codec = ccap::VideoCodec::H264;
457+
cfg.container = ccap::VideoFormat::MP4;
458+
459+
if (writer.open("camera_record.mp4", cfg)) {
460+
while (auto frame = provider.grab(3000)) {
461+
// timestampNs == 0 表示根据 frameRate 自动生成时间戳。
462+
writer.writeFrame(*frame, 0);
463+
}
464+
writer.close();
465+
}
466+
}
467+
```
468+
469+
说明:
470+
471+
- 写入输入像素格式支持 `NV12``I420``BGR24``BGRA32`
472+
- 写入链路会尊重 `VideoFrame::orientation`(包括 Windows RGB 常见的 `BottomToTop`)。
473+
- `CCAP_ENABLE_VIDEO_WRITER``CCAP_ENABLE_FILE_PLAYBACK` 为独立开关。
474+
432475
### 工具函数
433476
434477
```cpp
@@ -544,6 +587,20 @@ void ccap_provider_stop(CcapProvider* provider);
544587
bool ccap_provider_is_started(CcapProvider* provider);
545588
```
546589
590+
##### 视频写入 API(C)
591+
592+
```c
593+
CcapVideoWriter* ccap_video_writer_create(void);
594+
void ccap_video_writer_destroy(CcapVideoWriter* writer);
595+
bool ccap_video_writer_open(CcapVideoWriter* writer, const char* filePath, const CcapWriterConfig* config);
596+
bool ccap_video_writer_write_frame(CcapVideoWriter* writer, const CcapVideoFrameInfo* frameInfo, uint64_t timestampNs);
597+
void ccap_video_writer_close(CcapVideoWriter* writer);
598+
bool ccap_video_writer_is_opened(const CcapVideoWriter* writer);
599+
CcapVideoCodec ccap_video_writer_actual_codec(const CcapVideoWriter* writer);
600+
```
601+
602+
`timestampNs == 0` 会被视为“自动时间戳哨兵值”(按配置帧率推导),而不是一个字面上的时间轴时间戳。
603+
547604
##### 帧捕获和处理
548605
549606
```c
@@ -650,6 +707,7 @@ C 接口的详细使用说明和示例请参见:[C 接口文档](./docs/conten
650707
- 多后端测试(CPU、AVX2、Apple Accelerate、NEON)
651708
- 性能基准测试和精度验证
652709
- 像素格式转换 95%+ 精度
710+
- 视频写入回归测试(`ccap_video_writer_test`),覆盖 C++/C API、codec 回退、MOV 容器、`BottomToTop` 方向与转码时长校验
653711
654712
```bash
655713
./scripts/run_tests.sh

cli/args_parser.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ CLIOptions parseArgs(int argc, char* argv[]) {
352352
opts.showVersion = true;
353353
} else if (arg == "--verbose") {
354354
opts.verbose = true;
355+
opts.quiet = false;
356+
} else if (arg == "-q" || arg == "--quiet") {
357+
opts.quiet = true;
358+
opts.verbose = false;
355359
} else if (arg == "--json") {
356360
opts.jsonOutput = true;
357361
} else if (arg == "--schema-version") {

cli/args_parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct CLIOptions {
3434
bool listDevices = false;
3535
bool showDeviceInfo = false;
3636
bool verbose = false;
37+
bool quiet = false;
3738
bool jsonOutput = false;
3839
std::string schemaVersion = "1.0";
3940

cli/ccap_cli.cpp

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,10 @@ int main(int argc, char* argv[]) {
9191
// Set log level based on options
9292
if (opts.verbose) {
9393
ccap::setLogLevel(ccap::LogLevel::Verbose);
94+
} else if (opts.quiet) {
95+
ccap::setLogLevel(ccap::LogLevel::Error);
9496
} else {
95-
// Check if -q/--quiet was specified by looking at argv
96-
bool quietMode = false;
97-
for (int i = 1; i < argc; ++i) {
98-
std::string arg = argv[i];
99-
if (arg == "-q" || arg == "--quiet") {
100-
quietMode = true;
101-
break;
102-
}
103-
}
104-
105-
if (quietMode) {
106-
ccap::setLogLevel(ccap::LogLevel::Error);
107-
} else {
108-
ccap::setLogLevel(ccap::LogLevel::Info);
109-
}
97+
ccap::setLogLevel(ccap::LogLevel::Info);
11098
}
11199

112100
// Set error callback

0 commit comments

Comments
 (0)