uint64_t time_scale;
double duration;
char *title;
+ EbmlBin date_utc;
EbmlList tracks;
EbmlList attachments;
EbmlList chapters;
{ MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext,title) },
{ MATROSKA_ID_WRITINGAPP, EBML_NONE },
{ MATROSKA_ID_MUXINGAPP, EBML_NONE },
- { MATROSKA_ID_DATEUTC, EBML_NONE },
+ { MATROSKA_ID_DATEUTC, EBML_BIN, 0, offsetof(MatroskaDemuxContext,date_utc) },
{ MATROSKA_ID_SEGMENTUID, EBML_NONE },
{ 0 }
};
{ 0 }
};
-static const char *matroska_doctypes[] = { "matroska", "webm" };
+static const char *const matroska_doctypes[] = { "matroska", "webm" };
+
+static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
+{
+ AVIOContext *pb = matroska->ctx->pb;
+ uint32_t id;
+ matroska->current_id = 0;
+ matroska->num_levels = 0;
+
+ // seek to next position to resync from
+ if (avio_seek(pb, last_pos + 1, SEEK_SET) < 0 || avio_tell(pb) <= last_pos)
+ goto eof;
+
+ id = avio_rb32(pb);
+
+ // try to find a toplevel element
+ while (!url_feof(pb)) {
+ if (id == MATROSKA_ID_INFO || id == MATROSKA_ID_TRACKS ||
+ id == MATROSKA_ID_CUES || id == MATROSKA_ID_TAGS ||
+ id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS ||
+ id == MATROSKA_ID_CLUSTER || id == MATROSKA_ID_CHAPTERS)
+ {
+ matroska->current_id = id;
+ return 0;
+ }
+ id = (id << 8) | avio_r8(pb);
+ }
+eof:
+ matroska->done = 1;
+ return AVERROR_EOF;
+}
/*
* Return: Whether we reached the end of a level in the hierarchy or not.
*/
static int ebml_read_ascii(AVIOContext *pb, int size, char **str)
{
- av_free(*str);
+ char *res;
+
/* EBML strings are usually not 0-terminated, so we allocate one
* byte more, read the string and NULL-terminate it ourselves. */
- if (!(*str = av_malloc(size + 1)))
+ if (!(res = av_malloc(size + 1)))
return AVERROR(ENOMEM);
- if (avio_read(pb, (uint8_t *) *str, size) != size) {
- av_freep(str);
+ if (avio_read(pb, (uint8_t *) res, size) != size) {
+ av_free(res);
return AVERROR(EIO);
}
- (*str)[size] = '\0';
+ (res)[size] = '\0';
+ av_free(*str);
+ *str = res;
return 0;
}
uint8_t* data = *buf;
int isize = *buf_size;
uint8_t* pkt_data = NULL;
- uint8_t* newpktdata;
+ uint8_t av_unused *newpktdata;
int pkt_size = isize;
int result = 0;
int olen;
int i;
for (i=0; i < list->nb_elem; i++) {
- const char *lang = strcmp(tags[i].lang, "und") ? tags[i].lang : NULL;
+ const char *lang= (tags[i].lang && strcmp(tags[i].lang, "und")) ? tags[i].lang : NULL;
if (!tags[i].name) {
av_log(s, AV_LOG_WARNING, "Skipping invalid tag with no TagName.\n");
continue;
}
- if (matroska_parse_seekhead_entry(matroska, i) < 0)
+ if (matroska_parse_seekhead_entry(matroska, i) < 0) {
+ // mark index as broken
+ matroska->cues_parsing_deferred = -1;
break;
+ }
}
}
break;
assert(i <= seekhead_list->nb_elem);
- matroska_parse_seekhead_entry(matroska, i);
+ if (matroska_parse_seekhead_entry(matroska, i) < 0)
+ matroska->cues_parsing_deferred = -1;
matroska_add_index_entries(matroska);
}
return sri;
}
+static void matroska_metadata_creation_time(AVDictionary **metadata, int64_t date_utc)
+{
+ char buffer[32];
+ /* Convert to seconds and adjust by number of seconds between 2001-01-01 and Epoch */
+ time_t creation_time = date_utc / 1000000000 + 978307200;
+ struct tm *ptm = gmtime(&creation_time);
+ if (!ptm) return;
+ strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm);
+ av_dict_set(metadata, "creation_time", buffer, 0);
+}
+
static int matroska_read_header(AVFormatContext *s)
{
MatroskaDemuxContext *matroska = s->priv_data;
MatroskaChapter *chapters;
MatroskaTrack *tracks;
uint64_t max_start = 0;
+ int64_t pos;
Ebml ebml = { 0 };
AVStream *st;
int i, j, k, res;
/* First read the EBML header. */
if (ebml_parse(matroska, ebml_syntax, &ebml)
|| ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t)
- || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 3) {
+ || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 3 || !ebml.doctype) {
av_log(matroska->ctx, AV_LOG_ERROR,
"EBML header using unsupported features\n"
"(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
ebml_free(ebml_syntax, &ebml);
/* The next thing is a segment. */
- if ((res = ebml_parse(matroska, matroska_segments, matroska)) < 0)
- return res;
+ pos = avio_tell(matroska->ctx->pb);
+ res = ebml_parse(matroska, matroska_segments, matroska);
+ // try resyncing until we find a EBML_STOP type element.
+ while (res != 1) {
+ res = matroska_resync(matroska, pos);
+ if (res < 0)
+ return res;
+ pos = avio_tell(matroska->ctx->pb);
+ res = ebml_parse(matroska, matroska_segment, matroska);
+ }
matroska_execute_seekhead(matroska);
if (!matroska->time_scale)
* 1000 / AV_TIME_BASE;
av_dict_set(&s->metadata, "title", matroska->title, 0);
+ if (matroska->date_utc.size == 8)
+ matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data));
+
tracks = matroska->tracks.elem;
for (i=0; i < matroska->tracks.nb_elem; i++) {
MatroskaTrack *track = &tracks[i];
&& (track->codec_priv.size >= 86)
&& (track->codec_priv.data != NULL)) {
fourcc = AV_RL32(track->codec_priv.data);
- codec_id = ff_codec_get_id(codec_movvideo_tags, fourcc);
+ codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc);
} else if (codec_id == CODEC_ID_PCM_S16BE) {
switch (track->audio.bitdepth) {
case 8: codec_id = CODEC_ID_PCM_U8; break;
st->codec->height * track->video.display_width,
st->codec-> width * track->video.display_height,
255);
- if (st->codec->codec_id != CODEC_ID_H264)
st->need_parsing = AVSTREAM_PARSE_HEADERS;
if (track->default_duration)
st->avg_frame_rate = av_d2q(1000000000.0/track->default_duration, INT_MAX);
if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) {
av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n");
- return res;
+ return AVERROR_INVALIDDATA;
}
data += n;
size -= n;
if (!track || !track->stream) {
av_log(matroska->ctx, AV_LOG_INFO,
"Invalid stream %"PRIu64" or size %u\n", num, size);
- return res;
+ return AVERROR_INVALIDDATA;
} else if (size <= 3)
return 0;
st = track->stream;
if (!track->audio.pkt_cnt) {
if (track->audio.sub_packet_cnt == 0)
track->audio.buf_timecode = timecode;
- if (st->codec->codec_id == CODEC_ID_RA_288)
+ if (st->codec->codec_id == CODEC_ID_RA_288) {
+ if (size < cfs * h / 2) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Corrupt int4 RM-style audio packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
for (x=0; x<h/2; x++)
memcpy(track->audio.buf+x*2*w+y*cfs,
data+x*cfs, cfs);
- else if (st->codec->codec_id == CODEC_ID_SIPR)
+ } else if (st->codec->codec_id == CODEC_ID_SIPR) {
+ if (size < w) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Corrupt sipr RM-style audio packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
memcpy(track->audio.buf + y*w, data, w);
- else
+ } else {
+ if (size < sps * w / sps) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Corrupt generic RM-style audio packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
for (x=0; x<w/sps; x++)
memcpy(track->audio.buf+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), data+x*sps, sps);
+ }
if (++track->audio.sub_packet_cnt >= h) {
if (st->codec->codec_id == CODEC_ID_SIPR)
pos);
}
ebml_free(matroska_cluster, &cluster);
- if (res < 0) matroska->done = 1;
return res;
}
MatroskaDemuxContext *matroska = s->priv_data;
while (matroska_deliver_packet(matroska, pkt)) {
+ int64_t pos = avio_tell(matroska->ctx->pb);
if (matroska->done)
return AVERROR_EOF;
- matroska_parse_cluster(matroska);
+ if (matroska_parse_cluster(matroska) < 0)
+ matroska_resync(matroska, pos);
}
return 0;
int i, index, index_sub, index_min;
/* Parse the CUES now since we need the index data to seek. */
- if (matroska->cues_parsing_deferred) {
- matroska_parse_cues(matroska);
+ if (matroska->cues_parsing_deferred > 0) {
matroska->cues_parsing_deferred = 0;
+ matroska_parse_cues(matroska);
}
if (!st->nb_index_entries)
- return 0;
+ goto err;
timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
}
matroska_clear_queue(matroska);
- if (index < 0)
- return 0;
+ if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->nb_index_entries - 1))
+ goto err;
index_min = index;
for (i=0; i < matroska->tracks.nb_elem; i++) {
matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
matroska->skip_to_timecode = st->index_entries[index].timestamp;
matroska->done = 0;
+ matroska->num_levels = 0;
ff_update_cur_dts(s, st, st->index_entries[index].timestamp);
return 0;
+err:
+ // slightly hackish but allows proper fallback to
+ // the generic seeking code.
+ matroska_clear_queue(matroska);
+ matroska->current_id = 0;
+ matroska->skip_to_keyframe = 0;
+ matroska->done = 0;
+ matroska->num_levels = 0;
+ return -1;
}
static int matroska_read_close(AVFormatContext *s)