Mercurial
view third_party/ffmpeg/ffmpeg_cli.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 543df0fe7168 |
| children |
line wrap: on
line source
#include "third_party/ffmpeg/ffmpeg_cli.h" #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #define MAX_ERROR_LENGTH 512 #define MAX_PATH_LENGTH 1024 static char g_last_error[MAX_ERROR_LENGTH]; static void set_error(const char *format, ...) { va_list arguments; va_start(arguments, format); vsnprintf(g_last_error, sizeof(g_last_error), format, arguments); va_end(arguments); } static bool file_exists(const char *path) { struct stat status; return path && stat(path, &status) == 0 && S_ISREG(status.st_mode); } static bool ensure_directory(const char *path) { struct stat status; if (stat(path, &status) == 0) return S_ISDIR(status.st_mode); return mkdir(path, 0755) == 0; } static int run_process(char *const arguments[]) { pid_t child = fork(); if (child < 0) { set_error("fork failed: %s", strerror(errno)); return -1; } if (child == 0) { execvp(arguments[0], arguments); _exit(127); } int status = 0; while (waitpid(child, &status, 0) < 0) { if (errno == EINTR) continue; set_error("waitpid failed: %s", strerror(errno)); return -1; } if (!WIFEXITED(status)) return -1; return WEXITSTATUS(status); } Fmp4HlsOpts ffmpeg_fmp4_hls_opts_default(void) { return (Fmp4HlsOpts){ .segment_duration = 2, .video_bitrate = 900, .audio_bitrate = 96, .max_width = 960, .max_height = 720, .include_audio = true, .use_mpeg_ts = false, .codec = FFMPEG_HLS_VP9_OPUS, }; } bool ffmpeg_is_available(void) { char *const arguments[] = { "ffmpeg", "-version", NULL, }; return run_process(arguments) == 0; } const char *ffmpeg_last_error(void) { return g_last_error; } FfmpegResult ffmpeg_video_to_fmp4_hls( const char *input_path, const char *output_dir, const char *name, const Fmp4HlsOpts *options) { if (!input_path || !output_dir || !name || name[0] == '\0') { set_error("input path, output directory, and name are required"); return FFMPEG_ERR_INVALID_ARGS; } if (!file_exists(input_path)) { set_error("input file not found: %s", input_path); return FFMPEG_ERR_INPUT_NOT_FOUND; } if (!ensure_directory(output_dir)) { set_error("unable to create output directory: %s", output_dir); return FFMPEG_ERR_OUTPUT_FAILED; } Fmp4HlsOpts defaults = ffmpeg_fmp4_hls_opts_default(); const Fmp4HlsOpts *opts = options ? options : &defaults; if (opts->segment_duration <= 0 || opts->video_bitrate <= 0 || opts->max_width <= 0 || opts->max_height <= 0) { set_error("invalid fMP4 HLS options"); return FFMPEG_ERR_INVALID_ARGS; } char playlist_path[MAX_PATH_LENGTH]; char init_name[256]; char segment_pattern[MAX_PATH_LENGTH]; char segment_duration[16]; char video_bitrate[32]; char audio_bitrate[32]; char keyframe_expression[32]; char scale_filter[256]; snprintf( playlist_path, sizeof(playlist_path), "%s/%s-stream.m3u8", output_dir, name); snprintf(init_name, sizeof(init_name), "%s-init.mp4", name); snprintf( segment_pattern, sizeof(segment_pattern), "%s/%s-segment%%03d.%s", output_dir, name, opts->use_mpeg_ts ? "ts" : "m4s"); snprintf(segment_duration, sizeof(segment_duration), "%d", opts->segment_duration); snprintf(video_bitrate, sizeof(video_bitrate), "%dk", opts->video_bitrate); snprintf(audio_bitrate, sizeof(audio_bitrate), "%dk", opts->audio_bitrate); snprintf( keyframe_expression, sizeof(keyframe_expression), "expr:gte(t,n_forced*%d)", opts->segment_duration); snprintf( scale_filter, sizeof(scale_filter), "scale=w='min(iw,%d)':h='min(ih,%d)':force_original_aspect_ratio=decrease," "scale=trunc(iw/2)*2:trunc(ih/2)*2", opts->max_width, opts->max_height); char *arguments[48]; size_t count = 0; #define ADD_ARGUMENT(value) arguments[count++] = (char *)(value) ADD_ARGUMENT("ffmpeg"); ADD_ARGUMENT("-hide_banner"); ADD_ARGUMENT("-loglevel"); ADD_ARGUMENT("error"); ADD_ARGUMENT("-nostdin"); ADD_ARGUMENT("-y"); ADD_ARGUMENT("-i"); ADD_ARGUMENT(input_path); ADD_ARGUMENT("-map"); ADD_ARGUMENT("0:v:0"); if (opts->include_audio) { ADD_ARGUMENT("-map"); ADD_ARGUMENT("0:a:0?"); } ADD_ARGUMENT("-c:v"); if (opts->codec == FFMPEG_HLS_H264_AAC) { ADD_ARGUMENT("libx264"); ADD_ARGUMENT("-preset"); ADD_ARGUMENT("medium"); ADD_ARGUMENT("-profile:v"); ADD_ARGUMENT("main"); ADD_ARGUMENT("-level:v"); ADD_ARGUMENT("3.1"); } else { ADD_ARGUMENT("libvpx-vp9"); ADD_ARGUMENT("-deadline"); ADD_ARGUMENT("good"); ADD_ARGUMENT("-cpu-used"); ADD_ARGUMENT("4"); } ADD_ARGUMENT("-pix_fmt"); ADD_ARGUMENT("yuv420p"); ADD_ARGUMENT("-b:v"); ADD_ARGUMENT(video_bitrate); ADD_ARGUMENT("-vf"); ADD_ARGUMENT(scale_filter); ADD_ARGUMENT("-force_key_frames"); ADD_ARGUMENT(keyframe_expression); if (opts->include_audio) { ADD_ARGUMENT("-c:a"); ADD_ARGUMENT( opts->codec == FFMPEG_HLS_H264_AAC ? "aac" : "libopus"); ADD_ARGUMENT("-b:a"); ADD_ARGUMENT(audio_bitrate); } else { ADD_ARGUMENT("-an"); } ADD_ARGUMENT("-f"); ADD_ARGUMENT("hls"); ADD_ARGUMENT("-hls_time"); ADD_ARGUMENT(segment_duration); ADD_ARGUMENT("-hls_playlist_type"); ADD_ARGUMENT("vod"); if (!opts->use_mpeg_ts) { ADD_ARGUMENT("-hls_segment_type"); ADD_ARGUMENT("fmp4"); ADD_ARGUMENT("-hls_fmp4_init_filename"); ADD_ARGUMENT(init_name); } ADD_ARGUMENT("-hls_segment_filename"); ADD_ARGUMENT(segment_pattern); ADD_ARGUMENT(playlist_path); arguments[count] = NULL; #undef ADD_ARGUMENT int result = run_process(arguments); if (result != 0) { set_error("ffmpeg HLS conversion failed with exit code %d", result); return FFMPEG_ERR_PROCESS_FAILED; } if (!file_exists(playlist_path)) { set_error("playlist was not created: %s", playlist_path); return FFMPEG_ERR_OUTPUT_FAILED; } return FFMPEG_OK; }