Add AVRecorder movie mode

This commit is contained in:
James R 2023-02-12 05:17:34 -08:00
parent 79b1a8fd63
commit c65f4ff893
7 changed files with 76 additions and 3 deletions

View file

@ -62,6 +62,7 @@
#include "deh_tables.h" #include "deh_tables.h"
#include "m_perfstats.h" #include "m_perfstats.h"
#include "k_specialstage.h" #include "k_specialstage.h"
#include "m_avrecorder.h"
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
#include "discord.h" #include "discord.h"
@ -903,6 +904,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_moviemode); CV_RegisterVar(&cv_moviemode);
CV_RegisterVar(&cv_movie_option); CV_RegisterVar(&cv_movie_option);
CV_RegisterVar(&cv_movie_folder); CV_RegisterVar(&cv_movie_folder);
M_AVRecorder_AddCommands();
// PNG variables // PNG variables
CV_RegisterVar(&cv_zlib_level); CV_RegisterVar(&cv_zlib_level);
CV_RegisterVar(&cv_zlib_memory); CV_RegisterVar(&cv_zlib_memory);

View file

@ -185,6 +185,18 @@ void M_AVRecorder_Close(void)
g_av_recorder.reset(); 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) boolean M_AVRecorder_IsExpired(void)
{ {
SRB2_ASSERT(g_av_recorder != nullptr); SRB2_ASSERT(g_av_recorder != nullptr);

View file

@ -18,6 +18,8 @@ extern "C" {
void M_AVRecorder_AddCommands(void); void M_AVRecorder_AddCommands(void);
const char *M_AVRecorder_GetFileExtension(void);
// True if successully opened. // True if successully opened.
boolean M_AVRecorder_Open(const char *filename); 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. // Check whether AVRecorder is still valid. Call M_AVRecorder_Close if expired.
boolean M_AVRecorder_IsExpired(void); boolean M_AVRecorder_IsExpired(void);
const char *M_AVRecorder_GetCurrentFormat(void);
extern consvar_t extern consvar_t
cv_movie_custom_resolution, cv_movie_custom_resolution,
cv_movie_duration, cv_movie_duration,

View file

@ -44,6 +44,7 @@
#include "command.h" // cv_execversion #include "command.h" // cv_execversion
#include "m_anigif.h" #include "m_anigif.h"
#include "m_avrecorder.h"
// So that the screenshot menu auto-updates... // So that the screenshot menu auto-updates...
#include "k_menu.h" #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); 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}}; 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", "GIF", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange); 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_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); 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 #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) void M_StartMovie(void)
{ {
#if NUMSCREENS > 2 #if NUMSCREENS > 2
@ -1332,6 +1352,9 @@ void M_StartMovie(void)
case MM_SCREENSHOT: case MM_SCREENSHOT:
moviemode = MM_SCREENSHOT; moviemode = MM_SCREENSHOT;
break; break;
case MM_AVRECORDER:
moviemode = M_StartMovieAVRecorder(pathname);
break;
default: //??? default: //???
return; return;
} }
@ -1342,6 +1365,8 @@ void M_StartMovie(void)
CONS_Printf(M_GetText("Movie mode enabled (%s).\n"), "GIF"); CONS_Printf(M_GetText("Movie mode enabled (%s).\n"), "GIF");
else if (moviemode == MM_SCREENSHOT) else if (moviemode == MM_SCREENSHOT)
CONS_Printf(M_GetText("Movie mode enabled (%s).\n"), "screenshots"); 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); //singletics = (moviemode != MM_OFF);
#endif #endif
@ -1353,6 +1378,15 @@ void M_SaveFrame(void)
// paranoia: should be unnecessary without singletics // paranoia: should be unnecessary without singletics
static tic_t oldtic = 0; static tic_t oldtic = 0;
if (moviemode == MM_AVRECORDER)
{
if (M_AVRecorder_IsExpired())
{
M_StopMovie();
}
return;
}
if (oldtic == I_GetTime()) if (oldtic == I_GetTime())
return; return;
else else
@ -1440,6 +1474,9 @@ void M_StopMovie(void)
#endif #endif
case MM_SCREENSHOT: case MM_SCREENSHOT:
break; break;
case MM_AVRECORDER:
M_AVRecorder_Close();
break;
default: default:
return; return;
} }

View file

@ -29,7 +29,8 @@ typedef enum {
MM_OFF = 0, MM_OFF = 0,
MM_APNG, MM_APNG,
MM_GIF, MM_GIF,
MM_SCREENSHOT MM_SCREENSHOT,
MM_AVRECORDER,
} moviemode_t; } moviemode_t;
extern moviemode_t moviemode; extern moviemode_t moviemode;

View file

@ -170,6 +170,11 @@ void Impl::worker()
valid_ = false; valid_ = false;
} }
const char* AVRecorder::file_extension()
{
return "webm";
}
AVRecorder::AVRecorder(const Config config) : impl_(std::make_unique<Impl>(config)) AVRecorder::AVRecorder(const Config config) : impl_(std::make_unique<Impl>(config))
{ {
} }
@ -186,6 +191,11 @@ AVRecorder::~AVRecorder()
std::thread([_ = std::move(impl_)] {}).detach(); 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) void AVRecorder::push_audio_samples(audio_buffer_t buffer)
{ {
const auto _ = impl_->queue_guard(); const auto _ = impl_->queue_guard();

View file

@ -57,11 +57,18 @@ public:
std::optional<Video> video; std::optional<Video> video;
}; };
// Returns the canonical file extension minus the dot.
// E.g. "webm" (not ".webm").
static const char* file_extension();
AVRecorder(Config config); AVRecorder(Config config);
~AVRecorder(); ~AVRecorder();
void push_audio_samples(audio_buffer_t buffer); void push_audio_samples(audio_buffer_t buffer);
// Proper name of the container format.
const char* format_name() const;
// True if this instance has terminated. Continuing to use // True if this instance has terminated. Continuing to use
// this interface is useless and the object should be // this interface is useless and the object should be
// destructed immediately. // destructed immediately.