[FFmpeg-devel] [PATCH] Compute individual stream durations in matroska muxer. Write them as binary tags. Parse the binary tags in matroska demuxer, and write them to AVStream

Sasi Inguva isasi at google.com
Tue Jul 28 21:39:59 CEST 2015


Signed-off-by: Sasi Inguva <isasi at google.com>
---
 libavformat/matroska.h       |  1 +
 libavformat/matroskadec.c    | 21 +++++++++++++-
 libavformat/matroskaenc.c    | 66 ++++++++++++++++++++++++++++++++++++++------
 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 ++++++++++++++---------------
 8 files changed, 109 insertions(+), 41 deletions(-)

diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index 344b2c3..e44b001 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -167,6 +167,7 @@
 #define MATROSKA_ID_SIMPLETAG           0x67C8
 #define MATROSKA_ID_TAGNAME             0x45A3
 #define MATROSKA_ID_TAGSTRING           0x4487
+#define MATROSKA_ID_TAGBINARY           0x4485
 #define MATROSKA_ID_TAGLANG             0x447A
 #define MATROSKA_ID_TAGDEFAULT          0x4484
 #define MATROSKA_ID_TAGDEFAULT_BUG      0x44B4
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 1807cae..92698a1 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -221,6 +221,7 @@ typedef struct MatroskaIndex {
 typedef struct MatroskaTag {
     char *name;
     char *string;
+    EbmlBin binary;
     char *lang;
     uint64_t def;
     EbmlList sub;
@@ -528,6 +529,7 @@ static const EbmlSyntax matroska_index[] = {
 static const EbmlSyntax matroska_simpletag[] = {
     { MATROSKA_ID_TAGNAME,        EBML_UTF8, 0,                   offsetof(MatroskaTag, name) },
     { MATROSKA_ID_TAGSTRING,      EBML_UTF8, 0,                   offsetof(MatroskaTag, string) },
+    { MATROSKA_ID_TAGBINARY,      EBML_BIN,  0,                   offsetof(MatroskaTag, binary) },
     { MATROSKA_ID_TAGLANG,        EBML_STR,  0,                   offsetof(MatroskaTag, lang), { .s = "und" } },
     { MATROSKA_ID_TAGDEFAULT,     EBML_UINT, 0,                   offsetof(MatroskaTag, def) },
     { MATROSKA_ID_TAGDEFAULT_BUG, EBML_UINT, 0,                   offsetof(MatroskaTag, def) },
@@ -1375,10 +1377,16 @@ static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
             av_log(s, AV_LOG_WARNING, "Skipping invalid tag with no TagName.\n");
             continue;
         }
+
+        if (!tags[i].string) {
+            continue;
+        }
+
         if (prefix)
             snprintf(key, sizeof(key), "%s/%s", prefix, tags[i].name);
         else
             av_strlcpy(key, tags[i].name, sizeof(key));
+
         if (tags[i].def || !lang) {
             av_dict_set(metadata, key, tags[i].string, 0);
             if (tags[i].sub.nb_elem)
@@ -1419,9 +1427,20 @@ static void matroska_convert_tags(AVFormatContext *s)
         } else if (tags[i].target.trackuid) {
             MatroskaTrack *track = matroska->tracks.elem;
             for (j = 0; j < matroska->tracks.nb_elem; j++)
-                if (track[j].uid == tags[i].target.trackuid && track[j].stream)
+                if (track[j].uid == tags[i].target.trackuid && track[j].stream) {
+                    MatroskaTag *track_tags = tags[i].tag.elem;
+                    for (int ind = 0; ind < tags[i].tag.nb_elem; ++ind) {
+                        if (track_tags[ind].name &&
+                            !av_strcasecmp(track_tags[ind].name, "duration") &&
+                            track_tags[ind].binary.data &&
+                            track_tags[ind].binary.size == 8) {
+                            track[j].stream->duration = AV_RL64(track_tags[ind].binary.data);
+                        }
+                    }
+
                     matroska_convert_tag(s, &tags[i].tag,
                                          &track[j].stream->metadata, NULL);
+                }
         } else {
             matroska_convert_tag(s, &tags[i].tag, &s->metadata,
                                  tags[i].target.type);
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 2d0d5f6..cb6e24d 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -131,6 +131,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;
 
@@ -1117,9 +1120,11 @@ static int mkv_write_chapters(AVFormatContext *s)
     return 0;
 }
 
-static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t)
+static int mkv_write_simpletag(AVIOContext *pb,
+                               const char* tag_name, const void* data, int size,
+                               int write_as_string, int64_t* tag_offset)
 {
-    uint8_t *key = av_strdup(t->key);
+    uint8_t *key = av_strdup(tag_name);
     uint8_t *p   = key;
     const uint8_t *lang = NULL;
     ebml_master tag;
@@ -1144,19 +1149,23 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t)
     put_ebml_string(pb, MATROSKA_ID_TAGNAME, key);
     if (lang)
         put_ebml_string(pb, MATROSKA_ID_TAGLANG, lang);
-    put_ebml_string(pb, MATROSKA_ID_TAGSTRING, t->value);
+
+    if (tag_offset)
+        *tag_offset = avio_tell(pb);
+
+    put_ebml_binary(pb, write_as_string == 0 ? MATROSKA_ID_TAGBINARY : MATROSKA_ID_TAGSTRING, data, size);
     end_ebml_master(pb, tag);
 
     av_freep(&key);
     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,17 +1175,30 @@ 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") &&
             av_strcasecmp(t->key, "stereo_mode") &&
             av_strcasecmp(t->key, "encoding_tool")) {
-            ret = mkv_write_simpletag(s->pb, t);
+            ret = mkv_write_simpletag(s->pb, t->key, t->value, strlen(t->value), 1, NULL);
             if (ret < 0)
                 return ret;
         }
@@ -1220,6 +1242,18 @@ static int mkv_write_tags(AVFormatContext *s)
         if (ret < 0) return ret;
     }
 
+    mkv->stream_durations = av_mallocz(s->nb_streams * sizeof(int64_t));
+    mkv->stream_duration_offsets = av_mallocz(s->nb_streams * sizeof(int64_t));
+
+    for (i = 0; i < s->nb_streams; i++) {
+      ebml_master tag;
+      mkv_write_tag_targets(s, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tags, &tag);
+      mkv_write_simpletag(s->pb, "duration",
+                          mkv->stream_durations + i, sizeof(int64_t),
+                          0, mkv->stream_duration_offsets + i);
+      end_ebml_master(s->pb, tag);
+    }
+
     for (i = 0; i < s->nb_chapters; i++) {
         AVChapter *ch = s->chapters[i];
 
@@ -1801,6 +1835,9 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_
     }
 
     mkv->duration = FFMAX(mkv->duration, ts + duration);
+    mkv->stream_durations[pkt->stream_index] =
+        FFMAX(mkv->stream_durations[pkt->stream_index], ts + duration);
+
     return 0;
 }
 
@@ -1978,6 +2015,15 @@ 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
+        for (int i = 0; i < s->nb_streams; ++i) {
+            av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i,
+                   mkv->stream_durations[i]);
+            avio_seek(pb, mkv->stream_duration_offsets[i], SEEK_SET);
+            put_ebml_binary(pb, MATROSKA_ID_TAGBINARY, mkv->stream_durations + i,
+                            sizeof(int64_t));
+        }
+
         avio_seek(pb, currentpos, SEEK_SET);
     }
 
@@ -1987,6 +2033,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..e3d0edd 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 = d0f5cbf0cfdc630ccf19fecd44034b06
 
 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 = 3c6252a863b5cc713c62721c60767367
 
 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..ccef17b 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
+e271e3ce535ac8c9cb89c9af42e349b8 *tests/data/fate/acodec-tta.matroska
+331136 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..c28580a 100644
--- a/tests/ref/fate/binsub-mksenc
+++ b/tests/ref/fate/binsub-mksenc
@@ -1 +1 @@
-2dad5f63688ec613a04e94c8d4d167db
+e68ea99e034876b82ac957b1a7e30c90
diff --git a/tests/ref/lavf/mkv b/tests/ref/lavf/mkv
index edbfe60..46b378a 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
+5200b77dabf4513e028358fe3783d2b1 *./tests/data/lavf/lavf.mkv
+472836 ./tests/data/lavf/lavf.mkv
 ./tests/data/lavf/lavf.mkv CRC=0xec6c3c68
-c93950920d4ee57eb3ff5ba0cf0c8b19 *./tests/data/lavf/lavf.mkv
-320412 ./tests/data/lavf/lavf.mkv
+bb7f2d72e3d455a7ab66315804e599ac *./tests/data/lavf/lavf.mkv
+320524 ./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..ed0a2c2 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:    773 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:    989 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: 292403 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: 292403 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:    989 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: 320244 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: 146956 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:    989 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: 292403 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:    773 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: 320244 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: 146956 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:    989 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: 292403 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:    773 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:    989 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: 292403 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: 292403 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:    989 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: 320244 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: 146956 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:    989 size: 27837
-- 
2.5.0.rc2.392.g76e840b



More information about the ffmpeg-devel mailing list