On Mon, Jul 18, 2016 at 2:34 AM, Soft Works <[email protected]> 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 <[email protected]>
> ---
> 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
> [email protected]
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
_______________________________________________
ffmpeg-devel mailing list
[email protected]
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel