cv::VideoWriter无法写入帧至MKV文件的问题排查求助
核心问题排查
1. 错误创建VideoCapture抢占文件资源
你代码中创建了cv::VideoCapture cap(out_vid_name + ".mkv", cv::CAP_FFMPEG);,这会尝试读取你正在写入的MKV文件,导致文件句柄被占用,VideoWriter无法正常写入帧数据。这是无帧写入的最直接原因。
2. 未检查VideoWriter初始化状态
OpenCV的VideoWriter如果初始化失败(比如编码器不支持、参数不匹配)会静默失败,后续write()调用不会产生任何效果。你没有检查writer.isOpened()的返回值,无法及时发现初始化问题。
3. 未明确指定FFmpeg作为编码后端
你的VideoWriter构造函数未指定编码后端,OpenCV可能默认使用了不支持FFV1编码的后端(如V4L2),导致无法生成FFV1编码的MKV文件。
4. 帧格式/尺寸不匹配风险
虽然imwrite正常,但需确认写入的Mat是否为3通道BGR格式(对应CV_8UC3)——你构造Writer时最后一个参数设为true(彩色模式),如果帧是单通道或其他格式,Writer会拒绝写入。
分步解决方案
1. 删除无效的VideoCapture代码
直接移除cv::VideoCapture cap(out_vid_name + ".mkv", cv::CAP_FFMPEG);这一行,释放文件资源给VideoWriter使用。
2. 检查VideoWriter初始化状态
构造Writer后立即检查是否成功打开,添加错误处理:
cv::VideoWriter writer(out_vid_name + ".mkv", cv::CAP_FFMPEG, cv::VideoWriter::fourcc('F','F','V','1'), cam_fps, frame_double_size, true); if (!writer.isOpened()) { std::cerr << "Failed to initialize VideoWriter! Check encoder support and parameters." << std::endl; return -1; // 添加退出逻辑,避免后续无效执行 }
3. 明确指定FFmpeg后端
在构造VideoWriter时,第二个参数传入cv::CAP_FFMPEG,强制使用FFmpeg API编码,确保支持FFV1和MKV容器:
// 修正后的构造函数 cv::VideoWriter writer( out_vid_name + ".mkv", cv::CAP_FFMPEG, // 明确指定FFmpeg后端 cv::VideoWriter::fourcc('F','F','V','1'), cam_fps, frame_double_size, true );
4. 验证并修正帧格式与尺寸
在写入前添加帧信息验证,确保与Writer设置一致:
cv::Mat frame = doubleSaveFrame->getHost(); // 打印帧尺寸(确认和frame_double_size一致:3840x1080) std::cout << "Frame size: " << frame.size() << std::endl; // 打印帧格式(CV_8UC3对应值为16,彩色BGR) std::cout << "Frame type: " << frame.type() << std::endl; // 格式/尺寸不匹配时自动修正 if (frame.channels() != 3) { cv::cvtColor(frame, frame, cv::COLOR_GRAY2BGR); } if (frame.size() != frame_double_size) { cv::resize(frame, frame, frame_double_size); } // 执行写入 writer.write(frame);
5. 确认FFmpeg支持FFV1
如果上述步骤仍无效,检查OpenCV编译时是否启用FFmpeg,且FFmpeg库支持FFV1编码。可通过以下命令验证:
ffmpeg -codecs | grep FFV1
若输出包含FFV1且标记为E(编码支持),则说明FFmpeg支持该编码;否则需重新编译FFmpeg或OpenCV,启用FFV1编码支持。
修正后的完整写入循环示例
// 初始化Writer并检查状态 cv::VideoWriter writer( out_vid_name + ".mkv", cv::CAP_FFMPEG, cv::VideoWriter::fourcc('F','F','V','1'), cam_fps, frame_double_size, true ); if (!writer.isOpened()) { std::cerr << "VideoWriter initialization failed!" << std::endl; return -1; } while (controller->running && ros::ok()) { std::shared_lock<std::shared_mutex> guard(doubleSaveFrame->mtx); doubleSaveFrame->cond_var.wait(guard, [&]{ return doubleSaveFrame->cond_bool_save; }); doubleSaveFrame->cond_bool_save = false; std_msgs::String msg; msg.data = std::to_string(frame_count); frame_count++; frame_pub.publish(msg); cv::Mat frame = doubleSaveFrame->getHost(); // 验证并修正帧格式/尺寸 if (frame.channels() != 3) { cv::cvtColor(frame, frame, cv::COLOR_GRAY2BGR); } if (frame.size() != frame_double_size) { cv::resize(frame, frame, frame_double_size); } // 写入帧 writer.write(frame); // 打印调试信息 std::cout << "Written frame " << frame_count << ", size: " << frame.size() << ", type: " << frame.type() << std::endl; // 修正imwrite路径(添加根目录/) cv::imwrite("/home/Documents/camera/imwritefun" + std::to_string(frame_count) + ".JPG", frame); } writer.release();
内容的提问来源于stack exchange,提问作者user18953215

