Browse Source

Merge branch 'windows'

rexim 2 years ago
parent
commit
43cd6e8652
4 changed files with 180 additions and 22 deletions
  1. 8 4
      ffmpeg.h
  2. 23 13
      ffmpeg_linux.c
  3. 148 0
      ffmpeg_windows.c
  4. 1 5
      main_raw.c

+ 8 - 4
ffmpeg.h

@@ -3,9 +3,13 @@
 
 #include <stddef.h>
 
-int ffmpeg_start_rendering(size_t width, size_t height, size_t fps);
-void ffmpeg_send_frame(int pipe, void *data, size_t width, size_t height);
-void ffmpeg_send_frame_flipped(int pipe, void *data, size_t width, size_t height);
-void ffmpeg_end_rendering(int pipe);
+typedef void FFMPEG;
+
+FFMPEG *ffmpeg_start_rendering(size_t width, size_t height, size_t fps);
+// TODO: handle potential error that may happen in all of the ffmpeg function
+void ffmpeg_send_frame(FFMPEG *ffmpeg, void *data, size_t width, size_t height);
+// TODO: use -vflip of ffmpeg instead
+void ffmpeg_send_frame_flipped(FFMPEG *ffmpeg, void *data, size_t width, size_t height);
+void ffmpeg_end_rendering(FFMPEG *ffmpeg);
 
 #endif // FFMPEG_H_

+ 23 - 13
ffmpeg_linux.c

@@ -1,5 +1,6 @@
 #include <assert.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
@@ -11,25 +12,30 @@
 #define READ_END 0
 #define WRITE_END 1
 
-int ffmpeg_start_rendering(size_t width, size_t height, size_t fps)
+typedef struct {
+    int pid;
+    int pipe;
+} FFMPEG;
+
+FFMPEG *ffmpeg_start_rendering(size_t width, size_t height, size_t fps)
 {
     int pipefd[2];
 
     if (pipe(pipefd) < 0) {
         fprintf(stderr, "ERROR: could not create a pipe: %s\n", strerror(errno));
-        return -1;
+        return NULL;
     }
 
     pid_t child = fork();
     if (child < 0) {
         fprintf(stderr, "ERROR: could not fork a child: %s\n", strerror(errno));
-        return -1;
+        return NULL;
     }
 
     if (child == 0) {
         if (dup2(pipefd[READ_END], STDIN_FILENO) < 0) {
             fprintf(stderr, "ERROR: could not reopen read end of pipe as stdin: %s\n", strerror(errno));
-            return -1;
+            exit(1);
         }
         close(pipefd[WRITE_END]);
 
@@ -60,30 +66,34 @@ int ffmpeg_start_rendering(size_t width, size_t height, size_t fps)
         );
         if (ret < 0) {
             fprintf(stderr, "ERROR: could not run ffmpeg as a child process: %s\n", strerror(errno));
-            return -1;
+            exit(1);
         }
         assert(0 && "unreachable");
     }
 
     close(pipefd[READ_END]);
 
-    return pipefd[WRITE_END];
+    FFMPEG *ffmpeg = malloc(sizeof(FFMPEG));
+    assert(ffmpeg != NULL && "Buy MORE RAM lol!!");
+    ffmpeg->pid = child;
+    ffmpeg->pipe = pipefd[WRITE_END];
+    return ffmpeg;
 }
 
-void ffmpeg_end_rendering(int pipe)
+void ffmpeg_end_rendering(FFMPEG *ffmpeg)
 {
-    close(pipe);
-    wait(NULL);
+    close(ffmpeg->pipe);
+    waitpid(ffmpeg->pid, NULL, 0);
 }
 
-void ffmpeg_send_frame(int pipe, void *data, size_t width, size_t height)
+void ffmpeg_send_frame(FFMPEG *ffmpeg, void *data, size_t width, size_t height)
 {
-    write(pipe, data, sizeof(uint32_t)*width*height);
+    write(ffmpeg->pipe, data, sizeof(uint32_t)*width*height);
 }
 
-void ffmpeg_send_frame_flipped(int pipe, void *data, size_t width, size_t height)
+void ffmpeg_send_frame_flipped(FFMPEG *ffmpeg, void *data, size_t width, size_t height)
 {
     for (size_t y = height; y > 0; --y) {
-        write(pipe, (uint32_t*)data + (y - 1)*width, sizeof(uint32_t)*width);
+        write(ffmpeg->pipe, (uint32_t*)data + (y - 1)*width, sizeof(uint32_t)*width);
     }
 }

+ 148 - 0
ffmpeg_windows.c

@@ -0,0 +1,148 @@
+#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 -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);
+}
+

+ 1 - 5
main_raw.c

@@ -4,10 +4,6 @@
 #include <string.h>
 #include <errno.h>
 
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
 #define OLIVEC_IMPLEMENTATION
 #include "olive.c"
 
@@ -22,7 +18,7 @@ uint32_t pixels[WIDTH*HEIGHT];
 
 int main(void)
 {
-    int ffmpeg = ffmpeg_start_rendering(WIDTH, HEIGHT, FPS);
+    FFMPEG *ffmpeg = ffmpeg_start_rendering(WIDTH, HEIGHT, FPS);
 
     Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);