Forráskód Böngészése

Initial Windows implementation

rexim 2 éve
szülő
commit
22d6c6169d
1 módosított fájl, 143 hozzáadás és 0 törlés
  1. 143 0
      ffmpeg_windows.c

+ 143 - 0
ffmpeg_windows.c

@@ -0,0 +1,143 @@
+#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;
+    }
+
+    // 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 -t 10 -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);
+}
+