[FFmpeg-devel] [PATCH] libavformat/matroska: Write stream durations in metadata, in the format of mkvmerge.
Sasi Inguva
isasi at google.com
Wed Aug 5 07:09:57 CEST 2015
Compute individual stream durations in matroska muxer.
Write them as string tags in the same format as mkvmerge tool does.
Signed-off-by: Sasi Inguva <isasi at google.com>
---
libavformat/matroskaenc.c | 79 +++++++++++++++++++++++++++++++++++++++++---
tests/fate/wavpack.mak | 4 +--
tests/ref/acodec/tta | 4 +--
tests/ref/fate/binsub-mksenc | 2 +-
tests/ref/lavf/mkv | 8 ++---
tests/ref/seek/lavf-mkv | 44 ++++++++++++------------
6 files changed, 105 insertions(+), 36 deletions(-)
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 2d0d5f6..703abc3 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -44,6 +44,7 @@
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/random_seed.h"
+#include "libavutil/rational.h"
#include "libavutil/samplefmt.h"
#include "libavutil/sha.h"
#include "libavutil/stereo3d.h"
@@ -131,6 +132,9 @@ typedef struct MatroskaMuxContext {
int64_t last_track_timestamp[MAX_TRACKS];
+ int64_t* stream_durations;
+ int64_t* stream_duration_offsets;
+
int allow_raw_vfw;
} MatroskaMuxContext;
@@ -1151,12 +1155,12 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t)
return 0;
}
-static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int elementid,
- unsigned int uid, ebml_master *tags)
+static int mkv_write_tag_targets(AVFormatContext *s,
+ unsigned int elementid, unsigned int uid,
+ ebml_master *tags, ebml_master* tag)
{
MatroskaMuxContext *mkv = s->priv_data;
- ebml_master tag, targets;
- AVDictionaryEntry *t = NULL;
+ ebml_master targets;
int ret;
if (!tags->pos) {
@@ -1166,11 +1170,24 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme
*tags = start_ebml_master(s->pb, MATROSKA_ID_TAGS, 0);
}
- tag = start_ebml_master(s->pb, MATROSKA_ID_TAG, 0);
+ *tag = start_ebml_master(s->pb, MATROSKA_ID_TAG, 0);
targets = start_ebml_master(s->pb, MATROSKA_ID_TAGTARGETS, 0);
if (elementid)
put_ebml_uint(s->pb, elementid, uid);
end_ebml_master(s->pb, targets);
+ return 0;
+}
+
+static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int elementid,
+ unsigned int uid, ebml_master *tags)
+{
+ ebml_master tag;
+ int ret;
+ AVDictionaryEntry *t = NULL;
+
+ ret = mkv_write_tag_targets(s, elementid, uid, tags, &tag);
+ if (ret < 0)
+ return ret;
while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) {
if (av_strcasecmp(t->key, "title") &&
@@ -1220,6 +1237,25 @@ static int mkv_write_tags(AVFormatContext *s)
if (ret < 0) return ret;
}
+ if (!mkv->is_live) {
+ for (i = 0; i < s->nb_streams; i++) {
+ ebml_master tag_target;
+ ebml_master tag;
+
+ mkv_write_tag_targets(s, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tags, &tag_target);
+
+ tag = start_ebml_master(s->pb, MATROSKA_ID_SIMPLETAG, 0);
+ put_ebml_string(s->pb, MATROSKA_ID_TAGNAME, "DURATION");
+ mkv->stream_duration_offsets[i] = avio_tell(s->pb);
+
+ // Reserve space to write duration as a 20-byte string.
+ // 2 (ebml id) + 1 (data size) + 20 (data)
+ put_ebml_void(s->pb, 23);
+ end_ebml_master(s->pb, tag);
+ end_ebml_master(s->pb, tag_target);
+ }
+ }
+
for (i = 0; i < s->nb_chapters; i++) {
AVChapter *ch = s->chapters[i];
@@ -1430,6 +1466,10 @@ static int mkv_write_header(AVFormatContext *s)
}
end_ebml_master(pb, segment_info);
+ // initialize stream_duration fields
+ mkv->stream_durations = av_mallocz(s->nb_streams * sizeof(int64_t));
+ mkv->stream_duration_offsets = av_mallocz(s->nb_streams * sizeof(int64_t));
+
ret = mkv_write_tracks(s);
if (ret < 0)
return ret;
@@ -1801,6 +1841,11 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_
}
mkv->duration = FFMAX(mkv->duration, ts + duration);
+
+ if (mkv->stream_durations)
+ mkv->stream_durations[pkt->stream_index] =
+ FFMAX(mkv->stream_durations[pkt->stream_index], ts + duration);
+
return 0;
}
@@ -1978,6 +2023,28 @@ static int mkv_write_trailer(AVFormatContext *s)
avio_seek(pb, mkv->duration_offset, SEEK_SET);
put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration);
+ // update stream durations
+ if (mkv->stream_durations) {
+ for (int i = 0; i < s->nb_streams; ++i) {
+ AVStream *st = s->streams[i];
+ double duration_sec = mkv->stream_durations[i] * av_q2d(st->time_base);
+ char duration_string[20] = "";
+
+ av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i,
+ mkv->stream_durations[i]);
+
+ if (!mkv->is_live && mkv->stream_duration_offsets[i] > 0) {
+ avio_seek(pb, mkv->stream_duration_offsets[i], SEEK_SET);
+
+ snprintf(duration_string, 20, "%02d:%02d:%012.9f",
+ (int) duration_sec / 3600, ((int) duration_sec / 60) % 60,
+ fmod(duration_sec, 60));
+
+ put_ebml_binary(pb, MATROSKA_ID_TAGSTRING, duration_string, 20);
+ }
+ }
+ }
+
avio_seek(pb, currentpos, SEEK_SET);
}
@@ -1987,6 +2054,8 @@ static int mkv_write_trailer(AVFormatContext *s)
av_freep(&mkv->tracks);
av_freep(&mkv->cues->entries);
av_freep(&mkv->cues);
+ av_freep(&mkv->stream_durations);
+ av_freep(&mkv->stream_duration_offsets);
return 0;
}
diff --git a/tests/fate/wavpack.mak b/tests/fate/wavpack.mak
index 240f5ea..a825a02 100644
--- a/tests/fate/wavpack.mak
+++ b/tests/fate/wavpack.mak
@@ -91,12 +91,12 @@ fate-wavpack-matroskamode: CMD = md5 -i $(TARGET_SAMPLES)/wavpack/special/matros
FATE_WAVPACK-$(call DEMMUX, WV, MATROSKA) += fate-wavpack-matroska_mux-mono
fate-wavpack-matroska_mux-mono: CMD = md5 -i $(TARGET_SAMPLES)/wavpack/num_channels/mono_16bit_int.wv -c copy -fflags +bitexact -f matroska
fate-wavpack-matroska_mux-mono: CMP = oneline
-fate-wavpack-matroska_mux-mono: REF = a2987e2e51e01a35e47e7da13eb47a35
+fate-wavpack-matroska_mux-mono: REF = 4befcc41dab6c690a15d0c396c324468
FATE_WAVPACK-$(call DEMMUX, WV, MATROSKA) += fate-wavpack-matroska_mux-61
fate-wavpack-matroska_mux-61: CMD = md5 -i $(TARGET_SAMPLES)/wavpack/num_channels/eva_2.22_6.1_16bit-partial.wv -c copy -fflags +bitexact -f matroska
fate-wavpack-matroska_mux-61: CMP = oneline
-fate-wavpack-matroska_mux-61: REF = ffba4ddea1ba71f7a5901d9ed1a267be
+fate-wavpack-matroska_mux-61: REF = 7fedbfc3b9ea7348761db664626c29f4
FATE_SAMPLES_AVCONV += $(FATE_WAVPACK-yes)
fate-wavpack: $(FATE_WAVPACK-yes)
diff --git a/tests/ref/acodec/tta b/tests/ref/acodec/tta
index b4b9611..0f60345 100644
--- a/tests/ref/acodec/tta
+++ b/tests/ref/acodec/tta
@@ -1,4 +1,4 @@
-aeeb0f2e75d044dbe2f89b7e70a54c82 *tests/data/fate/acodec-tta.matroska
-331080 tests/data/fate/acodec-tta.matroska
+6c260836d7a32e4bd714453a3546c0d5 *tests/data/fate/acodec-tta.matroska
+331148 tests/data/fate/acodec-tta.matroska
95e54b261530a1bcf6de6fe3b21dc5f6 *tests/data/fate/acodec-tta.out.wav
stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400
diff --git a/tests/ref/fate/binsub-mksenc b/tests/ref/fate/binsub-mksenc
index c473497..128ca31 100644
--- a/tests/ref/fate/binsub-mksenc
+++ b/tests/ref/fate/binsub-mksenc
@@ -1 +1 @@
-2dad5f63688ec613a04e94c8d4d167db
+37a212f8d56ad71e7466d5129f88e756
diff --git a/tests/ref/lavf/mkv b/tests/ref/lavf/mkv
index edbfe60..39a48d8 100644
--- a/tests/ref/lavf/mkv
+++ b/tests/ref/lavf/mkv
@@ -1,6 +1,6 @@
-bab98f5a04a9f7991fb960041c996478 *./tests/data/lavf/lavf.mkv
-472668 ./tests/data/lavf/lavf.mkv
+7c6509f597fb57bab002cbceec960011 *./tests/data/lavf/lavf.mkv
+472872 ./tests/data/lavf/lavf.mkv
./tests/data/lavf/lavf.mkv CRC=0xec6c3c68
-c93950920d4ee57eb3ff5ba0cf0c8b19 *./tests/data/lavf/lavf.mkv
-320412 ./tests/data/lavf/lavf.mkv
+5f8cb4b7e98610347dd8d0d58a828a0f *./tests/data/lavf/lavf.mkv
+320548 ./tests/data/lavf/lavf.mkv
./tests/data/lavf/lavf.mkv CRC=0xec6c3c68
diff --git a/tests/ref/seek/lavf-mkv b/tests/ref/seek/lavf-mkv
index 11275d6..af13ddb 100644
--- a/tests/ref/seek/lavf-mkv
+++ b/tests/ref/seek/lavf-mkv
@@ -1,48 +1,48 @@
-ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 661 size: 208
+ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 797 size: 208
ret: 0 st:-1 flags:0 ts:-1.000000
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
ret: 0 st:-1 flags:1 ts: 1.894167
-ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292291 size: 27834
+ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292427 size: 27834
ret: 0 st: 0 flags:0 ts: 0.788000
-ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292291 size: 27834
+ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292427 size: 27834
ret: 0 st: 0 flags:1 ts:-0.317000
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
ret:-1 st: 1 flags:0 ts: 2.577000
ret: 0 st: 1 flags:1 ts: 1.471000
-ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320132 size: 209
+ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320268 size: 209
ret: 0 st:-1 flags:0 ts: 0.365002
-ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146844 size: 27925
+ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146980 size: 27925
ret: 0 st:-1 flags:1 ts:-0.740831
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
ret:-1 st: 0 flags:0 ts: 2.153000
ret: 0 st: 0 flags:1 ts: 1.048000
-ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292291 size: 27834
+ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292427 size: 27834
ret: 0 st: 1 flags:0 ts:-0.058000
-ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 661 size: 208
+ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 797 size: 208
ret: 0 st: 1 flags:1 ts: 2.836000
-ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320132 size: 209
+ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320268 size: 209
ret:-1 st:-1 flags:0 ts: 1.730004
ret: 0 st:-1 flags:1 ts: 0.624171
-ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146844 size: 27925
+ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146980 size: 27925
ret: 0 st: 0 flags:0 ts:-0.482000
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
ret: 0 st: 0 flags:1 ts: 2.413000
-ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292291 size: 27834
+ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292427 size: 27834
ret:-1 st: 1 flags:0 ts: 1.307000
ret: 0 st: 1 flags:1 ts: 0.201000
-ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 661 size: 208
+ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 797 size: 208
ret: 0 st:-1 flags:0 ts:-0.904994
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
ret: 0 st:-1 flags:1 ts: 1.989173
-ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292291 size: 27834
+ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292427 size: 27834
ret: 0 st: 0 flags:0 ts: 0.883000
-ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292291 size: 27834
+ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292427 size: 27834
ret: 0 st: 0 flags:1 ts:-0.222000
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
ret:-1 st: 1 flags:0 ts: 2.672000
ret: 0 st: 1 flags:1 ts: 1.566000
-ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320132 size: 209
+ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320268 size: 209
ret: 0 st:-1 flags:0 ts: 0.460008
-ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146844 size: 27925
+ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146980 size: 27925
ret: 0 st:-1 flags:1 ts:-0.645825
-ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 877 size: 27837
+ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1013 size: 27837
--
2.5.0.rc2.392.g76e840b
More information about the ffmpeg-devel
mailing list