Ensure the threadsafe for video upload

This commit is contained in:
2026-04-17 07:03:03 +10:00
parent 2abfd6e87c
commit a63feab0ff
3 changed files with 1117 additions and 1154 deletions

View File

@@ -2114,24 +2114,6 @@ namespace ANSCENTER
constexpr const char* kX264WriterOpts =
"video_codec;libx264|crf;26|preset;slow|tune;stillimage|movflags;+faststart";
std::string prevWriterOpts;
bool hadPrevWriterOpts = false;
if (const char* prev = std::getenv(kWriterOptsEnv)) {
prevWriterOpts = prev;
hadPrevWriterOpts = true;
}
auto setX264Opts = [&]() {
_putenv_s(kWriterOptsEnv, kX264WriterOpts);
};
auto restoreOpts = [&]() {
if (hadPrevWriterOpts) {
_putenv_s(kWriterOptsEnv, prevWriterOpts.c_str());
} else {
_putenv_s(kWriterOptsEnv, "");
}
};
// Try codecs in order of preference.
// avc1 / H264 / x264 route to libx264 via FFmpeg when forced via the
// video_codec option above. MP4V and MJPG are last-resort fallbacks —
@@ -2146,34 +2128,68 @@ namespace ANSCENTER
bool codecFound = false;
std::string usedCodec;
for (const auto& [name, fourcc] : codecs) {
const bool isH264Family =
(name == "avc1" || name == "H264" || name == "x264");
if (isH264Family) {
setX264Opts();
} else {
restoreOpts();
// ──────────────────────────────────────────────────────────────
// PARALLEL-SAFETY: OPENCV_FFMPEG_WRITER_OPTIONS is process-wide.
// Two workers racing through this block would clobber each
// other's env var between setX264Opts() and VideoWriter::open(),
// silently mis-tuning or mis-picking a codec for one of them.
// Serialize the env-var + open() window with a function-local
// static mutex. The frame-write loop after this block is fully
// parallel because `videoWriter` is self-contained once opened.
// ──────────────────────────────────────────────────────────────
{
static std::mutex g_writerOptsMutex;
std::lock_guard<std::mutex> optsLock(g_writerOptsMutex);
std::string prevWriterOpts;
bool hadPrevWriterOpts = false;
if (const char* prev = std::getenv(kWriterOptsEnv)) {
prevWriterOpts = prev;
hadPrevWriterOpts = true;
}
videoWriter.open(mp4OutputPath, fourcc, fps,
cv::Size(videoWidth, videoHeight), true);
auto setX264Opts = [&]() {
_putenv_s(kWriterOptsEnv, kX264WriterOpts);
};
auto restoreOpts = [&]() {
if (hadPrevWriterOpts) {
_putenv_s(kWriterOptsEnv, prevWriterOpts.c_str());
} else {
_putenv_s(kWriterOptsEnv, "");
}
};
if (videoWriter.isOpened()) {
std::cout << "Using codec: " << name
<< (isH264Family ? " (libx264 forced, crf=26, preset=slow, tune=stillimage)" : "")
<< std::endl;
usedCodec = name;
codecFound = true;
break;
for (const auto& [name, fourcc] : codecs) {
const bool isH264Family =
(name == "avc1" || name == "H264" || name == "x264");
if (isH264Family) {
setX264Opts();
} else {
restoreOpts();
}
videoWriter.open(mp4OutputPath, fourcc, fps,
cv::Size(videoWidth, videoHeight), true);
if (videoWriter.isOpened()) {
std::cout << "Using codec: " << name
<< (isH264Family ? " (libx264 forced, crf=26, preset=slow, tune=stillimage)" : "")
<< std::endl;
usedCodec = name;
codecFound = true;
break;
}
videoWriter.release();
}
videoWriter.release();
// Always restore the env var before releasing the mutex
// — don't leak the libx264 override into the rest of the
// process.
restoreOpts();
}
// Always restore the env var after we're done — don't leak the
// libx264 override into the rest of the process.
restoreOpts();
if (!codecFound) {
std::cerr << "Error: Could not open video writer with any codec!" << std::endl;
return false;