#include #include #include #include #include #include "ffmpeg.h" FFMPEG* ffmpegStart(const char* filename, uint32_t width, uint32_t height, uint32_t fps) { int pipefd[2]; if (pipe(pipefd) < 0) { TraceLog(LOG_ERROR, "FFMPEG: Could not create a pipe: %s", strerror(errno)); return NULL; } pid_t child = fork(); if (child < 0) { TraceLog(LOG_ERROR, "FFMPEG: could not fork a child: %s", strerror(errno)); return NULL; } if (child == 0) { if (dup2(pipefd[READ_END], STDIN_FILENO) < 0) { TraceLog(LOG_ERROR, "FFMPEG CHILD: could not reopen read end of pipe as stdin: %s", strerror(errno)); exit(1); } close(pipefd[WRITE_END]); char resolution[64]; snprintf(resolution, sizeof(resolution), "%zux%zu", (size_t)width, (size_t)height); char framerate[64]; snprintf(framerate, sizeof(framerate), "%zu", (size_t)fps); int ret = execlp("ffmpeg", "ffmpeg", "-loglevel", "verbose", "-y", "-f", "rawvideo", "-pix_fmt", "rgba", "-s", resolution, "-r", framerate, "-i", "-", "-c:v", "libx264", "-vb", "2500k", "-c:a", "aac", "-ab", "200k", "-pix_fmt", "yuv420p", filename, NULL ); if (ret < 0) { TraceLog(LOG_ERROR, "FFMPEG CHILD: could not run ffmpeg as a child process: %s", strerror(errno)); exit(1); } assert(0 && "unreachable"); exit(1); } if (close(pipefd[READ_END]) < 0) { TraceLog(LOG_WARNING, "FFMPEG: could not close read end of the pipe on the parent's end: %s", strerror(errno)); } FFMPEG* ffmpeg = malloc(sizeof(FFMPEG)); assert(ffmpeg != NULL && "Buy MORE RAM lol!!"); ffmpeg->pid = child; ffmpeg->pipe = pipefd[WRITE_END]; return ffmpeg; } int ffmpegSendFrame(FFMPEG* ffmpeg, void* data, uint32_t width, uint32_t height) { for (uint32_t y = height; y > 0; --y) { if (write(ffmpeg->pipe, (uint32_t*)data + (y - 1) * width, sizeof(uint32_t) * width) < 0) { TraceLog(LOG_ERROR, "FFMPEG: failed to write into ffmpeg pipe: %s", strerror(errno)); return -1; } } return 0; } int ffmpegEnd(FFMPEG* ffmpeg, bool cancel) { int pipe = ffmpeg->pipe; pid_t pid = ffmpeg->pid; free(ffmpeg); if (close(pipe) < 0) { TraceLog(LOG_WARNING, "FFMPEG: could not close write end of the pipe on the parent's end: %s", strerror(errno)); } if (cancel) { kill(pid, SIGKILL); } while (true) { int wstatus = 0; if (waitpid(pid, &wstatus, 0) < 0) { TraceLog(LOG_ERROR, "FFMPEG: could not wait for ffmpeg child process to finish: %s", strerror(errno)); return -1; } if (WIFEXITED(wstatus)) { int exit_status = WEXITSTATUS(wstatus); if (exit_status != 0) { TraceLog(LOG_ERROR, "FFMPEG: ffmpeg exited with code %d", exit_status); return -1; } return 0; } if (WIFSIGNALED(wstatus)) { TraceLog(LOG_ERROR, "FFMPEG: ffmpeg got terminated by %s", strsignal(WTERMSIG(wstatus))); return -1; } } assert(0 && "unreachable"); return 0; }