aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README4
-rw-r--r--api.c140
-rw-r--r--api.h55
-rw-r--r--log.h3
-rw-r--r--main.c15
-rw-r--r--render.c147
-rw-r--r--render.h20
8 files changed, 281 insertions, 105 deletions
diff --git a/Makefile b/Makefile
index 40fe2a6..c5ccb5f 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CC = clang
WARNINGS=-Wall -Wpedantic -Wextra -Wno-gnu-zero-variadic-macro-arguments \
-D_CRT_SECURE_NO_WARNINGS
override CFLAGS+=-m32 -flto -I3p -std=c23
-override LDFLAGS+=-lmf -lmfplat -lmfuuid -lmfreadwrite -lstrmiids
+override LDFLAGS+=-lmf -lmfplat -lole32 -lmfuuid -lmfreadwrite -lstrmiids
OBJS = main.o api.o 3p/sst/x86.o hook.o render.o
diff --git a/README b/README
index 3e7f87f..70ceb1b 100644
--- a/README
+++ b/README
@@ -4,11 +4,9 @@ rendertools
orange box engine demo renderer
usage:
- rt [-w <width>] [-h <height>] [-g <game>] [-r <fps>] [-s <qvs>] [-1] [-q <quality>] OR [-b <bitrate>]
+ rt [-w <width>] [-h <height>] [-g <game>] [-r <fps>] [-s <qvs>] [-q <quality>] OR [-b <bitrate>]
path/to/video.mp4 path/to/demo1.dem...
--1 flag combines multiple renders into 1 file (UNIMPLEMENTED)
-
bitrate is in kbps, quality 1-100
cannot specify quality and bitrate at the same time
demo path is relative to the gamedir (e.g. hl2)
diff --git a/api.c b/api.c
index 0562bba..f2be734 100644
--- a/api.c
+++ b/api.c
@@ -21,14 +21,28 @@
#include "log.h"
#include "sst/x86.h"
-struct engserver *engserver = NULL;
-struct engclient *engclient = NULL;
-struct engineapi *engineapi = NULL;
-struct enginetools *enginetools = NULL;
-struct demoplayer *demoplayer = NULL;
-struct videomode **videomode = NULL;
-struct movieinfo *movieinfo = NULL;
-void (*cbuf_addtext)(char *) = NULL;
+struct engserver *engserver = 0;
+struct engclient *engclient = 0;
+struct engineapi *engineapi = 0;
+struct enginetools *enginetools = 0;
+struct cvar *cvar = 0;
+struct demoplayer *demoplayer = 0;
+struct videomode **videomode = 0;
+struct movieinfo *movieinfo = 0;
+struct audio_device **audio_device = 0;
+struct soundstate *snd = 0;
+void (*snd_recordbuffer)(void) = 0;
+void (*cbuf_addtext)(char *) = 0;
+
+// This macro is Copyright 2024 Michael Smith under the same license as above.
+#define NEXT_INSN(p, tgt) do { \
+ int _len = x86_len(p); \
+ if (_len == -1) { \
+ bail("unknown or invalid instruction looking for %s", tgt); \
+ return false; \
+ } \
+ (p) += _len; \
+} while (0)
// TODO: should this be split up?
@@ -36,15 +50,22 @@ bool api_init(void) {
void *engine_dll = os_dlhandle("engine");
createinterface_func engine_factory =
(createinterface_func)os_dlsym(engine_dll, "CreateInterface");
+
+ void *vstdlib_dll = os_dlhandle("vstdlib");
+ createinterface_func vstdlib_factory =
+ (createinterface_func)os_dlsym(vstdlib_dll, "CreateInterface");
+
if (!engine_factory) bail("couldn't get engine factory");
- engserver = engine_factory(VENGINE_SERVER_INTERFACE_VERSION, NULL);
+ engserver = engine_factory(VENGINE_SERVER_INTERFACE_VERSION, 0);
if (!engserver) bail("couldn't get IVEngineServer from engine");
- engclient = engine_factory(VENGINE_CLIENT_INTERFACE_VERSION, NULL);
+ engclient = engine_factory(VENGINE_CLIENT_INTERFACE_VERSION, 0);
if (!engclient) bail("couldn't get IVEngineClient from engine");
- engineapi = engine_factory(VENGINE_LAUNCHER_INTERFACE_VERSION, NULL);
+ engineapi = engine_factory(VENGINE_LAUNCHER_INTERFACE_VERSION, 0);
if (!engineapi) bail("couldn't get IEngineAPI from engine");
- enginetools = engine_factory(VENGINE_TOOL_INTERFACE_VERSION, NULL);
+ enginetools = engine_factory(VENGINE_TOOL_INTERFACE_VERSION, 0);
if (!engineapi) bail("couldn't get IEngineTools from engine");
+ cvar = vstdlib_factory(VENGINE_CVAR_INTERFACE_VERSION, 0);
+ if (!cvar) bail("couldn't get ICVar from vstdlib");
// find cbuf_addtext
const u8 *instr = (const u8 *)engserver->vt->server_command;
@@ -55,10 +76,7 @@ bool api_init(void) {
// jump is relative to after the instruction
cbuf_addtext = (void (*)(char *))(p + 5 + *(i32 *)(p + 1));
}
- int l = x86_len(p);
- if (l == -1)
- bail("invalid instruction looking for cbuf_addtext");
- p += l;
+ NEXT_INSN(p, "CBuf_AddText");
}
if (!cbuf_addtext) bail("couldn't find cbuf_addtext");
@@ -85,10 +103,7 @@ bool api_init(void) {
videomode = *(struct videomode ***)(p + 2);
break;
}
- int l = x86_len(p);
- if (l == -1)
- bail("invalid instruction looking for videomode");
- p += l;
+ NEXT_INSN(p, "videomode");
}
debug("videomode = %p", (void *)videomode);
@@ -97,16 +112,13 @@ bool api_init(void) {
// step 1: find CL_IsRecordingMovie() in
// CEngineTools::StartMovieRecording
instr = (const u8 *)enginetools->vt->start_movie_recording;
- void *cl_isrecordingmovie = NULL;
+ void *cl_isrecordingmovie = 0;
for (const u8 *p = instr; p - instr < 64;) {
// this is the first call in the function
if (*p == X86_CALL) {
cl_isrecordingmovie = (void *)(p + 5 + *(i32 *)(p + 1));
}
- int l = x86_len(p);
- if (l == -1)
- bail("invalid instruction looking for CL_IsRecordingMovie");
- p += l;
+ NEXT_INSN(p, "CL_IsRecordingMovie");
}
if (!cl_isrecordingmovie)
bail("couldn't find cbuf_addtext CL_IsRecordingMovie");
@@ -120,6 +132,84 @@ bool api_init(void) {
debug("movieinfo = %p", (void *)movieinfo);
}
+ // find SND_RecordBuffer... this is something!
+ {
+ // step 1: Find S_Init() in snd_restart
+ struct concommand *snd_restart = cvar->vt->find_command(cvar, "snd_restart");
+ instr = (const u8 *)snd_restart->callback;
+ void *s_init = 0;
+ // S_Init is the 3rd call in Snd_Restart_f
+ int call_count = 0;
+ for (const u8 *p = instr; p - instr < 64;) {
+ if (*p == X86_CALL && ++call_count == 3)
+ s_init = (void *)(p + 5 + *(i32 *)(p + 1));
+ NEXT_INSN(p, "S_Init");
+ }
+ debug("S_Init = %p", (void *)s_init);
+ if (!s_init) bail("couldn't find S_Init");
+ // step 2: find g_AudioDevice in S_Init
+ // g_AudioDevice is the first variable assignment after the check for
+ // the -nosound command line arg. look for that.
+ instr = (const u8 *)s_init;
+ bool nosound_push = false;
+ for (const u8 *p = instr; p - instr < 180;) {
+ if (p[0] == X86_PUSHIW
+ && !strcmp(*(const char **)(p + 1), "-nosound"))
+ nosound_push = true;
+ if (nosound_push && p[0] == X86_MOVIIEAX) {
+ audio_device = *(struct audio_device ***)(p + 1);
+ break;
+ }
+ NEXT_INSN(p, "g_AudioDevice");
+ }
+ debug("g_AudioDevice = %p", (void *)audio_device);
+ if (!audio_device) bail("couldn't get ptr to g_AudioDevice");
+ }
+ return true;
+}
+
+bool api_find_snd_recordbuffer(void) {
+ // continuation, we must do this part later
+ // step 3: Find S_TransferStereo16 in CAudioDirectSound::TransferSamples
+ void *transferstereo16 = 0;
+ const u8 *instr = (*audio_device)->vt->transfer_samples;
+ debug("CAudioDirectSound::TransferSamples = %p", (void *)instr);
+ // S_TransferStereo16 is the 8th call in TransferSamples
+ int call_count = 0;
+ for (const u8 *p = instr; p - instr < 384 /* big func! */;) {
+ if (*p == X86_CALL && ++call_count == 8)
+ transferstereo16 = (void *)(p + 5 + *(i32 *)(p + 1));
+ NEXT_INSN(p, "S_TransferStereo16");
+ }
+ if (!transferstereo16)
+ bail("couldn't find transferstereo16");
+ debug("S_TransferStereo16 = %p", transferstereo16);
+ // step 4: find SND_RecordBuffer in S_TransferStereo16
+ // it should be the next call after an 'add ecx, ecx'
+ // We are also going to get Snd_WriteLinearBlastStereo16 to get some other
+ // variables we need.
+ instr = (const u8 *)transferstereo16;
+ bool found_double = false;
+ bool found_recbuf = false;
+ for (const u8 *p = instr; p - instr < 192;) {
+ if (p[0] == X86_ADDRMW && p[1] == X86_MODRM(3, 1, 1))
+ found_double = true;
+ else if (p[0] == X86_CALL && found_double && !found_recbuf) {
+ found_recbuf = true;
+ snd_recordbuffer = (void (*)(void))(p + 5 + *(i32 *)(p + 1));
+ }
+ // this will be the next call
+ else if (p[0] == X86_CALL && found_double && found_recbuf) {
+ // this looks outrageous! that is because it is.
+ snd = (struct soundstate *)(p + 5 + *(i32 *)(p + 1) + 3);
+ break;
+ }
+ NEXT_INSN(p, "SND_RecordBuffer");
+ }
+ if (!snd_recordbuffer)
+ bail("couldn't find snd_recordbuffer");
+ debug("SND_RecordBuffer = %p", (void *)snd_recordbuffer);
+ debug("Snd_WriteLinearBlastStereo16 + 3 = %p", (void *)snd);
return true;
}
diff --git a/api.h b/api.h
index 8f42eaf..df8c191 100644
--- a/api.h
+++ b/api.h
@@ -21,6 +21,7 @@
#define VENGINE_CLIENT_INTERFACE_VERSION "VEngineClient013"
#define VENGINE_LAUNCHER_INTERFACE_VERSION "VENGINE_LAUNCHER_API_VERSION004"
#define VENGINE_TOOL_INTERFACE_VERSION "VENGINETOOL003"
+#define VENGINE_CVAR_INTERFACE_VERSION "VEngineCvar004"
#include "intdef.h"
#include "os.h"
@@ -55,6 +56,34 @@ struct enginetools {
} *vt;
};
+struct cvar {
+ struct {
+ usize _pad[10];
+ struct concommand * (*VIRTUAL find_command)(struct cvar *this, const char *name);
+ } *vt;
+};
+
+struct cmdbase {
+ void **vt;
+ struct cmdbase *next;
+ bool registered;
+ const char *name;
+ const char *helpstring;
+ int flags;
+};
+
+struct concommand {
+ struct cmdbase base;
+ void *callback;
+};
+
+struct audio_device {
+ struct {
+ usize _pad[22];
+ void *transfer_samples;
+ } *vt;
+};
+
struct movieinfo {
char name[256];
int curframe;
@@ -132,6 +161,29 @@ struct demoplayer {
} *vt;
};
+/* EPIC HACK:
+ * Snd_WriteLinearBlastStereo16 is an inline asm function that starts with
+ *
+ * mov ebx,snd_p
+ * mov edi,snd_out
+ * mov ecx,snd_linear_count
+ * mov esi,snd_vol
+ *
+ * Luckily for us, these are all the things that we need!!!!
+ * So, we just use the instructions as a struct so we don't have to copy
+ * anything. It just works.
+ */
+struct soundstate {
+ u16 _mov_ebx;
+ int **snd_p;
+ u16 _mov_edi;
+ short **snd_out;
+ u16 _mov_ecx;
+ int *snd_linear_count;
+ u16 _mov_esi;
+ int *snd_vol;
+} __attribute__((packed));
+
#ifndef NO_EXTERNS
extern struct engserver *engserver;
extern struct engclient *engclient;
@@ -141,10 +193,13 @@ extern struct demoplayer *demoplayer;
extern struct videomode **videomode;
extern struct movieinfo *movieinfo;
extern void (*cbuf_addtext)(char *);
+extern void (*snd_recordbuffer)(void);
+extern struct soundstate *snd;
#endif
// initializes required engine apis. returns false on error.
bool api_init(void);
+bool api_find_snd_recordbuffer(void);
#endif
diff --git a/log.h b/log.h
index 2f866d1..584ef28 100644
--- a/log.h
+++ b/log.h
@@ -15,6 +15,7 @@
*/
#include <stdio.h>
+#include <sys/timeb.h>
#define die(fmt, ...) {\
fprintf(stderr, "err: %s: " fmt "\n", __func__ __VA_OPT__(,) __VA_ARGS__); \
@@ -34,5 +35,3 @@
#define debug(fmt, ...) \
fprintf(stderr, "dbg: %s: " fmt "\n", __func__ __VA_OPT__(,) __VA_ARGS__)
-
-// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/main.c b/main.c
index 4775b48..440e9c3 100644
--- a/main.c
+++ b/main.c
@@ -35,6 +35,7 @@ struct {
int qvs;
const char *out;
const char **demo;
+ int demo_count;
bool combine;
} args = {0};
@@ -42,13 +43,14 @@ void (*orig_cbuf_addtext)(char *);
void hook_cbuf_addtext(char *str) {
orig_cbuf_addtext(str);
// this is the last thing that happens when the game is opened
- if (!strcmp(str, "exec modsettings.cfg mod\n")) {
- bool use_bitrate = !!args.bitrate;
- if (!render_init(args.width, args.height, args.fps, use_bitrate,
- use_bitrate ? args.bitrate : args.quality, args.out, args.qvs))
+ if (!strcmp(str, "exec valve.rc\n")) {
+ // after sound has been init'd, we can finish this
+ if (!api_find_snd_recordbuffer())
+ die("couldn't find SND_RecordBuffer");
+ if (!render_init())
die("couldn't init render");
- // play the demo
demoplayer->vt->start_playback(demoplayer, args.demo[0], false);
+ orig_cbuf_addtext("exec prerender");
}
}
@@ -69,7 +71,7 @@ void WINAPI *hook_LoadLibraryExA(const char *filename, void *hfile, int flags) {
for (const char *p = filename; *p; p++)
if (*p == '\\') basename = p + 1;
- if (!strcmp(basename, "engine.dll")) {
+ if (!strcmp(basename, "gameui.dll")) {
if (!api_init()) die("couldn't get apis");
orig_cbuf_addtext = (void (*)(char *))
hook_inline((void *)cbuf_addtext, (void *)hook_cbuf_addtext);
@@ -146,6 +148,7 @@ int main(int argc, const char **argv) {
}
args.out = argv[0];
args.demo = argv + 1;
+ args.demo_count = argc - 2;
sprintf(cmdline, "hl2.exe -game %s -w %d -h %d -window -console",
args.game, args.width, args.height);
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 <codecapi.h>
+#include <combaseapi.h>
#include <mfapi.h>
-#include <mfobjects.h>
+#include <mfidl.h>
#include <mfreadwrite.h>
+#include <codecapi.h>
+#include <mftransform.h>
#include <stdlib.h>
#include <string.h>
#include <strmif.h>
#include <winerror.h>
+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;
}
diff --git a/render.h b/render.h
index 49ccce3..e437466 100644
--- a/render.h
+++ b/render.h
@@ -14,24 +14,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-#include "intdef.h"
-
-#include <mfapi.h>
-#include <mfidl.h>
-#include <mfreadwrite.h>
-
-bool render_init(int width, int height, int framerate, bool use_bitrate,
- int quality, const char *output_file, int qvs);
-
-struct renderctx {
- int width;
- int height;
- int fps;
- int bit_rate;
- GUID enc_format;
- GUID in_fmt;
- IMFSinkWriter *sink_writer;
- ulong stream_index;
-};
+bool render_init(void);
// vi: sw=4 ts=4 noet tw=80 cc=80