Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Hybrid MP4 Muxer #10608

Merged
merged 3 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions UI/api-interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,23 @@ struct OBSStudioAPI : obs_frontend_callbacks {
}
}

bool obs_frontend_recording_add_chapter(const char *name) override
{
if (!os_atomic_load_bool(&recording_active) ||
os_atomic_load_bool(&recording_paused))
return false;

proc_handler_t *ph = obs_output_get_proc_handler(
main->outputHandler->fileOutput);

calldata cd;
calldata_init(&cd);
calldata_set_string(&cd, "chapter_name", name);
bool result = proc_handler_call(ph, "add_chapter", &cd);
calldata_free(&cd);
return result;
}

void obs_frontend_replay_buffer_start(void) override
{
QMetaObject::invokeMethod(main, "StartReplayBuffer");
Expand Down
2 changes: 2 additions & 0 deletions UI/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,7 @@ Basic.Main.StopRecording="Stop Recording"
Basic.Main.PauseRecording="Pause Recording"
Basic.Main.UnpauseRecording="Unpause Recording"
Basic.Main.SplitFile="Split Recording File"
Basic.Main.AddChapterMarker="Add Chapter Marker"
Basic.Main.StoppingRecording="Stopping Recording..."
Basic.Main.StopReplayBuffer="Stop Replay Buffer"
Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..."
Expand Down Expand Up @@ -966,6 +967,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."
Expand Down
14 changes: 14 additions & 0 deletions UI/ffmpeg-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ static const unordered_map<string, unordered_set<string>> 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",
Expand Down
2 changes: 2 additions & 0 deletions UI/obs-app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
6 changes: 6 additions & 0 deletions UI/obs-frontend-api/obs-frontend-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ bool obs_frontend_recording_split_file(void)
: false;
}

bool obs_frontend_recording_add_chapter(const char *name)
{
return !!callbacks_valid() ? c->obs_frontend_recording_add_chapter(name)
: false;
}

void obs_frontend_replay_buffer_start(void)
{
if (callbacks_valid())
Expand Down
1 change: 1 addition & 0 deletions UI/obs-frontend-api/obs-frontend-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ EXPORT bool obs_frontend_recording_active(void);
EXPORT void obs_frontend_recording_pause(bool pause);
EXPORT bool obs_frontend_recording_paused(void);
EXPORT bool obs_frontend_recording_split_file(void);
EXPORT bool obs_frontend_recording_add_chapter(const char *name);

EXPORT void obs_frontend_replay_buffer_start(void);
EXPORT void obs_frontend_replay_buffer_save(void);
Expand Down
1 change: 1 addition & 0 deletions UI/obs-frontend-api/obs-frontend-internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct obs_frontend_callbacks {
virtual void obs_frontend_recording_pause(bool pause) = 0;
virtual bool obs_frontend_recording_paused(void) = 0;
virtual bool obs_frontend_recording_split_file(void) = 0;
virtual bool obs_frontend_recording_add_chapter(const char *name) = 0;

virtual void obs_frontend_replay_buffer_start(void) = 0;
virtual void obs_frontend_replay_buffer_save(void) = 0;
Expand Down
13 changes: 11 additions & 2 deletions UI/window-basic-main-outputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)";
Expand Down
18 changes: 18 additions & 0 deletions UI/window-basic-main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2808,6 +2808,23 @@ void OBSBasic::CreateHotkeys()
this);
LoadHotkey(splitFileHotkey, "OBSBasic.SplitFile");

/* Adding chapters is only supported by the native MP4 output */
const string_view output_id =
obs_output_get_id(outputHandler->fileOutput);
if (output_id == "mp4_output") {
addChapterHotkey = obs_hotkey_register_frontend(
"OBSBasic.AddChapterMarker",
Str("Basic.Main.AddChapterMarker"),
[](void *, obs_hotkey_id, obs_hotkey_t *,
bool pressed) {
if (pressed)
obs_frontend_recording_add_chapter(
nullptr);
},
this);
LoadHotkey(addChapterHotkey, "OBSBasic.AddChapterMarker");
}

replayBufHotkeys = obs_hotkey_pair_register_frontend(
"OBSBasic.StartReplayBuffer",
Str("Basic.Main.StartReplayBuffer"),
Expand Down Expand Up @@ -2936,6 +2953,7 @@ void OBSBasic::ClearHotkeys()
obs_hotkey_pair_unregister(recordingHotkeys);
obs_hotkey_pair_unregister(pauseHotkeys);
obs_hotkey_unregister(splitFileHotkey);
obs_hotkey_unregister(addChapterHotkey);
obs_hotkey_pair_unregister(replayBufHotkeys);
obs_hotkey_pair_unregister(vcamHotkeys);
obs_hotkey_pair_unregister(togglePreviewHotkeys);
Expand Down
3 changes: 2 additions & 1 deletion UI/window-basic-main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ class OBSBasic : public OBSMainWindow {
obs_hotkey_pair_id streamingHotkeys, recordingHotkeys, pauseHotkeys,
replayBufHotkeys, vcamHotkeys, togglePreviewHotkeys,
contextBarHotkeys;
obs_hotkey_id forceStreamingStopHotkey, splitFileHotkey;
obs_hotkey_id forceStreamingStopHotkey, splitFileHotkey,
addChapterHotkey;

void InitDefaultTransitions();
void InitTransition(obs_source_t *transition);
Expand Down
2 changes: 2 additions & 0 deletions UI/window-basic-settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
Expand Down
5 changes: 5 additions & 0 deletions plugins/obs-outputs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ target_sources(
librtmp/rtmp.c
librtmp/rtmp.h
librtmp/rtmp_sys.h
mp4-mux-internal.h
mp4-mux.c
mp4-mux.h
mp4-output.c
net-if.c
net-if.h
null-output.c
Expand All @@ -60,6 +64,7 @@ target_link_libraries(
obs-outputs
PRIVATE OBS::libobs
OBS::happy-eyeballs
OBS::opts-parser
MbedTLS::MbedTLS
ZLIB::ZLIB
$<$<PLATFORM_ID:Windows>:OBS::w32-pthreads>
Expand Down
5 changes: 5 additions & 0 deletions plugins/obs-outputs/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ FLVOutput="FLV File Output"
FLVOutput.FilePath="File Path"
Default="Default"

MP4Output="MP4 File Output"
MP4Output.FilePath="File Path"
MP4Output.StartChapter="Start"
MP4Output.UnnamedChapter="Unnamed"

IPFamily="IP Address Family"
IPFamily.Both="IPv4 and IPv6 (Default)"
IPFamily.V4Only="IPv4 Only"
Expand Down