diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 96b8cce4c..5710617d6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -62,6 +62,7 @@ #include "deh_tables.h" #include "m_perfstats.h" #include "k_specialstage.h" +#include "m_avrecorder.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -903,6 +904,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_moviemode); CV_RegisterVar(&cv_movie_option); CV_RegisterVar(&cv_movie_folder); + M_AVRecorder_AddCommands(); // PNG variables CV_RegisterVar(&cv_zlib_level); CV_RegisterVar(&cv_zlib_memory); diff --git a/src/m_avrecorder.cpp b/src/m_avrecorder.cpp index 78c9f2929..de43b21fe 100644 --- a/src/m_avrecorder.cpp +++ b/src/m_avrecorder.cpp @@ -185,6 +185,18 @@ void M_AVRecorder_Close(void) g_av_recorder.reset(); } +const char* M_AVRecorder_GetFileExtension(void) +{ + return AVRecorder::file_extension(); +} + +const char* M_AVRecorder_GetCurrentFormat(void) +{ + SRB2_ASSERT(g_av_recorder != nullptr); + + return g_av_recorder->format_name(); +} + boolean M_AVRecorder_IsExpired(void) { SRB2_ASSERT(g_av_recorder != nullptr); diff --git a/src/m_avrecorder.h b/src/m_avrecorder.h index 1d60d4dbf..3683002d3 100644 --- a/src/m_avrecorder.h +++ b/src/m_avrecorder.h @@ -18,6 +18,8 @@ extern "C" { void M_AVRecorder_AddCommands(void); +const char *M_AVRecorder_GetFileExtension(void); + // True if successully opened. boolean M_AVRecorder_Open(const char *filename); @@ -26,6 +28,8 @@ void M_AVRecorder_Close(void); // Check whether AVRecorder is still valid. Call M_AVRecorder_Close if expired. boolean M_AVRecorder_IsExpired(void); +const char *M_AVRecorder_GetCurrentFormat(void); + extern consvar_t cv_movie_custom_resolution, cv_movie_duration, diff --git a/src/m_misc.c b/src/m_misc.c index 817ac152b..22d015c67 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -44,6 +44,7 @@ #include "command.h" // cv_execversion #include "m_anigif.h" +#include "m_avrecorder.h" // So that the screenshot menu auto-updates... #include "k_menu.h" @@ -113,8 +114,8 @@ consvar_t cv_screenshot_folder = CVAR_INIT ("screenshot_folder", "", CV_SAVE, NU consvar_t cv_screenshot_colorprofile = CVAR_INIT ("screenshot_colorprofile", "Yes", CV_SAVE, CV_YesNo, NULL); -static CV_PossibleValue_t moviemode_cons_t[] = {{MM_GIF, "GIF"}, {MM_APNG, "aPNG"}, {MM_SCREENSHOT, "Screenshots"}, {0, NULL}}; -consvar_t cv_moviemode = CVAR_INIT ("moviemode_mode", "GIF", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange); +static CV_PossibleValue_t moviemode_cons_t[] = {{MM_GIF, "GIF"}, {MM_APNG, "aPNG"}, {MM_SCREENSHOT, "Screenshots"}, {MM_AVRECORDER, "WebM"}, {0, NULL}}; +consvar_t cv_moviemode = CVAR_INIT ("moviemode_mode", "WebM", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange); consvar_t cv_movie_option = CVAR_INIT ("movie_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Moviemode_option_Onchange); consvar_t cv_movie_folder = CVAR_INIT ("movie_folder", "", CV_SAVE, NULL, NULL); @@ -1295,6 +1296,25 @@ static inline moviemode_t M_StartMovieGIF(const char *pathname) } #endif +static inline moviemode_t M_StartMovieAVRecorder(const char *pathname) +{ + const char *ext = M_AVRecorder_GetFileExtension(); + const char *freename; + + if (!(freename = Newsnapshotfile(pathname, ext))) + { + CONS_Alert(CONS_ERROR, "Couldn't create %s file: no slots open in %s\n", ext, pathname); + return MM_OFF; + } + + if (!M_AVRecorder_Open(va(pandf,pathname,freename))) + { + return MM_OFF; + } + + return MM_AVRECORDER; +} + void M_StartMovie(void) { #if NUMSCREENS > 2 @@ -1332,6 +1352,9 @@ void M_StartMovie(void) case MM_SCREENSHOT: moviemode = MM_SCREENSHOT; break; + case MM_AVRECORDER: + moviemode = M_StartMovieAVRecorder(pathname); + break; default: //??? return; } @@ -1342,6 +1365,8 @@ void M_StartMovie(void) CONS_Printf(M_GetText("Movie mode enabled (%s).\n"), "GIF"); else if (moviemode == MM_SCREENSHOT) CONS_Printf(M_GetText("Movie mode enabled (%s).\n"), "screenshots"); + else if (moviemode == MM_AVRECORDER) + CONS_Printf(M_GetText("Movie mode enabled (%s).\n"), M_AVRecorder_GetCurrentFormat()); //singletics = (moviemode != MM_OFF); #endif @@ -1353,6 +1378,15 @@ void M_SaveFrame(void) // paranoia: should be unnecessary without singletics static tic_t oldtic = 0; + if (moviemode == MM_AVRECORDER) + { + if (M_AVRecorder_IsExpired()) + { + M_StopMovie(); + } + return; + } + if (oldtic == I_GetTime()) return; else @@ -1440,6 +1474,9 @@ void M_StopMovie(void) #endif case MM_SCREENSHOT: break; + case MM_AVRECORDER: + M_AVRecorder_Close(); + break; default: return; } diff --git a/src/m_misc.h b/src/m_misc.h index ec979dcb3..2df52fa96 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -29,7 +29,8 @@ typedef enum { MM_OFF = 0, MM_APNG, MM_GIF, - MM_SCREENSHOT + MM_SCREENSHOT, + MM_AVRECORDER, } moviemode_t; extern moviemode_t moviemode; diff --git a/src/media/avrecorder.cpp b/src/media/avrecorder.cpp index ef1962fed..d6ce488dc 100644 --- a/src/media/avrecorder.cpp +++ b/src/media/avrecorder.cpp @@ -170,6 +170,11 @@ void Impl::worker() valid_ = false; } +const char* AVRecorder::file_extension() +{ + return "webm"; +} + AVRecorder::AVRecorder(const Config config) : impl_(std::make_unique(config)) { } @@ -186,6 +191,11 @@ AVRecorder::~AVRecorder() std::thread([_ = std::move(impl_)] {}).detach(); } +const char* AVRecorder::format_name() const +{ + return impl_->container_->name(); +} + void AVRecorder::push_audio_samples(audio_buffer_t buffer) { const auto _ = impl_->queue_guard(); diff --git a/src/media/avrecorder.hpp b/src/media/avrecorder.hpp index 45834ec08..8b06d3122 100644 --- a/src/media/avrecorder.hpp +++ b/src/media/avrecorder.hpp @@ -57,11 +57,18 @@ public: std::optional