diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index e6d2aac244..9f68bc8b90 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -79,6 +79,126 @@ The format option may be needed for raw input files. @c man end DESCRIPTION +@chapter Detailed description +@c man begin DETAILED DESCRIPTION + +The transcoding process in @command{ffmpeg} for each output can be described by +the following diagram: + +@example + _______ ______________ _________ ______________ ________ +| | | | | | | | | | +| input | demuxer | encoded data | decoder | decoded | encoder | encoded data | muxer | output | +| file | ---------> | packets | ---------> | frames | ---------> | packets | -------> | file | +|_______| |______________| |_________| |______________| |________| + +@end example + +@command{ffmpeg} calls the libavformat library (containing demuxers) to read +input files and get packets containing encoded data from them. When there are +multiple input files, @command{ffmpeg} tries to keep them synchronized by +tracking lowest timestamp on any active input stream. + +Encoded packets are then passed to the decoder (unless streamcopy is selected +for the stream, see further for a description). The decoder produces +uncompressed frames (raw video/PCM audio/...) which can be processed further by +filtering (see next section). After filtering the frames are passed to the +encoder, which encodes them and outputs encoded packets again. Finally those are +passed to the muxer, which writes the encoded packets to the output file. + +@section Filtering +Before encoding, @command{ffmpeg} can process raw audio and video frames using +filters from the libavfilter library. Several chained filters form a filter +graph. @command{ffmpeg} distinguishes between two types of filtergraphs - +simple and complex. + +@subsection Simple filtergraphs +Simple filtergraphs are those that have exactly one input and output, both of +the same type. In the above diagram they can be represented by simply inserting +an additional step between decoding and encoding: + +@example + _________ __________ ______________ +| | | | | | +| decoded | simple filtergraph | filtered | encoder | encoded data | +| frames | -------------------> | frames | ---------> | packets | +|_________| |__________| |______________| + +@end example + +Simple filtergraphs are configured with the per-stream @option{-filter} option +(with @option{-vf} and @option{-af} aliases for video and audio respectively). +A simple filtergraph for video can look for example like this: + +@example + _______ _____________ _______ _____ ________ +| | | | | | | | | | +| input | ---> | deinterlace | ---> | scale | ---> | fps | ---> | output | +|_______| |_____________| |_______| |_____| |________| + +@end example + +Note that some filters change frame properties but not frame contents. E.g. the +@code{fps} filter in the example above changes number of frames, but does not +touch the frame contents. Another example is the @code{setpts} filter, which +only sets timestamps and otherwise passes the frames unchanged. + +@subsection Complex filtergraphs +Complex filtergraphs are those which cannot be described as simply a linear +processing chain applied to one stream. This is the case e.g. when the graph has +more than one input and/or output, or when output stream type is different from +input. They can be represented with the following diagram: + +@example + _________ +| | +| input 0 |\ __________ +|_________| \ | | + \ _________ /| output 0 | + \ | | / |__________| + _________ \| complex | / +| | | |/ +| input 1 |---->| filter |\ +|_________| | | \ __________ + /| graph | \ | | + / | | \| output 1 | + _________ / |_________| |__________| +| | / +| input 2 |/ +|_________| + +@end example + +Complex filtergraphs are configured with the @option{-filter_complex} option. +Note that this option is global, since a complex filtergraph by its nature +cannot be unambiguously associated with a single stream or file. + +A trivial example of a complex filtergraph is the @code{overlay} filter, which +has two video inputs and one video output, containing one video overlaid on top +of the other. Its audio counterpart is the @code{amix} filter. + +@section Stream copy +Stream copy is a mode selected by supplying the @code{copy} parameter to the +@option{-codec} option. It makes @command{ffmpeg} omit the decoding and encoding +step for the specified stream, so it does only demuxing and muxing. It is useful +for changing the container format or modifying container-level metadata. The +diagram above will in this case simplify to this: + +@example + _______ ______________ ________ +| | | | | | +| input | demuxer | encoded data | muxer | output | +| file | ---------> | packets | -------> | file | +|_______| |______________| |________| + +@end example + +Since there is no decoding or encoding, it is very fast and there is no quality +loss. However it might not work in some cases because of many factors. Applying +filters is obviously also impossible, since filters work on uncompressed data. + +@c man end DETAILED DESCRIPTION + @chapter Stream selection @c man begin STREAM SELECTION diff --git a/ffmpeg.c b/ffmpeg.c index 4d36bc443c..a897c9e917 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -2027,7 +2027,7 @@ static int poll_filters(void) OutputFile *of = output_files[ost->file_index]; int ret = 0; - if (!ost->filter || ost->is_past_recording_time) + if (!ost->filter) continue; if (!ost->filtered_frame && !(ost->filtered_frame = avcodec_alloc_frame())) { @@ -2036,7 +2036,7 @@ static int poll_filters(void) avcodec_get_frame_defaults(ost->filtered_frame); filtered_frame = ost->filtered_frame; - while (1) { + while (!ost->is_past_recording_time) { if (ost->enc->type == AVMEDIA_TYPE_AUDIO && !(ost->enc->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)) ret = av_buffersink_read_samples(ost->filter->filter, &picref, @@ -3307,6 +3307,57 @@ static int transcode_init(void) return 0; } +/** + * @return 1 if there are still streams where more output is wanted, + * 0 otherwise + */ +static int need_output(void) +{ + int i; + + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = output_streams[i]; + OutputFile *of = output_files[ost->file_index]; + AVFormatContext *os = output_files[ost->file_index]->ctx; + + if (ost->is_past_recording_time || + (os->pb && avio_tell(os->pb) >= of->limit_filesize)) + continue; + if (ost->frame_number >= ost->max_frames) { + int j; + for (j = 0; j < of->ctx->nb_streams; j++) + output_streams[of->ost_index + j]->is_past_recording_time = 1; + continue; + } + + return 1; + } + + return 0; +} + +static int select_input_file(uint8_t *no_packet) +{ + int64_t ipts_min = INT64_MAX; + int i, file_index = -1; + + for (i = 0; i < nb_input_streams; i++) { + InputStream *ist = input_streams[i]; + int64_t ipts = ist->pts; + + if (ist->discard || no_packet[ist->file_index]) + continue; + if (!input_files[ist->file_index]->eof_reached) { + if (ipts < ipts_min) { + ipts_min = ipts; + file_index = ist->file_index; + } + } + } + + return file_index; +} + /* * The following code is the main loop of the file converter */ @@ -3335,12 +3386,10 @@ static int transcode(void) timer_start = av_gettime(); for (; received_sigterm == 0;) { - int file_index, ist_index, past_recording_time = 1; + int file_index, ist_index; AVPacket pkt; - int64_t ipts_min; int64_t cur_time= av_gettime(); - ipts_min = INT64_MAX; /* if 'q' pressed, exits */ if (!using_stdin) { static int64_t last_time; @@ -3433,41 +3482,13 @@ static int transcode(void) } /* check if there's any stream where output is still needed */ - for (i = 0; i < nb_output_streams; i++) { - OutputFile *of; - ost = output_streams[i]; - of = output_files[ost->file_index]; - os = output_files[ost->file_index]->ctx; - if (ost->is_past_recording_time || - (os->pb && avio_tell(os->pb) >= of->limit_filesize)) - continue; - if (ost->frame_number >= ost->max_frames) { - int j; - for (j = 0; j < of->ctx->nb_streams; j++) - output_streams[of->ost_index + j]->is_past_recording_time = 1; - continue; - } - past_recording_time = 0; - } - if (past_recording_time) + if (!need_output()) { + av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n"); break; - - /* select the stream that we must read now by looking at the - smallest output pts */ - file_index = -1; - for (i = 0; i < nb_input_streams; i++) { - int64_t ipts; - ist = input_streams[i]; - ipts = ist->pts; - if (ist->discard || no_packet[ist->file_index]) - continue; - if (!input_files[ist->file_index]->eof_reached) { - if (ipts < ipts_min) { - ipts_min = ipts; - file_index = ist->file_index; - } - } } + + /* select the stream that we must read now */ + file_index = select_input_file(no_packet); /* if none, if is finished */ if (file_index < 0) { if (no_packet_count) { diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 8d2312e05d..da5015ca88 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -102,7 +102,15 @@ void avfilter_graph_set_auto_convert(AVFilterGraph *graph, unsigned flags) graph->disable_auto_convert = flags; } -int ff_avfilter_graph_check_validity(AVFilterGraph *graph, AVClass *log_ctx) +/** + * Check for the validity of graph. + * + * A graph is considered valid if all its input and output pads are + * connected. + * + * @return 0 in case of success, a negative value otherwise + */ +static int graph_check_validity(AVFilterGraph *graph, AVClass *log_ctx) { AVFilterContext *filt; int i, j; @@ -132,7 +140,12 @@ int ff_avfilter_graph_check_validity(AVFilterGraph *graph, AVClass *log_ctx) return 0; } -int ff_avfilter_graph_config_links(AVFilterGraph *graph, AVClass *log_ctx) +/** + * Configure all the links of graphctx. + * + * @return 0 in case of success, a negative value otherwise + */ +static int graph_config_links(AVFilterGraph *graph, AVClass *log_ctx) { AVFilterContext *filt; int i, ret; @@ -688,7 +701,10 @@ static int pick_formats(AVFilterGraph *graph) return 0; } -int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) +/** + * Configure the formats of all the links in the graph. + */ +static int graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) { int ret; @@ -759,11 +775,11 @@ int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) { int ret; - if ((ret = ff_avfilter_graph_check_validity(graphctx, log_ctx))) + if ((ret = graph_check_validity(graphctx, log_ctx))) return ret; - if ((ret = ff_avfilter_graph_config_formats(graphctx, log_ctx))) + if ((ret = graph_config_formats(graphctx, log_ctx))) return ret; - if ((ret = ff_avfilter_graph_config_links(graphctx, log_ctx))) + if ((ret = graph_config_links(graphctx, log_ctx))) return ret; if ((ret = ff_avfilter_graph_config_pointers(graphctx, log_ctx))) return ret; diff --git a/libavfilter/internal.h b/libavfilter/internal.h index bb45274e27..595fdaeb12 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -44,28 +44,6 @@ typedef struct AVFilterCommand { struct AVFilterCommand *next; } AVFilterCommand; -/** - * Check for the validity of graph. - * - * A graph is considered valid if all its input and output pads are - * connected. - * - * @return 0 in case of success, a negative value otherwise - */ -int ff_avfilter_graph_check_validity(AVFilterGraph *graphctx, AVClass *log_ctx); - -/** - * Configure all the links of graphctx. - * - * @return 0 in case of success, a negative value otherwise - */ -int ff_avfilter_graph_config_links(AVFilterGraph *graphctx, AVClass *log_ctx); - -/** - * Configure the formats of all the links in the graph. - */ -int ff_avfilter_graph_config_formats(AVFilterGraph *graphctx, AVClass *log_ctx); - /** * Update the position of a link in the age heap. */ diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 24aacd04fd..fea069434e 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -40,8 +40,8 @@ typedef struct AVIStream { int remaining; int packet_size; - int scale; - int rate; + uint32_t scale; + uint32_t rate; int sample_size; /* size of one sample (or packet) (in the rate/scale sense) in bytes */ int64_t cum_len; /* temporary storage (used during seek) */ diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 54deef4177..fce6bfc7c8 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -422,7 +422,6 @@ static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track) } else if (track->enc->codec_id == CODEC_ID_AMR_NB) { mov_write_amr_tag(pb, track); } else if (track->enc->codec_id == CODEC_ID_AC3) { - mov_write_chan_tag(pb, track); mov_write_ac3_tag(pb, track); } else if (track->enc->codec_id == CODEC_ID_ALAC) { mov_write_extradata_tag(pb, track); @@ -695,6 +694,9 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track) else if (track->vos_len > 0) mov_write_glbl_tag(pb, track); + if (track->mode == MODE_MOV && track->enc->codec_type == AVMEDIA_TYPE_AUDIO) + mov_write_chan_tag(pb, track); + return update_size(pb, pos); } diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 1df804472c..9e95718a61 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -232,10 +232,6 @@ typedef struct MpegTSWriteStream { int64_t payload_dts; int payload_flags; uint8_t *payload; - - uint8_t *adata; - int adata_pos; - int adata_size; AVFormatContext *amux; } MpegTSWriteStream; @@ -503,19 +499,6 @@ static void section_write_packet(MpegTSSection *s, const uint8_t *packet) avio_write(ctx->pb, packet, TS_PACKET_SIZE); } -/* Write callback for audio packetizer */ -static int mpegts_audio_write(void *opaque, uint8_t *buf, int size) -{ - MpegTSWriteStream *ts_st = (MpegTSWriteStream *)opaque; - if (ts_st->adata_pos + (int64_t)size > ts_st->adata_size) - return AVERROR(EIO); - - memcpy(ts_st->adata + ts_st->adata_pos, buf, size); - ts_st->adata_pos += size; - - return 0; -} - static int mpegts_write_header(AVFormatContext *s) { MpegTSWrite *ts = s->priv_data; @@ -616,25 +599,11 @@ static int mpegts_write_header(AVFormatContext *s) st->codec->extradata_size > 0) { AVStream *ast; - uint8_t *buffer; - int buffer_size = 32768; ts_st->amux = avformat_alloc_context(); if (!ts_st->amux) { ret = AVERROR(ENOMEM); goto fail; } - buffer = av_malloc(buffer_size); - if (!buffer) { - ret = AVERROR(ENOMEM); - goto fail; - } - ts_st->amux->pb = avio_alloc_context(buffer, buffer_size, AVIO_FLAG_WRITE, - ts_st, NULL, mpegts_audio_write, NULL); - if (!ts_st->amux->pb) { - av_free(buffer); - ret = AVERROR(ENOMEM); - goto fail; - } ts_st->amux->oformat = av_guess_format((ts->flags & MPEGTS_FLAG_AAC_LATM) ? "latm" : "adts", NULL, NULL); if (!ts_st->amux->oformat) { ret = AVERROR(EINVAL); @@ -723,8 +692,6 @@ static int mpegts_write_header(AVFormatContext *s) if (ts_st) { av_freep(&ts_st->payload); if (ts_st->amux) { - av_freep(&ts_st->amux->pb->buffer); - av_freep(&ts_st->amux->pb); avformat_free_context(ts_st->amux); ts_st->amux = NULL; } @@ -1130,24 +1097,20 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) av_init_packet(&pkt2); pkt2.data = pkt->data; pkt2.size = pkt->size; - ts_st->adata_size = 1024 + pkt->size; - ts_st->adata = data = av_malloc(ts_st->adata_size); - ts_st->adata_pos = 0; - if (!data) + ret = avio_open_dyn_buf(&ts_st->amux->pb); + if (ret < 0) return AVERROR(ENOMEM); ret = av_write_frame(ts_st->amux, &pkt2); if (ret < 0) { + avio_close_dyn_buf(ts_st->amux->pb, &data); + ts_st->amux->pb = NULL; av_free(data); return ret; } - avio_flush(ts_st->amux->pb); - if (ts_st->amux->pb->error < 0) { - av_free(data); - return ts_st->amux->pb->error; - } - buf = ts_st->adata; - size = ts_st->adata_pos; + size = avio_close_dyn_buf(ts_st->amux->pb, &data); + ts_st->amux->pb = NULL; + buf = data; } } @@ -1236,8 +1199,6 @@ static int mpegts_write_end(AVFormatContext *s) MpegTSWriteStream *ts_st = st->priv_data; av_freep(&ts_st->payload); if (ts_st->amux) { - av_freep(&ts_st->amux->pb->buffer); - av_freep(&ts_st->amux->pb); avformat_free_context(ts_st->amux); ts_st->amux = NULL; } diff --git a/tests/ref/fate/acodec-alac b/tests/ref/fate/acodec-alac index 6f52d8b084..d923719ed8 100644 --- a/tests/ref/fate/acodec-alac +++ b/tests/ref/fate/acodec-alac @@ -1,4 +1,4 @@ -1e5266b204b33ab4608e368d309716cb *tests/data/fate/acodec-alac.mov -388994 tests/data/fate/acodec-alac.mov +f01f453dd13c4e88266409cddf2a7177 *tests/data/fate/acodec-alac.mov +389018 tests/data/fate/acodec-alac.mov 64151e4bcc2b717aa5a8454d424d6a1f *tests/data/fate/acodec-alac.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/fate/acodec-pcm-s16be b/tests/ref/fate/acodec-pcm-s16be index 00285b8bfd..1cf04ed45e 100644 --- a/tests/ref/fate/acodec-pcm-s16be +++ b/tests/ref/fate/acodec-pcm-s16be @@ -1,4 +1,4 @@ -a874f5c08b8d104a6bbf41b21454180d *tests/data/fate/acodec-pcm-s16be.mov -1059045 tests/data/fate/acodec-pcm-s16be.mov +8bffa66afe9e17366af11e77882518a0 *tests/data/fate/acodec-pcm-s16be.mov +1059069 tests/data/fate/acodec-pcm-s16be.mov 64151e4bcc2b717aa5a8454d424d6a1f *tests/data/fate/acodec-pcm-s16be.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/fate/acodec-pcm-s24be b/tests/ref/fate/acodec-pcm-s24be index 894844a1c2..d0376a6eaf 100644 --- a/tests/ref/fate/acodec-pcm-s24be +++ b/tests/ref/fate/acodec-pcm-s24be @@ -1,4 +1,4 @@ -833d4afd907139af7106a5642a9c23d3 *tests/data/fate/acodec-pcm-s24be.mov -1588245 tests/data/fate/acodec-pcm-s24be.mov +e3013cfce9b792acb9d572268012160d *tests/data/fate/acodec-pcm-s24be.mov +1588269 tests/data/fate/acodec-pcm-s24be.mov 64151e4bcc2b717aa5a8454d424d6a1f *tests/data/fate/acodec-pcm-s24be.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/fate/acodec-pcm-s32be b/tests/ref/fate/acodec-pcm-s32be index 5360fc1038..656a6b2a61 100644 --- a/tests/ref/fate/acodec-pcm-s32be +++ b/tests/ref/fate/acodec-pcm-s32be @@ -1,4 +1,4 @@ -c02c96e37b321f2c978968e3a102c669 *tests/data/fate/acodec-pcm-s32be.mov -2117449 tests/data/fate/acodec-pcm-s32be.mov +f3ef00480e89c5c791e87b8af1cc167c *tests/data/fate/acodec-pcm-s32be.mov +2117473 tests/data/fate/acodec-pcm-s32be.mov 64151e4bcc2b717aa5a8454d424d6a1f *tests/data/fate/acodec-pcm-s32be.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/fate/acodec-pcm-s8 b/tests/ref/fate/acodec-pcm-s8 index e7c09f4430..cabf004983 100644 --- a/tests/ref/fate/acodec-pcm-s8 +++ b/tests/ref/fate/acodec-pcm-s8 @@ -1,4 +1,4 @@ -111d465266385298fde83005402ac171 *tests/data/fate/acodec-pcm-s8.mov -529829 tests/data/fate/acodec-pcm-s8.mov +2c504a4e48c19ea1b0e1705893b771bf *tests/data/fate/acodec-pcm-s8.mov +529853 tests/data/fate/acodec-pcm-s8.mov 651d4eb8d98dfcdda96ae6c43d8f156b *tests/data/fate/acodec-pcm-s8.out.wav stddev: 147.89 PSNR: 52.93 MAXDIFF: 255 bytes: 1058400/ 1058400 diff --git a/tests/ref/lavf/mov b/tests/ref/lavf/mov index 7487ff2f84..b9b020cd80 100644 --- a/tests/ref/lavf/mov +++ b/tests/ref/lavf/mov @@ -1,12 +1,12 @@ -83754d7142a7770ac374f75b729a9319 *./tests/data/lavf/lavf.mov -367322 ./tests/data/lavf/lavf.mov +484aeef3be3eb4deef05c83bdc2dd484 *./tests/data/lavf/lavf.mov +367346 ./tests/data/lavf/lavf.mov ./tests/data/lavf/lavf.mov CRC=0x2f6a9b26 -9a0b239ff596da58debcf210dece3985 *./tests/data/lavf/lavf.mov -357821 ./tests/data/lavf/lavf.mov +305a68397e3cdb505704841fedcdc352 *./tests/data/lavf/lavf.mov +357845 ./tests/data/lavf/lavf.mov ./tests/data/lavf/lavf.mov CRC=0x2f6a9b26 -cea874222a6d40b1761d75ea11ebe681 *./tests/data/lavf/lavf.mov -367251 ./tests/data/lavf/lavf.mov +6e047bce400f2c4a840f783dee1ae030 *./tests/data/lavf/lavf.mov +367275 ./tests/data/lavf/lavf.mov ./tests/data/lavf/lavf.mov CRC=0xab307eb9 -9a0b239ff596da58debcf210dece3985 *./tests/data/lavf/lavf.mov -357821 ./tests/data/lavf/lavf.mov +305a68397e3cdb505704841fedcdc352 *./tests/data/lavf/lavf.mov +357845 ./tests/data/lavf/lavf.mov ./tests/data/lavf/lavf.mov CRC=0x2f6a9b26