Adopt lerna and typescript

This commit is contained in:
Jerome Wu
2022-09-13 17:23:21 +08:00
parent d2ea0066a7
commit 94bb51d073
139 changed files with 35770 additions and 11094 deletions

0
src/bind/bind.js Normal file
View File

View File

@@ -0,0 +1,10 @@
const EXPORTED_RUNTIME_METHODS = [
"FS",
"setValue",
"getValue",
"UTF8ToString",
"lengthBytesUTF8",
"stringToUTF8",
];
console.log(EXPORTED_RUNTIME_METHODS.join(","));

3
src/bind/export.js Normal file
View File

@@ -0,0 +1,3 @@
const EXPORTED_FUNCTIONS = ["_ffmpeg"];
console.log(EXPORTED_FUNCTIONS.join(","));

View File

@@ -1,10 +0,0 @@
import pkg from '../../package.json';
/*
* Default options for browser environment
*/
const corePath = typeof process !== 'undefined' && process.env.NODE_ENV === 'development'
? new URL('/node_modules/@ffmpeg/core/dist/ffmpeg-core.js', import.meta.url).href
: `https://unpkg.com/@ffmpeg/core@${pkg.devDependencies['@ffmpeg/core'].substring(1)}/dist/ffmpeg-core.js`;
export default { corePath };

View File

@@ -1,38 +0,0 @@
const readFromBlobOrFile = (blob) => (
new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = ({ target: { error: { code } } }) => {
reject(Error(`File could not be read! Code=${code}`));
};
fileReader.readAsArrayBuffer(blob);
})
);
// eslint-disable-next-line
export const fetchFile = async (_data) => {
let data = _data;
if (typeof _data === 'undefined') {
return new Uint8Array();
}
if (typeof _data === 'string') {
/* From base64 format */
if (/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(_data)) {
data = atob(_data.split(',')[1])
.split('')
.map((c) => c.charCodeAt(0));
/* From remote server/URL */
} else {
const res = await fetch(new URL(_data, import.meta.url).href);
data = await res.arrayBuffer();
}
/* From Blob or File */
} else if (_data instanceof File || _data instanceof Blob) {
data = await readFromBlobOrFile(_data);
}
return new Uint8Array(data);
};

View File

@@ -1,114 +0,0 @@
/* eslint-disable no-undef */
import { log } from '../utils/log';
import {
CREATE_FFMPEG_CORE_IS_NOT_DEFINED,
} from '../utils/errors';
/*
* Fetch data from remote URL and convert to blob URL
* to avoid CORS issue
*/
const toBlobURL = async (url, mimeType) => {
log('info', `fetch ${url}`);
const buf = await (await fetch(url)).arrayBuffer();
log('info', `${url} file size = ${buf.byteLength} bytes`);
const blob = new Blob([buf], { type: mimeType });
const blobURL = URL.createObjectURL(blob);
log('info', `${url} blob URL = ${blobURL}`);
return blobURL;
};
// eslint-disable-next-line
export const getCreateFFmpegCore = async ({
corePath: _corePath,
workerPath: _workerPath,
wasmPath: _wasmPath,
}) => {
// in Web Worker context
// eslint-disable-next-line
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
if (typeof _corePath !== 'string') {
throw Error('corePath should be a string!');
}
const coreRemotePath = new URL(_corePath, import.meta.url).href;
const corePath = await toBlobURL(
coreRemotePath,
'application/javascript',
);
const wasmPath = await toBlobURL(
_wasmPath !== undefined ? _wasmPath : coreRemotePath.replace('ffmpeg-core.js', 'ffmpeg-core.wasm'),
'application/wasm',
);
const workerPath = await toBlobURL(
_workerPath !== undefined ? _workerPath : coreRemotePath.replace('ffmpeg-core.js', 'ffmpeg-core.worker.js'),
'application/javascript',
);
if (typeof createFFmpegCore === 'undefined') {
return new Promise((resolve) => {
globalThis.importScripts(corePath);
if (typeof createFFmpegCore === 'undefined') {
throw Error(CREATE_FFMPEG_CORE_IS_NOT_DEFINED(coreRemotePath));
}
log('info', 'ffmpeg-core.js script loaded');
resolve({
createFFmpegCore,
corePath,
wasmPath,
workerPath,
});
});
}
log('info', 'ffmpeg-core.js script is loaded already');
return Promise.resolve({
createFFmpegCore,
corePath,
wasmPath,
workerPath,
});
}
if (typeof _corePath !== 'string') {
throw Error('corePath should be a string!');
}
const coreRemotePath = new URL(_corePath, import.meta.url).href;
const corePath = await toBlobURL(
coreRemotePath,
'application/javascript',
);
const wasmPath = await toBlobURL(
coreRemotePath.replace('ffmpeg-core.js', 'ffmpeg-core.wasm'),
'application/wasm',
);
const workerPath = await toBlobURL(
coreRemotePath.replace('ffmpeg-core.js', 'ffmpeg-core.worker.js'),
'application/javascript',
);
if (typeof createFFmpegCore === 'undefined') {
return new Promise((resolve) => {
const script = document.createElement('script');
const eventHandler = () => {
script.removeEventListener('load', eventHandler);
if (typeof createFFmpegCore === 'undefined') {
throw Error(CREATE_FFMPEG_CORE_IS_NOT_DEFINED(coreRemotePath));
}
log('info', 'ffmpeg-core.js script loaded');
resolve({
createFFmpegCore,
corePath,
wasmPath,
workerPath,
});
};
script.src = corePath;
script.type = 'text/javascript';
script.addEventListener('load', eventHandler);
document.getElementsByTagName('head')[0].appendChild(script);
});
}
log('info', 'ffmpeg-core.js script is loaded already');
return Promise.resolve({
createFFmpegCore,
corePath,
wasmPath,
workerPath,
});
};

View File

@@ -1,5 +0,0 @@
import defaultOptions from './defaultOptions';
import { getCreateFFmpegCore } from './getCreateFFmpegCore';
import { fetchFile } from './fetchFile';
export { defaultOptions, getCreateFFmpegCore, fetchFile };

View File

@@ -1,50 +0,0 @@
module.exports = {
defaultArgs: [
/* args[0] is always the binary path */
'./ffmpeg',
/* Disable interaction mode */
'-nostdin',
/* Force to override output file */
'-y',
],
baseOptions: {
/* Flag to turn on/off log messages in console */
log: false,
/*
* Custom logger to get ffmpeg.wasm output messages.
* a sample logger looks like this:
*
* ```
* logger = ({ type, message }) => {
* console.log(type, message);
* }
* ```
*
* type can be one of following:
*
* info: internal workflow debug messages
* fferr: ffmpeg native stderr output
* ffout: ffmpeg native stdout output
*/
logger: () => {},
/*
* Progress handler to get current progress of ffmpeg command.
* a sample progress handler looks like this:
*
* ```
* progress = ({ ratio }) => {
* console.log(ratio);
* }
* ```
*
* ratio is a float number between 0 to 1.
*/
progress: () => {},
/*
* Path to find/download ffmpeg.wasm-core,
* this value should be overwriten by `defaultOptions` in
* each environment.
*/
corePath: '',
},
};

View File

@@ -1,279 +0,0 @@
const { defaultArgs, baseOptions } = require('./config');
const parseArgs = require('./utils/parseArgs');
const { defaultOptions, getCreateFFmpegCore } = require('./node');
const { version } = require('../package.json');
const NO_LOAD = Error('ffmpeg.wasm is not ready, make sure you have completed load().');
module.exports = (_options = {}) => {
const {
log: optLog,
logger,
progress: optProgress,
...options
} = {
...baseOptions,
...defaultOptions,
..._options,
};
let Core = null;
let ffmpeg = null;
let runResolve = null;
let runReject = null;
let running = false;
let customLogger = () => {};
let logging = optLog;
let progress = optProgress;
let duration = 0;
let frames = 0;
let readFrames = false;
let ratio = 0;
const detectCompletion = (message) => {
if (message === 'FFMPEG_END' && runResolve !== null) {
runResolve();
runResolve = null;
runReject = null;
running = false;
}
};
const log = (type, message) => {
customLogger({ type, message });
if (logging) {
console.log(`[${type}] ${message}`);
}
};
const ts2sec = (ts) => {
const [h, m, s] = ts.split(':');
return (parseFloat(h) * 60 * 60) + (parseFloat(m) * 60) + parseFloat(s);
};
const parseProgress = (message, prog) => {
if (typeof message === 'string') {
if (message.startsWith(' Duration')) {
const ts = message.split(', ')[0].split(': ')[1];
const d = ts2sec(ts);
prog({ duration: d, ratio });
if (duration === 0 || duration > d) {
duration = d;
readFrames = true;
}
} else if (readFrames && message.startsWith(' Stream')) {
const match = message.match(/([\d.]+) fps/);
if (match) {
const fps = parseFloat(match[1]);
frames = duration * fps;
} else {
frames = 0;
}
readFrames = false;
} else if (message.startsWith('frame') || message.startsWith('size')) {
const ts = message.split('time=')[1].split(' ')[0];
const t = ts2sec(ts);
const match = message.match(/frame=\s*(\d+)/);
if (frames && match) {
const f = parseFloat(match[1]);
ratio = Math.min(f / frames, 1);
} else {
ratio = t / duration;
}
prog({ ratio, time: t });
} else if (message.startsWith('video:')) {
prog({ ratio: 1 });
duration = 0;
}
}
};
const parseMessage = ({ type, message }) => {
log(type, message);
parseProgress(message, progress);
detectCompletion(message);
};
/*
* Load ffmpeg.wasm-core script.
* In browser environment, the ffmpeg.wasm-core script is fetch from
* CDN and can be assign to a local path by assigning `corePath`.
* In node environment, we use dynamic require and the default `corePath`
* is `$ffmpeg/core`.
*
* Typically the load() func might take few seconds to minutes to complete,
* better to do it as early as possible.
*
*/
const load = async () => {
log('info', 'load ffmpeg-core');
if (Core === null) {
log('info', 'loading ffmpeg-core');
/*
* In node environment, all paths are undefined as there
* is no need to set them.
*/
const {
createFFmpegCore,
corePath,
workerPath,
wasmPath,
} = await getCreateFFmpegCore(options);
Core = await createFFmpegCore({
/*
* Assign mainScriptUrlOrBlob fixes chrome extension web worker issue
* as there is no document.currentScript in the context of content_scripts
*/
mainScriptUrlOrBlob: corePath,
printErr: (message) => parseMessage({ type: 'fferr', message }),
print: (message) => parseMessage({ type: 'ffout', message }),
/*
* locateFile overrides paths of files that is loaded by main script (ffmpeg-core.js).
* It is critical for browser environment and we override both wasm and worker paths
* as we are using blob URL instead of original URL to avoid cross origin issues.
*/
locateFile: (path, prefix) => {
if (typeof window !== 'undefined' || typeof WorkerGlobalScope !== 'undefined') {
if (typeof wasmPath !== 'undefined'
&& path.endsWith('ffmpeg-core.wasm')) {
return wasmPath;
}
if (typeof workerPath !== 'undefined'
&& path.endsWith('ffmpeg-core.worker.js')) {
return workerPath;
}
}
return prefix + path;
},
});
ffmpeg = Core.cwrap(options.mainName || 'proxy_main', 'number', ['number', 'number']);
log('info', 'ffmpeg-core loaded');
} else {
throw Error('ffmpeg.wasm was loaded, you should not load it again, use ffmpeg.isLoaded() to check next time.');
}
};
/*
* Determine whether the Core is loaded.
*/
const isLoaded = () => Core !== null;
/*
* Run ffmpeg command.
* This is the major function in ffmpeg.wasm, you can just imagine it
* as ffmpeg native cli and what you need to pass is the same.
*
* For example, you can convert native command below:
*
* ```
* $ ffmpeg -i video.avi -c:v libx264 video.mp4
* ```
*
* To
*
* ```
* await ffmpeg.run('-i', 'video.avi', '-c:v', 'libx264', 'video.mp4');
* ```
*
*/
const run = (..._args) => {
log('info', `run ffmpeg command: ${_args.join(' ')}`);
if (Core === null) {
throw NO_LOAD;
} else if (running) {
throw Error('ffmpeg.wasm can only run one command at a time');
} else {
running = true;
return new Promise((resolve, reject) => {
const args = [...defaultArgs, ..._args].filter((s) => s.length !== 0);
runResolve = resolve;
runReject = reject;
ffmpeg(...parseArgs(Core, args));
});
}
};
/*
* Run FS operations.
* For input/output file of ffmpeg.wasm, it is required to save them to MEMFS
* first so that ffmpeg.wasm is able to consume them. Here we rely on the FS
* methods provided by Emscripten.
*
* Common methods to use are:
* ffmpeg.FS('writeFile', 'video.avi', new Uint8Array(...)): writeFile writes
* data to MEMFS. You need to use Uint8Array for binary data.
* ffmpeg.FS('readFile', 'video.mp4'): readFile from MEMFS.
* ffmpeg.FS('unlink', 'video.map'): delete file from MEMFS.
*
* For more info, check https://emscripten.org/docs/api_reference/Filesystem-API.html
*
*/
const FS = (method, ...args) => {
log('info', `run FS.${method} ${args.map((arg) => (typeof arg === 'string' ? arg : `<${arg.length} bytes binary file>`)).join(' ')}`);
if (Core === null) {
throw NO_LOAD;
} else {
let ret = null;
try {
ret = Core.FS[method](...args);
} catch (e) {
if (method === 'readdir') {
throw Error(`ffmpeg.FS('readdir', '${args[0]}') error. Check if the path exists, ex: ffmpeg.FS('readdir', '/')`);
} else if (method === 'readFile') {
throw Error(`ffmpeg.FS('readFile', '${args[0]}') error. Check if the path exists`);
} else {
throw Error('Oops, something went wrong in FS operation.');
}
}
return ret;
}
};
/**
* forcibly terminate the ffmpeg program.
*/
const exit = () => {
if (Core === null) {
throw NO_LOAD;
} else {
// if there's any pending runs, reject them
if (runReject) {
runReject('ffmpeg has exited');
}
running = false;
try {
Core.exit(1);
} catch (err) {
log(err.message);
if (runReject) {
runReject(err);
}
} finally {
Core = null;
ffmpeg = null;
runResolve = null;
runReject = null;
}
}
};
const setProgress = (_progress) => {
progress = _progress;
};
const setLogger = (_logger) => {
customLogger = _logger;
};
const setLogging = (_logging) => {
logging = _logging;
};
log('info', `use ffmpeg.wasm v${version}`);
return {
setProgress,
setLogger,
setLogging,
load,
isLoaded,
run,
exit,
FS,
};
};

1018
src/fftools/cmdutils.c Normal file

File diff suppressed because it is too large Load Diff

455
src/fftools/cmdutils.h Normal file
View File

@@ -0,0 +1,455 @@
/*
* Various utilities for command line tools
* copyright (c) 2003 Fabrice Bellard
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef FFTOOLS_CMDUTILS_H
#define FFTOOLS_CMDUTILS_H
#include <stdint.h>
#include "config.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#ifdef _WIN32
#undef main /* We don't want SDL to override our main() */
#endif
/**
* program name, defined by the program for show_version().
*/
extern const char program_name[];
/**
* program birth year, defined by the program for show_banner()
*/
extern const int program_birth_year;
extern AVDictionary *sws_dict;
extern AVDictionary *swr_opts;
extern AVDictionary *format_opts, *codec_opts;
extern int hide_banner;
/**
* Register a program-specific cleanup routine.
*/
void register_exit(void (*cb)(int ret));
/**
* Wraps exit with a program-specific cleanup routine.
*/
void exit_program(int ret) av_noreturn;
/**
* Initialize dynamic library loading
*/
void init_dynload(void);
/**
* Uninitialize the cmdutils option system, in particular
* free the *_opts contexts and their contents.
*/
void uninit_opts(void);
/**
* Trivial log callback.
* Only suitable for opt_help and similar since it lacks prefix handling.
*/
void log_callback_help(void* ptr, int level, const char* fmt, va_list vl);
/**
* Fallback for options that are not explicitly handled, these will be
* parsed through AVOptions.
*/
int opt_default(void *optctx, const char *opt, const char *arg);
/**
* Limit the execution time.
*/
int opt_timelimit(void *optctx, const char *opt, const char *arg);
/**
* Parse a string and return its corresponding value as a double.
* Exit from the application if the string cannot be correctly
* parsed or the corresponding value is invalid.
*
* @param context the context of the value to be set (e.g. the
* corresponding command line option name)
* @param numstr the string to be parsed
* @param type the type (OPT_INT64 or OPT_FLOAT) as which the
* string should be parsed
* @param min the minimum valid accepted value
* @param max the maximum valid accepted value
*/
double parse_number_or_die(const char *context, const char *numstr, int type,
double min, double max);
/**
* Parse a string specifying a time and return its corresponding
* value as a number of microseconds. Exit from the application if
* the string cannot be correctly parsed.
*
* @param context the context of the value to be set (e.g. the
* corresponding command line option name)
* @param timestr the string to be parsed
* @param is_duration a flag which tells how to interpret timestr, if
* not zero timestr is interpreted as a duration, otherwise as a
* date
*
* @see av_parse_time()
*/
int64_t parse_time_or_die(const char *context, const char *timestr,
int is_duration);
typedef struct SpecifierOpt {
char *specifier; /**< stream/chapter/program/... specifier */
union {
uint8_t *str;
int i;
int64_t i64;
uint64_t ui64;
float f;
double dbl;
} u;
} SpecifierOpt;
typedef struct OptionDef {
const char *name;
int flags;
#define HAS_ARG 0x0001
#define OPT_BOOL 0x0002
#define OPT_EXPERT 0x0004
#define OPT_STRING 0x0008
#define OPT_VIDEO 0x0010
#define OPT_AUDIO 0x0020
#define OPT_INT 0x0080
#define OPT_FLOAT 0x0100
#define OPT_SUBTITLE 0x0200
#define OPT_INT64 0x0400
#define OPT_EXIT 0x0800
#define OPT_DATA 0x1000
#define OPT_PERFILE 0x2000 /* the option is per-file (currently ffmpeg-only).
implied by OPT_OFFSET or OPT_SPEC */
#define OPT_OFFSET 0x4000 /* option is specified as an offset in a passed optctx */
#define OPT_SPEC 0x8000 /* option is to be stored in an array of SpecifierOpt.
Implies OPT_OFFSET. Next element after the offset is
an int containing element count in the array. */
#define OPT_TIME 0x10000
#define OPT_DOUBLE 0x20000
#define OPT_INPUT 0x40000
#define OPT_OUTPUT 0x80000
union {
void *dst_ptr;
int (*func_arg)(void *, const char *, const char *);
size_t off;
} u;
const char *help;
const char *argname;
} OptionDef;
/**
* Print help for all options matching specified flags.
*
* @param options a list of options
* @param msg title of this group. Only printed if at least one option matches.
* @param req_flags print only options which have all those flags set.
* @param rej_flags don't print options which have any of those flags set.
* @param alt_flags print only options that have at least one of those flags set
*/
void show_help_options(const OptionDef *options, const char *msg, int req_flags,
int rej_flags, int alt_flags);
/**
* Show help for all options with given flags in class and all its
* children.
*/
void show_help_children(const AVClass *class, int flags);
/**
* Per-fftool specific help handler. Implemented in each
* fftool, called by show_help().
*/
void show_help_default(const char *opt, const char *arg);
/**
* Parse the command line arguments.
*
* @param optctx an opaque options context
* @param argc number of command line arguments
* @param argv values of command line arguments
* @param options Array with the definitions required to interpret every
* option of the form: -option_name [argument]
* @param parse_arg_function Name of the function called to process every
* argument without a leading option name flag. NULL if such arguments do
* not have to be processed.
*/
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (* parse_arg_function)(void *optctx, const char*));
/**
* Parse one given option.
*
* @return on success 1 if arg was consumed, 0 otherwise; negative number on error
*/
int parse_option(void *optctx, const char *opt, const char *arg,
const OptionDef *options);
/**
* An option extracted from the commandline.
* Cannot use AVDictionary because of options like -map which can be
* used multiple times.
*/
typedef struct Option {
const OptionDef *opt;
const char *key;
const char *val;
} Option;
typedef struct OptionGroupDef {
/**< group name */
const char *name;
/**
* Option to be used as group separator. Can be NULL for groups which
* are terminated by a non-option argument (e.g. ffmpeg output files)
*/
const char *sep;
/**
* Option flags that must be set on each option that is
* applied to this group
*/
int flags;
} OptionGroupDef;
typedef struct OptionGroup {
const OptionGroupDef *group_def;
const char *arg;
Option *opts;
int nb_opts;
AVDictionary *codec_opts;
AVDictionary *format_opts;
AVDictionary *sws_dict;
AVDictionary *swr_opts;
} OptionGroup;
/**
* A list of option groups that all have the same group type
* (e.g. input files or output files)
*/
typedef struct OptionGroupList {
const OptionGroupDef *group_def;
OptionGroup *groups;
int nb_groups;
} OptionGroupList;
typedef struct OptionParseContext {
OptionGroup global_opts;
OptionGroupList *groups;
int nb_groups;
/* parsing state */
OptionGroup cur_group;
} OptionParseContext;
/**
* Parse an options group and write results into optctx.
*
* @param optctx an app-specific options context. NULL for global options group
*/
int parse_optgroup(void *optctx, OptionGroup *g);
/**
* Split the commandline into an intermediate form convenient for further
* processing.
*
* The commandline is assumed to be composed of options which either belong to a
* group (those with OPT_SPEC, OPT_OFFSET or OPT_PERFILE) or are global
* (everything else).
*
* A group (defined by an OptionGroupDef struct) is a sequence of options
* terminated by either a group separator option (e.g. -i) or a parameter that
* is not an option (doesn't start with -). A group without a separator option
* must always be first in the supplied groups list.
*
* All options within the same group are stored in one OptionGroup struct in an
* OptionGroupList, all groups with the same group definition are stored in one
* OptionGroupList in OptionParseContext.groups. The order of group lists is the
* same as the order of group definitions.
*/
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups);
/**
* Free all allocated memory in an OptionParseContext.
*/
void uninit_parse_context(OptionParseContext *octx);
/**
* Find the '-loglevel' option in the command line args and apply it.
*/
void parse_loglevel(int argc, char **argv, const OptionDef *options);
/**
* Return index of option opt in argv or 0 if not found.
*/
int locate_option(int argc, char **argv, const OptionDef *options,
const char *optname);
/**
* Check if the given stream matches a stream specifier.
*
* @param s Corresponding format context.
* @param st Stream from s to be checked.
* @param spec A stream specifier of the [v|a|s|d]:[\<stream index\>] form.
*
* @return 1 if the stream matches, 0 if it doesn't, <0 on error
*/
int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec);
/**
* Filter out options for given codec.
*
* Create a new options dictionary containing only the options from
* opts which apply to the codec with ID codec_id.
*
* @param opts dictionary to place options in
* @param codec_id ID of the codec that should be filtered for
* @param s Corresponding format context.
* @param st A stream from s for which the options should be filtered.
* @param codec The particular codec for which the options should be filtered.
* If null, the default one is looked up according to the codec id.
* @return a pointer to the created dictionary
*/
AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec);
/**
* Setup AVCodecContext options for avformat_find_stream_info().
*
* Create an array of dictionaries, one dictionary for each stream
* contained in s.
* Each dictionary will contain the options from codec_opts which can
* be applied to the corresponding stream codec context.
*
* @return pointer to the created array of dictionaries.
* Calls exit() on failure.
*/
AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
AVDictionary *codec_opts);
/**
* Print an error message to stderr, indicating filename and a human
* readable description of the error code err.
*
* If strerror_r() is not available the use of this function in a
* multithreaded application may be unsafe.
*
* @see av_strerror()
*/
void print_error(const char *filename, int err);
/**
* Print the program banner to stderr. The banner contents depend on the
* current version of the repository and of the libav* libraries used by
* the program.
*/
void show_banner(int argc, char **argv, const OptionDef *options);
/**
* Return a positive value if a line read from standard input
* starts with [yY], otherwise return 0.
*/
int read_yesno(void);
/**
* Get a file corresponding to a preset file.
*
* If is_path is non-zero, look for the file in the path preset_name.
* Otherwise search for a file named arg.ffpreset in the directories
* $FFMPEG_DATADIR (if set), $HOME/.ffmpeg, and in the datadir defined
* at configuration time or in a "ffpresets" folder along the executable
* on win32, in that order. If no such file is found and
* codec_name is defined, then search for a file named
* codec_name-preset_name.avpreset in the above-mentioned directories.
*
* @param filename buffer where the name of the found filename is written
* @param filename_size size in bytes of the filename buffer
* @param preset_name name of the preset to search
* @param is_path tell if preset_name is a filename path
* @param codec_name name of the codec for which to look for the
* preset, may be NULL
*/
FILE *get_preset_file(char *filename, size_t filename_size,
const char *preset_name, int is_path, const char *codec_name);
/**
* Realloc array to hold new_size elements of elem_size.
* Calls exit() on failure.
*
* @param array array to reallocate
* @param elem_size size in bytes of each element
* @param size new element count will be written here
* @param new_size number of elements to place in reallocated array
* @return reallocated array
*/
void *grow_array(void *array, int elem_size, int *size, int new_size);
/**
* Atomically add a new element to an array of pointers, i.e. allocate
* a new entry, reallocate the array of pointers and make the new last
* member of this array point to the newly allocated buffer.
* Calls exit() on failure.
*
* @param array array of pointers to reallocate
* @param elem_size size of the new element to allocate
* @param nb_elems pointer to the number of elements of the array array;
* *nb_elems will be incremented by one by this function.
* @return pointer to the newly allocated entry
*/
void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems);
#define GROW_ARRAY(array, nb_elems)\
array = grow_array(array, sizeof(*array), &nb_elems, nb_elems + 1)
#define ALLOC_ARRAY_ELEM(array, nb_elems)\
allocate_array_elem(&array, sizeof(*array[0]), &nb_elems)
#define GET_PIX_FMT_NAME(pix_fmt)\
const char *name = av_get_pix_fmt_name(pix_fmt);
#define GET_CODEC_NAME(id)\
const char *name = avcodec_descriptor_get(id)->name;
#define GET_SAMPLE_FMT_NAME(sample_fmt)\
const char *name = av_get_sample_fmt_name(sample_fmt)
#define GET_SAMPLE_RATE_NAME(rate)\
char name[16];\
snprintf(name, sizeof(name), "%d", rate);
double get_rotation(int32_t *displaymatrix);
#endif /* FFTOOLS_CMDUTILS_H */

4579
src/fftools/ffmpeg.c Normal file

File diff suppressed because it is too large Load Diff

707
src/fftools/ffmpeg.h Normal file
View File

@@ -0,0 +1,707 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef FFTOOLS_FFMPEG_H
#define FFTOOLS_FFMPEG_H
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <signal.h>
#include "cmdutils.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/bsf.h"
#include "libavfilter/avfilter.h"
#include "libavutil/avutil.h"
#include "libavutil/dict.h"
#include "libavutil/eval.h"
#include "libavutil/fifo.h"
#include "libavutil/hwcontext.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
#include "libavutil/thread.h"
#include "libavutil/threadmessage.h"
#include "libswresample/swresample.h"
enum VideoSyncMethod {
VSYNC_AUTO = -1,
VSYNC_PASSTHROUGH,
VSYNC_CFR,
VSYNC_VFR,
VSYNC_VSCFR,
VSYNC_DROP,
};
#define MAX_STREAMS 1024 /* arbitrary sanity check value */
enum HWAccelID {
HWACCEL_NONE = 0,
HWACCEL_AUTO,
HWACCEL_GENERIC,
};
typedef struct HWDevice {
const char *name;
enum AVHWDeviceType type;
AVBufferRef *device_ref;
} HWDevice;
/* select an input stream for an output stream */
typedef struct StreamMap {
int disabled; /* 1 is this mapping is disabled by a negative map */
int file_index;
int stream_index;
int sync_file_index;
int sync_stream_index;
char *linklabel; /* name of an output link, for mapping lavfi outputs */
} StreamMap;
typedef struct {
int file_idx, stream_idx, channel_idx; // input
int ofile_idx, ostream_idx; // output
} AudioChannelMap;
typedef struct OptionsContext {
OptionGroup *g;
/* input/output options */
int64_t start_time;
int64_t start_time_eof;
int seek_timestamp;
const char *format;
SpecifierOpt *codec_names;
int nb_codec_names;
SpecifierOpt *audio_ch_layouts;
int nb_audio_ch_layouts;
SpecifierOpt *audio_channels;
int nb_audio_channels;
SpecifierOpt *audio_sample_rate;
int nb_audio_sample_rate;
SpecifierOpt *frame_rates;
int nb_frame_rates;
SpecifierOpt *max_frame_rates;
int nb_max_frame_rates;
SpecifierOpt *frame_sizes;
int nb_frame_sizes;
SpecifierOpt *frame_pix_fmts;
int nb_frame_pix_fmts;
/* input options */
int64_t input_ts_offset;
int loop;
int rate_emu;
float readrate;
int accurate_seek;
int thread_queue_size;
int input_sync_ref;
SpecifierOpt *ts_scale;
int nb_ts_scale;
SpecifierOpt *dump_attachment;
int nb_dump_attachment;
SpecifierOpt *hwaccels;
int nb_hwaccels;
SpecifierOpt *hwaccel_devices;
int nb_hwaccel_devices;
SpecifierOpt *hwaccel_output_formats;
int nb_hwaccel_output_formats;
SpecifierOpt *autorotate;
int nb_autorotate;
/* output options */
StreamMap *stream_maps;
int nb_stream_maps;
AudioChannelMap *audio_channel_maps; /* one info entry per -map_channel */
int nb_audio_channel_maps; /* number of (valid) -map_channel settings */
int metadata_global_manual;
int metadata_streams_manual;
int metadata_chapters_manual;
const char **attachments;
int nb_attachments;
int chapters_input_file;
int64_t recording_time;
int64_t stop_time;
uint64_t limit_filesize;
float mux_preload;
float mux_max_delay;
int shortest;
int bitexact;
int video_disable;
int audio_disable;
int subtitle_disable;
int data_disable;
/* indexed by output file stream index */
int *streamid_map;
int nb_streamid_map;
SpecifierOpt *metadata;
int nb_metadata;
SpecifierOpt *max_frames;
int nb_max_frames;
SpecifierOpt *bitstream_filters;
int nb_bitstream_filters;
SpecifierOpt *codec_tags;
int nb_codec_tags;
SpecifierOpt *sample_fmts;
int nb_sample_fmts;
SpecifierOpt *qscale;
int nb_qscale;
SpecifierOpt *forced_key_frames;
int nb_forced_key_frames;
SpecifierOpt *fps_mode;
int nb_fps_mode;
SpecifierOpt *force_fps;
int nb_force_fps;
SpecifierOpt *frame_aspect_ratios;
int nb_frame_aspect_ratios;
SpecifierOpt *rc_overrides;
int nb_rc_overrides;
SpecifierOpt *intra_matrices;
int nb_intra_matrices;
SpecifierOpt *inter_matrices;
int nb_inter_matrices;
SpecifierOpt *chroma_intra_matrices;
int nb_chroma_intra_matrices;
SpecifierOpt *top_field_first;
int nb_top_field_first;
SpecifierOpt *metadata_map;
int nb_metadata_map;
SpecifierOpt *presets;
int nb_presets;
SpecifierOpt *copy_initial_nonkeyframes;
int nb_copy_initial_nonkeyframes;
SpecifierOpt *copy_prior_start;
int nb_copy_prior_start;
SpecifierOpt *filters;
int nb_filters;
SpecifierOpt *filter_scripts;
int nb_filter_scripts;
SpecifierOpt *reinit_filters;
int nb_reinit_filters;
SpecifierOpt *fix_sub_duration;
int nb_fix_sub_duration;
SpecifierOpt *canvas_sizes;
int nb_canvas_sizes;
SpecifierOpt *pass;
int nb_pass;
SpecifierOpt *passlogfiles;
int nb_passlogfiles;
SpecifierOpt *max_muxing_queue_size;
int nb_max_muxing_queue_size;
SpecifierOpt *muxing_queue_data_threshold;
int nb_muxing_queue_data_threshold;
SpecifierOpt *guess_layout_max;
int nb_guess_layout_max;
SpecifierOpt *apad;
int nb_apad;
SpecifierOpt *discard;
int nb_discard;
SpecifierOpt *disposition;
int nb_disposition;
SpecifierOpt *program;
int nb_program;
SpecifierOpt *time_bases;
int nb_time_bases;
SpecifierOpt *enc_time_bases;
int nb_enc_time_bases;
SpecifierOpt *autoscale;
int nb_autoscale;
SpecifierOpt *bits_per_raw_sample;
int nb_bits_per_raw_sample;
} OptionsContext;
typedef struct InputFilter {
AVFilterContext *filter;
struct InputStream *ist;
struct FilterGraph *graph;
uint8_t *name;
enum AVMediaType type; // AVMEDIA_TYPE_SUBTITLE for sub2video
AVFifo *frame_queue;
// parameters configured for this input
int format;
int width, height;
AVRational sample_aspect_ratio;
int sample_rate;
AVChannelLayout ch_layout;
AVBufferRef *hw_frames_ctx;
int32_t *displaymatrix;
int eof;
} InputFilter;
typedef struct OutputFilter {
AVFilterContext *filter;
struct OutputStream *ost;
struct FilterGraph *graph;
uint8_t *name;
/* temporary storage until stream maps are processed */
AVFilterInOut *out_tmp;
enum AVMediaType type;
/* desired output stream properties */
int width, height;
AVRational frame_rate;
int format;
int sample_rate;
AVChannelLayout ch_layout;
// those are only set if no format is specified and the encoder gives us multiple options
// They point directly to the relevant lists of the encoder.
const int *formats;
const AVChannelLayout *ch_layouts;
const int *sample_rates;
} OutputFilter;
typedef struct FilterGraph {
int index;
const char *graph_desc;
AVFilterGraph *graph;
int reconfiguration;
// true when the filtergraph contains only meta filters
// that do not modify the frame data
int is_meta;
InputFilter **inputs;
int nb_inputs;
OutputFilter **outputs;
int nb_outputs;
} FilterGraph;
typedef struct InputStream {
int file_index;
AVStream *st;
int discard; /* true if stream data should be discarded */
int user_set_discard;
int decoding_needed; /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */
#define DECODING_FOR_OST 1
#define DECODING_FOR_FILTER 2
int processing_needed; /* non zero if the packets must be processed */
AVCodecContext *dec_ctx;
const AVCodec *dec;
AVFrame *decoded_frame;
AVPacket *pkt;
int64_t prev_pkt_pts;
int64_t start; /* time when read started */
/* predicted dts of the next packet read for this stream or (when there are
* several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */
int64_t next_dts;
int64_t first_dts; ///< dts of the first packet read for this stream (in AV_TIME_BASE units)
int64_t dts; ///< dts of the last packet read for this stream (in AV_TIME_BASE units)
int64_t next_pts; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units)
int64_t pts; ///< current pts of the decoded frame (in AV_TIME_BASE units)
int wrap_correction_done;
int64_t filter_in_rescale_delta_last;
int64_t min_pts; /* pts with the smallest value in a current stream */
int64_t max_pts; /* pts with the higher value in a current stream */
// when forcing constant input framerate through -r,
// this contains the pts that will be given to the next decoded frame
int64_t cfr_next_pts;
int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */
double ts_scale;
int saw_first_ts;
AVDictionary *decoder_opts;
AVRational framerate; /* framerate forced with -r */
int top_field_first;
int guess_layout_max;
int autorotate;
int fix_sub_duration;
struct { /* previous decoded subtitle and related variables */
int got_output;
int ret;
AVSubtitle subtitle;
} prev_sub;
struct sub2video {
int64_t last_pts;
int64_t end_pts;
AVFifo *sub_queue; ///< queue of AVSubtitle* before filter init
AVFrame *frame;
int w, h;
unsigned int initialize; ///< marks if sub2video_update should force an initialization
} sub2video;
/* decoded data from this stream goes into all those filters
* currently video and audio only */
InputFilter **filters;
int nb_filters;
int reinit_filters;
/* hwaccel options */
enum HWAccelID hwaccel_id;
enum AVHWDeviceType hwaccel_device_type;
char *hwaccel_device;
enum AVPixelFormat hwaccel_output_format;
/* hwaccel context */
void *hwaccel_ctx;
void (*hwaccel_uninit)(AVCodecContext *s);
int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
enum AVPixelFormat hwaccel_pix_fmt;
enum AVPixelFormat hwaccel_retrieved_pix_fmt;
/* stats */
// combined size of all the packets read
uint64_t data_size;
/* number of packets successfully read for this stream */
uint64_t nb_packets;
// number of frames/samples retrieved from the decoder
uint64_t frames_decoded;
uint64_t samples_decoded;
int64_t *dts_buffer;
int nb_dts_buffer;
int got_output;
} InputStream;
typedef struct InputFile {
AVFormatContext *ctx;
int eof_reached; /* true if eof reached */
int eagain; /* true if last read attempt returned EAGAIN */
int ist_index; /* index of first stream in input_streams */
int loop; /* set number of times input stream should be looped */
int64_t duration; /* actual duration of the longest stream in a file
at the moment when looping happens */
AVRational time_base; /* time base of the duration */
int64_t input_ts_offset;
int input_sync_ref;
int64_t ts_offset;
int64_t last_ts;
int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
int64_t recording_time;
int nb_streams; /* number of stream that ffmpeg is aware of; may be different
from ctx.nb_streams if new streams appear during av_read_frame() */
int nb_streams_warn; /* number of streams that the user was warned of */
int rate_emu;
float readrate;
int accurate_seek;
AVPacket *pkt;
#if HAVE_THREADS
AVThreadMessageQueue *in_thread_queue;
pthread_t thread; /* thread reading from this file */
int non_blocking; /* reading packets from the thread should not block */
int joined; /* the thread has been joined */
int thread_queue_size; /* maximum number of queued packets */
#endif
} InputFile;
enum forced_keyframes_const {
FKF_N,
FKF_N_FORCED,
FKF_PREV_FORCED_N,
FKF_PREV_FORCED_T,
FKF_T,
FKF_NB
};
#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0)
#define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 << 1)
extern const char *const forced_keyframes_const_names[];
typedef enum {
ENCODER_FINISHED = 1,
MUXER_FINISHED = 2,
} OSTFinished ;
typedef struct OutputStream {
int file_index; /* file index */
int index; /* stream index in the output file */
int source_index; /* InputStream index */
AVStream *st; /* stream in the output file */
int encoding_needed; /* true if encoding needed for this stream */
int64_t frame_number;
/* input pts and corresponding output pts
for A/V sync */
struct InputStream *sync_ist; /* input stream to sync against */
int64_t sync_opts; /* output frame counter, could be changed to some true timestamp */ // FIXME look at frame_number
/* pts of the first frame encoded for this stream, used for limiting
* recording time */
int64_t first_pts;
/* dts of the last packet sent to the muxer */
int64_t last_mux_dts;
// the timebase of the packets sent to the muxer
AVRational mux_timebase;
AVRational enc_timebase;
AVBSFContext *bsf_ctx;
AVCodecContext *enc_ctx;
AVCodecParameters *ref_par; /* associated input codec parameters with encoders options applied */
const AVCodec *enc;
int64_t max_frames;
AVFrame *filtered_frame;
AVFrame *last_frame;
AVPacket *pkt;
int64_t last_dropped;
int64_t last_nb0_frames[3];
void *hwaccel_ctx;
/* video only */
AVRational frame_rate;
AVRational max_frame_rate;
enum VideoSyncMethod vsync_method;
int is_cfr;
const char *fps_mode;
int force_fps;
int top_field_first;
int rotate_overridden;
int autoscale;
int bits_per_raw_sample;
double rotate_override_value;
AVRational frame_aspect_ratio;
/* forced key frames */
int64_t forced_kf_ref_pts;
int64_t *forced_kf_pts;
int forced_kf_count;
int forced_kf_index;
char *forced_keyframes;
AVExpr *forced_keyframes_pexpr;
double forced_keyframes_expr_const_values[FKF_NB];
int dropped_keyframe;
/* audio only */
int *audio_channels_map; /* list of the channels id to pick from the source stream */
int audio_channels_mapped; /* number of channels in audio_channels_map */
char *logfile_prefix;
FILE *logfile;
OutputFilter *filter;
char *avfilter;
char *filters; ///< filtergraph associated to the -filter option
char *filters_script; ///< filtergraph script associated to the -filter_script option
AVDictionary *encoder_opts;
AVDictionary *sws_dict;
AVDictionary *swr_opts;
char *apad;
OSTFinished finished; /* no more packets should be written for this stream */
int unavailable; /* true if the steram is unavailable (possibly temporarily) */
int stream_copy;
// init_output_stream() has been called for this stream
// The encoder and the bitstream filters have been initialized and the stream
// parameters are set in the AVStream.
int initialized;
int inputs_done;
const char *attachment_filename;
int streamcopy_started;
int copy_initial_nonkeyframes;
int copy_prior_start;
char *disposition;
int keep_pix_fmt;
/* stats */
// combined size of all the packets written
uint64_t data_size;
// number of packets send to the muxer
uint64_t packets_written;
// number of frames/samples sent to the encoder
uint64_t frames_encoded;
uint64_t samples_encoded;
// number of packets received from the encoder
uint64_t packets_encoded;
/* packet quality factor */
int quality;
int max_muxing_queue_size;
/* the packets are buffered here until the muxer is ready to be initialized */
AVFifo *muxing_queue;
/*
* The size of the AVPackets' buffers in queue.
* Updated when a packet is either pushed or pulled from the queue.
*/
size_t muxing_queue_data_size;
/* Threshold after which max_muxing_queue_size will be in effect */
size_t muxing_queue_data_threshold;
/* packet picture type */
int pict_type;
/* frame encode sum of squared error values */
int64_t error[4];
} OutputStream;
typedef struct OutputFile {
int index;
const AVOutputFormat *format;
AVFormatContext *ctx;
AVDictionary *opts;
int ost_index; /* index of the first stream in output_streams */
int64_t recording_time; ///< desired length of the resulting file in microseconds == AV_TIME_BASE units
int64_t start_time; ///< start time in microseconds == AV_TIME_BASE units
uint64_t limit_filesize; /* filesize limit expressed in bytes */
int shortest;
int header_written;
} OutputFile;
extern InputStream **input_streams;
extern int nb_input_streams;
extern InputFile **input_files;
extern int nb_input_files;
extern OutputStream **output_streams;
extern int nb_output_streams;
extern OutputFile **output_files;
extern int nb_output_files;
extern FilterGraph **filtergraphs;
extern int nb_filtergraphs;
extern char *vstats_filename;
extern char *sdp_filename;
extern float audio_drift_threshold;
extern float dts_delta_threshold;
extern float dts_error_threshold;
extern int audio_volume;
extern int audio_sync_method;
extern enum VideoSyncMethod video_sync_method;
extern float frame_drop_threshold;
extern int do_benchmark;
extern int do_benchmark_all;
extern int do_deinterlace;
extern int do_hex_dump;
extern int do_pkt_dump;
extern int copy_ts;
extern int start_at_zero;
extern int copy_tb;
extern int debug_ts;
extern int exit_on_error;
extern int abort_on_flags;
extern int print_stats;
extern int64_t stats_period;
extern int qp_hist;
extern int stdin_interaction;
extern int frame_bits_per_raw_sample;
extern AVIOContext *progress_avio;
extern float max_error_rate;
extern char *filter_nbthreads;
extern int filter_complex_nbthreads;
extern int vstats_version;
extern int auto_conversion_filters;
extern const AVIOInterruptCB int_cb;
extern const OptionDef options[];
#if CONFIG_QSV
extern char *qsv_device;
#endif
extern HWDevice *filter_hw_device;
extern int want_sdp;
extern unsigned nb_output_dumped;
extern int main_return_code;
void term_init(void);
void term_exit(void);
void show_usage(void);
void remove_avoptions(AVDictionary **a, AVDictionary *b);
void assert_avoptions(AVDictionary *m);
int guess_input_channel_layout(InputStream *ist);
int configure_filtergraph(FilterGraph *fg);
void check_filter_outputs(void);
int filtergraph_is_simple(FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
int init_complex_filtergraph(FilterGraph *fg);
void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
int ffmpeg_parse_options(int argc, char **argv);
int videotoolbox_init(AVCodecContext *s);
int qsv_init(AVCodecContext *s);
HWDevice *hw_device_get_by_name(const char *name);
int hw_device_init_from_string(const char *arg, HWDevice **dev);
void hw_device_free_all(void);
int hw_device_setup_for_decode(InputStream *ist);
int hw_device_setup_for_encode(OutputStream *ost);
int hw_device_setup_for_filter(FilterGraph *fg);
int hwaccel_decode_init(AVCodecContext *avctx);
/* open the muxer when all the streams are initialized */
int of_check_init(OutputFile *of);
int of_write_trailer(OutputFile *of);
void of_close(OutputFile **pof);
void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
int unqueue);
#endif /* FFTOOLS_FFMPEG_H */

1204
src/fftools/ffmpeg_filter.c Normal file

File diff suppressed because it is too large Load Diff

583
src/fftools/ffmpeg_hw.c Normal file
View File

@@ -0,0 +1,583 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include "libavutil/avstring.h"
#include "libavutil/pixdesc.h"
#include "libavfilter/buffersink.h"
#include "ffmpeg.h"
static int nb_hw_devices;
static HWDevice **hw_devices;
static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type)
{
HWDevice *found = NULL;
int i;
for (i = 0; i < nb_hw_devices; i++) {
if (hw_devices[i]->type == type) {
if (found)
return NULL;
found = hw_devices[i];
}
}
return found;
}
HWDevice *hw_device_get_by_name(const char *name)
{
int i;
for (i = 0; i < nb_hw_devices; i++) {
if (!strcmp(hw_devices[i]->name, name))
return hw_devices[i];
}
return NULL;
}
static HWDevice *hw_device_add(void)
{
int err;
err = av_reallocp_array(&hw_devices, nb_hw_devices + 1,
sizeof(*hw_devices));
if (err) {
nb_hw_devices = 0;
return NULL;
}
hw_devices[nb_hw_devices] = av_mallocz(sizeof(HWDevice));
if (!hw_devices[nb_hw_devices])
return NULL;
return hw_devices[nb_hw_devices++];
}
static char *hw_device_default_name(enum AVHWDeviceType type)
{
// Make an automatic name of the form "type%d". We arbitrarily
// limit at 1000 anonymous devices of the same type - there is
// probably something else very wrong if you get to this limit.
const char *type_name = av_hwdevice_get_type_name(type);
char *name;
size_t index_pos;
int index, index_limit = 1000;
index_pos = strlen(type_name);
name = av_malloc(index_pos + 4);
if (!name)
return NULL;
for (index = 0; index < index_limit; index++) {
snprintf(name, index_pos + 4, "%s%d", type_name, index);
if (!hw_device_get_by_name(name))
break;
}
if (index >= index_limit) {
av_freep(&name);
return NULL;
}
return name;
}
int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
{
// "type=name"
// "type=name,key=value,key2=value2"
// "type=name:device,key=value,key2=value2"
// "type:device,key=value,key2=value2"
// -> av_hwdevice_ctx_create()
// "type=name@name"
// "type@name"
// -> av_hwdevice_ctx_create_derived()
AVDictionary *options = NULL;
const char *type_name = NULL, *name = NULL, *device = NULL;
enum AVHWDeviceType type;
HWDevice *dev, *src;
AVBufferRef *device_ref = NULL;
int err;
const char *errmsg, *p, *q;
size_t k;
k = strcspn(arg, ":=@");
p = arg + k;
type_name = av_strndup(arg, k);
if (!type_name) {
err = AVERROR(ENOMEM);
goto fail;
}
type = av_hwdevice_find_type_by_name(type_name);
if (type == AV_HWDEVICE_TYPE_NONE) {
errmsg = "unknown device type";
goto invalid;
}
if (*p == '=') {
k = strcspn(p + 1, ":@,");
name = av_strndup(p + 1, k);
if (!name) {
err = AVERROR(ENOMEM);
goto fail;
}
if (hw_device_get_by_name(name)) {
errmsg = "named device already exists";
goto invalid;
}
p += 1 + k;
} else {
name = hw_device_default_name(type);
if (!name) {
err = AVERROR(ENOMEM);
goto fail;
}
}
if (!*p) {
// New device with no parameters.
err = av_hwdevice_ctx_create(&device_ref, type,
NULL, NULL, 0);
if (err < 0)
goto fail;
} else if (*p == ':') {
// New device with some parameters.
++p;
q = strchr(p, ',');
if (q) {
if (q - p > 0) {
device = av_strndup(p, q - p);
if (!device) {
err = AVERROR(ENOMEM);
goto fail;
}
}
err = av_dict_parse_string(&options, q + 1, "=", ",", 0);
if (err < 0) {
errmsg = "failed to parse options";
goto invalid;
}
}
err = av_hwdevice_ctx_create(&device_ref, type,
q ? device : p[0] ? p : NULL,
options, 0);
if (err < 0)
goto fail;
} else if (*p == '@') {
// Derive from existing device.
src = hw_device_get_by_name(p + 1);
if (!src) {
errmsg = "invalid source device name";
goto invalid;
}
err = av_hwdevice_ctx_create_derived(&device_ref, type,
src->device_ref, 0);
if (err < 0)
goto fail;
} else if (*p == ',') {
err = av_dict_parse_string(&options, p + 1, "=", ",", 0);
if (err < 0) {
errmsg = "failed to parse options";
goto invalid;
}
err = av_hwdevice_ctx_create(&device_ref, type,
NULL, options, 0);
if (err < 0)
goto fail;
} else {
errmsg = "parse error";
goto invalid;
}
dev = hw_device_add();
if (!dev) {
err = AVERROR(ENOMEM);
goto fail;
}
dev->name = name;
dev->type = type;
dev->device_ref = device_ref;
if (dev_out)
*dev_out = dev;
name = NULL;
err = 0;
done:
av_freep(&type_name);
av_freep(&name);
av_freep(&device);
av_dict_free(&options);
return err;
invalid:
av_log(NULL, AV_LOG_ERROR,
"Invalid device specification \"%s\": %s\n", arg, errmsg);
err = AVERROR(EINVAL);
goto done;
fail:
av_log(NULL, AV_LOG_ERROR,
"Device creation failed: %d.\n", err);
av_buffer_unref(&device_ref);
goto done;
}
static int hw_device_init_from_type(enum AVHWDeviceType type,
const char *device,
HWDevice **dev_out)
{
AVBufferRef *device_ref = NULL;
HWDevice *dev;
char *name;
int err;
name = hw_device_default_name(type);
if (!name) {
err = AVERROR(ENOMEM);
goto fail;
}
err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR,
"Device creation failed: %d.\n", err);
goto fail;
}
dev = hw_device_add();
if (!dev) {
err = AVERROR(ENOMEM);
goto fail;
}
dev->name = name;
dev->type = type;
dev->device_ref = device_ref;
if (dev_out)
*dev_out = dev;
return 0;
fail:
av_freep(&name);
av_buffer_unref(&device_ref);
return err;
}
void hw_device_free_all(void)
{
int i;
for (i = 0; i < nb_hw_devices; i++) {
av_freep(&hw_devices[i]->name);
av_buffer_unref(&hw_devices[i]->device_ref);
av_freep(&hw_devices[i]);
}
av_freep(&hw_devices);
nb_hw_devices = 0;
}
static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
{
const AVCodecHWConfig *config;
HWDevice *dev;
int i;
for (i = 0;; i++) {
config = avcodec_get_hw_config(codec, i);
if (!config)
return NULL;
if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
continue;
dev = hw_device_get_by_type(config->device_type);
if (dev)
return dev;
}
}
int hw_device_setup_for_decode(InputStream *ist)
{
const AVCodecHWConfig *config;
enum AVHWDeviceType type;
HWDevice *dev = NULL;
int err, auto_device = 0;
if (ist->hwaccel_device) {
dev = hw_device_get_by_name(ist->hwaccel_device);
if (!dev) {
if (ist->hwaccel_id == HWACCEL_AUTO) {
auto_device = 1;
} else if (ist->hwaccel_id == HWACCEL_GENERIC) {
type = ist->hwaccel_device_type;
err = hw_device_init_from_type(type, ist->hwaccel_device,
&dev);
} else {
// This will be dealt with by API-specific initialisation
// (using hwaccel_device), so nothing further needed here.
return 0;
}
} else {
if (ist->hwaccel_id == HWACCEL_AUTO) {
ist->hwaccel_device_type = dev->type;
} else if (ist->hwaccel_device_type != dev->type) {
av_log(ist->dec_ctx, AV_LOG_ERROR, "Invalid hwaccel device "
"specified for decoder: device %s of type %s is not "
"usable with hwaccel %s.\n", dev->name,
av_hwdevice_get_type_name(dev->type),
av_hwdevice_get_type_name(ist->hwaccel_device_type));
return AVERROR(EINVAL);
}
}
} else {
if (ist->hwaccel_id == HWACCEL_AUTO) {
auto_device = 1;
} else if (ist->hwaccel_id == HWACCEL_GENERIC) {
type = ist->hwaccel_device_type;
dev = hw_device_get_by_type(type);
// When "-qsv_device device" is used, an internal QSV device named
// as "__qsv_device" is created. Another QSV device is created too
// if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices
// if both "-qsv_device device" and "-init_hw_device qsv=name:device"
// are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL.
// To keep back-compatibility with the removed ad-hoc libmfx setup code,
// call hw_device_get_by_name("__qsv_device") to select the internal QSV
// device.
if (!dev && type == AV_HWDEVICE_TYPE_QSV)
dev = hw_device_get_by_name("__qsv_device");
if (!dev)
err = hw_device_init_from_type(type, NULL, &dev);
} else {
dev = hw_device_match_by_codec(ist->dec);
if (!dev) {
// No device for this codec, but not using generic hwaccel
// and therefore may well not need one - ignore.
return 0;
}
}
}
if (auto_device) {
int i;
if (!avcodec_get_hw_config(ist->dec, 0)) {
// Decoder does not support any hardware devices.
return 0;
}
for (i = 0; !dev; i++) {
config = avcodec_get_hw_config(ist->dec, i);
if (!config)
break;
type = config->device_type;
dev = hw_device_get_by_type(type);
if (dev) {
av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
"hwaccel type %s with existing device %s.\n",
av_hwdevice_get_type_name(type), dev->name);
}
}
for (i = 0; !dev; i++) {
config = avcodec_get_hw_config(ist->dec, i);
if (!config)
break;
type = config->device_type;
// Try to make a new device of this type.
err = hw_device_init_from_type(type, ist->hwaccel_device,
&dev);
if (err < 0) {
// Can't make a device of this type.
continue;
}
if (ist->hwaccel_device) {
av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
"hwaccel type %s with new device created "
"from %s.\n", av_hwdevice_get_type_name(type),
ist->hwaccel_device);
} else {
av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
"hwaccel type %s with new default device.\n",
av_hwdevice_get_type_name(type));
}
}
if (dev) {
ist->hwaccel_device_type = type;
} else {
av_log(ist->dec_ctx, AV_LOG_INFO, "Auto hwaccel "
"disabled: no device found.\n");
ist->hwaccel_id = HWACCEL_NONE;
return 0;
}
}
if (!dev) {
av_log(ist->dec_ctx, AV_LOG_ERROR, "No device available "
"for decoder: device type %s needed for codec %s.\n",
av_hwdevice_get_type_name(type), ist->dec->name);
return err;
}
ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ist->dec_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
return 0;
}
int hw_device_setup_for_encode(OutputStream *ost)
{
const AVCodecHWConfig *config;
HWDevice *dev = NULL;
AVBufferRef *frames_ref = NULL;
int i;
if (ost->filter) {
frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
if (frames_ref &&
((AVHWFramesContext*)frames_ref->data)->format ==
ost->enc_ctx->pix_fmt) {
// Matching format, will try to use hw_frames_ctx.
} else {
frames_ref = NULL;
}
}
for (i = 0;; i++) {
config = avcodec_get_hw_config(ost->enc, i);
if (!config)
break;
if (frames_ref &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
(config->pix_fmt == AV_PIX_FMT_NONE ||
config->pix_fmt == ost->enc_ctx->pix_fmt)) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input "
"frames context (format %s) with %s encoder.\n",
av_get_pix_fmt_name(ost->enc_ctx->pix_fmt),
ost->enc->name);
ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref);
if (!ost->enc_ctx->hw_frames_ctx)
return AVERROR(ENOMEM);
return 0;
}
if (!dev &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
dev = hw_device_get_by_type(config->device_type);
}
if (dev) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s "
"(type %s) with %s encoder.\n", dev->name,
av_hwdevice_get_type_name(dev->type), ost->enc->name);
ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ost->enc_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
} else {
// No device required, or no device available.
}
return 0;
}
static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
{
InputStream *ist = avctx->opaque;
AVFrame *output = NULL;
enum AVPixelFormat output_format = ist->hwaccel_output_format;
int err;
if (input->format == output_format) {
// Nothing to do.
return 0;
}
output = av_frame_alloc();
if (!output)
return AVERROR(ENOMEM);
output->format = output_format;
err = av_hwframe_transfer_data(output, input, 0);
if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to "
"output frame: %d.\n", err);
goto fail;
}
err = av_frame_copy_props(output, input);
if (err < 0) {
av_frame_unref(output);
goto fail;
}
av_frame_unref(input);
av_frame_move_ref(input, output);
av_frame_free(&output);
return 0;
fail:
av_frame_free(&output);
return err;
}
int hwaccel_decode_init(AVCodecContext *avctx)
{
InputStream *ist = avctx->opaque;
ist->hwaccel_retrieve_data = &hwaccel_retrieve_data;
return 0;
}
int hw_device_setup_for_filter(FilterGraph *fg)
{
HWDevice *dev;
int i;
// Pick the last hardware device if the user doesn't pick the device for
// filters explicitly with the filter_hw_device option.
if (filter_hw_device)
dev = filter_hw_device;
else if (nb_hw_devices > 0) {
dev = hw_devices[nb_hw_devices - 1];
if (nb_hw_devices > 1)
av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device "
"%s of type %s is picked for filters by default. Set hardware "
"device explicitly with the filter_hw_device option if device "
"%s is not usable for filters.\n",
nb_hw_devices, dev->name,
av_hwdevice_get_type_name(dev->type), dev->name);
} else
dev = NULL;
if (dev) {
for (i = 0; i < fg->graph->nb_filters; i++) {
fg->graph->filters[i]->hw_device_ctx =
av_buffer_ref(dev->device_ref);
if (!fg->graph->filters[i]->hw_device_ctx)
return AVERROR(ENOMEM);
}
}
return 0;
}

317
src/fftools/ffmpeg_mux.c Normal file
View File

@@ -0,0 +1,317 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <string.h>
#include "ffmpeg.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavutil/timestamp.h"
#include "libavcodec/packet.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
static void close_all_output_streams(OutputStream *ost, OSTFinished this_stream, OSTFinished others)
{
int i;
for (i = 0; i < nb_output_streams; i++) {
OutputStream *ost2 = output_streams[i];
ost2->finished |= ost == ost2 ? this_stream : others;
}
}
void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
int unqueue)
{
AVFormatContext *s = of->ctx;
AVStream *st = ost->st;
int ret;
/*
* Audio encoders may split the packets -- #frames in != #packets out.
* But there is no reordering, so we can limit the number of output packets
* by simply dropping them here.
* Counting encoded video frames needs to be done separately because of
* reordering, see do_video_out().
* Do not count the packet when unqueued because it has been counted when queued.
*/
if (!(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->encoding_needed) && !unqueue) {
if (ost->frame_number >= ost->max_frames) {
av_packet_unref(pkt);
return;
}
ost->frame_number++;
}
if (!of->header_written) {
AVPacket *tmp_pkt;
/* the muxer is not initialized yet, buffer the packet */
if (!av_fifo_can_write(ost->muxing_queue)) {
size_t cur_size = av_fifo_can_read(ost->muxing_queue);
unsigned int are_we_over_size =
(ost->muxing_queue_data_size + pkt->size) > ost->muxing_queue_data_threshold;
size_t limit = are_we_over_size ? ost->max_muxing_queue_size : SIZE_MAX;
size_t new_size = FFMIN(2 * cur_size, limit);
if (new_size <= cur_size) {
av_log(NULL, AV_LOG_ERROR,
"Too many packets buffered for output stream %d:%d.\n",
ost->file_index, ost->st->index);
exit_program(1);
}
ret = av_fifo_grow2(ost->muxing_queue, new_size - cur_size);
if (ret < 0)
exit_program(1);
}
ret = av_packet_make_refcounted(pkt);
if (ret < 0)
exit_program(1);
tmp_pkt = av_packet_alloc();
if (!tmp_pkt)
exit_program(1);
av_packet_move_ref(tmp_pkt, pkt);
ost->muxing_queue_data_size += tmp_pkt->size;
av_fifo_write(ost->muxing_queue, &tmp_pkt, 1);
return;
}
if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) ||
(st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_sync_method < 0))
pkt->pts = pkt->dts = AV_NOPTS_VALUE;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (ost->frame_rate.num && ost->is_cfr) {
if (pkt->duration > 0)
av_log(NULL, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n");
pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate),
ost->mux_timebase);
}
}
av_packet_rescale_ts(pkt, ost->mux_timebase, ost->st->time_base);
if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
if (pkt->dts != AV_NOPTS_VALUE &&
pkt->pts != AV_NOPTS_VALUE &&
pkt->dts > pkt->pts) {
av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n",
pkt->dts, pkt->pts,
ost->file_index, ost->st->index);
pkt->pts =
pkt->dts = pkt->pts + pkt->dts + ost->last_mux_dts + 1
- FFMIN3(pkt->pts, pkt->dts, ost->last_mux_dts + 1)
- FFMAX3(pkt->pts, pkt->dts, ost->last_mux_dts + 1);
}
if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) &&
pkt->dts != AV_NOPTS_VALUE &&
ost->last_mux_dts != AV_NOPTS_VALUE) {
int64_t max = ost->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
if (pkt->dts < max) {
int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG;
if (exit_on_error)
loglevel = AV_LOG_ERROR;
av_log(s, loglevel, "Non-monotonous DTS in output stream "
"%d:%d; previous: %"PRId64", current: %"PRId64"; ",
ost->file_index, ost->st->index, ost->last_mux_dts, pkt->dts);
if (exit_on_error) {
av_log(NULL, AV_LOG_FATAL, "aborting.\n");
exit_program(1);
}
av_log(s, loglevel, "changing to %"PRId64". This may result "
"in incorrect timestamps in the output file.\n",
max);
if (pkt->pts >= pkt->dts)
pkt->pts = FFMAX(pkt->pts, max);
pkt->dts = max;
}
}
}
ost->last_mux_dts = pkt->dts;
ost->data_size += pkt->size;
ost->packets_written++;
pkt->stream_index = ost->index;
if (debug_ts) {
av_log(NULL, AV_LOG_INFO, "muxer <- type:%s "
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n",
av_get_media_type_string(ost->enc_ctx->codec_type),
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base),
pkt->size
);
}
ret = av_interleaved_write_frame(s, pkt);
if (ret < 0) {
print_error("av_interleaved_write_frame()", ret);
main_return_code = 1;
close_all_output_streams(ost, MUXER_FINISHED | ENCODER_FINISHED, ENCODER_FINISHED);
}
}
static int print_sdp(void)
{
char sdp[16384];
int i;
int j, ret;
AVIOContext *sdp_pb;
AVFormatContext **avc;
for (i = 0; i < nb_output_files; i++) {
if (!output_files[i]->header_written)
return 0;
}
avc = av_malloc_array(nb_output_files, sizeof(*avc));
if (!avc)
exit_program(1);
for (i = 0, j = 0; i < nb_output_files; i++) {
if (!strcmp(output_files[i]->ctx->oformat->name, "rtp")) {
avc[j] = output_files[i]->ctx;
j++;
}
}
if (!j) {
av_log(NULL, AV_LOG_ERROR, "No output streams in the SDP.\n");
ret = AVERROR(EINVAL);
goto fail;
}
ret = av_sdp_create(avc, j, sdp, sizeof(sdp));
if (ret < 0)
goto fail;
if (!sdp_filename) {
printf("SDP:\n%s\n", sdp);
fflush(stdout);
} else {
ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename);
goto fail;
}
avio_print(sdp_pb, sdp);
avio_closep(&sdp_pb);
av_freep(&sdp_filename);
}
fail:
av_freep(&avc);
return ret;
}
/* open the muxer when all the streams are initialized */
int of_check_init(OutputFile *of)
{
int ret, i;
for (i = 0; i < of->ctx->nb_streams; i++) {
OutputStream *ost = output_streams[of->ost_index + i];
if (!ost->initialized)
return 0;
}
ret = avformat_write_header(of->ctx, &of->opts);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Could not write header for output file #%d "
"(incorrect codec parameters ?): %s\n",
of->index, av_err2str(ret));
return ret;
}
//assert_avoptions(of->opts);
of->header_written = 1;
av_dump_format(of->ctx, of->index, of->ctx->url, 1);
nb_output_dumped++;
if (sdp_filename || want_sdp) {
ret = print_sdp();
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error writing the SDP.\n");
return ret;
}
}
/* flush the muxing queues */
for (i = 0; i < of->ctx->nb_streams; i++) {
OutputStream *ost = output_streams[of->ost_index + i];
AVPacket *pkt;
/* try to improve muxing time_base (only possible if nothing has been written yet) */
if (!av_fifo_can_read(ost->muxing_queue))
ost->mux_timebase = ost->st->time_base;
while (av_fifo_read(ost->muxing_queue, &pkt, 1) >= 0) {
ost->muxing_queue_data_size -= pkt->size;
of_write_packet(of, pkt, ost, 1);
av_packet_free(&pkt);
}
}
return 0;
}
int of_write_trailer(OutputFile *of)
{
int ret;
if (!of->header_written) {
av_log(NULL, AV_LOG_ERROR,
"Nothing was written into output file %d (%s), because "
"at least one of its streams received no packets.\n",
of->index, of->ctx->url);
return AVERROR(EINVAL);
}
ret = av_write_trailer(of->ctx);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s\n", of->ctx->url, av_err2str(ret));
return ret;
}
return 0;
}
void of_close(OutputFile **pof)
{
OutputFile *of = *pof;
AVFormatContext *s;
if (!of)
return;
s = of->ctx;
if (s && s->oformat && !(s->oformat->flags & AVFMT_NOFILE))
avio_closep(&s->pb);
avformat_free_context(s);
av_dict_free(&of->opts);
av_freep(pof);
}

4001
src/fftools/ffmpeg_opt.c Normal file

File diff suppressed because it is too large Load Diff

3758
src/fftools/ffplay.c Normal file

File diff suppressed because it is too large Load Diff

4171
src/fftools/ffprobe.c Normal file

File diff suppressed because it is too large Load Diff

71
src/fftools/fopen_utf8.h Normal file
View File

@@ -0,0 +1,71 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef FFTOOLS_FOPEN_UTF8_H
#define FFTOOLS_FOPEN_UTF8_H
#include <stdio.h>
/* The fopen_utf8 function here is essentially equivalent to avpriv_fopen_utf8,
* except that it doesn't set O_CLOEXEC, and that it isn't exported
* from a different library. (On Windows, each DLL might use a different
* CRT, and FILE* handles can't be shared across them.) */
#ifdef _WIN32
#include "libavutil/wchar_filename.h"
static inline FILE *fopen_utf8(const char *path_utf8, const char *mode)
{
wchar_t *path_w, *mode_w;
FILE *f;
/* convert UTF-8 to wide chars */
if (get_extended_win32_path(path_utf8, &path_w)) /* This sets errno on error. */
return NULL;
if (!path_w)
goto fallback;
if (utf8towchar(mode, &mode_w))
return NULL;
if (!mode_w) {
/* If failing to interpret the mode string as utf8, it is an invalid
* parameter. */
av_freep(&path_w);
errno = EINVAL;
return NULL;
}
f = _wfopen(path_w, mode_w);
av_freep(&path_w);
av_freep(&mode_w);
return f;
fallback:
/* path may be in CP_ACP */
return fopen(path_utf8, mode);
}
#else
static inline FILE *fopen_utf8(const char *path, const char *mode)
{
return fopen(path, mode);
}
#endif
#endif /* FFTOOLS_FOPEN_UTF8_H */

1465
src/fftools/opt_common.c Normal file

File diff suppressed because it is too large Load Diff

231
src/fftools/opt_common.h Normal file
View File

@@ -0,0 +1,231 @@
/*
* Option handlers shared between the tools.
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef FFTOOLS_OPT_COMMON_H
#define FFTOOLS_OPT_COMMON_H
#include "config.h"
#include "cmdutils.h"
#if CONFIG_AVDEVICE
/**
* Print a listing containing autodetected sinks of the output device.
* Device name with options may be passed as an argument to limit results.
*/
int show_sinks(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing autodetected sources of the input device.
* Device name with options may be passed as an argument to limit results.
*/
int show_sources(void *optctx, const char *opt, const char *arg);
#endif
#if CONFIG_AVDEVICE
#define CMDUTILS_COMMON_OPTIONS_AVDEVICE \
{ "sources" , OPT_EXIT | HAS_ARG, { .func_arg = show_sources }, \
"list sources of the input device", "device" }, \
{ "sinks" , OPT_EXIT | HAS_ARG, { .func_arg = show_sinks }, \
"list sinks of the output device", "device" }, \
#else
#define CMDUTILS_COMMON_OPTIONS_AVDEVICE
#endif
/**
* Print the license of the program to stdout. The license depends on
* the license of the libraries compiled into the program.
* This option processing function does not utilize the arguments.
*/
int show_license(void *optctx, const char *opt, const char *arg);
/**
* Generic -h handler common to all fftools.
*/
int show_help(void *optctx, const char *opt, const char *arg);
/**
* Print the version of the program to stdout. The version message
* depends on the current versions of the repository and of the libav*
* libraries.
* This option processing function does not utilize the arguments.
*/
int show_version(void *optctx, const char *opt, const char *arg);
/**
* Print the build configuration of the program to stdout. The contents
* depend on the definition of FFMPEG_CONFIGURATION.
* This option processing function does not utilize the arguments.
*/
int show_buildconf(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the formats supported by the
* program (including devices).
* This option processing function does not utilize the arguments.
*/
int show_formats(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the muxers supported by the
* program (including devices).
* This option processing function does not utilize the arguments.
*/
int show_muxers(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the demuxer supported by the
* program (including devices).
* This option processing function does not utilize the arguments.
*/
int show_demuxers(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the devices supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_devices(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the codecs supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_codecs(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the decoders supported by the
* program.
*/
int show_decoders(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the encoders supported by the
* program.
*/
int show_encoders(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the bit stream filters supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_bsfs(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the protocols supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_protocols(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the filters supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_filters(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the pixel formats supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_pix_fmts(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the standard channel layouts supported by
* the program.
* This option processing function does not utilize the arguments.
*/
int show_layouts(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the sample formats supported by the
* program.
*/
int show_sample_fmts(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all supported stream dispositions.
*/
int show_dispositions(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the color names and values recognized
* by the program.
*/
int show_colors(void *optctx, const char *opt, const char *arg);
/**
* Set the libav* libraries log level.
*/
int opt_loglevel(void *optctx, const char *opt, const char *arg);
int opt_report(void *optctx, const char *opt, const char *arg);
int init_report(const char *env, FILE **file);
int opt_max_alloc(void *optctx, const char *opt, const char *arg);
/**
* Override the cpuflags.
*/
int opt_cpuflags(void *optctx, const char *opt, const char *arg);
/**
* Override the cpucount.
*/
int opt_cpucount(void *optctx, const char *opt, const char *arg);
#define CMDUTILS_COMMON_OPTIONS \
{ "L", OPT_EXIT, { .func_arg = show_license }, "show license" }, \
{ "h", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "?", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "-help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "version", OPT_EXIT, { .func_arg = show_version }, "show version" }, \
{ "buildconf", OPT_EXIT, { .func_arg = show_buildconf }, "show build configuration" }, \
{ "formats", OPT_EXIT, { .func_arg = show_formats }, "show available formats" }, \
{ "muxers", OPT_EXIT, { .func_arg = show_muxers }, "show available muxers" }, \
{ "demuxers", OPT_EXIT, { .func_arg = show_demuxers }, "show available demuxers" }, \
{ "devices", OPT_EXIT, { .func_arg = show_devices }, "show available devices" }, \
{ "codecs", OPT_EXIT, { .func_arg = show_codecs }, "show available codecs" }, \
{ "decoders", OPT_EXIT, { .func_arg = show_decoders }, "show available decoders" }, \
{ "encoders", OPT_EXIT, { .func_arg = show_encoders }, "show available encoders" }, \
{ "bsfs", OPT_EXIT, { .func_arg = show_bsfs }, "show available bit stream filters" }, \
{ "protocols", OPT_EXIT, { .func_arg = show_protocols }, "show available protocols" }, \
{ "filters", OPT_EXIT, { .func_arg = show_filters }, "show available filters" }, \
{ "pix_fmts", OPT_EXIT, { .func_arg = show_pix_fmts }, "show available pixel formats" }, \
{ "layouts", OPT_EXIT, { .func_arg = show_layouts }, "show standard channel layouts" }, \
{ "sample_fmts", OPT_EXIT, { .func_arg = show_sample_fmts }, "show available audio sample formats" }, \
{ "dispositions", OPT_EXIT, { .func_arg = show_dispositions}, "show available stream dispositions" }, \
{ "colors", OPT_EXIT, { .func_arg = show_colors }, "show available color names" }, \
{ "loglevel", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \
{ "v", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \
{ "report", 0, { .func_arg = opt_report }, "generate a report" }, \
{ "max_alloc", HAS_ARG, { .func_arg = opt_max_alloc }, "set maximum size of a single allocated block", "bytes" }, \
{ "cpuflags", HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags }, "force specific cpu flags", "flags" }, \
{ "cpucount", HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpucount }, "force specific cpu count", "count" }, \
{ "hide_banner", OPT_BOOL | OPT_EXPERT, {&hide_banner}, "do not show program banner", "hide_banner" }, \
CMDUTILS_COMMON_OPTIONS_AVDEVICE \
#endif /* FFTOOLS_OPT_COMMON_H */

120
src/index.d.ts vendored
View File

@@ -1,120 +0,0 @@
export const FS: {
writeFile: (fileName: string, binaryData: Uint8Array | string) => void,
readFile: (fileName: string) => Uint8Array,
readdir: (pathName: string) => string[],
unlink: (fileName: string) => void,
mkdir: (fileName: string) => void,
}
type FSMethodNames = { [K in keyof typeof FS]: (typeof FS)[K] extends (...args: any[]) => any ? K : never }[keyof typeof FS];
type FSMethodArgs = { [K in FSMethodNames]: Parameters<(typeof FS)[K]> };
type FSMethodReturn = { [K in FSMethodNames]: ReturnType<(typeof FS)[K]> };
type LogCallback = (logParams: { type: string; message: string }) => any;
type ProgressCallback = (progressParams: { ratio: number }) => any;
export interface CreateFFmpegOptions {
/** path for ffmpeg-core.js script */
corePath?: string;
/** path for ffmpeg-worker.js script */
workerPath?: string;
/** path for ffmpeg-core.wasm script */
wasmPath?: string;
/** a boolean to turn on all logs, default is false */
log?: boolean;
/** a function to get log messages, a quick example is ({ message }) => console.log(message) */
logger?: LogCallback;
/** a function to trace the progress, a quick example is p => console.log(p) */
progress?: ProgressCallback;
/** name of the main function of the ffmpeg-core.js script */
mainName?: string;
}
export interface FFmpeg {
/*
* Load ffmpeg.wasm-core script.
* In browser environment, the ffmpeg.wasm-core script is fetch from
* CDN and can be assign to a local path by assigning `corePath`.
* In node environment, we use dynamic require and the default `corePath`
* is `$ffmpeg/core`.
*
* Typically the load() func might take few seconds to minutes to complete,
* better to do it as early as possible.
*
*/
load(): Promise<void>;
/*
* Determine whether the Core is loaded.
*/
isLoaded(): boolean;
/*
* Run ffmpeg command.
* This is the major function in ffmpeg.wasm, you can just imagine it
* as ffmpeg native cli and what you need to pass is the same.
*
* For example, you can convert native command below:
*
* ```
* $ ffmpeg -i video.avi -c:v libx264 video.mp4
* ```
*
* To
*
* ```
* await ffmpeg.run('-i', 'video.avi', '-c:v', 'libx264', 'video.mp4');
* ```
*
*/
run(...args: string[]): Promise<void>;
/*
* Run FS operations.
* For input/output file of ffmpeg.wasm, it is required to save them to MEMFS
* first so that ffmpeg.wasm is able to consume them. Here we rely on the FS
* methods provided by Emscripten.
*
* Common methods to use are:
* ffmpeg.FS('writeFile', 'video.avi', new Uint8Array(...)): writeFile writes
* data to MEMFS. You need to use Uint8Array for binary data.
* ffmpeg.FS('readFile', 'video.mp4'): readFile from MEMFS.
* ffmpeg.FS('unlink', 'video.map'): delete file from MEMFS.
*
* For more info, check https://emscripten.org/docs/api_reference/Filesystem-API.html
*
*/
FS<Method extends FSMethodNames>(method: Method, ...args: FSMethodArgs[Method]): FSMethodReturn[Method];
setProgress(progress: ProgressCallback): void;
setLogger(log: LogCallback): void;
setLogging(logging: boolean): void;
exit(): void;
}
/*
* Create ffmpeg instance.
* Each ffmpeg instance owns an isolated MEMFS and works
* independently.
*
* For example:
*
* ```
* const ffmpeg = createFFmpeg({
* log: true,
* logger: () => {},
* progress: () => {},
* corePath: '',
* })
* ```
*
* For the usage of these four arguments, check config.js
*
*/
export function createFFmpeg(options?: CreateFFmpegOptions): FFmpeg;
/*
* Helper function for fetching files from various resource.
* Sometimes the video/audio file you want to process may located
* in a remote URL and somewhere in your local file system.
*
* This helper function helps you to fetch to file and return an
* Uint8Array variable for ffmpeg.wasm to consume.
*
*/
export function fetchFile(data: string | Buffer | Blob | File): Promise<Uint8Array>;

View File

@@ -1,36 +0,0 @@
require('regenerator-runtime/runtime');
const createFFmpeg = require('./createFFmpeg');
const { fetchFile } = require('./node');
module.exports = {
/*
* Create ffmpeg instance.
* Each ffmpeg instance owns an isolated MEMFS and works
* independently.
*
* For example:
*
* ```
* const ffmpeg = createFFmpeg({
* log: true,
* logger: () => {},
* progress: () => {},
* corePath: '',
* })
* ```
*
* For the usage of these four arguments, check config.js
*
*/
createFFmpeg,
/*
* Helper function for fetching files from various resource.
* Sometimes the video/audio file you want to process may located
* in a remote URL and somewhere in your local file system.
*
* This helper function helps you to fetch to file and return an
* Uint8Array variable for ffmpeg.wasm to consume.
*
*/
fetchFile,
};

View File

@@ -1,6 +0,0 @@
/*
* Default options for node environment
*/
module.exports = {
corePath: '@ffmpeg/core',
};

View File

@@ -1,33 +0,0 @@
const util = require('util');
const fs = require('fs');
const fetch = require('node-fetch');
const isURL = require('is-url');
module.exports = async (_data) => {
let data = _data;
if (typeof _data === 'undefined') {
return new Uint8Array();
}
if (typeof _data === 'string') {
/* From remote URL/server */
if (isURL(_data)
|| _data.startsWith('moz-extension://')
|| _data.startsWith('chrome-extension://')
|| _data.startsWith('file://')) {
const res = await fetch(_data);
data = await res.arrayBuffer();
/* From base64 format */
} else if (/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(_data)) {
data = Buffer.from(_data.split(',')[1], 'base64');
/* From local file path */
} else {
data = await util.promisify(fs.readFile)(_data);
}
/* From Buffer */
} else if (Buffer.isBuffer(_data)) {
data = _data;
}
return new Uint8Array(data);
};

View File

@@ -1,7 +0,0 @@
const { log } = require('../utils/log');
module.exports = ({ corePath }) => new Promise((resolve) => {
log('info', `fetch ffmpeg.wasm-core script from ${corePath}`);
// eslint-disable-next-line import/no-dynamic-require
resolve({ createFFmpegCore: require(corePath) });
});

View File

@@ -1,9 +0,0 @@
const defaultOptions = require('./defaultOptions');
const getCreateFFmpegCore = require('./getCreateFFmpegCore');
const fetchFile = require('./fetchFile');
module.exports = {
defaultOptions,
getCreateFFmpegCore,
fetchFile,
};

10
src/types/core/ffmpeg-core.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/// <reference types="emscripten" />
type Pointer = number;
type StringArrayPointer = Pointer;
interface FFmpegCoreModule extends EmscriptenModule {
_ffmpeg: (number, StringArrayPointer) => number;
}
declare const createFFmpegCore: EmscriptenModuleFactory<FFmpegCoreModule>;
export default createFFmpegCore;

View File

@@ -1,11 +0,0 @@
const CREATE_FFMPEG_CORE_IS_NOT_DEFINED = (corePath) => (`
createFFmpegCore is not defined. ffmpeg.wasm is unable to find createFFmpegCore after loading ffmpeg-core.js from ${corePath}. Use another URL when calling createFFmpeg():
const ffmpeg = createFFmpeg({
corePath: 'http://localhost:3000/ffmpeg-core.js',
});
`);
module.exports = {
CREATE_FFMPEG_CORE_IS_NOT_DEFINED,
};

View File

@@ -1,24 +0,0 @@
let logging = false;
let customLogger = () => {};
const setLogging = (_logging) => {
logging = _logging;
};
const setCustomLogger = (logger) => {
customLogger = logger;
};
const log = (type, message) => {
customLogger({ type, message });
if (logging) {
console.log(`[${type}] ${message}`);
}
};
module.exports = {
logging,
setLogging,
setCustomLogger,
log,
};

View File

@@ -1,10 +0,0 @@
module.exports = (Core, args) => {
const argsPtr = Core._malloc(args.length * Uint32Array.BYTES_PER_ELEMENT);
args.forEach((s, idx) => {
const sz = Core.lengthBytesUTF8(s) + 1;
const buf = Core._malloc(sz);
Core.stringToUTF8(s, buf, sz);
Core.setValue(argsPtr + (Uint32Array.BYTES_PER_ELEMENT * idx), buf, 'i32');
});
return [args.length, argsPtr];
};