#include "libavutil/intreadwrite.h"
#include "libavutil/avstring.h"
#include "libavutil/lzo.h"
+#include "libavutil/dict.h"
#if CONFIG_ZLIB
#include <zlib.h>
#endif
uint64_t display_height;
uint64_t pixel_width;
uint64_t pixel_height;
- uint64_t fourcc;
+ EbmlBin color_space;
uint64_t stereo_mode;
} MatroskaTrackVideo;
/* What to skip before effectively reading a packet. */
int skip_to_keyframe;
uint64_t skip_to_timecode;
+
+ /* File has a CUES element, but we defer parsing until it is needed. */
+ int cues_parsing_deferred;
} MatroskaDemuxContext;
typedef struct {
{ MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_height) },
{ MATROSKA_ID_VIDEOPIXELWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_width) },
{ MATROSKA_ID_VIDEOPIXELHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_height) },
- { MATROSKA_ID_VIDEOCOLORSPACE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,fourcc) },
+ { MATROSKA_ID_VIDEOCOLORSPACE, EBML_BIN, 0, offsetof(MatroskaTrackVideo,color_space) },
{ MATROSKA_ID_VIDEOSTEREOMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,stereo_mode) },
{ MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE },
{ MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE },
uint32_t id = syntax->id;
uint64_t length;
int res;
+ void *newelem;
data = (char *)data + syntax->data_offset;
if (syntax->list_elem_size) {
EbmlList *list = data;
- list->elem = av_realloc(list->elem, (list->nb_elem+1)*syntax->list_elem_size);
+ newelem = av_realloc(list->elem, (list->nb_elem+1)*syntax->list_elem_size);
+ if (!newelem)
+ return AVERROR(ENOMEM);
+ list->elem = newelem;
data = (char*)list->elem + list->nb_elem*syntax->list_elem_size;
memset(data, 0, syntax->list_elem_size);
list->nb_elem++;
* Not fully fool-proof, but good enough. */
for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) {
int probelen = strlen(matroska_doctypes[i]);
+ if (total < probelen)
+ continue;
for (n = 4+size; n <= 4+size+total-probelen; n++)
if (!memcmp(p->buf+n, matroska_doctypes[i], probelen))
return AVPROBE_SCORE_MAX;
uint8_t* data = *buf;
int isize = *buf_size;
uint8_t* pkt_data = NULL;
+ uint8_t* newpktdata;
int pkt_size = isize;
int result = 0;
int olen;
zstream.avail_in = isize;
do {
pkt_size *= 3;
- pkt_data = av_realloc(pkt_data, pkt_size);
+ newpktdata = av_realloc(pkt_data, pkt_size);
+ if (!newpktdata) {
+ inflateEnd(&zstream);
+ goto failed;
+ }
+ pkt_data = newpktdata;
zstream.avail_out = pkt_size - zstream.total_out;
zstream.next_out = pkt_data + zstream.total_out;
- result = inflate(&zstream, Z_NO_FLUSH);
+ if (pkt_data) {
+ result = inflate(&zstream, Z_NO_FLUSH);
+ } else
+ result = Z_MEM_ERROR;
} while (result==Z_OK && pkt_size<10000000);
pkt_size = zstream.total_out;
inflateEnd(&zstream);
bzstream.avail_in = isize;
do {
pkt_size *= 3;
- pkt_data = av_realloc(pkt_data, pkt_size);
+ newpktdata = av_realloc(pkt_data, pkt_size);
+ if (!newpktdata) {
+ BZ2_bzDecompressEnd(&bzstream);
+ goto failed;
+ }
+ pkt_data = newpktdata;
bzstream.avail_out = pkt_size - bzstream.total_out_lo32;
bzstream.next_out = pkt_data + bzstream.total_out_lo32;
- result = BZ2_bzDecompress(&bzstream);
+ if (pkt_data) {
+ result = BZ2_bzDecompress(&bzstream);
+ } else
+ result = BZ_MEM_ERROR;
} while (result==BZ_OK && pkt_size<10000000);
pkt_size = bzstream.total_out_lo32;
BZ2_bzDecompressEnd(&bzstream);
char *line, *layer, *ptr = pkt->data, *end = ptr+pkt->size;
for (; *ptr!=',' && ptr<end-1; ptr++);
if (*ptr == ',')
- layer = ++ptr;
+ ptr++;
+ layer = ptr;
for (; *ptr!=',' && ptr<end-1; ptr++);
if (*ptr == ',') {
int64_t end_pts = pkt->pts + display_duration;
}
}
-static void matroska_merge_packets(AVPacket *out, AVPacket *in)
+static int matroska_merge_packets(AVPacket *out, AVPacket *in)
{
- out->data = av_realloc(out->data, out->size+in->size);
+ void *newdata = av_realloc(out->data, out->size+in->size);
+ if (!newdata)
+ return AVERROR(ENOMEM);
+ out->data = newdata;
memcpy(out->data+out->size, in->data, in->size);
out->size += in->size;
av_destruct_packet(in);
av_free(in);
+ return 0;
}
static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
- AVMetadata **metadata, char *prefix)
+ AVDictionary **metadata, char *prefix)
{
MatroskaTag *tags = list->elem;
char key[1024];
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_metadata_set2(metadata, key, tags[i].string, 0);
+ av_dict_set(metadata, key, tags[i].string, 0);
if (tags[i].sub.nb_elem)
matroska_convert_tag(s, &tags[i].sub, metadata, key);
}
if (lang) {
av_strlcat(key, "-", sizeof(key));
av_strlcat(key, lang, sizeof(key));
- av_metadata_set2(metadata, key, tags[i].string, 0);
+ av_dict_set(metadata, key, tags[i].string, 0);
if (tags[i].sub.nb_elem)
matroska_convert_tag(s, &tags[i].sub, metadata, key);
}
}
}
-static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
+static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska, int idx)
{
EbmlList *seekhead_list = &matroska->seekhead;
MatroskaSeekhead *seekhead = seekhead_list->elem;
int64_t before_pos = avio_tell(matroska->ctx->pb);
uint32_t saved_id = matroska->current_id;
MatroskaLevel level;
+ int64_t offset;
+ int ret = 0;
+
+ if (idx >= seekhead_list->nb_elem
+ || seekhead[idx].id == MATROSKA_ID_SEEKHEAD
+ || seekhead[idx].id == MATROSKA_ID_CLUSTER)
+ return 0;
+
+ /* seek */
+ offset = seekhead[idx].pos + matroska->segment_start;
+ if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) {
+ /* We don't want to lose our seekhead level, so we add
+ * a dummy. This is a crude hack. */
+ if (matroska->num_levels == EBML_MAX_DEPTH) {
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "Max EBML element depth (%d) reached, "
+ "cannot parse further.\n", EBML_MAX_DEPTH);
+ ret = AVERROR_INVALIDDATA;
+ } else {
+ level.start = 0;
+ level.length = (uint64_t)-1;
+ matroska->levels[matroska->num_levels] = level;
+ matroska->num_levels++;
+ matroska->current_id = 0;
+
+ ret = ebml_parse(matroska, matroska_segment, matroska);
+
+ /* remove dummy level */
+ while (matroska->num_levels) {
+ uint64_t length = matroska->levels[--matroska->num_levels].length;
+ if (length == (uint64_t)-1)
+ break;
+ }
+ }
+ }
+ /* seek back */
+ avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
+ matroska->level_up = level_up;
+ matroska->current_id = saved_id;
+
+ return ret;
+}
+
+static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
+{
+ EbmlList *seekhead_list = &matroska->seekhead;
+ MatroskaSeekhead *seekhead = seekhead_list->elem;
+ int64_t before_pos = avio_tell(matroska->ctx->pb);
int i;
// we should not do any seeking in the streaming case
(matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
return;
- for (i=0; i<seekhead_list->nb_elem; i++) {
- int64_t offset = seekhead[i].pos + matroska->segment_start;
-
- if (seekhead[i].pos <= before_pos
- || seekhead[i].id == MATROSKA_ID_SEEKHEAD
- || seekhead[i].id == MATROSKA_ID_CLUSTER)
+ for (i = 0; i < seekhead_list->nb_elem; i++) {
+ if (seekhead[i].pos <= before_pos)
continue;
- /* seek */
- if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) != offset)
+ // defer cues parsing until we actually need cue data.
+ if (seekhead[i].id == MATROSKA_ID_CUES) {
+ matroska->cues_parsing_deferred = 1;
continue;
-
- /* We don't want to lose our seekhead level, so we add
- * a dummy. This is a crude hack. */
- if (matroska->num_levels == EBML_MAX_DEPTH) {
- av_log(matroska->ctx, AV_LOG_INFO,
- "Max EBML element depth (%d) reached, "
- "cannot parse further.\n", EBML_MAX_DEPTH);
- break;
}
- level.start = 0;
- level.length = (uint64_t)-1;
- matroska->levels[matroska->num_levels] = level;
- matroska->num_levels++;
- matroska->current_id = 0;
+ if (matroska_parse_seekhead_entry(matroska, i) < 0)
+ break;
+ }
+}
- ebml_parse(matroska, matroska_segment, matroska);
+static void matroska_add_index_entries(MatroskaDemuxContext *matroska) {
+ EbmlList *index_list;
+ MatroskaIndex *index;
+ int index_scale = 1;
+ int i, j;
- /* remove dummy level */
- while (matroska->num_levels) {
- uint64_t length = matroska->levels[--matroska->num_levels].length;
- if (length == (uint64_t)-1)
- break;
+ index_list = &matroska->index;
+ index = index_list->elem;
+ if (index_list->nb_elem
+ && index[0].time > 1E14/matroska->time_scale) {
+ av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
+ index_scale = matroska->time_scale;
+ }
+ for (i = 0; i < index_list->nb_elem; i++) {
+ EbmlList *pos_list = &index[i].pos;
+ MatroskaIndexPos *pos = pos_list->elem;
+ for (j = 0; j < pos_list->nb_elem; j++) {
+ MatroskaTrack *track = matroska_find_track_by_num(matroska, pos[j].track);
+ if (track && track->stream)
+ av_add_index_entry(track->stream,
+ pos[j].pos + matroska->segment_start,
+ index[i].time/index_scale, 0, 0,
+ AVINDEX_KEYFRAME);
}
}
+}
- /* seek back */
- avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
- matroska->level_up = level_up;
- matroska->current_id = saved_id;
+static void matroska_parse_cues(MatroskaDemuxContext *matroska) {
+ EbmlList *seekhead_list = &matroska->seekhead;
+ MatroskaSeekhead *seekhead = seekhead_list->elem;
+ int i;
+
+ for (i = 0; i < seekhead_list->nb_elem; i++)
+ if (seekhead[i].id == MATROSKA_ID_CUES)
+ break;
+ assert(i <= seekhead_list->nb_elem);
+
+ matroska_parse_seekhead_entry(matroska, i);
+ matroska_add_index_entries(matroska);
}
static int matroska_aac_profile(char *codec_id)
{
int sri;
- for (sri=0; sri<FF_ARRAY_ELEMS(ff_mpeg4audio_sample_rates); sri++)
- if (ff_mpeg4audio_sample_rates[sri] == samplerate)
+ for (sri=0; sri<FF_ARRAY_ELEMS(avpriv_mpeg4audio_sample_rates); sri++)
+ if (avpriv_mpeg4audio_sample_rates[sri] == samplerate)
break;
return sri;
}
EbmlList *chapters_list = &matroska->chapters;
MatroskaChapter *chapters;
MatroskaTrack *tracks;
- EbmlList *index_list;
- MatroskaIndex *index;
- int index_scale = 1;
uint64_t max_start = 0;
Ebml ebml = { 0 };
AVStream *st;
if (matroska->duration)
matroska->ctx->duration = matroska->duration * matroska->time_scale
* 1000 / AV_TIME_BASE;
- av_metadata_set2(&s->metadata, "title", matroska->title, 0);
+ av_dict_set(&s->metadata, "title", matroska->title, 0);
tracks = matroska->tracks.elem;
for (i=0; i < matroska->tracks.nb_elem; i++) {
MatroskaTrack *track = &tracks[i];
enum CodecID codec_id = CODEC_ID_NONE;
- EbmlList *encodings_list = &tracks->encodings;
+ EbmlList *encodings_list = &track->encodings;
MatroskaTrackEncoding *encodings = encodings_list->elem;
uint8_t *extradata = NULL;
int extradata_size = 0;
int extradata_offset = 0;
+ uint32_t fourcc = 0;
AVIOContext b;
/* Apply some sanity checks. */
track->video.display_width = track->video.pixel_width;
if (!track->video.display_height)
track->video.display_height = track->video.pixel_height;
+ if (track->video.color_space.size == 4)
+ fourcc = AV_RL32(track->video.color_space.data);
} else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
if (!track->audio.out_samplerate)
track->audio.out_samplerate = track->audio.samplerate;
}
if (encodings_list->nb_elem > 1) {
av_log(matroska->ctx, AV_LOG_ERROR,
- "Multiple combined encodings no supported");
+ "Multiple combined encodings not supported");
} else if (encodings_list->nb_elem == 1) {
if (encodings[0].type ||
(encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP &&
}
}
- st = track->stream = av_new_stream(s, 0);
+ st = track->stream = avformat_new_stream(s, NULL);
if (st == NULL)
return AVERROR(ENOMEM);
&& track->codec_priv.size >= 40
&& track->codec_priv.data != NULL) {
track->ms_compat = 1;
- track->video.fourcc = AV_RL32(track->codec_priv.data + 16);
- codec_id = ff_codec_get_id(ff_codec_bmp_tags, track->video.fourcc);
+ fourcc = AV_RL32(track->codec_priv.data + 16);
+ codec_id = ff_codec_get_id(ff_codec_bmp_tags, fourcc);
extradata_offset = 40;
} else if (!strcmp(track->codec_id, "A_MS/ACM")
&& track->codec_priv.size >= 14
} else if (!strcmp(track->codec_id, "V_QUICKTIME")
&& (track->codec_priv.size >= 86)
&& (track->codec_priv.data != NULL)) {
- track->video.fourcc = AV_RL32(track->codec_priv.data);
- codec_id=ff_codec_get_id(codec_movvideo_tags, track->video.fourcc);
+ fourcc = AV_RL32(track->codec_priv.data);
+ codec_id = ff_codec_get_id(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->codec_id = codec_id;
st->start_time = 0;
if (strcmp(track->language, "und"))
- av_metadata_set2(&st->metadata, "language", track->language, 0);
- av_metadata_set2(&st->metadata, "title", track->name, 0);
+ av_dict_set(&st->metadata, "language", track->language, 0);
+ av_dict_set(&st->metadata, "title", track->name, 0);
if (track->flag_default)
st->disposition |= AV_DISPOSITION_DEFAULT;
MatroskaTrackPlane *planes = track->operation.combine_planes.elem;
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codec->codec_tag = track->video.fourcc;
+ st->codec->codec_tag = fourcc;
st->codec->width = track->video.pixel_width;
st->codec->height = track->video.pixel_height;
av_reduce(&st->sample_aspect_ratio.num,
/* export stereo mode flag as metadata tag */
if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREO_MODE_COUNT)
- av_metadata_set2(&st->metadata, "stereo_mode", matroska_video_stereo_mode[track->video.stereo_mode], 0);
+ av_dict_set(&st->metadata, "stereo_mode", matroska_video_stereo_mode[track->video.stereo_mode], 0);
/* if we have virtual track, mark the real tracks */
for (j=0; j < track->operation.combine_planes.nb_elem; j++) {
matroska_video_stereo_plane[planes[j].type], i);
for (k=0; k < matroska->tracks.nb_elem; k++)
if (planes[j].uid == tracks[k].uid) {
- av_metadata_set2(&s->streams[k]->metadata,
- "stereo_mode", buf, 0);
+ av_dict_set(&s->streams[k]->metadata,
+ "stereo_mode", buf, 0);
break;
}
}
attachements[j].bin.data && attachements[j].bin.size > 0)) {
av_log(matroska->ctx, AV_LOG_ERROR, "incomplete attachment\n");
} else {
- AVStream *st = av_new_stream(s, 0);
+ AVStream *st = avformat_new_stream(s, NULL);
if (st == NULL)
break;
- av_metadata_set2(&st->metadata, "filename",attachements[j].filename, 0);
+ av_dict_set(&st->metadata, "filename",attachements[j].filename, 0);
+ av_dict_set(&st->metadata, "mimetype", attachements[j].mime, 0);
st->codec->codec_id = CODEC_ID_NONE;
st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT;
st->codec->extradata = av_malloc(attachements[j].bin.size);
if (chapters[i].start != AV_NOPTS_VALUE && chapters[i].uid
&& (max_start==0 || chapters[i].start > max_start)) {
chapters[i].chapter =
- ff_new_chapter(s, chapters[i].uid, (AVRational){1, 1000000000},
+ avpriv_new_chapter(s, chapters[i].uid, (AVRational){1, 1000000000},
chapters[i].start, chapters[i].end,
chapters[i].title);
- av_metadata_set2(&chapters[i].chapter->metadata,
+ av_dict_set(&chapters[i].chapter->metadata,
"title", chapters[i].title, 0);
max_start = chapters[i].start;
}
- index_list = &matroska->index;
- index = index_list->elem;
- if (index_list->nb_elem
- && index[0].time > 100000000000000/matroska->time_scale) {
- av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
- index_scale = matroska->time_scale;
- }
- for (i=0; i<index_list->nb_elem; i++) {
- EbmlList *pos_list = &index[i].pos;
- MatroskaIndexPos *pos = pos_list->elem;
- for (j=0; j<pos_list->nb_elem; j++) {
- MatroskaTrack *track = matroska_find_track_by_num(matroska,
- pos[j].track);
- if (track && track->stream)
- av_add_index_entry(track->stream,
- pos[j].pos + matroska->segment_start,
- index[i].time/index_scale, 0, 0,
- AVINDEX_KEYFRAME);
- }
- }
+ matroska_add_index_entries(matroska);
matroska_convert_tags(s);
memcpy(pkt, matroska->packets[0], sizeof(AVPacket));
av_free(matroska->packets[0]);
if (matroska->num_packets > 1) {
+ void *newpackets;
memmove(&matroska->packets[0], &matroska->packets[1],
(matroska->num_packets - 1) * sizeof(AVPacket *));
- matroska->packets =
- av_realloc(matroska->packets, (matroska->num_packets - 1) *
- sizeof(AVPacket *));
+ newpackets = av_realloc(matroska->packets,
+ (matroska->num_packets - 1) * sizeof(AVPacket *));
+ if (newpackets)
+ matroska->packets = newpackets;
} else {
av_freep(&matroska->packets);
}
if (size <= 3 || !track || !track->stream) {
av_log(matroska->ctx, AV_LOG_INFO,
"Invalid stream %"PRIu64" or size %u\n", num, size);
- return res;
+ return AVERROR_INVALIDDATA;
}
st = track->stream;
if (st->discard >= AVDISCARD_ALL)
lace_size[n] = lace_size[n - 1] + snum;
total += lace_size[n];
}
- lace_size[n] = size - total;
+ lace_size[laces - 1] = size - total;
break;
}
}
res = ebml_parse(matroska, matroska_clusters, &cluster);
blocks_list = &cluster.blocks;
blocks = blocks_list->elem;
- for (i=0; i<blocks_list->nb_elem; i++)
+ for (i=0; i<blocks_list->nb_elem && !res; i++)
if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1;
res=matroska_parse_block(matroska,
static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt)
{
MatroskaDemuxContext *matroska = s->priv_data;
+ int ret = 0;
- while (matroska_deliver_packet(matroska, pkt)) {
+ while (!ret && matroska_deliver_packet(matroska, pkt)) {
if (matroska->done)
return AVERROR_EOF;
- matroska_parse_cluster(matroska);
+ ret = matroska_parse_cluster(matroska);
}
- return 0;
+ return ret;
}
static int matroska_read_seek(AVFormatContext *s, int stream_index,
AVStream *st = s->streams[stream_index];
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);
+ matroska->cues_parsing_deferred = 0;
+ }
+
if (!st->nb_index_entries)
return 0;
timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
avio_seek(s->pb, st->index_entries[st->nb_index_entries-1].pos, SEEK_SET);
+ matroska->current_id = 0;
while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0)
}
avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET);
+ matroska->current_id = 0;
matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
matroska->skip_to_timecode = st->index_entries[index].timestamp;
matroska->done = 0;
- av_update_cur_dts(s, st, st->index_entries[index].timestamp);
+ ff_update_cur_dts(s, st, st->index_entries[index].timestamp);
return 0;
}
}
AVInputFormat ff_matroska_demuxer = {
- "matroska,webm",
- NULL_IF_CONFIG_SMALL("Matroska/WebM file format"),
- sizeof(MatroskaDemuxContext),
- matroska_probe,
- matroska_read_header,
- matroska_read_packet,
- matroska_read_close,
- matroska_read_seek,
+ .name = "matroska,webm",
+ .long_name = NULL_IF_CONFIG_SMALL("Matroska/WebM file format"),
+ .priv_data_size = sizeof(MatroskaDemuxContext),
+ .read_probe = matroska_probe,
+ .read_header = matroska_read_header,
+ .read_packet = matroska_read_packet,
+ .read_close = matroska_read_close,
+ .read_seek = matroska_read_seek,
};