From 5f4e3897acefc44357e31db5b82f500aa1a47f8a Mon Sep 17 00:00:00 2001 From: linbo <1034003879@qq.com> Date: Wed, 12 Aug 2026 10:21:57 +0800 Subject: [PATCH] fix(camera): prevent double free in UVC recording --- .../camera/uvc_camera/include/uvc_camera.h | 1 + .../camera/uvc_camera/src/uvc_camera.cpp | 71 +++++++++---------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/cmvr-es/devices/camera/uvc_camera/include/uvc_camera.h b/cmvr-es/devices/camera/uvc_camera/include/uvc_camera.h index 3c5323a0..aa9a32e9 100644 --- a/cmvr-es/devices/camera/uvc_camera/include/uvc_camera.h +++ b/cmvr-es/devices/camera/uvc_camera/include/uvc_camera.h @@ -45,6 +45,7 @@ namespace cmvr::device { private: void streaming_worker_(); void recording_worker_(); + void cleanup_recording_resources_(); int fps_; int width_; diff --git a/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp b/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp index 02342f51..362a6c94 100644 --- a/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp +++ b/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp @@ -181,7 +181,7 @@ bool UVCCamera::start() { bool UVCCamera::stop() { //先停止录制再关闭摄像头 - if (state_.is_recording) { + if (state_.is_recording || recording_thread_ || packet_ || format_context_ || stream_) { stopRecording(); } std::lock_guard lock(ctrl_mtx_); @@ -278,6 +278,22 @@ void UVCCamera::getRGBDImages(cv::Mat &color, cv::Mat &depth, Rs2Intrinsics& int depth.release(); } +void UVCCamera::cleanup_recording_resources_() { + if (packet_) { + av_packet_free(&packet_); + } + + if (format_context_) { + if (!(format_context_->oformat->flags & AVFMT_NOFILE) && format_context_->pb) { + avio_closep(&format_context_->pb); + } + avformat_free_context(format_context_); + format_context_ = nullptr; + } + + stream_ = nullptr; +} + void UVCCamera::startRecording(const std::string &video_path) { std::lock_guard lock(ctrl_mtx_); clear_error_(); @@ -300,18 +316,6 @@ void UVCCamera::startRecording(const std::string &video_path) { return; } - auto cleanup_recording_resources = [this]() { - if (packet_) av_packet_free(&packet_); - if (stream_ && stream_->codecpar->extradata) av_free(stream_->codecpar->extradata); - if (format_context_) { - if (!(format_context_->oformat->flags & AVFMT_NOFILE) && format_context_->pb) avio_closep(&format_context_->pb); - avformat_free_context(format_context_); - } - stream_ = nullptr; - format_context_ = nullptr; - state_.is_recording = false; - }; - try { current_video_path_ = video_path; std::string temp_path = current_video_path_ + ".temp"; // 临时文件 @@ -348,7 +352,7 @@ void UVCCamera::startRecording(const std::string &video_path) { state_.is_error = true; state_.error_message = "failed to create video stream"; CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message; - cleanup_recording_resources(); + cleanup_recording_resources_(); return; } stream_ = stream; @@ -366,7 +370,7 @@ void UVCCamera::startRecording(const std::string &video_path) { state_.is_error = true; state_.error_message = "failed to allocate extradata"; CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message; - cleanup_recording_resources(); + cleanup_recording_resources_(); return; } memcpy(codecpar->extradata, rgbEncoder_->codec_context->extradata, rgbEncoder_->codec_context->extradata_size); @@ -379,7 +383,7 @@ void UVCCamera::startRecording(const std::string &video_path) { state_.is_error = true; state_.error_message = "failed to open output file: " + temp_path; CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message; - cleanup_recording_resources(); + cleanup_recording_resources_(); return; } } @@ -389,7 +393,7 @@ void UVCCamera::startRecording(const std::string &video_path) { state_.is_error = true; state_.error_message = "failed to write file header"; CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message; - cleanup_recording_resources(); + cleanup_recording_resources_(); return; } @@ -399,7 +403,7 @@ void UVCCamera::startRecording(const std::string &video_path) { state_.is_error = true; state_.error_message = "failed to allocate AVPacket"; CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message; - cleanup_recording_resources(); + cleanup_recording_resources_(); return; } //不在录像也不在流传输,但是采集线程没有退出时。 @@ -432,7 +436,8 @@ void UVCCamera::startRecording(const std::string &video_path) { recording_thread_ = make_shared(&UVCCamera::recording_worker_, this); } catch (const std::exception& e) { - cleanup_recording_resources(); + cleanup_recording_resources_(); + state_.is_recording = false; state_.is_error = true; state_.error_message = "[UVCCamera] (startRecording): " + std::string(e.what()); CMVR_LOG(ERROR) << state_.error_message; @@ -448,36 +453,24 @@ void UVCCamera::stopRecording() { CMVR_LOG(ERROR) << "[UVCCamera] (stopRecording): " << state_.error_message; return; } - if (!state_.is_recording) { + const bool has_recording_resources = + recording_thread_ || packet_ || format_context_ || stream_; + if (!state_.is_recording && !has_recording_resources) { CMVR_LOG(WARNING) << "[UVCCamera] (stopRecording): not recording"; return; } // 1. 停止录像线程 state_.is_recording = false; - if (recording_thread_ && recording_thread_->joinable()) { - recording_thread_->join(); + if (recording_thread_) { + if (recording_thread_->joinable()) { + recording_thread_->join(); + } recording_thread_.reset(); } // 2. 清理FFmpeg资源 - if (packet_) { - av_packet_free(&packet_); - packet_ = nullptr; - } - if (stream_ && stream_->codecpar->extradata) { - av_free(stream_->codecpar->extradata); - stream_->codecpar->extradata = nullptr; - stream_->codecpar->extradata_size = 0; - } - if (format_context_) { - if (!(format_context_->oformat->flags & AVFMT_NOFILE) && format_context_->pb) { - avio_closep(&format_context_->pb); // 关闭文件 - } - avformat_free_context(format_context_); // 释放格式上下文 - format_context_ = nullptr; - } - stream_ = nullptr; + cleanup_recording_resources_(); // 3. 重命名临时文件为目标文件 std::string temp_path = current_video_path_ + ".temp";