| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- #include <assert.h>
- #include <stdio.h>
- #include <stdint.h>
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- typedef struct {
- HANDLE hProcess;
- // HANDLE hPipeRead;
- HANDLE hPipeWrite;
- } FFMPEG;
- static LPSTR GetLastErrorAsString(void)
- {
- // https://stackoverflow.com/questions/1387064/how-to-get-the-error-message-from-the-error-code-returned-by-getlasterror
- DWORD errorMessageId = GetLastError();
- assert(errorMessageId != 0);
- LPSTR messageBuffer = NULL;
- DWORD size =
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // DWORD dwFlags,
- NULL, // LPCVOID lpSource,
- errorMessageId, // DWORD dwMessageId,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // DWORD dwLanguageId,
- (LPSTR) &messageBuffer, // LPTSTR lpBuffer,
- 0, // DWORD nSize,
- NULL // va_list *Arguments
- );
- return messageBuffer;
- }
- FFMPEG *ffmpeg_start_rendering(size_t width, size_t height, size_t fps)
- {
- HANDLE pipe_read;
- HANDLE pipe_write;
- SECURITY_ATTRIBUTES saAttr = {0};
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- if (!CreatePipe(&pipe_read, &pipe_write, &saAttr, 0)) {
- fprintf(stderr, "ERROR: Could not create pipe: %s\n", GetLastErrorAsString());
- return NULL;
- }
- if (!SetHandleInformation(pipe_write, HANDLE_FLAG_INHERIT, 0)) {
- fprintf(stderr, "ERROR: Could not SetHandleInformation: %s\n", GetLastErrorAsString());
- return NULL;
- }
- // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output
- STARTUPINFO siStartInfo;
- ZeroMemory(&siStartInfo, sizeof(siStartInfo));
- siStartInfo.cb = sizeof(STARTUPINFO);
- // NOTE: theoretically setting NULL to std handles should not be a problem
- // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior
- siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
- // TODO(#32): check for errors in GetStdHandle
- siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- siStartInfo.hStdInput = pipe_read;
- siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
- PROCESS_INFORMATION piProcInfo;
- ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
- char cmd_buffer[1024*2];
- snprintf(cmd_buffer, sizeof(cmd_buffer), "ffmpeg.exe -loglevel verbose -y -f rawvideo -pix_fmt rgba -s %dx%d -r %d -i - -c:v libx264 -vb 2500k -c:a aac -ab 200k -pix_fmt yuv420p output.mp4", width, height, fps);
- BOOL bSuccess =
- CreateProcess(
- NULL,
- cmd_buffer,
- NULL,
- NULL,
- TRUE,
- 0,
- NULL,
- NULL,
- &siStartInfo,
- &piProcInfo
- );
- if (!bSuccess) {
- fprintf(stderr, "ERROR: Could not create child process: %s\n", GetLastErrorAsString());
- return NULL;
- }
- CloseHandle(pipe_read);
- CloseHandle(piProcInfo.hThread);
- // TODO: use Windows specific allocation stuff?
- FFMPEG *ffmpeg = malloc(sizeof(FFMPEG));
- assert(ffmpeg != NULL && "Buy MORE RAM lol!!");
- ffmpeg->hProcess = piProcInfo.hProcess;
- // ffmpeg->hPipeRead = pipe_read;
- ffmpeg->hPipeWrite = pipe_write;
- return ffmpeg;
- }
- void ffmpeg_send_frame(FFMPEG *ffmpeg, void *data, size_t width, size_t height)
- {
- WriteFile(ffmpeg->hPipeWrite, data, sizeof(uint32_t)*width*height, NULL, NULL);
- }
- void ffmpeg_send_frame_flipped(FFMPEG *ffmpeg, void *data, size_t width, size_t height)
- {
- for (size_t y = height; y > 0; --y) {
- WriteFile(ffmpeg->hPipeWrite, (uint32_t*)data + (y - 1)*width, sizeof(uint32_t)*width, NULL, NULL);
- }
- }
- void ffmpeg_end_rendering(FFMPEG *ffmpeg)
- {
- FlushFileBuffers(ffmpeg->hPipeWrite);
- // FlushFileBuffers(ffmpeg->hPipeRead);
- CloseHandle(ffmpeg->hPipeWrite);
- // CloseHandle(ffmpeg->hPipeRead);
- DWORD result = WaitForSingleObject(
- ffmpeg->hProcess, // HANDLE hHandle,
- INFINITE // DWORD dwMilliseconds
- );
- if (result == WAIT_FAILED) {
- fprintf(stderr, "ERROR: could not wait on child process: %s\n", GetLastErrorAsString());
- return;
- }
- DWORD exit_status;
- if (GetExitCodeProcess(ffmpeg->hProcess, &exit_status) == 0) {
- fprintf(stderr, "ERROR: could not get process exit code: %lu\n", GetLastError());
- return;
- }
- if (exit_status != 0) {
- fprintf(stderr, "ERROR: command exited with exit code %lu\n", exit_status);
- return;
- }
- CloseHandle(ffmpeg->hProcess);
- }
|