1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

avconv: buffer the packets written while the muxer is not initialized

This commit is contained in:
Anton Khirnov 2016-05-23 10:48:26 +02:00
parent 1c169782ca
commit 398f015f07
4 changed files with 85 additions and 18 deletions

View File

@ -192,6 +192,13 @@ static void avconv_cleanup(int ret)
avcodec_free_context(&ost->enc_ctx); avcodec_free_context(&ost->enc_ctx);
while (av_fifo_size(ost->muxing_queue)) {
AVPacket pkt;
av_fifo_generic_read(ost->muxing_queue, &pkt, sizeof(pkt), NULL);
av_packet_unref(&pkt);
}
av_fifo_free(ost->muxing_queue);
av_freep(&output_streams[i]); av_freep(&output_streams[i]);
} }
for (i = 0; i < nb_input_files; i++) { for (i = 0; i < nb_input_files; i++) {
@ -255,11 +262,33 @@ static void abort_codec_experimental(AVCodec *c, int encoder)
exit_program(1); exit_program(1);
} }
static void write_packet(AVFormatContext *s, AVPacket *pkt, OutputStream *ost) static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost)
{ {
AVFormatContext *s = of->ctx;
AVStream *st = ost->st; AVStream *st = ost->st;
int ret; int ret;
if (!of->header_written) {
AVPacket tmp_pkt;
/* the muxer is not initialized yet, buffer the packet */
if (!av_fifo_space(ost->muxing_queue)) {
int new_size = FFMIN(2 * av_fifo_size(ost->muxing_queue),
ost->max_muxing_queue_size);
if (new_size <= av_fifo_size(ost->muxing_queue)) {
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_realloc2(ost->muxing_queue, new_size);
if (ret < 0)
exit_program(1);
}
av_packet_move_ref(&tmp_pkt, pkt);
av_fifo_generic_write(ost->muxing_queue, &tmp_pkt, sizeof(tmp_pkt), NULL);
return;
}
/* /*
* Audio encoders may split the packets -- #frames in != #packets out. * Audio encoders may split the packets -- #frames in != #packets out.
* But there is no reordering, so we can limit the number of output packets * But there is no reordering, so we can limit the number of output packets
@ -315,7 +344,7 @@ static void write_packet(AVFormatContext *s, AVPacket *pkt, OutputStream *ost)
} }
} }
static void output_packet(AVFormatContext *s, AVPacket *pkt, OutputStream *ost) static void output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost)
{ {
int ret = 0; int ret = 0;
@ -345,10 +374,10 @@ static void output_packet(AVFormatContext *s, AVPacket *pkt, OutputStream *ost)
goto finish; goto finish;
idx++; idx++;
} else } else
write_packet(s, pkt, ost); write_packet(of, pkt, ost);
} }
} else } else
write_packet(s, pkt, ost); write_packet(of, pkt, ost);
finish: finish:
if (ret < 0 && ret != AVERROR_EOF) { if (ret < 0 && ret != AVERROR_EOF) {
@ -371,7 +400,7 @@ static int check_recording_time(OutputStream *ost)
return 1; return 1;
} }
static void do_audio_out(AVFormatContext *s, OutputStream *ost, static void do_audio_out(OutputFile *of, OutputStream *ost,
AVFrame *frame) AVFrame *frame)
{ {
AVCodecContext *enc = ost->enc_ctx; AVCodecContext *enc = ost->enc_ctx;
@ -401,7 +430,7 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
goto error; goto error;
av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base); av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base);
output_packet(s, &pkt, ost); output_packet(of, &pkt, ost);
} }
return; return;
@ -410,7 +439,7 @@ error:
exit_program(1); exit_program(1);
} }
static void do_subtitle_out(AVFormatContext *s, static void do_subtitle_out(OutputFile *of,
OutputStream *ost, OutputStream *ost,
InputStream *ist, InputStream *ist,
AVSubtitle *sub, AVSubtitle *sub,
@ -475,11 +504,11 @@ static void do_subtitle_out(AVFormatContext *s,
else else
pkt.pts += 90 * sub->end_display_time; pkt.pts += 90 * sub->end_display_time;
} }
output_packet(s, &pkt, ost); output_packet(of, &pkt, ost);
} }
} }
static void do_video_out(AVFormatContext *s, static void do_video_out(OutputFile *of,
OutputStream *ost, OutputStream *ost,
AVFrame *in_picture, AVFrame *in_picture,
int *frame_size) int *frame_size)
@ -492,8 +521,8 @@ static void do_video_out(AVFormatContext *s,
format_video_sync = video_sync_method; format_video_sync = video_sync_method;
if (format_video_sync == VSYNC_AUTO) if (format_video_sync == VSYNC_AUTO)
format_video_sync = (s->oformat->flags & AVFMT_NOTIMESTAMPS) ? VSYNC_PASSTHROUGH : format_video_sync = (of->ctx->oformat->flags & AVFMT_NOTIMESTAMPS) ? VSYNC_PASSTHROUGH :
(s->oformat->flags & AVFMT_VARIABLE_FPS) ? VSYNC_VFR : VSYNC_CFR; (of->ctx->oformat->flags & AVFMT_VARIABLE_FPS) ? VSYNC_VFR : VSYNC_CFR;
if (format_video_sync != VSYNC_PASSTHROUGH && if (format_video_sync != VSYNC_PASSTHROUGH &&
ost->frame_number && ost->frame_number &&
in_picture->pts != AV_NOPTS_VALUE && in_picture->pts != AV_NOPTS_VALUE &&
@ -552,7 +581,7 @@ static void do_video_out(AVFormatContext *s,
goto error; goto error;
av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base); av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base);
output_packet(s, &pkt, ost); output_packet(of, &pkt, ost);
*frame_size = pkt.size; *frame_size = pkt.size;
/* if two pass, output log */ /* if two pass, output log */
@ -662,12 +691,12 @@ static int poll_filter(OutputStream *ost)
if (!ost->frame_aspect_ratio) if (!ost->frame_aspect_ratio)
ost->enc_ctx->sample_aspect_ratio = filtered_frame->sample_aspect_ratio; ost->enc_ctx->sample_aspect_ratio = filtered_frame->sample_aspect_ratio;
do_video_out(of->ctx, ost, filtered_frame, &frame_size); do_video_out(of, ost, filtered_frame, &frame_size);
if (vstats_filename && frame_size) if (vstats_filename && frame_size)
do_video_stats(ost, frame_size); do_video_stats(ost, frame_size);
break; break;
case AVMEDIA_TYPE_AUDIO: case AVMEDIA_TYPE_AUDIO:
do_audio_out(of->ctx, ost, filtered_frame); do_audio_out(of, ost, filtered_frame);
break; break;
default: default:
// TODO support subtitle filters // TODO support subtitle filters
@ -975,7 +1004,7 @@ static void flush_encoders(void)
for (i = 0; i < nb_output_streams; i++) { for (i = 0; i < nb_output_streams; i++) {
OutputStream *ost = output_streams[i]; OutputStream *ost = output_streams[i];
AVCodecContext *enc = ost->enc_ctx; AVCodecContext *enc = ost->enc_ctx;
AVFormatContext *os = output_files[ost->file_index]->ctx; OutputFile *of = output_files[ost->file_index];
int stop_encoding = 0; int stop_encoding = 0;
if (!ost->encoding_needed) if (!ost->encoding_needed)
@ -1022,7 +1051,7 @@ static void flush_encoders(void)
break; break;
} }
av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base); av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base);
output_packet(os, &pkt, ost); output_packet(of, &pkt, ost);
} }
if (stop_encoding) if (stop_encoding)
@ -1115,7 +1144,7 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p
opkt.size = pkt->size; opkt.size = pkt->size;
} }
output_packet(of->ctx, &opkt, ost); output_packet(of, &opkt, ost);
} }
// This does not quite work like avcodec_decode_audio4/avcodec_decode_video2. // This does not quite work like avcodec_decode_audio4/avcodec_decode_video2.
@ -1353,7 +1382,7 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
if (!check_output_constraints(ist, ost) || !ost->encoding_needed) if (!check_output_constraints(ist, ost) || !ost->encoding_needed)
continue; continue;
do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pkt->pts); do_subtitle_out(output_files[ost->file_index], ost, ist, &subtitle, pkt->pts);
} }
avsubtitle_free(&subtitle); avsubtitle_free(&subtitle);
@ -1657,6 +1686,17 @@ static int check_init_output_file(OutputFile *of, int file_index)
if (want_sdp) if (want_sdp)
print_sdp(); print_sdp();
/* flush the muxing queues */
for (i = 0; i < of->ctx->nb_streams; i++) {
OutputStream *ost = output_streams[of->ost_index + i];
while (av_fifo_size(ost->muxing_queue)) {
AVPacket pkt;
av_fifo_generic_read(ost->muxing_queue, &pkt, sizeof(pkt), NULL);
write_packet(of, &pkt, ost);
}
}
return 0; return 0;
} }

View File

@ -190,6 +190,8 @@ typedef struct OptionsContext {
int nb_pass; int nb_pass;
SpecifierOpt *passlogfiles; SpecifierOpt *passlogfiles;
int nb_passlogfiles; int nb_passlogfiles;
SpecifierOpt *max_muxing_queue_size;
int nb_max_muxing_queue_size;
} OptionsContext; } OptionsContext;
typedef struct InputFilter { typedef struct InputFilter {
@ -391,6 +393,11 @@ typedef struct OutputStream {
/* packet quality factor */ /* packet quality factor */
int quality; int quality;
int max_muxing_queue_size;
/* the packets are buffered here until the muxer is ready to be initialized */
AVFifoBuffer *muxing_queue;
} OutputStream; } OutputStream;
typedef struct OutputFile { typedef struct OutputFile {

View File

@ -1073,6 +1073,10 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e
ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale;
} }
ost->max_muxing_queue_size = 128;
MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, ost->max_muxing_queue_size, oc, st);
ost->max_muxing_queue_size *= sizeof(AVPacket);
if (oc->oformat->flags & AVFMT_GLOBALHEADER) if (oc->oformat->flags & AVFMT_GLOBALHEADER)
ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
@ -1083,6 +1087,10 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e
ost->pix_fmts[0] = ost->pix_fmts[1] = AV_PIX_FMT_NONE; ost->pix_fmts[0] = ost->pix_fmts[1] = AV_PIX_FMT_NONE;
ost->last_mux_dts = AV_NOPTS_VALUE; ost->last_mux_dts = AV_NOPTS_VALUE;
ost->muxing_queue = av_fifo_alloc(8 * sizeof(AVPacket));
if (!ost->muxing_queue)
exit_program(1);
return ost; return ost;
} }
@ -2648,6 +2656,9 @@ const OptionDef options[] = {
{ "bsf", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(bitstream_filters) }, { "bsf", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(bitstream_filters) },
"A comma-separated list of bitstream filters", "bitstream_filters" }, "A comma-separated list of bitstream filters", "bitstream_filters" },
{ "max_muxing_queue_size", HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(max_muxing_queue_size) },
"maximum number of packets that can be buffered while waiting for all streams to initialize", "packets" },
/* data codec support */ /* data codec support */
{ "dcodec", HAS_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_data_codec }, { "dcodec", HAS_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_data_codec },
"force data codec ('copy' to copy stream)", "codec" }, "force data codec ('copy' to copy stream)", "codec" },

View File

@ -956,6 +956,15 @@ This option enables or disables accurate seeking in input files with the
transcoding. Use @option{-noaccurate_seek} to disable it, which may be useful transcoding. Use @option{-noaccurate_seek} to disable it, which may be useful
e.g. when copying some streams and transcoding the others. e.g. when copying some streams and transcoding the others.
@item -max_muxing_queue_size @var{packets} (@emph{output,per-stream})
When transcoding audio and/or video streams, avconv will not begin writing into
the output until it has one packet for each such stream. While waiting for that
to happen, packets for other streams are buffered. This option sets the size of
this buffer, in packets, for the matching output stream.
The default value of this option should be high enough for most uses, so only
touch this option if you are sure that you need it.
@end table @end table
@c man end OPTIONS @c man end OPTIONS