On Mon, Jul 18, 2016 at 2:34 AM, Soft Works <softwo...@hotmail.com> wrote: > This commit addresses the following scenario: > > we are using ffmpeg to transcode or remux mkv (or something else) to mkv. The > result is being streamed on-the-fly to an HTML5 client (streaming starts > while ffmpeg is still running). The problem here is that the client is unable > to detect the duration because the duration is only written to the mkv at the > end of the transcoding/remoxing process. In matroskaenc.c, the duration is > only written during mkv_write_trailer but not during mkv_write_header. > > The approach: > > FFMPEG is currently putting quite some effort to estimate the durations of > source streams, but in many cases the source stream durations are still left > at 0 and these durations are nowhere mapped to or used for output streams. As > much as I would have liked to deduct or estimate output durations based on > input stream durations - I realized that this is a hard task (as Nicolas > already mentioned in a previous conversation). It would involve changes to > the duration calculation/estimation/deduction for input streams and > propagating these durations to output streams or the output context in a > correct way. > So I looked for a simple and small solution with better chances to get > accepted. In webmdashenc.c I found that a duration is written during > write_header and this duration is taken from the streams' metadata, so I > decided for a similar approach. > > And here's what it does: > > At first it is checking the duration of the AVFormatContext. In typical cases > this value is not set, but: It is set in cases where the user has specified a > recording_time or an end_time via the -t or -to parameters. > Then it is looking for a DURATION metadata field in the metadata of the > output context (AVFormatContext::metadata). This would only exist in case the > user has explicitly specified a metadata DURATION value from the command line. > Then it is iterating all streams looking for a "DURATION" metadata (this > works unless the option "-map_metadata -1" has been specified) and determines > the maximum value. > The precendence is as follows: 1. Use duration of AVFormatContext - 2. Use > explicitly specified metadata duration value - 3. Use maximum (mapped) > metadata duration over all streams. > > To test this: > > 1. With explicit recording time: > ffmpeg -i file:"src.mkv" -loglevel debug -t 01:38:36.000 -y "dest.mkv" > > 2. Take duration from metadata specified via command line parameters: > ffmpeg -i file:"src.mkv" -loglevel debug -map_metadata -1 -metadata > Duration="01:14:33.00" -y "dest.mkv" > > 3. Take duration from mapped input metadata: > ffmpeg -i file:"src.mkv" -loglevel debug -y "dest.mkv" > > Regression risk: > > Very low IMO because it only affects the header while ffmpeg is still > running. When ffmpeg completes the process, the duration is rewritten to the > header with the usual value (same like without this commit). > > Signed-off-by: SoftWorkz <softwo...@hotmail.com> > --- > libavformat/matroskaenc.c | 39 +++++++++++++++++++++++++++++++++++++++ > 1 file changed, 39 insertions(+) > > diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c > index 53353bd..75ee9fb 100644 > --- a/libavformat/matroskaenc.c > +++ b/libavformat/matroskaenc.c > @@ -44,6 +44,7 @@ > #include "libavutil/mastering_display_metadata.h" > #include "libavutil/mathematics.h" > #include "libavutil/opt.h" > +#include "libavutil/parseutils.h" > #include "libavutil/random_seed.h" > #include "libavutil/rational.h" > #include "libavutil/samplefmt.h" > @@ -1487,6 +1488,30 @@ static int mkv_write_attachments(AVFormatContext *s) > return 0; > } > > +static int64_t get_metadata_duration(AVFormatContext *s) > +{ > + int i = 0; > + int64_t max = 0; > + int64_t us; > + > + AVDictionaryEntry *explicitDuration = av_dict_get(s->metadata, > "DURATION", NULL, 0); > + if (explicitDuration && (av_parse_time(&us, explicitDuration->value, 1) > == 0) && us > 0) { > + av_log(s, AV_LOG_DEBUG, "get_metadata_duration found duration in > context metadata: %" PRId64 "\n", us); > + return us; > + } > + > + for (i = 0; i < s->nb_streams; i++) { > + int64_t us; > + AVDictionaryEntry *duration = av_dict_get(s->streams[i]->metadata, > "DURATION", NULL, 0); > + > + if (duration && (av_parse_time(&us, duration->value, 1) == 0)) > + max = FFMAX(max, us); > + } > + > + av_log(s, AV_LOG_DEBUG, "get_metadata_duration returned: %" PRId64 "\n", > max); > + return max; > +} > + > static int mkv_write_header(AVFormatContext *s) > { > MatroskaMuxContext *mkv = s->priv_data; > @@ -1495,6 +1520,7 @@ static int mkv_write_header(AVFormatContext *s) > AVDictionaryEntry *tag; > int ret, i, version = 2; > int64_t creation_time; > + int64_t metadataDuration; > > if (!strcmp(s->oformat->name, "webm")) > mkv->mode = MODE_WEBM; > @@ -1594,6 +1620,18 @@ static int mkv_write_header(AVFormatContext *s) > mkv->duration_offset = avio_tell(pb); > if (!mkv->is_live) { > put_ebml_void(pb, 11); // assumes double-precision > float to be written
The void element here is to reserve space for the duration. If you're actually writing a duration here, it makes no sense to keep the void element. Then you could also remove the change in write_trailer. > + > + metadataDuration = get_metadata_duration(s); > + > + if (s->duration > 0) { > + int64_t scaledDuration = av_rescale(s->duration, 1000, > AV_TIME_BASE); > + put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration); > + av_log(s, AV_LOG_DEBUG, "Write early duration from recording > time = %" PRIu64 "\n", scaledDuration); > + } else if (metadataDuration > 0) { > + int64_t scaledDuration = av_rescale(metadataDuration, 1000, > AV_TIME_BASE); > + put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration); > + av_log(s, AV_LOG_DEBUG, "Write early duration from metadata = %" > PRIu64 "\n", scaledDuration); > + } > } > end_ebml_master(pb, segment_info); > > @@ -2158,6 +2196,7 @@ static int mkv_write_trailer(AVFormatContext *s) > av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", > mkv->duration); > currentpos = avio_tell(pb); > avio_seek(pb, mkv->duration_offset, SEEK_SET); > + put_ebml_void(pb, 11); > put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration); > > // update stream durations > -- > 2.8.1.windows.1 > > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel