您的位置:首页 > 运维架构

FFmpeg源码剖析-通用:ffmpeg_parse_options()

2017-09-14 14:09 337 查看
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<ic->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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: