From de0cd8796d835061898edb3ea89d9a3c84df859f Mon Sep 17 00:00:00 2001 From: Matthew Wozniak Date: Sun, 10 Nov 2024 22:40:14 -0500 Subject: Small code changes; add ability to find audio functions and data. Audio is weird. I've been working for a few days to figure out how to correctly render the audio but in that time I've done some other minor refactors so I figured I migth as well just push those. Audio should be coming soon, but no promises. --- render.c | 147 ++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 49 deletions(-) (limited to 'render.c') diff --git a/render.c b/render.c index 4b633a4..7b03e79 100644 --- a/render.c +++ b/render.c @@ -16,18 +16,44 @@ #include "api.h" #include "log.h" +#include "hook.h" #include "render.h" #include "os.h" -#include +#include #include -#include +#include #include +#include +#include #include #include #include #include +extern struct { + int width; + int height; + const char *game; + int fps; + int quality; + int bitrate; + int qvs; + const char *out; + const char **demo; + int demo_count; + bool combine; +} args; + +struct renderctx { + GUID enc_format; + GUID in_fmt; + IMFSinkWriter *sink_writer; + ulong stream_index; + ulong audio_index; + int cur_demo; +}; + static struct renderctx ctx = { 0 }; static bool is_rendering = false; @@ -39,50 +65,63 @@ static bool is_rendering = false; bool do_frame(struct videomode *this, struct movieinfo *info) { is_rendering = true; - IMFSample *sample = NULL; - IMFMediaBuffer *imf_buffer = NULL; - int szpx = 4; - u32 *data = NULL; + const int szpx = 3; - HR(MFCreateMemoryBuffer(ctx.width * ctx.height * szpx, &imf_buffer)); + static struct { u8 bgr[3]; } *raw = 0; + if (!raw) raw = malloc(args.width * args.height * szpx); + + IMFMediaBuffer *imf_buffer = 0; + HR(MFCreateMemoryBuffer(args.width * args.height * szpx, &imf_buffer)); + struct { u8 bgr[3]; } *data = 0; HR(imf_buffer->lpVtbl->Lock(imf_buffer, (u8 **)&data, NULL, NULL)); // THIS IS SLOW!! - u32 *raw = malloc(ctx.width * ctx.height * szpx); - this->vt->read_screen_pixels(this, 0, 0, ctx.width, ctx.height, raw, IMAGE_FORMAT_BGRA8888); - for (int y = ctx.height - 1; y > 0; y--) { - memcpy(data + ((ctx.height - y) * ctx.width), raw + y * ctx.width, - szpx * ctx.width); - /* for (int x = 0; x < ctx.width; x++) { - data[(ctx.height - y) * ctx.width + x] = raw[y * ctx.width + x]; - } */ + this->vt->read_screen_pixels(this, 0, 0, args.width, args.height, raw, IMAGE_FORMAT_BGR888); + for (int y = args.height - 1; y > 0; y--) { + memcpy(data + ((args.height - y) * args.width), raw + y * args.width, + szpx * args.width); } - free(raw); HR(imf_buffer->lpVtbl->Unlock(imf_buffer)); - HR(imf_buffer->lpVtbl->SetCurrentLength(imf_buffer, ctx.width * ctx.height * szpx)); + HR(imf_buffer->lpVtbl->SetCurrentLength(imf_buffer, args.width * args.height * szpx)); + IMFSample *sample = 0; HR(MFCreateSample(&sample)); HR(sample->lpVtbl->AddBuffer(sample, imf_buffer)); - u64 sample_duration = (u64)((1.0 / (double)ctx.fps) * 10000000.0); + u64 sample_duration = (u64)((1.0 / (double)args.fps) * 10000000.0); HR(sample->lpVtbl->SetSampleDuration(sample, sample_duration)); u64 sample_time = sample_duration * info->curframe; HR(sample->lpVtbl->SetSampleTime(sample, sample_time)); HR(ctx.sink_writer->lpVtbl->WriteSample(ctx.sink_writer, ctx.stream_index, sample)); + HR(sample->lpVtbl->Release(sample)); + HR(imf_buffer->lpVtbl->Release(imf_buffer)); - sample->lpVtbl->Release(sample); - imf_buffer->lpVtbl->Release(imf_buffer); return true; } typeof((*videomode)->vt->write_movie_frame) orig_write_movie_frame; void VIRTUAL hook_write_movie_frame(struct videomode *this, struct movieinfo *info) { - if (!do_frame(this, info)) + if (!do_frame(this, info)) die("oopsie!"); } +bool do_audio_frame(void) { + // TODO: audio +#define CLIP16(n) (i16)(n < -32768 ? -32768 : (n > 32767 ? 32767 : n)) +#undef CLIP16 + return true; +} + +void (*orig_snd_recordbuffer)(void) = 0; +void hook_snd_recordbuffer(void) { + if (!is_rendering) { + orig_snd_recordbuffer(); + return; + } + do_audio_frame(); +} bool do_stop() { - HR(ctx.sink_writer->lpVtbl->Flush(ctx.sink_writer, ctx.stream_index)); + //HR(ctx.sink_writer->lpVtbl->Flush(ctx.sink_writer, ctx.stream_index)); HR(ctx.sink_writer->lpVtbl->Finalize(ctx.sink_writer)); HR(MFShutdown()); return true; @@ -94,14 +133,20 @@ void VIRTUAL hook_stop_playback(struct demoplayer *this) { if (!is_rendering) return; is_rendering = false; + + // do we have any more demos to play? + if (args.demo_count--) { + demoplayer->vt->start_playback(demoplayer, *++args.demo, false); + return; + } + if (!do_stop()) die("oopsie!"); - // TODO: IPC mode so we don't have to restart the game for every demo + exit(0); } -bool render_init(int width, int height, int framerate, bool use_bitrate, - int quality, const char *output_file, int qvs) { +bool render_init(void) { bool r = os_mprot((*videomode)->vt, 28 * sizeof(void *), PAGE_EXECUTE_READWRITE); if (!r) bail("couldn't mprotect videomode vtable"); @@ -113,20 +158,19 @@ bool render_init(int width, int height, int framerate, bool use_bitrate, orig_stop_playback = demoplayer->vt->stop_playback; demoplayer->vt->stop_playback = hook_stop_playback; + // hook the audio frame + orig_snd_recordbuffer = (void (*)(void)) + hook_inline((void *)snd_recordbuffer, (void *)hook_snd_recordbuffer); + // make the game thing we are recording a movie (we are!) memcpy(movieinfo->name, "a", 2); movieinfo->type = 0; char framerate_cmd[32] = { 0 }; - sprintf(framerate_cmd, "host_framerate %d;", framerate); + sprintf(framerate_cmd, "host_framerate %d;", args.fps); cbuf_addtext(framerate_cmd); - ctx.fps = framerate; - ctx.width = width; - ctx.height = height; - ctx.bit_rate = use_bitrate ? quality : 99999999; - - MFStartup(MF_VERSION, 0); + HR(MFStartup(MF_VERSION, 0)); // init sinkwriter { @@ -134,34 +178,35 @@ bool render_init(int width, int height, int framerate, bool use_bitrate, IMFMediaType *mt_in = NULL; u16 path[MAX_PATH] = {0}; - info("strlen = %d", strlen(output_file)); - mbstowcs(path, output_file, strlen(output_file)); + mbstowcs(path, args.out, strlen(args.out)); info("rendering to %S", path); - HR(MFCreateSinkWriterFromURL(path, NULL, NULL, &ctx.sink_writer)); + IMFAttributes *sinkwriter_attr; + HR(MFCreateAttributes(&sinkwriter_attr, 1)); + HR(sinkwriter_attr->lpVtbl->SetUINT32(sinkwriter_attr, &MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE)); + HR(MFCreateSinkWriterFromURL(path, NULL, sinkwriter_attr, &ctx.sink_writer)); + HR(MFCreateMediaType(&mt_out)); HR(mt_out->lpVtbl->SetGUID(mt_out, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)); HR(mt_out->lpVtbl->SetGUID(mt_out, &MF_MT_SUBTYPE, &MFVideoFormat_H264)); - HR(mt_out->lpVtbl->SetUINT32(mt_out, &MF_MT_AVG_BITRATE, ctx.bit_rate)); + HR(mt_out->lpVtbl->SetUINT32(mt_out, &MF_MT_AVG_BITRATE, args.bitrate ? args.bitrate : 9999999)); HR(mt_out->lpVtbl->SetUINT32(mt_out, &MF_MT_MPEG2_PROFILE , eAVEncH264VProfile_Main)); HR(mt_out->lpVtbl->SetUINT32(mt_out, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)); - HR(mt_out->lpVtbl->SetUINT64(mt_out, &MF_MT_FRAME_SIZE, ((u64)ctx.width << 32) | ctx.height)); - HR(mt_out->lpVtbl->SetUINT64(mt_out, &MF_MT_FRAME_RATE, ((u64)ctx.fps << 32) | 1)); + HR(mt_out->lpVtbl->SetUINT64(mt_out, &MF_MT_FRAME_SIZE, ((u64)args.width << 32) | args.height)); + HR(mt_out->lpVtbl->SetUINT64(mt_out, &MF_MT_FRAME_RATE, ((u64)args.fps << 32) | 1)); HR(mt_out->lpVtbl->SetUINT64(mt_out, &MF_MT_PIXEL_ASPECT_RATIO, (((u64)1 << 32) | 1))); HR(ctx.sink_writer->lpVtbl->AddStream(ctx.sink_writer, mt_out, &ctx.stream_index)); HR(MFCreateMediaType(&mt_in)); HR(mt_in->lpVtbl->SetGUID(mt_in, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)); - HR(mt_in->lpVtbl->SetGUID(mt_in, &MF_MT_SUBTYPE, &MFVideoFormat_ARGB32)); + HR(mt_in->lpVtbl->SetGUID(mt_in, &MF_MT_SUBTYPE, &MFVideoFormat_RGB24)); HR(mt_in->lpVtbl->SetUINT32(mt_in, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)); - HR(mt_in->lpVtbl->SetUINT64(mt_in, &MF_MT_FRAME_SIZE, ((u64)ctx.width << 32) | ctx.height)); - HR(mt_in->lpVtbl->SetUINT64(mt_in, &MF_MT_FRAME_RATE, ((u64)ctx.fps << 32) | 1)); + HR(mt_in->lpVtbl->SetUINT32(mt_in, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE)); + HR(mt_in->lpVtbl->SetUINT64(mt_in, &MF_MT_FRAME_SIZE, ((u64)args.width << 32) | args.height)); + HR(mt_in->lpVtbl->SetUINT64(mt_in, &MF_MT_FRAME_RATE, ((u64)args.fps << 32) | 1)); HR(mt_in->lpVtbl->SetUINT64(mt_in, &MF_MT_PIXEL_ASPECT_RATIO, (((u64)1 << 32) | 1))); HR(ctx.sink_writer->lpVtbl->SetInputMediaType(ctx.sink_writer, ctx.stream_index, mt_in, NULL)); - - HR(ctx.sink_writer->lpVtbl->BeginWriting(ctx.sink_writer)); - HR(ctx.sink_writer->lpVtbl->AddRef(ctx.sink_writer)); } // video encoding parameters @@ -169,20 +214,22 @@ bool render_init(int width, int height, int framerate, bool use_bitrate, ICodecAPI *codec; HR(ctx.sink_writer->lpVtbl->GetServiceForStream(ctx.sink_writer, 0, &GUID_NULL, &IID_ICodecAPI, (void*)&codec)); - // use a quality VBR - int ratecontrol = use_bitrate ? eAVEncCommonRateControlMode_UnconstrainedVBR : eAVEncCommonRateControlMode_Quality; + int ratecontrol = !!args.bitrate ? eAVEncCommonRateControlMode_UnconstrainedVBR : eAVEncCommonRateControlMode_Quality; VARIANT _ratecontrol = { .vt = VT_UI4, .ulVal = ratecontrol }; HR(codec->lpVtbl->SetValue(codec, &CODECAPI_AVEncCommonRateControlMode, &_ratecontrol)); // set the quality - VARIANT _quality = { .vt = VT_UI4, .ulVal = quality }; + VARIANT _quality = { + .vt = VT_UI4, + .ulVal = !!args.bitrate ? args.bitrate : args.quality + }; HR(codec->lpVtbl->SetValue(codec, &CODECAPI_AVEncCommonQuality, &_quality)); - VARIANT _qvs = { .vt = VT_UI4, .ulVal = qvs }; + VARIANT _qvs = { .vt = VT_UI4, .ulVal = args.qvs }; HR(codec->lpVtbl->SetValue(codec, &CODECAPI_AVEncCommonQualityVsSpeed, &_qvs)); // set the gop size to 2 seconds - VARIANT gop = { .vt = VT_UI4, .ulVal = 2 * ctx.fps }; + VARIANT gop = { .vt = VT_UI4, .ulVal = 2 * args.fps }; HR(codec->lpVtbl->SetValue(codec, &CODECAPI_AVEncMPVGOPSize, &gop)); // enable 2 b-frames @@ -192,6 +239,8 @@ bool render_init(int width, int height, int framerate, bool use_bitrate, codec->lpVtbl->Release(codec); } + HR(ctx.sink_writer->lpVtbl->BeginWriting(ctx.sink_writer)); + return true; } -- cgit v1.2.3-54-g00ecf