Current code tracks min/max pts for each stream separately; then when the file ends it combines them with last frame's duration to compute the total duration of each stream; finally it selects the longest of those durations as the file duration.
This is incorrect - the total file duration is the largest timestamp difference between any frames, regardless of the stream. Also change the way the last frame information is reported from decoders to the muxer - previously it would be just the last frame's duration, now the end timestamp is sent, which is simpler. Changes the result of the fate-ffmpeg-streamloop-transcode-av test, where the timestamps are shifted slightly forward. Note that the matroska demuxer does not return the first audio packet after seeking (due to buggy interaction betwen the generic code and the demuxer), so there is a gap in audio. --- fftools/ffmpeg.h | 13 +- fftools/ffmpeg_dec.c | 16 +- fftools/ffmpeg_demux.c | 113 +++++-------- fftools/ffmpeg_utils.h | 6 + tests/ref/fate/ffmpeg-streamloop-transcode-av | 160 +++++++++--------- 5 files changed, 141 insertions(+), 167 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index d52c954df5..41935d39d5 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -347,8 +347,6 @@ typedef struct InputStream { AVRational framerate_guessed; - int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */ - AVDictionary *decoder_opts; AVRational framerate; /* framerate forced with -r */ #if FFMPEG_OPT_TOP @@ -391,11 +389,6 @@ typedef struct InputStream { uint64_t decode_errors; } InputStream; -typedef struct LastFrameDuration { - int stream_idx; - int64_t duration; -} LastFrameDuration; - typedef struct InputFile { const AVClass *class; @@ -427,9 +420,9 @@ typedef struct InputFile { int accurate_seek; /* when looping the input file, this queue is used by decoders to report - * the last frame duration back to the demuxer thread */ - AVThreadMessageQueue *audio_duration_queue; - int audio_duration_queue_size; + * the last frame timestamp back to the demuxer thread */ + AVThreadMessageQueue *audio_ts_queue; + int audio_ts_queue_size; } InputFile; enum forced_keyframes_const { diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index 8795a94c1a..517d6b3ced 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -632,7 +632,6 @@ static int packet_decode(InputStream *ist, AVPacket *pkt, AVFrame *frame) if (dec->codec_type == AVMEDIA_TYPE_AUDIO) { ist->samples_decoded += frame->nb_samples; - ist->nb_samples = frame->nb_samples; audio_ts_process(ist, ist->decoder, frame); } else { @@ -724,14 +723,9 @@ static void *decoder_thread(void *arg) /* report last frame duration to the demuxer thread */ if (ist->dec->type == AVMEDIA_TYPE_AUDIO) { - LastFrameDuration dur; - - dur.stream_idx = ist->index; - dur.duration = av_rescale_q(ist->nb_samples, - (AVRational){ 1, ist->dec_ctx->sample_rate}, - ist->st->time_base); - - av_thread_message_queue_send(ifile->audio_duration_queue, &dur, 0); + Timestamp ts = { .ts = d->last_frame_pts + d->last_frame_duration_est, + .tb = d->last_frame_tb }; + av_thread_message_queue_send(ifile->audio_ts_queue, &ts, 0); } avcodec_flush_buffers(ist->dec_ctx); @@ -760,8 +754,8 @@ finish: // make sure the demuxer does not get stuck waiting for audio durations // that will never arrive - if (ifile->audio_duration_queue && ist->dec->type == AVMEDIA_TYPE_AUDIO) - av_thread_message_queue_set_err_recv(ifile->audio_duration_queue, AVERROR_EOF); + if (ifile->audio_ts_queue && ist->dec->type == AVMEDIA_TYPE_AUDIO) + av_thread_message_queue_set_err_recv(ifile->audio_ts_queue, AVERROR_EOF); dec_thread_uninit(&dt); diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index ec96daf26b..791952f120 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -74,9 +74,6 @@ typedef struct DemuxStream { ///< dts of the last packet read for this stream (in AV_TIME_BASE units) int64_t dts; - int64_t min_pts; /* pts with the smallest value in a current stream */ - int64_t max_pts; /* pts with the higher value in a current stream */ - /* number of packets successfully read for this stream */ uint64_t nb_packets; // combined size of all the packets read @@ -99,11 +96,11 @@ typedef struct Demuxer { /* number of times input stream should be looped */ int loop; - /* actual duration of the longest stream in a file at the moment when - * looping happens */ - int64_t duration; - /* time base of the duration */ - AVRational time_base; + /* duration of the looped segment of the input file */ + Timestamp duration; + /* pts with the smallest/largest values ever seen */ + Timestamp min_pts; + Timestamp max_pts; /* number of streams that the user was warned of */ int nb_streams_warn; @@ -156,23 +153,6 @@ static void report_new_stream(Demuxer *d, const AVPacket *pkt) d->nb_streams_warn = pkt->stream_index + 1; } -static void ifile_duration_update(Demuxer *d, DemuxStream *ds, - int64_t last_duration) -{ - /* the total duration of the stream, max_pts - min_pts is - * the duration of the stream without the last frame */ - if (ds->max_pts > ds->min_pts && - ds->max_pts - (uint64_t)ds->min_pts < INT64_MAX - last_duration) - last_duration += ds->max_pts - ds->min_pts; - - if (!d->duration || - av_compare_ts(d->duration, d->time_base, - last_duration, ds->ist.st->time_base) < 0) { - d->duration = last_duration; - d->time_base = ds->ist.st->time_base; - } -} - static int seek_to_start(Demuxer *d) { InputFile *ifile = &d->f; @@ -183,41 +163,28 @@ static int seek_to_start(Demuxer *d) if (ret < 0) return ret; - if (ifile->audio_duration_queue_size) { - /* duration is the length of the last frame in a stream - * when audio stream is present we don't care about - * last video frame length because it's not defined exactly */ - int got_durations = 0; + if (ifile->audio_ts_queue_size) { + int got_ts = 0; - while (got_durations < ifile->audio_duration_queue_size) { - DemuxStream *ds; - LastFrameDuration dur; - ret = av_thread_message_queue_recv(ifile->audio_duration_queue, &dur, 0); + while (got_ts < ifile->audio_ts_queue_size) { + Timestamp ts; + ret = av_thread_message_queue_recv(ifile->audio_ts_queue, &ts, 0); if (ret < 0) return ret; - got_durations++; + got_ts++; - ds = ds_from_ist(ifile->streams[dur.stream_idx]); - ifile_duration_update(d, ds, dur.duration); - } - } else { - for (int i = 0; i < ifile->nb_streams; i++) { - int64_t duration = 0; - InputStream *ist = ifile->streams[i]; - DemuxStream *ds = ds_from_ist(ist); - - if (ist->framerate.num) { - duration = av_rescale_q(1, av_inv_q(ist->framerate), ist->st->time_base); - } else if (ist->st->avg_frame_rate.num) { - duration = av_rescale_q(1, av_inv_q(ist->st->avg_frame_rate), ist->st->time_base); - } else { - duration = 1; - } - - ifile_duration_update(d, ds, duration); + if (d->max_pts.ts == AV_NOPTS_VALUE || + av_compare_ts(d->max_pts.ts, d->max_pts.tb, ts.ts, ts.tb) < 0) + d->max_pts = ts; } } + if (d->max_pts.ts != AV_NOPTS_VALUE) { + int64_t min_pts = d->min_pts.ts == AV_NOPTS_VALUE ? 0 : d->min_pts.ts; + d->duration.ts = d->max_pts.ts - av_rescale_q(min_pts, d->min_pts.tb, d->max_pts.tb); + } + d->duration.tb = d->max_pts.tb; + if (d->loop > 0) d->loop--; @@ -434,11 +401,27 @@ static int ts_fixup(Demuxer *d, AVPacket *pkt) if (pkt->dts != AV_NOPTS_VALUE) pkt->dts *= ds->ts_scale; - duration = av_rescale_q(d->duration, d->time_base, pkt->time_base); + duration = av_rescale_q(d->duration.ts, d->duration.tb, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) { + // audio decoders take precedence for estimating total file duration + int64_t pkt_duration = ifile->audio_ts_queue_size ? 0 : pkt->duration; + pkt->pts += duration; - ds->max_pts = FFMAX(pkt->pts, ds->max_pts); - ds->min_pts = FFMIN(pkt->pts, ds->min_pts); + + // update max/min pts that will be used to compute total file duration + // when using -stream_loop + if (d->max_pts.ts == AV_NOPTS_VALUE || + av_compare_ts(d->max_pts.ts, d->max_pts.tb, + pkt->pts + pkt_duration, pkt->time_base) < 0) { + d->max_pts = (Timestamp){ .ts = pkt->pts + pkt_duration, + .tb = pkt->time_base }; + } + if (d->min_pts.ts == AV_NOPTS_VALUE || + av_compare_ts(d->min_pts.ts, d->min_pts.tb, + pkt->pts, pkt->time_base) > 0) { + d->min_pts = (Timestamp){ .ts = pkt->pts, + .tb = pkt->time_base }; + } } if (pkt->dts != AV_NOPTS_VALUE) @@ -669,7 +652,7 @@ static void thread_stop(Demuxer *d) pthread_join(d->thread, NULL); av_thread_message_queue_free(&d->in_thread_queue); - av_thread_message_queue_free(&f->audio_duration_queue); + av_thread_message_queue_free(&f->audio_ts_queue); } static int thread_start(Demuxer *d) @@ -699,11 +682,11 @@ static int thread_start(Demuxer *d) } if (nb_audio_dec) { - ret = av_thread_message_queue_alloc(&f->audio_duration_queue, - nb_audio_dec, sizeof(LastFrameDuration)); + ret = av_thread_message_queue_alloc(&f->audio_ts_queue, + nb_audio_dec, sizeof(Timestamp)); if (ret < 0) goto fail; - f->audio_duration_queue_size = nb_audio_dec; + f->audio_ts_queue_size = nb_audio_dec; } } @@ -1053,13 +1036,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st) ist->discard = 1; st->discard = AVDISCARD_ALL; - ist->nb_samples = 0; ds->first_dts = AV_NOPTS_VALUE; ds->next_dts = AV_NOPTS_VALUE; - ds->min_pts = INT64_MAX; - ds->max_pts = INT64_MIN; - ds->ts_scale = 1.0; MATCH_PER_STREAM_OPT(ts_scale, dbl, ds->ts_scale, ic, st); @@ -1591,10 +1570,12 @@ int ifile_open(const OptionsContext *o, const char *filename) f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp); f->accurate_seek = o->accurate_seek; d->loop = o->loop; - d->duration = 0; - d->time_base = (AVRational){ 1, 1 }; d->nb_streams_warn = ic->nb_streams; + d->duration = (Timestamp){ .ts = 0, .tb = (AVRational){ 1, 1 } }; + d->min_pts = (Timestamp){ .ts = AV_NOPTS_VALUE, .tb = (AVRational){ 1, 1 } }; + d->max_pts = (Timestamp){ .ts = AV_NOPTS_VALUE, .tb = (AVRational){ 1, 1 } }; + f->format_nots = !!(ic->iformat->flags & AVFMT_NOTIMESTAMPS); f->readrate = o->readrate ? o->readrate : 0.0; diff --git a/fftools/ffmpeg_utils.h b/fftools/ffmpeg_utils.h index 20cde94969..bd225abc38 100644 --- a/fftools/ffmpeg_utils.h +++ b/fftools/ffmpeg_utils.h @@ -23,9 +23,15 @@ #include "libavutil/common.h" #include "libavutil/frame.h" +#include "libavutil/rational.h" #include "libavcodec/packet.h" +typedef struct Timestamp { + int64_t ts; + AVRational tb; +} Timestamp; + /** * Merge two return codes - return one of the error codes if at least one of * them was negative, 0 otherwise. diff --git a/tests/ref/fate/ffmpeg-streamloop-transcode-av b/tests/ref/fate/ffmpeg-streamloop-transcode-av index 6934e39d41..202fc9e53e 100644 --- a/tests/ref/fate/ffmpeg-streamloop-transcode-av +++ b/tests/ref/fate/ffmpeg-streamloop-transcode-av @@ -44,108 +44,108 @@ 1, 22488, 22488, 1024, 8192, 0x00000000 1, 23496, 23496, 1024, 8192, 0x00000000 0, 12, 12, 1, 1378560, 0x9c598000 -1, 25488, 25488, 1024, 8192, 0x00000000 +1, 25536, 25536, 1024, 8192, 0x00000000 0, 13, 13, 1, 1378560, 0xbaf121ba -1, 26512, 26512, 1024, 8192, 0x00000000 -1, 27528, 27528, 1024, 8192, 0x00000000 +1, 26560, 26560, 1024, 8192, 0x00000000 +1, 27576, 27576, 1024, 8192, 0x00000000 0, 14, 14, 1, 1378560, 0xbaf121ba -1, 28552, 28552, 1024, 8192, 0x00000000 -1, 29576, 29576, 1024, 8192, 0x00000000 +1, 28600, 28600, 1024, 8192, 0x00000000 +1, 29624, 29624, 1024, 8192, 0x00000000 0, 15, 15, 1, 1378560, 0x6579d31a -1, 30600, 30600, 1024, 8192, 0x00000000 -1, 31608, 31608, 1024, 8192, 0x00000000 +1, 30648, 30648, 1024, 8192, 0x00000000 +1, 31656, 31656, 1024, 8192, 0x00000000 0, 16, 16, 1, 1378560, 0xca1deba8 -1, 32688, 32688, 1024, 8192, 0x00000000 -1, 33712, 33712, 1024, 8192, 0x00000000 +1, 32736, 32736, 1024, 8192, 0x00000000 +1, 33760, 33760, 1024, 8192, 0x00000000 0, 17, 17, 1, 1378560, 0xd4eed467 -1, 34728, 34728, 1024, 8192, 0x00000000 -1, 35736, 35736, 1024, 8192, 0x00000000 +1, 34776, 34776, 1024, 8192, 0x00000000 +1, 35784, 35784, 1024, 8192, 0x00000000 0, 18, 18, 1, 1378560, 0xd6e1d5b7 -1, 36760, 36760, 1024, 8192, 0x00000000 -1, 37784, 37784, 1024, 8192, 0x00000000 +1, 36808, 36808, 1024, 8192, 0x00000000 +1, 37832, 37832, 1024, 8192, 0x00000000 0, 19, 19, 1, 1378560, 0x0b574d39 -1, 38808, 38808, 1024, 8192, 0x00000000 -1, 39816, 39816, 1024, 8192, 0x00000000 +1, 38856, 38856, 1024, 8192, 0x00000000 +1, 39864, 39864, 1024, 8192, 0x00000000 0, 20, 20, 1, 1378560, 0x1bdd4d61 -1, 40840, 40840, 1024, 8192, 0x00000000 -1, 41864, 41864, 1024, 8192, 0x00000000 +1, 40888, 40888, 1024, 8192, 0x00000000 +1, 41912, 41912, 1024, 8192, 0x00000000 0, 21, 21, 1, 1378560, 0x3b28f549 -1, 42888, 42888, 1024, 8192, 0x00000000 -1, 43896, 43896, 1024, 8192, 0x00000000 +1, 42936, 42936, 1024, 8192, 0x00000000 +1, 43944, 43944, 1024, 8192, 0x00000000 0, 22, 22, 1, 1378560, 0x45b2f57b -1, 44920, 44920, 1024, 8192, 0x00000000 -1, 45944, 45944, 1024, 8192, 0x00000000 +1, 44968, 44968, 1024, 8192, 0x00000000 +1, 45992, 45992, 1024, 8192, 0x00000000 0, 23, 23, 1, 1378560, 0x8955570e -1, 46968, 46968, 1024, 8192, 0x00000000 -1, 47976, 47976, 1024, 8192, 0x00000000 +1, 47016, 47016, 1024, 8192, 0x00000000 +1, 48024, 48024, 1024, 8192, 0x00000000 0, 24, 24, 1, 1378560, 0x9c598000 -1, 49968, 49968, 1024, 8192, 0x00000000 0, 25, 25, 1, 1378560, 0xbaf121ba -1, 50992, 50992, 1024, 8192, 0x00000000 -1, 52008, 52008, 1024, 8192, 0x00000000 +1, 50064, 50064, 1024, 8192, 0x00000000 +1, 51088, 51088, 1024, 8192, 0x00000000 0, 26, 26, 1, 1378560, 0xbaf121ba -1, 53032, 53032, 1024, 8192, 0x00000000 +1, 52104, 52104, 1024, 8192, 0x00000000 +1, 53128, 53128, 1024, 8192, 0x00000000 0, 27, 27, 1, 1378560, 0x6579d31a -1, 54056, 54056, 1024, 8192, 0x00000000 -1, 55080, 55080, 1024, 8192, 0x00000000 +1, 54152, 54152, 1024, 8192, 0x00000000 +1, 55176, 55176, 1024, 8192, 0x00000000 0, 28, 28, 1, 1378560, 0xca1deba8 -1, 56088, 56088, 1024, 8192, 0x00000000 -1, 57168, 57168, 1024, 8192, 0x00000000 +1, 56184, 56184, 1024, 8192, 0x00000000 +1, 57264, 57264, 1024, 8192, 0x00000000 0, 29, 29, 1, 1378560, 0xd4eed467 -1, 58192, 58192, 1024, 8192, 0x00000000 -1, 59208, 59208, 1024, 8192, 0x00000000 +1, 58288, 58288, 1024, 8192, 0x00000000 +1, 59304, 59304, 1024, 8192, 0x00000000 0, 30, 30, 1, 1378560, 0xd6e1d5b7 -1, 60216, 60216, 1024, 8192, 0x00000000 -1, 61240, 61240, 1024, 8192, 0x00000000 +1, 60312, 60312, 1024, 8192, 0x00000000 +1, 61336, 61336, 1024, 8192, 0x00000000 0, 31, 31, 1, 1378560, 0x0b574d39 -1, 62264, 62264, 1024, 8192, 0x00000000 -1, 63288, 63288, 1024, 8192, 0x00000000 +1, 62360, 62360, 1024, 8192, 0x00000000 +1, 63384, 63384, 1024, 8192, 0x00000000 0, 32, 32, 1, 1378560, 0x1bdd4d61 -1, 64296, 64296, 1024, 8192, 0x00000000 -1, 65320, 65320, 1024, 8192, 0x00000000 +1, 64392, 64392, 1024, 8192, 0x00000000 +1, 65416, 65416, 1024, 8192, 0x00000000 0, 33, 33, 1, 1378560, 0x3b28f549 -1, 66344, 66344, 1024, 8192, 0x00000000 -1, 67368, 67368, 1024, 8192, 0x00000000 +1, 66440, 66440, 1024, 8192, 0x00000000 +1, 67464, 67464, 1024, 8192, 0x00000000 0, 34, 34, 1, 1378560, 0x45b2f57b -1, 68376, 68376, 1024, 8192, 0x00000000 -1, 69400, 69400, 1024, 8192, 0x00000000 +1, 68472, 68472, 1024, 8192, 0x00000000 +1, 69496, 69496, 1024, 8192, 0x00000000 0, 35, 35, 1, 1378560, 0x8955570e -1, 70424, 70424, 1024, 8192, 0x00000000 -1, 71448, 71448, 1024, 8192, 0x00000000 -0, 36, 36, 1, 1378560, 0x9c598000 -1, 72456, 72456, 1024, 8192, 0x00000000 -0, 37, 37, 1, 1378560, 0xbaf121ba -1, 74448, 74448, 1024, 8192, 0x00000000 -1, 75472, 75472, 1024, 8192, 0x00000000 +1, 70520, 70520, 1024, 8192, 0x00000000 +1, 71544, 71544, 1024, 8192, 0x00000000 +1, 72552, 72552, 1024, 8192, 0x00000000 +0, 37, 37, 1, 1378560, 0x9c598000 +1, 74592, 74592, 1024, 8192, 0x00000000 +1, 75616, 75616, 1024, 8192, 0x00000000 0, 38, 38, 1, 1378560, 0xbaf121ba -1, 76488, 76488, 1024, 8192, 0x00000000 -1, 77512, 77512, 1024, 8192, 0x00000000 -0, 39, 39, 1, 1378560, 0x6579d31a -1, 78536, 78536, 1024, 8192, 0x00000000 -1, 79560, 79560, 1024, 8192, 0x00000000 -0, 40, 40, 1, 1378560, 0xca1deba8 -1, 80568, 80568, 1024, 8192, 0x00000000 -1, 81648, 81648, 1024, 8192, 0x00000000 -0, 41, 41, 1, 1378560, 0xd4eed467 -1, 82672, 82672, 1024, 8192, 0x00000000 -1, 83688, 83688, 1024, 8192, 0x00000000 -0, 42, 42, 1, 1378560, 0xd6e1d5b7 -1, 84696, 84696, 1024, 8192, 0x00000000 -1, 85720, 85720, 1024, 8192, 0x00000000 -0, 43, 43, 1, 1378560, 0x0b574d39 -1, 86744, 86744, 1024, 8192, 0x00000000 -1, 87768, 87768, 1024, 8192, 0x00000000 -0, 44, 44, 1, 1378560, 0x1bdd4d61 -1, 88776, 88776, 1024, 8192, 0x00000000 -1, 89800, 89800, 1024, 8192, 0x00000000 -0, 45, 45, 1, 1378560, 0x3b28f549 -1, 90824, 90824, 1024, 8192, 0x00000000 -1, 91848, 91848, 1024, 8192, 0x00000000 -0, 46, 46, 1, 1378560, 0x45b2f57b -1, 92856, 92856, 1024, 8192, 0x00000000 -1, 93880, 93880, 1024, 8192, 0x00000000 -0, 47, 47, 1, 1378560, 0x8955570e -1, 94904, 94904, 1024, 8192, 0x00000000 -1, 95928, 95928, 1024, 8192, 0x00000000 -1, 96936, 96936, 1024, 8192, 0x00000000 +1, 76632, 76632, 1024, 8192, 0x00000000 +1, 77656, 77656, 1024, 8192, 0x00000000 +0, 39, 39, 1, 1378560, 0xbaf121ba +1, 78680, 78680, 1024, 8192, 0x00000000 +1, 79704, 79704, 1024, 8192, 0x00000000 +0, 40, 40, 1, 1378560, 0x6579d31a +1, 80712, 80712, 1024, 8192, 0x00000000 +1, 81792, 81792, 1024, 8192, 0x00000000 +0, 41, 41, 1, 1378560, 0xca1deba8 +1, 82816, 82816, 1024, 8192, 0x00000000 +1, 83832, 83832, 1024, 8192, 0x00000000 +0, 42, 42, 1, 1378560, 0xd4eed467 +1, 84840, 84840, 1024, 8192, 0x00000000 +1, 85864, 85864, 1024, 8192, 0x00000000 +0, 43, 43, 1, 1378560, 0xd6e1d5b7 +1, 86888, 86888, 1024, 8192, 0x00000000 +1, 87912, 87912, 1024, 8192, 0x00000000 +0, 44, 44, 1, 1378560, 0x0b574d39 +1, 88920, 88920, 1024, 8192, 0x00000000 +1, 89944, 89944, 1024, 8192, 0x00000000 +0, 45, 45, 1, 1378560, 0x1bdd4d61 +1, 90968, 90968, 1024, 8192, 0x00000000 +1, 91992, 91992, 1024, 8192, 0x00000000 +0, 46, 46, 1, 1378560, 0x3b28f549 +1, 93000, 93000, 1024, 8192, 0x00000000 +1, 94024, 94024, 1024, 8192, 0x00000000 +0, 47, 47, 1, 1378560, 0x45b2f57b +1, 95048, 95048, 1024, 8192, 0x00000000 +1, 96072, 96072, 1024, 8192, 0x00000000 +0, 48, 48, 1, 1378560, 0x8955570e +1, 97080, 97080, 1024, 8192, 0x00000000 0, 49, 49, 1, 1378560, 0x9c598000 -- 2.42.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".