fix(camera): prevent double free in UVC recording
This commit is contained in:
parent
e94db8b66c
commit
5f4e3897ac
@ -45,6 +45,7 @@ namespace cmvr::device {
|
|||||||
private:
|
private:
|
||||||
void streaming_worker_();
|
void streaming_worker_();
|
||||||
void recording_worker_();
|
void recording_worker_();
|
||||||
|
void cleanup_recording_resources_();
|
||||||
|
|
||||||
int fps_;
|
int fps_;
|
||||||
int width_;
|
int width_;
|
||||||
|
|||||||
@ -181,7 +181,7 @@ bool UVCCamera::start() {
|
|||||||
|
|
||||||
bool UVCCamera::stop() {
|
bool UVCCamera::stop() {
|
||||||
//先停止录制再关闭摄像头
|
//先停止录制再关闭摄像头
|
||||||
if (state_.is_recording) {
|
if (state_.is_recording || recording_thread_ || packet_ || format_context_ || stream_) {
|
||||||
stopRecording();
|
stopRecording();
|
||||||
}
|
}
|
||||||
std::lock_guard lock(ctrl_mtx_);
|
std::lock_guard lock(ctrl_mtx_);
|
||||||
@ -278,6 +278,22 @@ void UVCCamera::getRGBDImages(cv::Mat &color, cv::Mat &depth, Rs2Intrinsics& int
|
|||||||
depth.release();
|
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) {
|
void UVCCamera::startRecording(const std::string &video_path) {
|
||||||
std::lock_guard lock(ctrl_mtx_);
|
std::lock_guard lock(ctrl_mtx_);
|
||||||
clear_error_();
|
clear_error_();
|
||||||
@ -300,18 +316,6 @@ void UVCCamera::startRecording(const std::string &video_path) {
|
|||||||
return;
|
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 {
|
try {
|
||||||
current_video_path_ = video_path;
|
current_video_path_ = video_path;
|
||||||
std::string temp_path = current_video_path_ + ".temp"; // 临时文件
|
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_.is_error = true;
|
||||||
state_.error_message = "failed to create video stream";
|
state_.error_message = "failed to create video stream";
|
||||||
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
||||||
cleanup_recording_resources();
|
cleanup_recording_resources_();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stream_ = stream;
|
stream_ = stream;
|
||||||
@ -366,7 +370,7 @@ void UVCCamera::startRecording(const std::string &video_path) {
|
|||||||
state_.is_error = true;
|
state_.is_error = true;
|
||||||
state_.error_message = "failed to allocate extradata";
|
state_.error_message = "failed to allocate extradata";
|
||||||
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
||||||
cleanup_recording_resources();
|
cleanup_recording_resources_();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(codecpar->extradata, rgbEncoder_->codec_context->extradata, rgbEncoder_->codec_context->extradata_size);
|
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_.is_error = true;
|
||||||
state_.error_message = "failed to open output file: " + temp_path;
|
state_.error_message = "failed to open output file: " + temp_path;
|
||||||
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
||||||
cleanup_recording_resources();
|
cleanup_recording_resources_();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,7 +393,7 @@ void UVCCamera::startRecording(const std::string &video_path) {
|
|||||||
state_.is_error = true;
|
state_.is_error = true;
|
||||||
state_.error_message = "failed to write file header";
|
state_.error_message = "failed to write file header";
|
||||||
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
||||||
cleanup_recording_resources();
|
cleanup_recording_resources_();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +403,7 @@ void UVCCamera::startRecording(const std::string &video_path) {
|
|||||||
state_.is_error = true;
|
state_.is_error = true;
|
||||||
state_.error_message = "failed to allocate AVPacket";
|
state_.error_message = "failed to allocate AVPacket";
|
||||||
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
CMVR_LOG(ERROR) << "[UVCCamera] (startRecording): " << state_.error_message;
|
||||||
cleanup_recording_resources();
|
cleanup_recording_resources_();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//不在录像也不在流传输,但是采集线程没有退出时。
|
//不在录像也不在流传输,但是采集线程没有退出时。
|
||||||
@ -432,7 +436,8 @@ void UVCCamera::startRecording(const std::string &video_path) {
|
|||||||
recording_thread_ = make_shared<thread>(&UVCCamera::recording_worker_, this);
|
recording_thread_ = make_shared<thread>(&UVCCamera::recording_worker_, this);
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
cleanup_recording_resources();
|
cleanup_recording_resources_();
|
||||||
|
state_.is_recording = false;
|
||||||
state_.is_error = true;
|
state_.is_error = true;
|
||||||
state_.error_message = "[UVCCamera] (startRecording): " + std::string(e.what());
|
state_.error_message = "[UVCCamera] (startRecording): " + std::string(e.what());
|
||||||
CMVR_LOG(ERROR) << state_.error_message;
|
CMVR_LOG(ERROR) << state_.error_message;
|
||||||
@ -448,36 +453,24 @@ void UVCCamera::stopRecording() {
|
|||||||
CMVR_LOG(ERROR) << "[UVCCamera] (stopRecording): " << state_.error_message;
|
CMVR_LOG(ERROR) << "[UVCCamera] (stopRecording): " << state_.error_message;
|
||||||
return;
|
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";
|
CMVR_LOG(WARNING) << "[UVCCamera] (stopRecording): not recording";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 停止录像线程
|
// 1. 停止录像线程
|
||||||
state_.is_recording = false;
|
state_.is_recording = false;
|
||||||
if (recording_thread_ && recording_thread_->joinable()) {
|
if (recording_thread_) {
|
||||||
recording_thread_->join();
|
if (recording_thread_->joinable()) {
|
||||||
|
recording_thread_->join();
|
||||||
|
}
|
||||||
recording_thread_.reset();
|
recording_thread_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 清理FFmpeg资源
|
// 2. 清理FFmpeg资源
|
||||||
if (packet_) {
|
cleanup_recording_resources_();
|
||||||
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;
|
|
||||||
|
|
||||||
// 3. 重命名临时文件为目标文件
|
// 3. 重命名临时文件为目标文件
|
||||||
std::string temp_path = current_video_path_ + ".temp";
|
std::string temp_path = current_video_path_ + ".temp";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user