ffmpeg_parse_options()函数位于ffmpeg_opt.c
1. 函数概述
它的功能主要有三个,
解析命令行参数;
打开输入文件,并解析数据,找到匹配每一个视频,音频,数据流的解码器;
打开输出文件,并设置好输出的视频,音频,数据流的编码器;
2. 函数调用结构图
图 ffmpeg_parse_options()函数调用结构
3. 代码分析
int ffmpeg_parse_options(int argc, char **argv)
{
OptionParseContext octx;
/* split the commandline into an internal representation */
ret = split_commandline(&octx, argc, argv, options, groups,
FF_ARRAY_ELEMS(groups));
/* apply global options */
ret = parse_optgroup(NULL, &octx.global_opts);
/* open input files */
ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
/* open output files */
ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
return ret;
}
它主要调用了两次open_files()函数,一次用于打开输入文件,一次用于打开输出文件。
3.1 open_files()
static int open_files(OptionGroupList *l, const char *inout,
int (*open_file)(OptionsContext*, const char*))
{
int i, ret;
for (i = 0; i < l->nb_groups; i++) {
OptionGroup *g = &l->groups[i];
OptionsContext o;
init_options(&o);
o.g = g;
ret = parse_optgroup(&o, g);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file "
"%s.\n", inout, g->arg);
return ret;
}
av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\n", inout, g->arg);
ret = open_file(&o, g->arg);
uninit_options(&o);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error opening %s file %s.\n",
inout, g->arg);
return ret;
}
av_log(NULL, AV_LOG_DEBUG, "Successfully opened the file.\n");
}
return 0;
}
用for循环,是当有多个输入文件时,需要一一打开;
其中open_file()是主要函数,它是一个函数指针,运行时调用的是指针所指向的函数。
3.2 open_input_file()
static int open_input_file(OptionsContext *o, const char *filename)
{
InputFile *f;
AVFormatContext *ic;
AVInputFormat *file_iformat = NULL;
int err, i, ret;
int64_t timestamp;
AVDictionary **opts;
AVDictionary *unused_opts = NULL;
AVDictionaryEntry *e = NULL;
int orig_nb_streams; // number of streams before avformat_find_stream_info
char * video_codec_name = NULL;
char * audio_codec_name = NULL;
char *subtitle_codec_name = NULL;
char * data_codec_name = NULL;
int scan_all_pmts_set = 0;
if (o->format) {
if (!(file_iformat = av_find_input_format(o->format))) {
av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format);
exit_program(1);
}
}
if (!strcmp(filename, "-"))
filename = "pipe:";
stdin_interaction &= strncmp(filename, "pipe:", 5) &&
strcmp(filename, "/dev/stdin");
/* get default parameters from command line */
ic = avformat_alloc_context();
if (!ic) {
print_error(filename, AVERROR(ENOMEM));
exit_program(1);
}
/* 这些参数没有设置的话,这段代码都不会执行*/
...
/* END */
/* If not enough info to get the stream parameters, we decode the
first frames to get it. (used in mpeg case for example) */
/* 读取文件数据,并进行相应的流分析,找到匹配的解码器 */
ret = avformat_find_stream_info(ic, opts);
if (ret < 0) {
av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename);
if (ic->nb_streams == 0) {
avformat_close_input(&ic);
exit_program(1);
}
}
if (o->start_time_eof != AV_NOPTS_VALUE) {
if (ic->duration>0) {
o->start_time = o->start_time_eof + ic->duration;
} else
av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\n", filename);
}
timestamp = (o->start_time == AV_NOPTS_VALUE) ? 0 : o->start_time;
/* add the stream start time */
if (!o->seek_timestamp && ic->start_time != AV_NOPTS_VALUE)
timestamp += ic->start_time;
/* if seeking requested, we execute it */
if (o->start_time != AV_NOPTS_VALUE) {
int64_t seek_timestamp = timestamp;
if (!(ic->iformat->flags & AVFMT_SEEK_TO_PTS)) {
int dts_heuristic = 0;
for (i=0; i
nb_streams; i++) {
const AVCodecParameters *par = ic->streams[i]->codecpar;
if (par->video_delay)
dts_heuristic = 1;
}
if (dts_heuristic) {
seek_timestamp -= 3*AV_TIME_BASE / 23;
}
}
ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0);
if (ret < 0) {
av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
filename, (double)timestamp / AV_TIME_BASE);
}
}
/* update the current parameters so that they match the one of the input stream */
add_input_streams(o, ic);
/* 在这个地方会打印出所有解析出来的输入文件的信息,如:
Input #0, flv, from '/opt/ffmpeg/test-samples/cosmos-30fps.flv':
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isomavc1
encoder : Lavf56.4.101
server :
srs_primary :
srs_authors :
server_version : 2.0.209
Duration: 00:02:06.38, start: 0.010000, bitrate: 615 kb/s
Stream #0:0: Audio: aac (LC), 48000 Hz, stereo, fltp
Stream #0:1: Video: h264 (High), yuv420p(progressive), 640x480 [SAR 135:101 DAR 180:101], 30.30 fps, 30 tbr, 1k tbn, 60 tbc
1067 GROW_ARRAY(input_files, nb_input_files);
*/
av_dump_format(ic, nb_input_files, filename, 0);
GROW_ARRAY(input_files, nb_input_files);
f = av_mallocz(sizeof(*f));
if (!f)
exit_program(1);
input_files[nb_input_files - 1] = f;
f->ctx = ic;
f->ist_index = nb_input_streams - ic->nb_streams;
f->start_time = o->start_time;
f->recording_time = o->recording_time;
f->input_ts_offset = o->input_ts_offset;
f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp);
f->nb_streams = ic->nb_streams;
f->rate_emu = o->rate_emu;
f->accurate_seek = o->accurate_seek;
f->loop = o->loop;
f->duration = 0;
f->time_base = (AVRational){ 1, 1 };
#if HAVE_PTHREADS
f->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8;
#endif
/* check if all codec options have been used */
unused_opts = strip_specifiers(o->g->codec_opts);
for (i = f->ist_index; i < nb_input_streams; i++) {
e = NULL;
while ((e = av_dict_get(input_streams[i]->decoder_opts, "", e,
AV_DICT_IGNORE_SUFFIX)))
av_dict_set(&unused_opts, e->key, NULL, 0);
}
e = NULL;
while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
const AVClass *class = avcodec_get_class();
const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
const AVClass *fclass = avformat_get_class();
const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
if (!option || foption)
continue;
if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) {
av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
"input file #%d (%s) is not a decoding option.\n", e->key,
option->help ? option->help : "", nb_input_files - 1,
filename);
exit_program(1);
}
av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
"input file #%d (%s) has not been used for any stream. The most "
"likely reason is either wrong type (e.g. a video option with "
"no video streams) or that it is a private option of some decoder "
"which was not actually used for any stream.\n", e->key,
option->help ? option->help : "", nb_input_files - 1, filename);
}
av_dict_free(&unused_opts);
for (i = 0; i < o->nb_dump_attachment; i++) {
int j;
for (j = 0; j < ic->nb_streams; j++) {
AVStream *st = ic->streams[j];
if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1)
dump_attachment(st, o->dump_attachment[i].u.str);
}
}
for (i = 0; i < orig_nb_streams; i++)
av_dict_free(&opts[i]);
av_freep(&opts);
input_stream_potentially_available = 1;
return 0;
}
3.2.1 avformat_find_stream_info()
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
int i, count = 0, ret = 0, j;
int64_t read_size;
AVStream *st;
AVCodecContext *avctx;
AVPacket pkt1, *pkt;
int64_t old_offset = avio_tell(ic->pb);
// new streams might appear, no options for those
int orig_nb_streams = ic->nb_streams;
int flush_codecs;
int64_t max_analyze_duration = ic->max_analyze_duration;
int64_t max_stream_analyze_duration;
int64_t max_subtitle_analyze_duration;
int64_t probesize = ic->probesize;
int eof_reached = 0;
int *missing_streams = av_opt_ptr(ic->iformat->priv_class, ic->priv_data, "missing_streams");
flush_codecs = probesize > 0;
av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);
max_stream_analyze_duration = max_analyze_duration;
max_subtitle_analyze_duration = max_analyze_duration;
/* 如果没有在命令行参数中设置 max_analyze_duration,
则设置max_stream_analyze_duration ,max_analyze_duration 为系统默认最大的分析时长(默认值为5000000)
设置 max_subtitle_analyze_duration 为30000000.
AV_TIME_BASE是FFmpeg内部的时钟基准的整数表示,
#define AV_TIME_BASE 1000000
*/
if (!max_analyze_duration) {
max_stream_analyze_duration =
max_analyze_duration = 5*AV_TIME_BASE;
max_subtitle_analyze_duration = 30*AV_TIME_BASE;
/* 如果输入文件是FLV格式,则需要设置更长的分析时间
if (!strcmp(ic->iformat->name, "flv"))
max_stream_analyze_duration = 90*AV_TIME_BASE;
if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
max_stream_analyze_duration = 7*AV_TIME_BASE;
}
if (ic->pb)
av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d nb_streams:%d\n",
avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, ic->nb_streams);
/* 读取文件的数据到AVPacket,解析出每个流对应的解码器,
像flv文件,可能要读120个TAG */
read_size = 0;
for (;;) {
int analyzed_all_streams;
if (ff_check_interrupt(&ic->interrupt_callback)) {
ret = AVERROR_EXIT;
av_log(ic, AV_LOG_DEBUG, "interrupted\n");
break;
}
/* check if one codec still needs to be handled */
/* 需要分析出每一个流所应该匹配的解码器 */
for (i = 0; i < ic->nb_streams; i++) {
int fps_analyze_framecount = 20;
st = ic->streams[i];
if (!has_codec_parameters(st, NULL))
break;
/* If the timebase is coarse (like the usual millisecond precision
* of mkv), we need to analyze more frames to reliably arrive at
* the correct fps. */
if (av_q2d(st->time_base) > 0.0005)
fps_analyze_framecount *= 2;
if (!tb_unreliable(st->internal->avctx))
fps_analyze_framecount = 0;
if (ic->fps_probe_size >= 0)
fps_analyze_framecount = ic->fps_probe_size;
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
fps_analyze_framecount = 0;
/* variable fps and no guess at the real fps */
if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
st->info->codec_info_duration_fields/2 :
st->info->duration_count;
if (count < fps_analyze_framecount)
break;
}
if (st->parser && st->parser->parser->split &&
!st->internal->avctx->extradata)
break;
if (st->first_dts == AV_NOPTS_VALUE &&
!(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
break;
}
analyzed_all_streams = 0;
if (!missing_streams || !*missing_streams)
if (i == ic->nb_streams) {
analyzed_all_streams = 1;
/* NOTE: If the format has no header, then we need to read some
* packets to get most of the streams, so we cannot stop here. */
if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
/* If we found the info for all the codecs, we can stop. */
ret = count;
av_log(ic, AV_LOG_DEBUG, "All info found\n");
flush_codecs = 0;
break;
}
}
/* We did not get all the codec info, but we read too much data. */
if (read_size >= probesize) {
ret = count;
av_log(ic, AV_LOG_DEBUG,
"Probe buffer size limit of %"PRId64" bytes reached\n", probesize);
for (i = 0; i < ic->nb_streams; i++)
if (!ic->streams[i]->r_frame_rate.num &&
ic->streams[i]->info->duration_count <= 1 &&
ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
strcmp(ic->iformat->name, "image2"))
av_log(ic, AV_LOG_WARNING,
"Stream #%d: not enough frames to estimate rate; "
"consider increasing probesize\n", i);
break;
}
/* NOTE: A new stream can be added there if no header in file
* (AVFMTCTX_NOHEADER). */
/* 从文件中读取数据包到AVPacket中,并进行分析 */
ret = read_frame_internal(ic, &pkt1);
if (ret == AVERROR(EAGAIN))
continue;
if (ret < 0) {
/* EOF or error*/
eof_reached = 1;
break;
}
pkt = &pkt1;
if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) {
/* 将pkt添加到ic->internal->packet_buffer的链表尾部(即ic->internal->packet_buffer_end)之后,*/
ret = add_to_pktbuf(&ic->internal->packet_buffer, pkt,
&ic->internal->packet_buffer_end, 0);
if (ret < 0)
goto find_stream_info_err;
}
st = ic->streams[pkt->stream_index];
if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
read_size += pkt->size;
avctx = st->internal->avctx;
if (!st->internal->avctx_inited) {
ret = avcodec_parameters_to_context(avctx, st->codecpar);
if (ret < 0)
goto find_stream_info_err;
st->internal->avctx_inited = 1;
}
#if FF_API_R_FRAME_RATE
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
ff_rfps_add_frame(ic, st, pkt->dts);
#endif
/* If still no information, we try to open the codec and to
* decompress the frame. We try to avoid that in most cases as
* it takes longer and uses more memory. For MPEG-4, we need to
* decompress for QuickTime.
*
* If AV_CODEC_CAP_CHANNEL_CONF is set this will force decoding of at
* least one frame of codec data, this makes sure the codec initializes
* the channel configuration and does not only trust the values from
* the container. */
try_decode_frame(ic, st, pkt,
(options && i < orig_nb_streams) ? &options[i] : NULL);
if (ic->flags & AVFMT_FLAG_NOBUFFER)
av_packet_unref(pkt);
st->codec_info_nb_frames++;
count++;
} // END OF for(;;)
if (eof_reached) {
int stream_index;
for (stream_index = 0; stream_index < ic->nb_streams; stream_index++) {
st = ic->streams[stream_index];
avctx = st->internal->avctx;
if (!has_codec_parameters(st, NULL)) {
const AVCodec *codec = find_probe_decoder(ic, st, st->codecpar->codec_id);
if (codec && !avctx->codec) {
if (avcodec_open2(avctx, codec, (options && stream_index < orig_nb_streams) ? &options[stream_index] : NULL) < 0)
av_log(ic, AV_LOG_WARNING,
"Failed to open codec in av_find_stream_info\n");
}
}
// EOF already reached while reading the stream above.
// So continue with reoordering DTS with whatever delay we have.
if (ic->internal->packet_buffer && !has_decode_delay_been_guessed(st)) {
update_dts_from_pts(ic, stream_index, ic->internal->packet_buffer);
}
}
}
if (flush_codecs) {
AVPacket empty_pkt = { 0 };
int err = 0;
av_init_packet(&empty_pkt);
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
/* flush the decoders */
if (st->info->found_decoder == 1) {
do {
err = try_decode_frame(ic, st, &empty_pkt,
(options && i < orig_nb_streams)
? &options[i] : NULL);
} while (err > 0 && !has_codec_parameters(st, NULL));
if (err < 0) {
av_log(ic, AV_LOG_INFO,
"decoding for stream %d failed\n", st->index);
}
}
}
}
// close codecs which were opened in try_decode_frame()
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
avcodec_close(st->internal->avctx);
}
ff_rfps_calculate(ic);
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
avctx = st->internal->avctx;
if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample) {
uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt);
if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == avctx->pix_fmt)
avctx->codec_tag= tag;
}
/* estimate average framerate if not set by demuxer */
if (st->info->codec_info_duration_fields &&
!st->avg_frame_rate.num &&
st->info->codec_info_duration) {
int best_fps = 0;
double best_error = 0.01;
if (st->info->codec_info_duration >= INT64_MAX / st->time_base.num / 2||
st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
st->info->codec_info_duration < 0)
continue;
av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
st->info->codec_info_duration_fields * (int64_t) st->time_base.den,
st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);
/* Round guessed framerate to a "standard" framerate if it's
* within 1% of the original estimate. */
for (j = 0; j < MAX_STD_TIMEBASES; j++) {
AVRational std_fps = { get_std_framerate(j), 12 * 1001 };
double error = fabs(av_q2d(st->avg_frame_rate) /
av_q2d(std_fps) - 1);
if (error < best_error) {
best_error = error;
best_fps = std_fps.num;
}
}
if (best_fps)
av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
best_fps, 12 * 1001, INT_MAX);
}
if (!st->r_frame_rate.num) {
if ( avctx->time_base.den * (int64_t) st->time_base.num
<= avctx->time_base.num * avctx->ticks_per_frame * (int64_t) st->time_base.den) {
st->r_frame_rate.num = avctx->time_base.den;
st->r_frame_rate.den = avctx->time_base.num * avctx->ticks_per_frame;
} else {
st->r_frame_rate.num = st->time_base.den;
st->r_frame_rate.den = st->time_base.num;
}
}
if (st->display_aspect_ratio.num && st->display_aspect_ratio.den) {
AVRational hw_ratio = { avctx->height, avctx->width };
st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio,
hw_ratio);
}
} else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
if (!avctx->bits_per_coded_sample)
avctx->bits_per_coded_sample =
av_get_bits_per_sample(avctx->codec_id);
// set stream disposition based on audio service type
switch (avctx->audio_service_type) {
case AV_AUDIO_SERVICE_TYPE_EFFECTS:
st->disposition = AV_DISPOSITION_CLEAN_EFFECTS;
break;
case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED;
break;
case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
st->disposition = AV_DISPOSITION_HEARING_IMPAIRED;
break;
case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
st->disposition = AV_DISPOSITION_COMMENT;
break;
case AV_AUDIO_SERVICE_TYPE_KARAOKE:
st->disposition = AV_DISPOSITION_KARAOKE;
break;
}
}
}
if (probesize)
estimate_timings(ic, old_offset);
av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN);
if (ret >= 0 && ic->nb_streams)
/* We could not have all the codec parameters before EOF. */
ret = -1;
for (i = 0; i < ic->nb_streams; i++) {
const char *errmsg;
st = ic->streams[i];
/* if no packet was ever seen, update context now for has_codec_parameters */
if (!st->internal->avctx_inited) {
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
st->codecpar->format == AV_SAMPLE_FMT_NONE)
st->codecpar->format = st->internal->avctx->sample_fmt;
ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
if (ret < 0)
goto find_stream_info_err;
}
if (!has_codec_parameters(st, &errmsg)) {
char buf[256];
avcodec_string(buf, sizeof(buf), st->internal->avctx, 0);
av_log(ic, AV_LOG_WARNING,
"Could not find codec parameters for stream %d (%s): %s\n"
"Consider increasing the value for the 'analyzeduration' and 'probesize' options\n",
i, buf, errmsg);
} else {
ret = 0;
}
}
compute_chapters_end(ic);
/* update the stream parameters from the internal codec contexts */
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->internal->avctx_inited) {
int orig_w = st->codecpar->width;
int orig_h = st->codecpar->height;
ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx);
if (ret < 0)
goto find_stream_info_err;
// The decoder might reduce the video size by the lowres factor.
if (av_codec_get_lowres(st->internal->avctx) && orig_w) {
st->codecpar->width = orig_w;
st->codecpar->height = orig_h;
}
}
st->internal->avctx_inited = 0;
}
find_stream_info_err:
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->info)
av_freep(&st->info->duration_error);
av_freep(&ic->streams[i]->info);
}
if (ic->pb)
av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n",
avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count);
return ret;
}
3.3 open_output_file()
static int open_output_file(OptionsContext *o, const char *filename)
{
AVFormatContext *oc;
int i, j, err;
AVOutputFormat *file_oformat;
OutputFile *of;
OutputStream *ost;
InputStream *ist;
AVDictionary *unused_opts = NULL;
AVDictionaryEntry *e = NULL;
/* 初始化 OutputFile *of */
GROW_ARRAY(output_files, nb_output_files);
of = av_mallocz(sizeof(*of));
if (!of)
exit_program(1);
output_files[nb_output_files - 1] = of;
of->ost_index = nb_output_streams;
of->recording_time = o->recording_time;
of->start_time = o->start_time;
of->limit_filesize = o->limit_filesize;
of->shortest = o->shortest;
av_dict_copy(&of->opts, o->g->format_opts, 0);
if (!strcmp(filename, "-"))
filename = "pipe:";
/* 初始化 AVFormatContext *oc */
err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
if (!oc) {
print_error(filename, err);
exit_program(1);
}
of->ctx = oc;
if (o->recording_time != INT64_MAX)
oc->duration = o->recording_time;
file_oformat= oc->oformat;
oc->interrupt_callback = int_cb;
/* ffserver seeking with date=... needs a date reference */
if (!strcmp(file_oformat->name, "ffm") &&
av_strstart(filename, "http:", NULL)) {
int err = parse_option(o, "metadata", "creation_time=now", options);
if (err < 0) {
print_error(filename, err);
exit_program(1);
}
}
if (!strcmp(file_oformat->name, "ffm") && !override_ffserver &&
av_strstart(filename, "http:", NULL)) {
...
} else if (!o->nb_stream_maps) {
char *subtitle_codec_name = NULL;
/* pick the "best" stream of each type */
/* video: highest resolution */
if (!o->video_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO) != AV_CODEC_ID_NONE) {
int area = 0, idx = -1;
int qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0);
for (i = 0; i < nb_input_streams; i++) {
int new_area;
ist = input_streams[i];
new_area = ist->st->codecpar->width * ist->st->codecpar->height + 100000000*!!ist->st->codec_info_nb_frames;
if((qcr!=MKTAG('A', 'P', 'I', 'C')) && (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
new_area = 1;
if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
new_area > area) {
if((qcr==MKTAG('A', 'P', 'I', 'C')) && !(ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
continue;
area = new_area;
idx = i;
}
}
/* 初始化输出视频流的结构体*/
if (idx >= 0)
new_video_stream(o, oc, idx);
}
/* audio: most channels */
if (!o->audio_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_AUDIO) != AV_CODEC_ID_NONE) {
int best_score = 0, idx = -1;
for (i = 0; i < nb_input_streams; i++) {
int score;
ist = input_streams[i];
score = ist->st->codecpar->channels + 100000000*!!ist->st->codec_info_nb_frames;
if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
score > best_score) {
best_score = score;
idx = i;
}
}
/* 初始化输出音频流的结构体*/
if (idx >= 0)
new_audio_stream(o, oc, idx);
}
}
#if FF_API_LAVF_AVCTX
for (i = nb_output_streams - oc->nb_streams; i < nb_output_streams; i++) { //for all streams of this output file
AVDictionaryEntry *e;
ost = output_streams[i];
if ((ost->stream_copy || ost->attachment_filename)
&& (e = av_dict_get(o->g->codec_opts, "flags", NULL, AV_DICT_IGNORE_SUFFIX))
&& (!e->key[5] || check_stream_specifier(oc, ost->st, e->key+6)))
if (av_opt_set(ost->st->codec, "flags", e->value, 0) < 0)
exit_program(1);
}
#endif
if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS)) {
av_dump_format(oc, nb_output_files - 1, oc->filename, 1);
av_log(NULL, AV_LOG_ERROR, "Output file #%d does not contain any stream\n", nb_output_files - 1);
exit_program(1);
}
/* check if all codec options have been used */
unused_opts = strip_specifiers(o->g->codec_opts);
for (i = of->ost_index; i < nb_output_streams; i++) {
e = NULL;
while ((e = av_dict_get(output_streams[i]->encoder_opts, "", e,
AV_DICT_IGNORE_SUFFIX)))
av_dict_set(&unused_opts, e->key, NULL, 0);
}
e = NULL;
while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
const AVClass *class = avcodec_get_class();
const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
const AVClass *fclass = avformat_get_class();
const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
if (!option || foption)
continue;
if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM)) {
av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
"output file #%d (%s) is not an encoding option.\n", e->key,
option->help ? option->help : "", nb_output_files - 1,
filename);
exit_program(1);
}
// gop_timecode is injected by generic code but not always used
if (!strcmp(e->key, "gop_timecode"))
continue;
av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
"output file #%d (%s) has not been used for any stream. The most "
"likely reason is either wrong type (e.g. a video option with "
"no video streams) or that it is a private option of some encoder "
"which was not actually used for any stream.\n", e->key,
option->help ? option->help : "", nb_output_files - 1, filename);
}
av_dict_free(&unused_opts);
/* set the decoding_needed flags and create simple filtergraphs */
for (i = of->ost_index; i < nb_output_streams; i++) {
OutputStream *ost = output_streams[i];
if (ost->encoding_needed && ost->source_index >= 0) {
InputStream *ist = input_streams[ost->source_index];
ist->decoding_needed |= DECODING_FOR_OST;
if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
err = init_simple_filtergraph(ist, ost);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR,
"Error initializing a simple filtergraph between streams "
"%d:%d->%d:%d\n", ist->file_index, ost->source_index,
nb_output_files - 1, ost->st->index);
exit_program(1);
}
}
}
}
/* check filename in case of an image number is expected */
if (oc->oformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(oc->filename)) {
print_error(oc->filename, AVERROR(EINVAL));
exit_program(1);
}
}
if (!(oc->oformat->flags & AVFMT_NOSTREAMS) && !input_stream_potentially_available) {
av_log(NULL, AV_LOG_ERROR,
"No input streams but output needs an input stream\n");
exit_program(1);
}
if (!(oc->oformat->flags & AVFMT_NOFILE)) {
/* test if it already exists to avoid losing precious files */
assert_file_overwrite(filename);
/* open the file */
if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,
&oc->interrupt_callback,
&of->opts)) < 0) {
print_error(filename, err);
exit_program(1);
}
} else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename))
assert_file_overwrite(filename);
if (o->mux_preload) {
av_dict_set_int(&of->opts, "preload", o->mux_preload*AV_TIME_BASE, 0);
}
oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE);
/* copy metadata */
for (i = 0; i < o->nb_metadata_map; i++) {
char *p;
int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);
if (in_file_index >= nb_input_files) {
av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while processing metadata maps\n", in_file_index);
exit_program(1);
}
copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc,
in_file_index >= 0 ?
input_files[in_file_index]->ctx : NULL, o);
}
/* copy chapters */
if (o->chapters_input_file >= nb_input_files) {
if (o->chapters_input_file == INT_MAX) {
/* copy chapters from the first input file that has them*/
o->chapters_input_file = -1;
for (i = 0; i < nb_input_files; i++)
if (input_files[i]->ctx->nb_chapters) {
o->chapters_input_file = i;
break;
}
} else {
av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n",
o->chapters_input_file);
exit_program(1);
}
}
if (o->chapters_input_file >= 0)
copy_chapters(input_files[o->chapters_input_file], of,
!o->metadata_chapters_manual);
/* copy global metadata by default */
if (!o->metadata_global_manual && nb_input_files){
av_dict_copy(&oc->metadata, input_files[0]->ctx->metadata,
AV_DICT_DONT_OVERWRITE);
if(o->recording_time != INT64_MAX)
av_dict_set(&oc->metadata, "duration", NULL, 0);
av_dict_set(&oc->metadata, "creation_time", NULL, 0);
}
if (!o->metadata_streams_manual)
for (i = of->ost_index; i < nb_output_streams; i++) {
InputStream *ist;
if (output_streams[i]->source_index < 0) /* this is true e.g. for attached files */
continue;
ist = input_streams[output_streams[i]->source_index];
av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
if (!output_streams[i]->stream_copy) {
av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
if (ist->autorotate)
av_dict_set(&output_streams[i]->st->metadata, "rotate", NULL, 0);
}
}
/* process manually set programs */
for (i = 0; i < o->nb_program; i++) {
const char *p = o->program[i].u.str;
int progid = i+1;
AVProgram *program;
while(*p) {
const char *p2 = av_get_token(&p, ":");
const char *to_dealloc = p2;
char *key;
if (!p2)
break;
if(*p) p++;
key = av_get_token(&p2, "=");
if (!key || !*p2) {
av_freep(&to_dealloc);
av_freep(&key);
break;
}
p2++;
if (!strcmp(key, "program_num"))
progid = strtol(p2, NULL, 0);
av_freep(&to_dealloc);
av_freep(&key);
}
program = av_new_program(oc, progid);
p = o->program[i].u.str;
while(*p) {
const char *p2 = av_get_token(&p, ":");
const char *to_dealloc = p2;
char *key;
if (!p2)
break;
if(*p) p++;
key = av_get_token(&p2, "=");
if (!key) {
av_log(NULL, AV_LOG_FATAL,
"No '=' character in program string %s.\n",
p2);
exit_program(1);
}
if (!*p2)
exit_program(1);
p2++;
if (!strcmp(key, "title")) {
av_dict_set(&program->metadata, "title", p2, 0);
} else if (!strcmp(key, "program_num")) {
} else if (!strcmp(key, "st")) {
int st_num = strtol(p2, NULL, 0);
av_program_add_stream_index(oc, progid, st_num);
} else {
av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
exit_program(1);
}
av_freep(&to_dealloc);
av_freep(&key);
}
}
/* process manually set metadata */
for (i = 0; i < o->nb_metadata; i++) {
AVDictionary **m;
char type, *val;
const char *stream_spec;
int index = 0, j, ret = 0;
val = strchr(o->metadata[i].u.str, '=');
if (!val) {
av_log(NULL, AV_LOG_FATAL, "No '=' character in metadata string %s.\n",
o->metadata[i].u.str);
exit_program(1);
}
*val++ = 0;
parse_meta_type(o->metadata[i].specifier, &type, &index, &stream_spec);
if (type == 's') {
for (j = 0; j < oc->nb_streams; j++) {
ost = output_streams[nb_output_streams - oc->nb_streams + j];
if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) {
av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
if (!strcmp(o->metadata[i].u.str, "rotate")) {
ost->rotate_overridden = 1;
}
} else if (ret < 0)
exit_program(1);
}
}
else {
switch (type) {
case 'g':
m = &oc->metadata;
break;
case 'c':
if (index < 0 || index >= oc->nb_chapters) {
av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index);
exit_program(1);
}
m = &oc->chapters[index]->metadata;
break;
case 'p':
if (index < 0 || index >= oc->nb_programs) {
av_log(NULL, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\n", index);
exit_program(1);
}
m = &oc->programs[index]->metadata;
break;
default:
av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier);
exit_program(1);
}
av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
}
}
return 0;
}