Adopt lerna and typescript
This commit is contained in:
0
src/bind/bind.js
Normal file
0
src/bind/bind.js
Normal file
10
src/bind/export-runtime.js
Normal file
10
src/bind/export-runtime.js
Normal 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
3
src/bind/export.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const EXPORTED_FUNCTIONS = ["_ffmpeg"];
|
||||
|
||||
console.log(EXPORTED_FUNCTIONS.join(","));
|
||||
@@ -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 };
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
import defaultOptions from './defaultOptions';
|
||||
import { getCreateFFmpegCore } from './getCreateFFmpegCore';
|
||||
import { fetchFile } from './fetchFile';
|
||||
|
||||
export { defaultOptions, getCreateFFmpegCore, fetchFile };
|
||||
@@ -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: '',
|
||||
},
|
||||
};
|
||||
@@ -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
1018
src/fftools/cmdutils.c
Normal file
File diff suppressed because it is too large
Load Diff
455
src/fftools/cmdutils.h
Normal file
455
src/fftools/cmdutils.h
Normal 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
4579
src/fftools/ffmpeg.c
Normal file
File diff suppressed because it is too large
Load Diff
707
src/fftools/ffmpeg.h
Normal file
707
src/fftools/ffmpeg.h
Normal 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
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
583
src/fftools/ffmpeg_hw.c
Normal 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
317
src/fftools/ffmpeg_mux.c
Normal 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
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
3758
src/fftools/ffplay.c
Normal file
File diff suppressed because it is too large
Load Diff
4171
src/fftools/ffprobe.c
Normal file
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
71
src/fftools/fopen_utf8.h
Normal 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
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
231
src/fftools/opt_common.h
Normal 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
120
src/index.d.ts
vendored
@@ -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>;
|
||||
36
src/index.js
36
src/index.js
@@ -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,
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
/*
|
||||
* Default options for node environment
|
||||
*/
|
||||
module.exports = {
|
||||
corePath: '@ffmpeg/core',
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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) });
|
||||
});
|
||||
@@ -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
10
src/types/core/ffmpeg-core.d.ts
vendored
Normal 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;
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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];
|
||||
};
|
||||
Reference in New Issue
Block a user