Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 23, 2025

Extends ccap::Provider to open video files with the same API used for camera capture, matching OpenCV's cv::VideoCapture pattern.

API Additions

  • PropertyName::Duration, CurrentTime, PlaybackSpeed, FrameCount, CurrentFrameIndex for file playback control
  • ErrorCode::FileOpenFailed, UnsupportedVideoFormat, SeekFailed
  • Provider::isFileMode() / ccap_provider_is_file_mode() to detect mode

Path Detection

  • looksLikeFilePath() checks for path separators or video extensions (.mp4, .mov, .avi, etc.)
  • Falls through to camera device lookup if not a file path

Platform Implementations

  • macOS: AVAssetReader with NV12/BGRA32 output, frame rate control, seek support
  • Windows: IMFSourceReader with NV12/RGB32 output, frame rate control, seek support

Usage

ccap::Provider provider;
provider.open("video.mp4", true);  // Auto-detects file mode, starts playback

double duration = provider.get(ccap::PropertyName::Duration);
provider.set(ccap::PropertyName::CurrentTime, 30.0);  // Seek
provider.set(ccap::PropertyName::PlaybackSpeed, 2.0); // 2x speed

while (auto frame = provider.grab()) {
    // Same frame handling as camera mode
}

Examples

  • examples/desktop/5-play_video.cpp (C++)
  • examples/desktop/5-play_video_c.c (C)
Original prompt

📋 任务:为 CameraCapture 添加视频文件播放支持

目标

扩展 ccap::Provider 类,使其能够像 OpenCV cv::VideoCapture 一样,用统一的接口既打开摄像头,也打开视频文件。

支持平台:Windows 和 macOS(不实现 Linux)


核心设计

统一接口(与 OpenCV cv::VideoCapture 相同)

ccap::Provider provider;

// 打开摄像头(现有功能)
provider.open("");              // 默认摄像头
provider.open("USB Camera");    // 摄像头名称

// 打开视频文件(新增功能,自动识别)
provider.open("video.mp4");
provider.open("/path/to/video.mov");

// 统一的帧获取方式
auto frame = provider.grab();

// 使用 get/set 访问属性(文件模式特有)
double duration = provider.get(PropertyName::Duration);
provider.set(PropertyName::CurrentTime, 30.0);  // Seek
provider.set(PropertyName::PlaybackSpeed, 2.0); // 倍速

路径识别

  • 包含 /\ → 文件
  • .mp4 .mov .avi 等结尾 → 文件
  • 文件存在性检查 → 文件
  • 其他 → 摄像头设备名称

主要改动

1. API 扩展(参考 cv::VideoCapture 设计)

include/ccap_def.hPropertyName 枚举中添加文件播放属性:

enum class PropertyName {
    // ... 现有属性 ...
    
    // 文件播放属性(仅文件模式有效)
    Duration = 0x50001,           // 视频总时长(秒),只读
    CurrentTime = 0x50002,        // 当前播放位置(秒),可读写(用于 seek)
    PlaybackSpeed = 0x50003,      // 播放速度倍率,可读写(1.0 = 正常)
    FrameCount = 0x50004,         // 总帧数,只读
    CurrentFrameIndex = 0x50005,  // 当前帧索引,可读写(用于按帧 seek)
};

include/ccap_core.hProvider 类中仅添加一个判断方法:

bool isFileMode() const;  // 判断是否为文件播放模式

使用方式(与 OpenCV 一致):

// Seek 到指定时间
provider.set(PropertyName::CurrentTime, 30.0);  // 跳转到 30 秒

// 获取视频时长
double duration = provider.get(PropertyName::Duration);

// 设置播放速度
provider.set(PropertyName::PlaybackSpeed, 2.0);  // 2倍速

// 按帧 seek
provider.set(PropertyName::CurrentFrameIndex, 100);

对应的 C 接口仅需在 include/ccap_c.h 添加:

bool ccap_provider_is_file_mode(CcapProvider* provider);

2. 内部架构

修改 src/ccap_imp.h

class ProviderImp {
    // 保持现有的 open() 虚函数不变,由子类内部判断设备/文件
    virtual bool open(std::string_view deviceNameOrFilePath) = 0;
    
    // 现有的 set/get 方法内部扩展,支持文件属性
    // bool set(PropertyName prop, double value);  // 已存在
    // double get(PropertyName prop);              // 已存在
    
    virtual bool isFileMode() const { return false; }
    
protected:
    bool m_isFileMode = false;
};

实现方式

  • 每个平台实现类在自己的 open() 方法中内部判断是设备还是文件(根据路径特征)
  • 判断逻辑作为实现细节,不暴露接口
  • 对外只有统一的 open() 接口,完全透明

3. macOS 实现

新增文件

  • src/ccap_file_reader_apple.h - 文件读取器接口
  • src/ccap_file_reader_apple.mm - 使用 AVAssetReader 实现

修改文件

  • src/ccap_imp_apple.h - 添加 CcapFileReaderApple* m_fileReader 成员
  • src/ccap_imp_apple.mm - 在 open() 方法中判断并处理文件/设备

实现逻辑

bool ProviderApple::open(std::string_view deviceNameOrFilePath) {
    // 内部判断:包含路径分隔符或文件扩展名 → 文件
    bool isFile = (deviceNameOrFilePath.find('/') != npos) ||
                  (deviceNameOrFilePath.find('\\') != npos) ||
                  deviceNameOrFilePath.ends_with(".mp4") ||
                  deviceNameOrFilePath.ends_with(".mov");
    
    if (isFile) {
        m_isFileMode = true;
        return initFileReader(deviceNameOrFilePath);
    } else {
        m_isFileMode = false;
        return initCameraCapture(deviceNameOrFilePath);
    }
}

技术栈AVAssetAVAssetReaderCMSampleBufferccap::VideoFrame

4. Windows 实现

新增文件

  • src/ccap_file_reader_windows.h - 文件读取器接口
  • src/ccap_file_reader_windows.cpp - 使用 Media Foundation 实现

修改文件

  • src/ccap_imp_windows.h - 添加 WindowsFileReader* m_fileReader 成员
  • src/ccap_imp_windows.cpp - 在 open() 方法中判断并处理文件/设备

实现逻辑

bool ProviderDirectShow::open(std::string_view deviceNameOrFilePath) {
    // 内部判断:包含路径分隔符或文件扩展名 → 文件
    bool isFile = (deviceNameOrFilePath.find('/') != npos) ||
                  (deviceNameOrFilePath.find('\\') != npos) ||
                  deviceNameOrFilePath.ends_with(".mp4") ||
                  deviceNameOrFilePath.ends_with(".avi");
    
    if (isFile) {
        m_isFileMode = true;
        return initFileReader(deviceNameOrFilePath);
    } else {
        m_isFileMode = false;
        return initCameraCapture(deviceNameOrFilePath);
    }
}

技术栈IMFSourceReaderIMFSampleIMFMediaBufferccap::VideoFrame

5. 帧时序控制

文件播放需要控制帧率(不同于摄像头的实时采集):

  • 根据视频的 PTS (Presentation Time Stamp) 计算帧间延迟
  • 支持倍速播放(修改延迟时间)
  • grab() 方法中实现时序控制

6. 示例和测试

新增示例

  • examples/desktop/5-play_video.cpp - C++ 示例
  • examples/desktop/5-play_video_c.c - C 示例

新增测试

  • tests/test_file_playback.cpp - 基础播放测试
  • tests/test_file_seek.cpp - Seek 功能测试
  • tests/test_unified_api.cpp - 统一接口测试

7. 错误处理

include/ccap_def.hErrorCode 枚举中添加:

FileOpenFailed = 0x5001,
UnsupportedVideoFormat = 0x5002,
SeekFailed = 0x5003,

关键实现要求

  1. 输出格式统一:文件解码输出 NV12 格式(与摄像头一致)
  2. 代码复用:复用现有的格式转换、内存管理、帧池逻辑
  3. 硬件加速:使用系统 API 的硬件解码能力
  4. 向后兼容:不破坏现有 API,所有现有代码无需修改
  5. 内存安全:使用 RAII,Windows 需要正确管理 COM 引用计数
  6. 错误报告:使用 reportError() 和全局 ErrorCallback

成功标准

provider.open("video.mp4") 成功打开文件
provider.grab() 返回视频帧,API 与摄像头完全一致
✅ 使用 `...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI changed the title [WIP] Add video file playback support to CameraCapture Add video file playback support for Windows and macOS Dec 23, 2025
Copilot AI requested a review from LeeGoDamn December 23, 2025 12:57
@wysaid wysaid force-pushed the copilot/add-video-file-support branch from e49b635 to d78540c Compare December 23, 2025 18:20
Copilot AI and others added 3 commits December 24, 2025 15:35
Co-authored-by: LeeGoDamn <243561453+LeeGoDamn@users.noreply.github.com>
Co-authored-by: LeeGoDamn <243561453+LeeGoDamn@users.noreply.github.com>
… string_view conversion

Co-authored-by: LeeGoDamn <243561453+LeeGoDamn@users.noreply.github.com>
@wysaid wysaid force-pushed the copilot/add-video-file-support branch from d78540c to 51196e9 Compare December 24, 2025 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants