fix(camera): prevent double free in UVC recording

This commit is contained in:
linbo 2026-08-12 10:21:57 +08:00
parent e94db8b66c
commit 5f4e3897ac
2 changed files with 33 additions and 39 deletions

View File

@ -45,6 +45,7 @@ namespace cmvr::device {
private:
void streaming_worker_();
void recording_worker_();
void cleanup_recording_resources_();
int fps_;
int width_;

View File

@ -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<thread>(&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";