#include "capture_video_capturer.h" #include "../base/logger.h" #include "../base/media_common.h" #include #include #ifdef _WIN32 #include #include #pragma comment(lib, "strmiids.lib") #endif extern "C" { #include #include #include #include #include } namespace av { namespace capture { VideoCapturer::VideoCapturer() : videoParams_(CapturerType::VIDEO_CAMERA) { AV_LOGGER_DEBUG("创建视频采集器"); // 注册设备 avdevice_register_all(); } VideoCapturer::~VideoCapturer() { close(); AV_LOGGER_DEBUG("视频采集器已销毁"); } ErrorCode VideoCapturer::initialize(const CapturerParams& params) { if (params.mediaType != MediaType::VIDEO) { AV_LOGGER_ERROR("参数媒体类型不是视频"); return ErrorCode::INVALID_ARGUMENT; } videoParams_ = static_cast(params); if (!validateParams(videoParams_)) { return ErrorCode::INVALID_PARAMS; } ErrorCode result = ErrorCode::OK; if (videoParams_.type == CapturerType::VIDEO_CAMERA) { result = initializeCamera(); } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { result = initializeScreen(); } else if (videoParams_.type == CapturerType::VIDEO_WINDOW) { result = initializeWindow(); } else { AV_LOGGER_ERROR("不支持的视频采集器类型"); return ErrorCode::NOT_SUPPORTED; } if (result == ErrorCode::OK) { setState(CapturerState::INITIALIZED); AV_LOGGER_INFOF("视频采集器初始化成功: {}x{}@{}fps", videoParams_.width, videoParams_.height, videoParams_.fps); } return result; } ErrorCode VideoCapturer::start() { std::lock_guard lock(captureMutex_); if (getState() != CapturerState::INITIALIZED) { AV_LOGGER_ERROR("采集器状态无效,无法启动"); return ErrorCode::INVALID_STATE; } shouldStop_ = false; // 启动采集线程 try { captureThread_ = std::thread(&VideoCapturer::captureThreadFunc, this); setState(CapturerState::STARTED); AV_LOGGER_INFO("视频采集已启动"); return ErrorCode::OK; } catch (const std::exception& e) { AV_LOGGER_ERRORF("启动采集线程失败: {}", e.what()); return ErrorCode::THREAD_ERROR; } } ErrorCode VideoCapturer::stop() { std::lock_guard lock(captureMutex_); if (getState() != CapturerState::STARTED) { return ErrorCode::OK; } shouldStop_ = true; // 唤醒暂停的线程 { std::lock_guard pauseLock(pauseMutex_); paused_ = false; pauseCondition_.notify_all(); } // 等待线程结束 if (captureThread_.joinable()) { captureThread_.join(); } setState(CapturerState::STOPPED); AV_LOGGER_INFO("视频采集已停止"); return ErrorCode::OK; } ErrorCode VideoCapturer::pause() { if (getState() != CapturerState::STARTED) { return ErrorCode::INVALID_STATE; } paused_ = true; AV_LOGGER_INFO("视频采集已暂停"); return ErrorCode::OK; } ErrorCode VideoCapturer::resume() { if (getState() != CapturerState::STARTED) { return ErrorCode::INVALID_STATE; } { std::lock_guard lock(pauseMutex_); paused_ = false; pauseCondition_.notify_all(); } AV_LOGGER_INFO("视频采集已恢复"); return ErrorCode::OK; } ErrorCode VideoCapturer::reset() { ErrorCode result = stop(); if (result != ErrorCode::OK) { return result; } // 清空帧队列 { std::lock_guard lock(queueMutex_); while (!frameQueue_.empty()) { frameQueue_.pop(); } } resetStats(); setState(CapturerState::INITIALIZED); AV_LOGGER_INFO("视频采集器已重置"); return ErrorCode::OK; } ErrorCode VideoCapturer::close() { stop(); // 清理资源 cleanupConverter(); if (codecCtx_) { avcodec_free_context(&codecCtx_); codecCtx_ = nullptr; } if (formatCtx_) { avformat_close_input(&formatCtx_); formatCtx_ = nullptr; } codec_ = nullptr; videoStreamIndex_ = -1; setState(CapturerState::IDLE); AV_LOGGER_INFO("视频采集器已关闭"); return ErrorCode::OK; } std::vector VideoCapturer::getAvailableDevices() const { std::vector devices; auto deviceInfos = getDetailedDeviceInfo(); for (const auto& info : deviceInfos) { devices.push_back(info.name); } return devices; } std::string VideoCapturer::getCurrentDevice() const { return videoParams_.deviceName; } std::vector VideoCapturer::getDetailedDeviceInfo() const { std::lock_guard lock(deviceCacheMutex_); if (!devicesCached_) { if (videoParams_.type == CapturerType::VIDEO_CAMERA) { cachedDevices_ = enumerateCameras(); } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { cachedDevices_ = enumerateScreens(); } else if (videoParams_.type == CapturerType::VIDEO_WINDOW) { cachedDevices_ = enumerateWindows(); } devicesCached_ = true; } return cachedDevices_; } ErrorCode VideoCapturer::setVideoParams(int width, int height, int fps) { if (getState() == CapturerState::STARTED) { AV_LOGGER_ERROR("无法在采集过程中修改参数"); return ErrorCode::INVALID_STATE; } videoParams_.width = width; videoParams_.height = height; videoParams_.fps = fps; AV_LOGGER_INFOF("视频参数已更新: {}x{}@{}fps", width, height, fps); return ErrorCode::OK; } ErrorCode VideoCapturer::setPixelFormat(AVPixelFormat format) { if (getState() == CapturerState::STARTED) { AV_LOGGER_ERROR("无法在采集过程中修改像素格式"); return ErrorCode::INVALID_STATE; } videoParams_.pixelFormat = format; AV_LOGGER_INFOF("像素格式已更新: {}", av_get_pix_fmt_name(format)); return ErrorCode::OK; } VideoCaptureParams VideoCapturer::getCurrentParams() const { return videoParams_; } bool VideoCapturer::validateParams(const CapturerParams& params) { const auto& videoParams = static_cast(params); if (videoParams.width <= 0 || videoParams.height <= 0) { AV_LOGGER_ERROR("视频分辨率无效"); return false; } if (videoParams.fps <= 0 || videoParams.fps > 120) { AV_LOGGER_ERROR("帧率无效"); return false; } if (videoParams.type == CapturerType::VIDEO_CAMERA) { if (videoParams.cameraIndex < 0) { AV_LOGGER_ERROR("摄像头索引无效"); return false; } } else if (videoParams.type == CapturerType::VIDEO_SCREEN) { if (videoParams.screenIndex < 0) { AV_LOGGER_ERROR("屏幕索引无效"); return false; } } else if (videoParams.type == CapturerType::VIDEO_WINDOW) { if (videoParams.windowTitle.empty() && !videoParams.windowHandle) { AV_LOGGER_ERROR("窗口标题和窗口句柄都为空"); return false; } } return true; } ErrorCode VideoCapturer::initializeCamera() { AV_LOGGER_INFOF("初始化摄像头采集器: 索引={}", videoParams_.cameraIndex); #ifdef _WIN32 return setupDirectShowCamera(); #elif defined(__linux__) return setupV4L2Camera(); #elif defined(__APPLE__) return setupAVFoundationCamera(); #else AV_LOGGER_ERROR("当前平台不支持摄像头采集"); return ErrorCode::NOT_SUPPORTED; #endif } ErrorCode VideoCapturer::initializeScreen() { AV_LOGGER_INFOF("初始化屏幕录制: 索引={}", videoParams_.screenIndex); #ifdef _WIN32 return setupGDIScreenCapture(); #elif defined(__linux__) return setupX11ScreenCapture(); #elif defined(__APPLE__) return setupCoreGraphicsScreenCapture(); #else AV_LOGGER_ERROR("当前平台不支持屏幕录制"); return ErrorCode::NOT_SUPPORTED; #endif } ErrorCode VideoCapturer::initializeWindow() { AV_LOGGER_INFOF("初始化窗口采集: 标题={}", videoParams_.windowTitle); #ifdef _WIN32 return setupGDIWindowCapture(); #else AV_LOGGER_ERROR("当前平台不支持窗口采集"); return ErrorCode::NOT_SUPPORTED; #endif } ErrorCode VideoCapturer::openInputDevice() { const AVInputFormat* inputFormat = getPlatformInputFormat(); if (!inputFormat) { AV_LOGGER_ERROR("获取输入格式失败"); return ErrorCode::NOT_SUPPORTED; } std::string deviceName = getPlatformDeviceName(); if (deviceName.empty()) { AV_LOGGER_ERROR("获取设备名称失败"); return ErrorCode::DEVICE_NOT_FOUND; } AV_LOGGER_INFOF("打开输入设备: {} (格式: {})", deviceName, inputFormat->name); // 设置输入选项 AVDictionary* options = nullptr; // 设置视频参数 av_dict_set(&options, "video_size", (std::to_string(videoParams_.width) + "x" + std::to_string(videoParams_.height)).c_str(), 0); av_dict_set(&options, "framerate", std::to_string(videoParams_.fps).c_str(), 0); if (videoParams_.type == CapturerType::VIDEO_SCREEN) { // 屏幕录制特定选项 if (videoParams_.captureCursor) { av_dict_set(&options, "draw_mouse", "1", 0); } if (videoParams_.offsetX != 0 || videoParams_.offsetY != 0) { av_dict_set(&options, "offset_x", std::to_string(videoParams_.offsetX).c_str(), 0); av_dict_set(&options, "offset_y", std::to_string(videoParams_.offsetY).c_str(), 0); } } else if (videoParams_.type == CapturerType::VIDEO_WINDOW) { // 窗口采集特定选项 if (videoParams_.captureCursor) { av_dict_set(&options, "draw_mouse", "1", 0); } if (videoParams_.offsetX != 0 || videoParams_.offsetY != 0) { av_dict_set(&options, "offset_x", std::to_string(videoParams_.offsetX).c_str(), 0); av_dict_set(&options, "offset_y", std::to_string(videoParams_.offsetY).c_str(), 0); } } // 打开输入 int ret = avformat_open_input(&formatCtx_, deviceName.c_str(), inputFormat, &options); av_dict_free(&options); if (ret < 0) { AV_LOGGER_ERRORF("打开输入设备失败: {} (设备: {})", ffmpeg_utils::errorToString(ret), deviceName); return static_cast(ret); } // 查找流信息 ret = avformat_find_stream_info(formatCtx_, nullptr); if (ret < 0) { AV_LOGGER_ERRORF("查找流信息失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } // 查找视频流 videoStreamIndex_ = av_find_best_stream(formatCtx_, AVMEDIA_TYPE_VIDEO, -1, -1, &codec_, 0); if (videoStreamIndex_ < 0) { AV_LOGGER_ERROR("未找到视频流"); return ErrorCode::STREAM_NOT_FOUND; } // 创建解码上下文 codecCtx_ = avcodec_alloc_context3(codec_); if (!codecCtx_) { AV_LOGGER_ERROR("分配解码上下文失败"); return ErrorCode::OUT_OF_MEMORY; } // 复制流参数到解码上下文 ret = avcodec_parameters_to_context(codecCtx_, formatCtx_->streams[videoStreamIndex_]->codecpar); if (ret < 0) { AV_LOGGER_ERRORF("复制流参数失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } // 打开解码器 ret = avcodec_open2(codecCtx_, codec_, nullptr); if (ret < 0) { AV_LOGGER_ERRORF("打开解码器失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } // 设置像素格式转换 return setupPixelFormatConversion(); } ErrorCode VideoCapturer::setupPixelFormatConversion() { AVPixelFormat srcFormat = codecCtx_->pix_fmt; AVPixelFormat dstFormat = videoParams_.pixelFormat; needConversion_ = (srcFormat != dstFormat); if (needConversion_) { AV_LOGGER_INFOF("需要像素格式转换: {} -> {}", av_get_pix_fmt_name(srcFormat), av_get_pix_fmt_name(dstFormat)); swsCtx_ = sws_getContext( codecCtx_->width, codecCtx_->height, srcFormat, videoParams_.width, videoParams_.height, dstFormat, SWS_BILINEAR, nullptr, nullptr, nullptr ); if (!swsCtx_) { AV_LOGGER_ERROR("创建像素格式转换上下文失败"); return ErrorCode::CONVERSION_ERROR; } // 创建转换后的帧 convertedFrame_ = makeAVFrame(); if (!convertedFrame_) { return ErrorCode::OUT_OF_MEMORY; } convertedFrame_->format = dstFormat; convertedFrame_->width = videoParams_.width; convertedFrame_->height = videoParams_.height; int ret = av_frame_get_buffer(convertedFrame_.get(), 32); if (ret < 0) { AV_LOGGER_ERRORF("分配转换帧缓冲区失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } } return ErrorCode::OK; } void VideoCapturer::captureThreadFunc() { AV_LOGGER_INFO("视频采集线程已启动"); while (!shouldStop_) { // 检查暂停状态 { std::unique_lock lock(pauseMutex_); pauseCondition_.wait(lock, [this] { return !paused_ || shouldStop_; }); } if (shouldStop_) { break; } ErrorCode result = captureFrame(); if (result != ErrorCode::OK) { onError(result, "采集帧失败"); // 短暂休眠后重试 std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } AV_LOGGER_INFO("视频采集线程已退出"); } ErrorCode VideoCapturer::captureFrame() { AVPacket* packet = av_packet_alloc(); if (!packet) { return ErrorCode::OUT_OF_MEMORY; } // 读取包 int ret = av_read_frame(formatCtx_, packet); if (ret < 0) { if (ret == AVERROR_EOF) { AV_LOGGER_WARNING("到达文件末尾"); return ErrorCode::END_OF_STREAM; } else { AV_LOGGER_ERRORF("读取帧失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } } // 检查是否是视频包 if (packet->stream_index != videoStreamIndex_) { av_packet_free(&packet); return ErrorCode::OK; } // 发送包到解码器 ret = avcodec_send_packet(codecCtx_, packet); av_packet_free(&packet); if (ret < 0) { AV_LOGGER_ERRORF("发送包到解码器失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } // 接收解码后的帧 AVFramePtr frame = makeAVFrame(); if (!frame) { return ErrorCode::OUT_OF_MEMORY; } ret = avcodec_receive_frame(codecCtx_, frame.get()); if (ret == AVERROR(EAGAIN)) { return ErrorCode::OK; // 需要更多输入 } else if (ret < 0) { AV_LOGGER_ERRORF("接收解码帧失败: {}", ffmpeg_utils::errorToString(ret)); return static_cast(ret); } // 像素格式转换 AVFramePtr outputFrame; if (needConversion_) { outputFrame = convertPixelFormat(frame); if (!outputFrame) { return ErrorCode::CONVERSION_ERROR; } } else { outputFrame = std::move(frame); } // 添加到队列或直接回调 onFrameCaptured(outputFrame); return ErrorCode::OK; } AVFramePtr VideoCapturer::convertPixelFormat(const AVFramePtr& srcFrame) { if (!srcFrame || !swsCtx_ || !convertedFrame_) { return nullptr; } // 执行像素格式转换 int ret = sws_scale(swsCtx_, srcFrame->data, srcFrame->linesize, 0, srcFrame->height, convertedFrame_->data, convertedFrame_->linesize); if (ret < 0) { AV_LOGGER_ERRORF("像素格式转换失败: {}", ffmpeg_utils::errorToString(ret)); return nullptr; } // 复制时间戳等信息 av_frame_copy_props(convertedFrame_.get(), srcFrame.get()); // 创建新的frame并复制数据 AVFramePtr outputFrame = makeAVFrame(); if (!outputFrame) { return nullptr; } av_frame_ref(outputFrame.get(), convertedFrame_.get()); return outputFrame; } void VideoCapturer::cleanupConverter() { if (swsCtx_) { sws_freeContext(swsCtx_); swsCtx_ = nullptr; } convertedFrame_.reset(); needConversion_ = false; } std::vector VideoCapturer::enumerateCameras() const { #ifdef _WIN32 return enumerateDirectShowDevices(); #elif defined(__linux__) return enumerateV4L2Devices(); #elif defined(__APPLE__) return enumerateAVFoundationDevices(); #else return {}; #endif } std::vector VideoCapturer::enumerateScreens() const { std::vector screens; // 简单的屏幕枚举实现 VideoDeviceInfo screen; screen.id = "desktop"; screen.name = "桌面"; screen.description = "主显示器"; // 添加常见分辨率 screen.supportedResolutions = { {1920, 1080}, {1680, 1050}, {1600, 900}, {1440, 900}, {1366, 768}, {1280, 1024}, {1280, 800}, {1024, 768} }; // 添加常见帧率 screen.supportedFps = {15, 24, 30, 60}; // 添加支持的像素格式 screen.supportedFormats = { AV_PIX_FMT_BGR24, AV_PIX_FMT_BGRA, AV_PIX_FMT_YUV420P }; screens.push_back(screen); return screens; } std::vector VideoCapturer::enumerateWindows() const { #ifdef _WIN32 return enumerateWindowsWindows(); #else return {}; #endif } const AVInputFormat* VideoCapturer::getPlatformInputFormat() const { #ifdef _WIN32 if (videoParams_.type == CapturerType::VIDEO_CAMERA) { return av_find_input_format("dshow"); } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { return av_find_input_format("gdigrab"); } else if (videoParams_.type == CapturerType::VIDEO_WINDOW) { return av_find_input_format("gdigrab"); } #elif defined(__linux__) if (videoParams_.type == CapturerType::VIDEO_CAMERA) { return av_find_input_format("v4l2"); } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { return av_find_input_format("x11grab"); } #elif defined(__APPLE__) if (videoParams_.type == CapturerType::VIDEO_CAMERA) { return av_find_input_format("avfoundation"); } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { return av_find_input_format("avfoundation"); } #endif return nullptr; } std::string VideoCapturer::getPlatformDeviceName() const { #ifdef _WIN32 if (videoParams_.type == CapturerType::VIDEO_CAMERA) { if (!videoParams_.deviceName.empty()) { return "video=" + videoParams_.deviceName; } else { return "video=" + std::to_string(videoParams_.cameraIndex); } } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { return "desktop"; } else if (videoParams_.type == CapturerType::VIDEO_WINDOW) { if (!videoParams_.windowTitle.empty()) { return "title=" + videoParams_.windowTitle; } else if (videoParams_.windowHandle) { return "hwnd=" + std::to_string(reinterpret_cast(videoParams_.windowHandle)); } else { return "desktop"; // 回退到桌面捕获 } } #elif defined(__linux__) if (videoParams_.type == CapturerType::VIDEO_CAMERA) { if (!videoParams_.deviceName.empty()) { return videoParams_.deviceName; } else { return "/dev/video" + std::to_string(videoParams_.cameraIndex); } } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { return ":0.0"; } #elif defined(__APPLE__) if (videoParams_.type == CapturerType::VIDEO_CAMERA) { return std::to_string(videoParams_.cameraIndex); } else if (videoParams_.type == CapturerType::VIDEO_SCREEN) { return "Capture screen 0"; } #endif return ""; } #ifdef _WIN32 std::vector VideoCapturer::enumerateDirectShowDevices() const { std::vector devices; // 简化的DirectShow设备枚举 // 实际实现需要使用COM接口 VideoDeviceInfo device; device.id = "0"; device.name = "默认摄像头"; device.description = "DirectShow摄像头设备"; // 添加常见分辨率 device.supportedResolutions = { {1920, 1080}, {1280, 720}, {960, 540}, {640, 480}, {320, 240} }; // 添加常见帧率 device.supportedFps = {15, 24, 30, 60}; // 添加支持的像素格式 device.supportedFormats = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUYV422, AV_PIX_FMT_BGR24 }; devices.push_back(device); return devices; } ErrorCode VideoCapturer::setupDirectShowCamera() { AV_LOGGER_INFO("设置DirectShow摄像头"); return openInputDevice(); } ErrorCode VideoCapturer::setupGDIScreenCapture() { AV_LOGGER_INFO("设置GDI屏幕录制"); return openInputDevice(); } std::vector VideoCapturer::enumerateWindowsWindows() const { std::vector windows; // 简化的窗口枚举实现 // 实际实现需要使用EnumWindows API VideoDeviceInfo window; window.id = "notepad"; window.name = "记事本"; window.description = "Windows记事本窗口"; // 窗口采集支持的分辨率取决于窗口大小 window.supportedResolutions = { {1920, 1080}, {1280, 720}, {800, 600}, {640, 480} }; // 添加常见帧率 window.supportedFps = {15, 24, 30, 60}; // 添加支持的像素格式 window.supportedFormats = { AV_PIX_FMT_BGR24, AV_PIX_FMT_BGRA, AV_PIX_FMT_YUV420P }; windows.push_back(window); return windows; } ErrorCode VideoCapturer::setupGDIWindowCapture() { AV_LOGGER_INFOF("设置GDI窗口采集: {}", videoParams_.windowTitle); return openInputDevice(); } #endif // VideoCaptureFactory 实现 std::unique_ptr VideoCapturer::VideoCaptureFactory::createCamera(int cameraIndex) { auto capturer = std::make_unique(); VideoCaptureParams params(CapturerType::VIDEO_CAMERA); params.cameraIndex = cameraIndex; ErrorCode result = capturer->initialize(params); if (result != ErrorCode::OK) { AV_LOGGER_ERRORF("创建摄像头采集器失败: {}", static_cast(result)); return nullptr; } return capturer; } std::unique_ptr VideoCapturer::VideoCaptureFactory::createScreen(int screenIndex) { auto capturer = std::make_unique(); VideoCaptureParams params(CapturerType::VIDEO_SCREEN); params.screenIndex = screenIndex; ErrorCode result = capturer->initialize(params); if (result != ErrorCode::OK) { AV_LOGGER_ERRORF("创建屏幕录制采集器失败: {}", static_cast(result)); return nullptr; } return capturer; } std::unique_ptr VideoCapturer::VideoCaptureFactory::createBestCamera() { return createCamera(0); // 默认使用第一个摄像头 } std::unique_ptr VideoCapturer::VideoCaptureFactory::createWindow(const std::string& windowTitle) { auto capturer = std::make_unique(); VideoCaptureParams params(CapturerType::VIDEO_WINDOW); params.windowTitle = windowTitle; ErrorCode result = capturer->initialize(params); if (result != ErrorCode::OK) { AV_LOGGER_ERRORF("创建窗口采集器失败: {}", static_cast(result)); return nullptr; } return capturer; } std::unique_ptr VideoCapturer::VideoCaptureFactory::createWindowByHandle(void* windowHandle) { auto capturer = std::make_unique(); VideoCaptureParams params(CapturerType::VIDEO_WINDOW); params.windowHandle = windowHandle; ErrorCode result = capturer->initialize(params); if (result != ErrorCode::OK) { AV_LOGGER_ERRORF("创建窗口采集器失败: {}", static_cast(result)); return nullptr; } return capturer; } } // namespace capture } // namespace av