Commit 31852540 authored by Sasi Inguva's avatar Sasi Inguva Committed by wm4

libavformat/matroska: Write stream durations in metadata, in the format of mkvmerge.

Compute individual stream durations in matroska muxer.
Write them as string tags in the same format as mkvmerge tool does.
Signed-off-by: 's avatarSasi Inguva <isasi@google.com>
parent b1f78632
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "libavutil/mathematics.h" #include "libavutil/mathematics.h"
#include "libavutil/opt.h" #include "libavutil/opt.h"
#include "libavutil/random_seed.h" #include "libavutil/random_seed.h"
#include "libavutil/rational.h"
#include "libavutil/samplefmt.h" #include "libavutil/samplefmt.h"
#include "libavutil/sha.h" #include "libavutil/sha.h"
#include "libavutil/stereo3d.h" #include "libavutil/stereo3d.h"
...@@ -131,6 +132,9 @@ typedef struct MatroskaMuxContext { ...@@ -131,6 +132,9 @@ typedef struct MatroskaMuxContext {
int64_t last_track_timestamp[MAX_TRACKS]; int64_t last_track_timestamp[MAX_TRACKS];
int64_t* stream_durations;
int64_t* stream_duration_offsets;
int allow_raw_vfw; int allow_raw_vfw;
} MatroskaMuxContext; } MatroskaMuxContext;
...@@ -1151,12 +1155,12 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t) ...@@ -1151,12 +1155,12 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t)
return 0; return 0;
} }
static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int elementid, static int mkv_write_tag_targets(AVFormatContext *s,
unsigned int uid, ebml_master *tags) unsigned int elementid, unsigned int uid,
ebml_master *tags, ebml_master* tag)
{ {
MatroskaMuxContext *mkv = s->priv_data; MatroskaMuxContext *mkv = s->priv_data;
ebml_master tag, targets; ebml_master targets;
AVDictionaryEntry *t = NULL;
int ret; int ret;
if (!tags->pos) { if (!tags->pos) {
...@@ -1166,11 +1170,24 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme ...@@ -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); *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); targets = start_ebml_master(s->pb, MATROSKA_ID_TAGTARGETS, 0);
if (elementid) if (elementid)
put_ebml_uint(s->pb, elementid, uid); put_ebml_uint(s->pb, elementid, uid);
end_ebml_master(s->pb, targets); 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))) { while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) {
if (av_strcasecmp(t->key, "title") && if (av_strcasecmp(t->key, "title") &&
...@@ -1220,6 +1237,25 @@ static int mkv_write_tags(AVFormatContext *s) ...@@ -1220,6 +1237,25 @@ static int mkv_write_tags(AVFormatContext *s)
if (ret < 0) return ret; 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++) { for (i = 0; i < s->nb_chapters; i++) {
AVChapter *ch = s->chapters[i]; AVChapter *ch = s->chapters[i];
...@@ -1430,6 +1466,10 @@ static int mkv_write_header(AVFormatContext *s) ...@@ -1430,6 +1466,10 @@ static int mkv_write_header(AVFormatContext *s)
} }
end_ebml_master(pb, segment_info); 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); ret = mkv_write_tracks(s);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1801,6 +1841,11 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_ ...@@ -1801,6 +1841,11 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_
} }
mkv->duration = FFMAX(mkv->duration, ts + duration); 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; return 0;
} }
...@@ -1978,6 +2023,28 @@ static int mkv_write_trailer(AVFormatContext *s) ...@@ -1978,6 +2023,28 @@ static int mkv_write_trailer(AVFormatContext *s)
avio_seek(pb, mkv->duration_offset, SEEK_SET); avio_seek(pb, mkv->duration_offset, SEEK_SET);
put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration); 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); avio_seek(pb, currentpos, SEEK_SET);
} }
...@@ -1987,6 +2054,8 @@ static int mkv_write_trailer(AVFormatContext *s) ...@@ -1987,6 +2054,8 @@ static int mkv_write_trailer(AVFormatContext *s)
av_freep(&mkv->tracks); av_freep(&mkv->tracks);
av_freep(&mkv->cues->entries); av_freep(&mkv->cues->entries);
av_freep(&mkv->cues); av_freep(&mkv->cues);
av_freep(&mkv->stream_durations);
av_freep(&mkv->stream_duration_offsets);
return 0; return 0;
} }
......
...@@ -91,12 +91,12 @@ fate-wavpack-matroskamode: CMD = md5 -i $(TARGET_SAMPLES)/wavpack/special/matros ...@@ -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-$(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: 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: 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-$(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: 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: CMP = oneline
fate-wavpack-matroska_mux-61: REF = ffba4ddea1ba71f7a5901d9ed1a267be fate-wavpack-matroska_mux-61: REF = 7fedbfc3b9ea7348761db664626c29f4
FATE_SAMPLES_AVCONV += $(FATE_WAVPACK-yes) FATE_SAMPLES_AVCONV += $(FATE_WAVPACK-yes)
fate-wavpack: $(FATE_WAVPACK-yes) fate-wavpack: $(FATE_WAVPACK-yes)
aeeb0f2e75d044dbe2f89b7e70a54c82 *tests/data/fate/acodec-tta.matroska 6c260836d7a32e4bd714453a3546c0d5 *tests/data/fate/acodec-tta.matroska
331080 tests/data/fate/acodec-tta.matroska 331148 tests/data/fate/acodec-tta.matroska
95e54b261530a1bcf6de6fe3b21dc5f6 *tests/data/fate/acodec-tta.out.wav 95e54b261530a1bcf6de6fe3b21dc5f6 *tests/data/fate/acodec-tta.out.wav
stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400
2dad5f63688ec613a04e94c8d4d167db 37a212f8d56ad71e7466d5129f88e756
bab98f5a04a9f7991fb960041c996478 *./tests/data/lavf/lavf.mkv 7c6509f597fb57bab002cbceec960011 *./tests/data/lavf/lavf.mkv
472668 ./tests/data/lavf/lavf.mkv 472872 ./tests/data/lavf/lavf.mkv
./tests/data/lavf/lavf.mkv CRC=0xec6c3c68 ./tests/data/lavf/lavf.mkv CRC=0xec6c3c68
c93950920d4ee57eb3ff5ba0cf0c8b19 *./tests/data/lavf/lavf.mkv 5f8cb4b7e98610347dd8d0d58a828a0f *./tests/data/lavf/lavf.mkv
320412 ./tests/data/lavf/lavf.mkv 320548 ./tests/data/lavf/lavf.mkv
./tests/data/lavf/lavf.mkv CRC=0xec6c3c68 ./tests/data/lavf/lavf.mkv CRC=0xec6c3c68
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:-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:-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: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 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:-1 st: 1 flags:0 ts: 2.577000
ret: 0 st: 1 flags:1 ts: 1.471000 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:-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:-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:-1 st: 0 flags:0 ts: 2.153000
ret: 0 st: 0 flags:1 ts: 1.048000 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: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 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:-1 st:-1 flags:0 ts: 1.730004
ret: 0 st:-1 flags:1 ts: 0.624171 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: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 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:-1 st: 1 flags:0 ts: 1.307000
ret: 0 st: 1 flags:1 ts: 0.201000 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:-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:-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: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 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:-1 st: 1 flags:0 ts: 2.672000
ret: 0 st: 1 flags:1 ts: 1.566000 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:-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:-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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment