Skip to content

Expose ffprobe command from ffmpeg.wasm #776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build/ffmpeg-wasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ CONF_FLAGS=(
-lswscale
-Wno-deprecated-declarations
$LDFLAGS
-sENVIRONMENT=worker
-sWASM_BIGINT # enable big int support
-sUSE_SDL=2 # use emscripten SDL2 lib port
-sMODULARIZE # modularized to use as a library
Expand All @@ -49,6 +50,7 @@ CONF_FLAGS=(
src/fftools/ffmpeg_mux.c
src/fftools/ffmpeg_opt.c
src/fftools/opt_common.c
src/fftools/ffprobe.c
)

emcc "${CONF_FLAGS[@]}" $@
37 changes: 37 additions & 0 deletions packages/ffmpeg/src/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class FFmpeg {
case FFMessageType.MOUNT:
case FFMessageType.UNMOUNT:
case FFMessageType.EXEC:
case FFMessageType.FFPROBE:
case FFMessageType.WRITE_FILE:
case FFMessageType.READ_FILE:
case FFMessageType.DELETE_FILE:
Expand Down Expand Up @@ -249,6 +250,42 @@ export class FFmpeg {
signal
) as Promise<number>;

/**
* Execute ffprobe command.
*
* @example
* ```ts
* const ffmpeg = new FFmpeg();
* await ffmpeg.load();
* await ffmpeg.writeFile("video.avi", ...);
* // Getting duration of a video in seconds: ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 video.avi -o output.txt
* await ffmpeg.ffprobe(["-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", "video.avi", "-o", "output.txt"]);
* const data = ffmpeg.readFile("output.txt");
* ```
*
* @returns `0` if no error, `!= 0` if timeout (1) or error.
* @category FFmpeg
*/
public ffprobe = (
/** ffprobe command line args */
args: string[],
/**
* milliseconds to wait before stopping the command execution.
*
* @defaultValue -1
*/
timeout = -1,
{ signal }: FFMessageOptions = {}
): Promise<number> =>
this.#send(
{
type: FFMessageType.FFPROBE,
data: { args, timeout },
},
undefined,
signal
) as Promise<number>;

/**
* Terminate all ongoing API calls and terminate web worker.
* `FFmpeg.load()` must be called again before calling any other APIs.
Expand Down
1 change: 1 addition & 0 deletions packages/ffmpeg/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const CORE_URL = `https://unpkg.com/@ffmpeg/core@${CORE_VERSION}/dist/umd
export enum FFMessageType {
LOAD = "LOAD",
EXEC = "EXEC",
FFPROBE = "FFPROBE",
WRITE_FILE = "WRITE_FILE",
READ_FILE = "READ_FILE",
DELETE_FILE = "DELETE_FILE",
Expand Down
11 changes: 11 additions & 0 deletions packages/ffmpeg/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ const exec = ({ args, timeout = -1 }: FFMessageExecData): ExitCode => {
return ret;
};

const ffprobe = ({ args, timeout = -1 }: FFMessageExecData): ExitCode => {
ffmpeg.setTimeout(timeout);
ffmpeg.ffprobe(...args);
const ret = ffmpeg.ret;
ffmpeg.reset();
return ret;
};

const writeFile = ({ path, data }: FFMessageWriteFileData): OK => {
ffmpeg.FS.writeFile(path, data);
return true;
Expand Down Expand Up @@ -170,6 +178,9 @@ self.onmessage = async ({
case FFMessageType.EXEC:
data = exec(_data as FFMessageExecData);
break;
case FFMessageType.FFPROBE:
data = ffprobe(_data as FFMessageExecData);
break;
case FFMessageType.WRITE_FILE:
data = writeFile(_data as FFMessageWriteFileData);
break;
Expand Down
31 changes: 21 additions & 10 deletions packages/types/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,28 @@ export interface Stat {
blocks: number;
}

export interface FSFilesystemWORKERFS {

}
export interface FSFilesystemWORKERFS {}

export interface FSFilesystemMEMFS {

}
export interface FSFilesystemMEMFS {}

export interface FSFilesystems {
WORKERFS: FSFilesystemWORKERFS;
MEMFS: FSFilesystemMEMFS;
}

export type FSFilesystem =
| FSFilesystemWORKERFS
| FSFilesystemMEMFS;
export type FSFilesystem = FSFilesystemWORKERFS | FSFilesystemMEMFS;

export interface OptionReadFile {
encoding: string;
}

export interface WorkerFSMountConfig {
blobs?: {
name: string;
data: Blob;
}[];
files?: File[];
}

/**
* Functions to interact with Emscripten FS library.
Expand All @@ -75,7 +81,11 @@ export interface FS {
isFile: (mode: number) => boolean;
/** mode is a numeric notation of permission, @see [Numeric Notation](https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation) */
isDir: (mode: number) => boolean;
mount: (fileSystemType: FSFilesystem, data: WorkerFSMountConfig, path: string) => void;
mount: (
fileSystemType: FSFilesystem,
data: WorkerFSMountConfig,
path: string
) => void;
unmount: (path: string) => void;
filesystems: FSFilesystems;
}
Expand Down Expand Up @@ -115,6 +125,7 @@ export interface FFmpegCoreModule {
mainScriptUrlOrBlob: string;

exec: (...args: string[]) => number;
ffprobe: (...args: string[]) => number;
reset: () => void;
setLogger: (logger: (log: Log) => void) => void;
setTimeout: (timeout: number) => void;
Expand Down
15 changes: 15 additions & 0 deletions src/bind/ffmpeg/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
const NULL = 0;
const SIZE_I32 = Uint32Array.BYTES_PER_ELEMENT;
const DEFAULT_ARGS = ["./ffmpeg", "-nostdin", "-y"];
const DEFAULT_ARGS_FFPROBE = ["./ffprobe"];

Module["NULL"] = NULL;
Module["SIZE_I32"] = SIZE_I32;
Module["DEFAULT_ARGS"] = DEFAULT_ARGS;
Module["DEFAULT_ARGS_FFPROBE"] = DEFAULT_ARGS_FFPROBE;

/**
* Variables
Expand Down Expand Up @@ -62,6 +64,18 @@ function exec(..._args) {
return Module["ret"];
}

function ffprobe(..._args) {
const args = [...Module["DEFAULT_ARGS_FFPROBE"], ..._args];
try {
Module["_ffprobe"](args.length, stringsToPtr(args));
} catch (e) {
if (!e.message.startsWith("Aborted")) {
throw e;
}
}
return Module["ret"];
}

function setLogger(logger) {
Module["logger"] = logger;
}
Expand Down Expand Up @@ -121,6 +135,7 @@ Module["printErr"] = printErr;
Module["locateFile"] = _locateFile;

Module["exec"] = exec;
Module["ffprobe"] = ffprobe;
Module["setLogger"] = setLogger;
Module["setTimeout"] = setTimeout;
Module["setProgress"] = setProgress;
Expand Down
2 changes: 1 addition & 1 deletion src/bind/ffmpeg/export.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const EXPORTED_FUNCTIONS = ["_ffmpeg", "_abort", "_malloc"];
const EXPORTED_FUNCTIONS = ["_ffmpeg", "_abort", "_malloc", "_ffprobe"];

console.log(EXPORTED_FUNCTIONS.join(","));
65 changes: 59 additions & 6 deletions src/fftools/ffprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ typedef struct InputFile {
int nb_streams;
} InputFile;

const char program_name[] = "ffprobe";
const int program_birth_year = 2007;
const char program_name_ffprobe[] = "ffprobe";
const int program_birth_year_ffprobe = 2007;

static int do_bitexact = 0;
static int do_count_frames = 0;
Expand Down Expand Up @@ -382,6 +382,58 @@ static void ffprobe_cleanup(int ret)
#if HAVE_THREADS
pthread_mutex_destroy(&log_mutex);
#endif

do_bitexact = 0;
do_count_frames = 0;
do_count_packets = 0;
do_read_frames = 0;
do_read_packets = 0;
do_show_chapters = 0;
do_show_error = 0;
do_show_format = 0;
do_show_frames = 0;
do_show_packets = 0;
do_show_programs = 0;
do_show_streams = 0;
do_show_stream_disposition = 0;
do_show_data = 0;
do_show_program_version = 0;
do_show_library_versions = 0;
do_show_pixel_formats = 0;
do_show_pixel_format_flags = 0;
do_show_pixel_format_components = 0;
do_show_log = 0;
do_show_chapter_tags = 0;
do_show_format_tags = 0;
do_show_frame_tags = 0;
do_show_program_tags = 0;
do_show_stream_tags = 0;
do_show_packet_tags = 0;
show_value_unit = 0;
use_value_prefix = 0;
use_byte_value_binary_prefix = 0;
use_value_sexagesimal_format = 0;
show_private_data = 1;
show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO;
print_format = NULL;
stream_specifier = NULL;
show_data_hash = NULL;
read_intervals = NULL;
read_intervals_nb = 0;
find_stream_info = 1;
input_filename = NULL;
print_input_filename = NULL;
iformat = NULL;
output_filename = NULL;
hash = NULL;
nb_streams = 0;
nb_streams_packets = NULL;
nb_streams_frames = NULL;
selected_streams = NULL;
log_buffer = NULL;
log_buffer_size = 0;

av_log(NULL, AV_LOG_DEBUG, "FFprobe: Cleanup done.\n");
}

struct unit_value {
Expand Down Expand Up @@ -3491,7 +3543,7 @@ static int probe_file(WriterContext *wctx, const char *filename,
static void show_usage(void)
{
av_log(NULL, AV_LOG_INFO, "Simple multimedia streams analyzer\n");
av_log(NULL, AV_LOG_INFO, "usage: %s [OPTIONS] INPUT_FILE\n", program_name);
av_log(NULL, AV_LOG_INFO, "usage: %s [OPTIONS] INPUT_FILE\n", program_name_ffprobe);
av_log(NULL, AV_LOG_INFO, "\n");
}

Expand All @@ -3503,7 +3555,7 @@ static void ffprobe_show_program_version(WriterContext *w)
writer_print_section_header(w, SECTION_ID_PROGRAM_VERSION);
print_str("version", FFMPEG_VERSION);
print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers",
program_birth_year, CONFIG_THIS_YEAR);
program_birth_year_ffprobe, CONFIG_THIS_YEAR);
print_str("compiler_ident", CC_IDENT);
print_str("configuration", FFMPEG_CONFIGURATION);
writer_print_section_footer(w);
Expand Down Expand Up @@ -3740,7 +3792,7 @@ static int opt_print_filename(void *optctx, const char *opt, const char *arg)
return 0;
}

void show_help_default(const char *opt, const char *arg)
void show_help_default_ffprobe(const char *opt, const char *arg)
{
av_log_set_callback(log_callback_help);
show_usage();
Expand Down Expand Up @@ -4037,6 +4089,7 @@ int ffprobe(int argc, char **argv)
}
#endif
av_log_set_flags(AV_LOG_SKIP_REPEATED);
ffprobe_cleanup(0);
register_exit(ffprobe_cleanup);

options = real_options;
Expand Down Expand Up @@ -4142,7 +4195,7 @@ int ffprobe(int argc, char **argv)
(!do_show_program_version && !do_show_library_versions && !do_show_pixel_formats))) {
show_usage();
av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n");
av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name_ffprobe);
ret = AVERROR(EINVAL);
} else if (input_filename) {
ret = probe_file(wctx, input_filename, print_input_filename);
Expand Down