diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index b9325190d31380..b0681b67099fb3 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -966,6 +966,7 @@ Basic.Settings.Output.Format.MP4="MPEG-4 (.mp4)" Basic.Settings.Output.Format.MOV="QuickTime (.mov)" Basic.Settings.Output.Format.TS="MPEG-TS (.ts)" Basic.Settings.Output.Format.HLS="HLS (.m3u8 + .ts)" +Basic.Settings.Output.Format.hMP4="Hybrid MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmented MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmented MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmented MOV writes the recording in chunks and does not require the same finalization as traditional MOV files.\nThis ensures the file remains playable even if writing to disk is interrupted, for example, as a result of a BSOD or power loss.\n\nThis may not be compatible with all players and editors. Use File → Remux Recordings to convert the file into a more compatible format if necessary." diff --git a/UI/ffmpeg-utils.cpp b/UI/ffmpeg-utils.cpp index c3cd54eb3f8e33..b8f36cd00ce9f1 100644 --- a/UI/ffmpeg-utils.cpp +++ b/UI/ffmpeg-utils.cpp @@ -185,6 +185,20 @@ static const unordered_map> codec_compat = { "pcm_f32le", #endif }}, + // Not part of FFmpeg, see obs-outputs module + {"hybrid_mp4", + { + "h264", + "hevc", + "av1", + "aac", + "opus", + "alac", + "flac", + "pcm_s16le", + "pcm_s24le", + "pcm_f32le", + }}, {"mov", { "h264", diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 88ee9c66acdc86..853630a02f4cad 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1783,6 +1783,8 @@ string GetFormatExt(const char *container) string ext = container; if (ext == "fragmented_mp4") ext = "mp4"; + if (ext == "hybrid_mp4") + ext = "mp4"; else if (ext == "fragmented_mov") ext = "mov"; else if (ext == "hls") diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index f87e16dcd29ba6..21548c6e261469 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -697,6 +697,9 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) if (!ffmpegOutput) { bool useReplayBuffer = config_get_bool(main->Config(), "SimpleOutput", "RecRB"); + const char *recFormat = config_get_string( + main->Config(), "SimpleOutput", "RecFormat2"); + if (useReplayBuffer) { OBSDataAutoRelease hotkey; const char *str = config_get_string( @@ -728,8 +731,10 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) OBSReplayBufferSaved, this); } + bool use_native = strcmp(recFormat, "hybrid_mp4") == 0; fileOutput = obs_output_create( - "ffmpeg_muxer", "simple_file_output", nullptr, nullptr); + use_native ? "mp4_output" : "ffmpeg_muxer", + "simple_file_output", nullptr, nullptr); if (!fileOutput) throw "Failed to create recording output " "(simple output)"; @@ -1568,6 +1573,8 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) config_get_string(main->Config(), "AdvOut", "RecEncoder"); const char *recAudioEncoder = config_get_string(main->Config(), "AdvOut", "RecAudioEncoder"); + const char *recFormat = + config_get_string(main->Config(), "AdvOut", "RecFormat2"); #ifdef __APPLE__ translate_macvth264_encoder(streamEncoder); translate_macvth264_encoder(recordEncoder); @@ -1623,8 +1630,10 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) OBSReplayBufferSaved, this); } + bool native_muxer = strcmp(recFormat, "hybrid_mp4") == 0; fileOutput = obs_output_create( - "ffmpeg_muxer", "adv_file_output", nullptr, nullptr); + native_muxer ? "mp4_output" : "ffmpeg_muxer", + "adv_file_output", nullptr, nullptr); if (!fileOutput) throw "Failed to create recording output " "(advanced output)"; diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 6d1b4ae943f27b..58fba4854dd23c 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -1152,6 +1152,7 @@ void OBSBasicSettings::LoadFormats() ui->simpleOutRecFormat->addItem(FORMAT_STR("MKV"), "mkv"); ui->simpleOutRecFormat->addItem(FORMAT_STR("MP4"), "mp4"); ui->simpleOutRecFormat->addItem(FORMAT_STR("MOV"), "mov"); + ui->simpleOutRecFormat->addItem(FORMAT_STR("hMP4"), "hybrid_mp4"); ui->simpleOutRecFormat->addItem(FORMAT_STR("fMP4"), "fragmented_mp4"); ui->simpleOutRecFormat->addItem(FORMAT_STR("fMOV"), "fragmented_mov"); ui->simpleOutRecFormat->addItem(FORMAT_STR("TS"), "mpegts"); @@ -1160,6 +1161,7 @@ void OBSBasicSettings::LoadFormats() ui->advOutRecFormat->addItem(FORMAT_STR("MKV"), "mkv"); ui->advOutRecFormat->addItem(FORMAT_STR("MP4"), "mp4"); ui->advOutRecFormat->addItem(FORMAT_STR("MOV"), "mov"); + ui->advOutRecFormat->addItem(FORMAT_STR("hMP4"), "hybrid_mp4"); ui->advOutRecFormat->addItem(FORMAT_STR("fMP4"), "fragmented_mp4"); ui->advOutRecFormat->addItem(FORMAT_STR("fMOV"), "fragmented_mov"); ui->advOutRecFormat->addItem(FORMAT_STR("TS"), "mpegts");