char language[4];
char *codec_id;
- char *codec_name;
unsigned char *codec_priv;
int codec_priv_size;
+ double time_scale;
uint64_t default_duration;
MatroskaTrackFlags flags;
uint32_t fourcc;
- MatroskaAspectRatioMode ar_mode;
- MatroskaEyeMode eye_mode;
-
//..
} MatroskaVideoTrack;
MatroskaLevel levels[EBML_MAX_DEPTH];
int level_up;
- /* matroska stuff */
- char *writing_app;
- char *muxing_app;
- int64_t created;
-
/* timescale in the file */
int64_t time_scale;
AVStream *skip_to_stream;
} MatroskaDemuxContext;
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+
/*
* The first few functions handle EBML file parsing. The rest
* is the document interpretation. Matroska really just is a
offset_t pos = url_ftell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos);
+ av_free(*str);
return AVERROR(EIO);
}
(*str)[size] = '\0';
return ebml_read_ascii(matroska, id, str);
}
-/*
- * Read the next element as a date (nanoseconds since 1/1/2000).
- * 0 is success, < 0 is failure.
- */
-
-static int
-ebml_read_date (MatroskaDemuxContext *matroska,
- uint32_t *id,
- int64_t *date)
-{
- return ebml_read_sint(matroska, id, date);
-}
-
/*
* Read the next element, but only the header. The contents
* are supposed to be sub-elements which can be read separately.
break;
}
- case MATROSKA_ID_WRITINGAPP: {
- char *text;
- if ((res = ebml_read_utf8(matroska, &id, &text)) < 0)
- break;
- matroska->writing_app = text;
- break;
- }
-
- case MATROSKA_ID_MUXINGAPP: {
- char *text;
- if ((res = ebml_read_utf8(matroska, &id, &text)) < 0)
- break;
- matroska->muxing_app = text;
- break;
- }
-
- case MATROSKA_ID_DATEUTC: {
- int64_t time;
- if ((res = ebml_read_date(matroska, &id, &time)) < 0)
- break;
- matroska->created = time;
- break;
- }
-
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown entry 0x%x in info header\n", id);
/* fall-through */
+ case MATROSKA_ID_WRITINGAPP:
+ case MATROSKA_ID_MUXINGAPP:
+ case MATROSKA_ID_DATEUTC:
+ case MATROSKA_ID_SEGMENTUID:
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
return res;
}
+static int
+matroska_decode_buffer(uint8_t** buf, int* buf_size, MatroskaTrack *track)
+{
+ uint8_t* data = *buf;
+ int isize = *buf_size;
+ uint8_t* pkt_data = NULL;
+ int pkt_size = isize;
+ int result = 0;
+ int olen;
+
+ switch (track->encoding_algo) {
+ case MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP:
+ return track->encoding_settings_len;
+ case MATROSKA_TRACK_ENCODING_COMP_LZO:
+ do {
+ olen = pkt_size *= 3;
+ pkt_data = av_realloc(pkt_data,
+ pkt_size+LZO_OUTPUT_PADDING);
+ result = lzo1x_decode(pkt_data, &olen, data, &isize);
+ } while (result==LZO_OUTPUT_FULL && pkt_size<10000000);
+ if (result)
+ goto failed;
+ pkt_size -= olen;
+ break;
+#ifdef CONFIG_ZLIB
+ case MATROSKA_TRACK_ENCODING_COMP_ZLIB: {
+ z_stream zstream = {0};
+ if (inflateInit(&zstream) != Z_OK)
+ return -1;
+ zstream.next_in = data;
+ zstream.avail_in = isize;
+ do {
+ pkt_size *= 3;
+ pkt_data = av_realloc(pkt_data, pkt_size);
+ zstream.avail_out = pkt_size - zstream.total_out;
+ zstream.next_out = pkt_data + zstream.total_out;
+ result = inflate(&zstream, Z_NO_FLUSH);
+ } while (result==Z_OK && pkt_size<10000000);
+ pkt_size = zstream.total_out;
+ inflateEnd(&zstream);
+ if (result != Z_STREAM_END)
+ goto failed;
+ break;
+ }
+#endif
+#ifdef CONFIG_BZLIB
+ case MATROSKA_TRACK_ENCODING_COMP_BZLIB: {
+ bz_stream bzstream = {0};
+ if (BZ2_bzDecompressInit(&bzstream, 0, 0) != BZ_OK)
+ return -1;
+ bzstream.next_in = data;
+ bzstream.avail_in = isize;
+ do {
+ pkt_size *= 3;
+ pkt_data = av_realloc(pkt_data, pkt_size);
+ bzstream.avail_out = pkt_size - bzstream.total_out_lo32;
+ bzstream.next_out = pkt_data + bzstream.total_out_lo32;
+ result = BZ2_bzDecompress(&bzstream);
+ } while (result==BZ_OK && pkt_size<10000000);
+ pkt_size = bzstream.total_out_lo32;
+ BZ2_bzDecompressEnd(&bzstream);
+ if (result != BZ_STREAM_END)
+ goto failed;
+ break;
+ }
+#endif
+ }
+
+ *buf = pkt_data;
+ *buf_size = pkt_size;
+ return 0;
+ failed:
+ av_free(pkt_data);
+ return -1;
+}
+
static int
matroska_add_stream (MatroskaDemuxContext *matroska)
{
uint32_t id;
MatroskaTrack *track;
+ /* start with the master */
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ return res;
+
av_log(matroska->ctx, AV_LOG_DEBUG, "parsing track, adding stream..,\n");
- /* Allocate a generic track. As soon as we know its type we'll realloc. */
+ /* Allocate a generic track. */
track = av_mallocz(MAX_TRACK_SIZE);
- matroska->num_tracks++;
+ track->time_scale = 1.0;
strcpy(track->language, "eng");
- /* start with the master */
- if ((res = ebml_read_master(matroska, &id)) < 0)
- return res;
-
/* try reading the trackentry headers */
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
track->type = MATROSKA_TRACK_TYPE_NONE;
break;
}
- matroska->tracks[matroska->num_tracks - 1] = track;
break;
}
break;
}
- /* stereo mode (whether the video has two streams,
- * where one is for the left eye and the other for
- * the right eye, which creates a 3D-like
- * effect) */
- case MATROSKA_ID_VIDEOSTEREOMODE: {
- uint64_t num;
- if ((res = ebml_read_uint(matroska, &id,
- &num)) < 0)
- break;
- if (num != MATROSKA_EYE_MODE_MONO &&
- num != MATROSKA_EYE_MODE_LEFT &&
- num != MATROSKA_EYE_MODE_RIGHT &&
- num != MATROSKA_EYE_MODE_BOTH) {
- av_log(matroska->ctx, AV_LOG_INFO,
- "Ignoring unknown eye mode 0x%x\n",
- (uint32_t) num);
- break;
- }
- videotrack->eye_mode = num;
- break;
- }
-
- /* aspect ratio behaviour */
- case MATROSKA_ID_VIDEOASPECTRATIO: {
- uint64_t num;
- if ((res = ebml_read_uint(matroska, &id,
- &num)) < 0)
- break;
- if (num != MATROSKA_ASPECT_RATIO_MODE_FREE &&
- num != MATROSKA_ASPECT_RATIO_MODE_KEEP &&
- num != MATROSKA_ASPECT_RATIO_MODE_FIXED) {
- av_log(matroska->ctx, AV_LOG_INFO,
- "Ignoring unknown aspect ratio 0x%x\n",
- (uint32_t) num);
- break;
- }
- videotrack->ar_mode = num;
- break;
- }
-
/* colorspace (only matters for raw video)
* fourcc */
case MATROSKA_ID_VIDEOCOLORSPACE: {
"0x%x - ignoring\n", id);
/* pass-through */
+ case MATROSKA_ID_VIDEOSTEREOMODE:
+ case MATROSKA_ID_VIDEOASPECTRATIO:
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
break;
}
- /* name of the codec */
- case MATROSKA_ID_CODECNAME: {
- char *text;
- if ((res = ebml_read_utf8(matroska, &id, &text)) < 0)
- break;
- track->codec_name = text;
- break;
- }
-
/* name of this track */
case MATROSKA_ID_TRACKNAME: {
char *text;
break;
}
+ case MATROSKA_ID_TRACKTIMECODESCALE: {
+ double num;
+ if ((res = ebml_read_float(matroska, &id, &num)) < 0)
+ break;
+ track->time_scale = num;
+ break;
+ }
+
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown track header entry 0x%x - ignoring\n", id);
case EBML_ID_VOID:
/* we ignore these because they're nothing useful. */
+ case MATROSKA_ID_TRACKFLAGFORCED:
+ case MATROSKA_ID_CODECNAME:
+ case MATROSKA_ID_CODECDECODEALL:
case MATROSKA_ID_CODECINFOURL:
case MATROSKA_ID_CODECDOWNLOADURL:
case MATROSKA_ID_TRACKMINCACHE:
}
}
+ if (track->codec_priv_size && track->encoding_scope & 2) {
+ uint8_t *orig_priv = track->codec_priv;
+ int offset = matroska_decode_buffer(&track->codec_priv,
+ &track->codec_priv_size, track);
+ if (offset > 0) {
+ track->codec_priv = av_malloc(track->codec_priv_size + offset);
+ memcpy(track->codec_priv, track->encoding_settings, offset);
+ memcpy(track->codec_priv+offset, orig_priv, track->codec_priv_size);
+ track->codec_priv_size += offset;
+ av_free(orig_priv);
+ } else if (!offset) {
+ av_free(orig_priv);
+ } else
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Failed to decode codec private data\n");
+ }
+
+ if (track->type && matroska->num_tracks < ARRAY_SIZE(matroska->tracks)) {
+ matroska->tracks[matroska->num_tracks++] = track;
+ } else {
+ av_free(track);
+ }
return res;
}
case MATROSKA_ID_SEEKENTRY: {
uint32_t seek_id = 0, peek_id_cache = 0;
uint64_t seek_pos = (uint64_t) -1, t;
+ int dummy_level = 0;
if ((res = ebml_read_master(matroska, &id)) < 0)
break;
level.length = (uint64_t)-1;
matroska->levels[matroska->num_levels] = level;
matroska->num_levels++;
+ dummy_level = 1;
/* check ID */
if (!(id = ebml_peek_id (matroska,
finish:
/* remove dummy level */
- while (matroska->num_levels) {
- matroska->num_levels--;
- length =
- matroska->levels[matroska->num_levels].length;
- if (length == (uint64_t)-1)
- break;
- }
+ if (dummy_level)
+ while (matroska->num_levels) {
+ matroska->num_levels--;
+ length =
+ matroska->levels[matroska->num_levels].length;
+ if (length == (uint64_t)-1)
+ break;
+ }
/* seek back */
if ((res = ebml_read_seek(matroska, before_pos)) < 0)
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown attachedfile ID 0x%x\n", id);
+ case MATROSKA_ID_FILEUID:
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
av_log(s, AV_LOG_INFO, "Ignoring unknown Edition entry ID 0x%x\n", id);
case MATROSKA_ID_EDITIONUID:
case MATROSKA_ID_EDITIONFLAGHIDDEN:
+ case MATROSKA_ID_EDITIONFLAGDEFAULT:
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
return res;
}
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
-
static int
matroska_aac_profile (char *codec_id)
{
st = av_new_stream(s, track->stream_index);
if (st == NULL)
return AVERROR(ENOMEM);
- av_set_pts_info(st, 64, matroska->time_scale, 1000*1000*1000); /* 64 bit pts in ns */
+ av_set_pts_info(st, 64, matroska->time_scale*track->time_scale, 1000*1000*1000); /* 64 bit pts in ns */
st->codec->codec_id = codec_id;
st->start_time = 0;
stream = matroska->tracks[track]->stream_index;
if (stream >= 0 && stream < matroska->ctx->nb_streams)
av_add_index_entry(matroska->ctx->streams[stream],
- idx->pos, idx->time/matroska->time_scale,
+ idx->pos, idx->time/AV_TIME_BASE,
0, 0, AVINDEX_KEYFRAME);
}
}
case 0x1: /* xiph lacing */
case 0x2: /* fixed-size lacing */
case 0x3: /* EBML lacing */
- if (size == 0) {
- res = -1;
- break;
- }
+ assert(size>0); // size <=3 is checked before size-=3 above
laces = (*data) + 1;
data += 1;
size -= 1;
matroska_queue_packet(matroska, pkt);
}
} else {
- int result, offset = 0, ilen, olen, pkt_size = lace_size[n];
+ int offset = 0, pkt_size = lace_size[n];
uint8_t *pkt_data = data;
if (matroska->tracks[track]->encoding_scope & 1) {
- switch (matroska->tracks[track]->encoding_algo) {
- case MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP:
- offset = matroska->tracks[track]->encoding_settings_len;
- break;
- case MATROSKA_TRACK_ENCODING_COMP_LZO:
- pkt_data = NULL;
- do {
- ilen = lace_size[n];
- olen = pkt_size *= 3;
- pkt_data = av_realloc(pkt_data,
- pkt_size+LZO_OUTPUT_PADDING);
- result = lzo1x_decode(pkt_data, &olen, data, &ilen);
- } while (result==LZO_OUTPUT_FULL && pkt_size<10000000);
- if (result) {
- av_free(pkt_data);
- continue;
- }
- pkt_size -= olen;
- break;
-#ifdef CONFIG_ZLIB
- case MATROSKA_TRACK_ENCODING_COMP_ZLIB: {
- z_stream zstream = {0};
- pkt_data = NULL;
- if (inflateInit(&zstream) != Z_OK)
- continue;
- zstream.next_in = data;
- zstream.avail_in = lace_size[n];
- do {
- pkt_size *= 3;
- pkt_data = av_realloc(pkt_data, pkt_size);
- zstream.avail_out = pkt_size - zstream.total_out;
- zstream.next_out = pkt_data + zstream.total_out;
- result = inflate(&zstream, Z_NO_FLUSH);
- } while (result==Z_OK && pkt_size<10000000);
- pkt_size = zstream.total_out;
- inflateEnd(&zstream);
- if (result != Z_STREAM_END) {
- av_free(pkt_data);
- continue;
- }
- break;
- }
-#endif
-#ifdef CONFIG_BZLIB
- case MATROSKA_TRACK_ENCODING_COMP_BZLIB: {
- bz_stream bzstream = {0};
- pkt_data = NULL;
- if (BZ2_bzDecompressInit(&bzstream, 0, 0) != BZ_OK)
- continue;
- bzstream.next_in = data;
- bzstream.avail_in = lace_size[n];
- do {
- pkt_size *= 3;
- pkt_data = av_realloc(pkt_data, pkt_size);
- bzstream.avail_out = pkt_size - bzstream.total_out_lo32;
- bzstream.next_out = pkt_data + bzstream.total_out_lo32;
- result = BZ2_bzDecompress(&bzstream);
- } while (result==BZ_OK && pkt_size<10000000);
- pkt_size = bzstream.total_out_lo32;
- BZ2_bzDecompressEnd(&bzstream);
- if (result != BZ_STREAM_END) {
- av_free(pkt_data);
- continue;
- }
- break;
- }
-#endif
- }
+ offset = matroska_decode_buffer(&pkt_data, &pkt_size,
+ matroska->tracks[track]);
+ if (offset < 0)
+ continue;
}
pkt = av_mallocz(sizeof(AVPacket));
/* XXX: prevent data copy... */
if (av_new_packet(pkt, pkt_size+offset) < 0) {
+ av_free(pkt);
res = AVERROR(ENOMEM);
n = laces-1;
break;
memcpy (pkt->data, matroska->tracks[track]->encoding_settings, offset);
memcpy (pkt->data+offset, pkt_data, pkt_size);
+ if (pkt_data != data)
+ av_free(pkt_data);
+
if (n == 0)
pkt->flags = is_keyframe;
pkt->stream_index = stream_index;
matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
matroska->skip_to_stream = st;
matroska->peek_id = 0;
+ av_update_cur_dts(s, st, st->index_entries[index].timestamp);
return 0;
}
MatroskaDemuxContext *matroska = s->priv_data;
int n = 0;
- av_free(matroska->writing_app);
- av_free(matroska->muxing_app);
av_free(matroska->index);
matroska_clear_queue(matroska);
for (n = 0; n < matroska->num_tracks; n++) {
MatroskaTrack *track = matroska->tracks[n];
av_free(track->codec_id);
- av_free(track->codec_name);
av_free(track->codec_priv);
av_free(track->name);
AVInputFormat matroska_demuxer = {
"matroska",
- "Matroska file format",
+ NULL_IF_CONFIG_SMALL("Matroska file format"),
sizeof(MatroskaDemuxContext),
matroska_probe,
matroska_read_header,