#include "avformat.h"
/* For codec_get_id(). */
#include "riff.h"
-#include "intfloat_readwrite.h"
#include "matroska.h"
+#include "libavcodec/mpeg4audio.h"
+#include "libavutil/intfloat_readwrite.h"
+#include "libavutil/lzo.h"
+#ifdef CONFIG_ZLIB
+#include <zlib.h>
+#endif
+#ifdef CONFIG_BZLIB
+#include <bzlib.h>
+#endif
typedef struct Track {
MatroskaTrackType type;
uint64_t default_duration;
MatroskaTrackFlags flags;
+
+ int encoding_scope;
+ MatroskaTrackEncodingCompAlgo encoding_algo;
+ uint8_t *encoding_settings;
+ int encoding_settings_len;
} MatroskaTrack;
typedef struct MatroskaVideoTrack {
typedef struct MatroskaSubtitleTrack {
MatroskaTrack track;
-
- int ass;
//..
} MatroskaSubtitleTrack;
-#define MAX_TRACK_SIZE (FFMAX(FFMAX(sizeof(MatroskaVideoTrack), \
- sizeof(MatroskaAudioTrack)), \
+#define MAX_TRACK_SIZE (FFMAX3(sizeof(MatroskaVideoTrack), \
+ sizeof(MatroskaAudioTrack), \
sizeof(MatroskaSubtitleTrack)))
typedef struct MatroskaLevel {
static int
ebml_read_element_level_up (MatroskaDemuxContext *matroska)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
offset_t pos = url_ftell(pb);
int num = 0;
int max_size,
uint64_t *number)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
int len_mask = 0x80, read = 1, n = 1;
int64_t total = 0;
ebml_read_seek (MatroskaDemuxContext *matroska,
offset_t offset)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
/* clear ID cache, if any */
matroska->peek_id = 0;
static int
ebml_read_skip (MatroskaDemuxContext *matroska)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
uint32_t id;
uint64_t length;
int res;
uint32_t *id,
uint64_t *num)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
int n = 0, size, res;
uint64_t rlength;
uint32_t *id,
int64_t *num)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
int size, n = 1, negative = 0, res;
uint64_t rlength;
uint32_t *id,
double *num)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
int size, res;
uint64_t rlength;
uint32_t *id,
char **str)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
int size, res;
uint64_t rlength;
ebml_read_master (MatroskaDemuxContext *matroska,
uint32_t *id)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
uint64_t length;
MatroskaLevel *level;
int res;
uint8_t **binary,
int *size)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
uint64_t rlength;
int res;
matroska->num_packets++;
}
+/*
+ * Free all packets in our internal queue.
+ */
+static void
+matroska_clear_queue (MatroskaDemuxContext *matroska)
+{
+ if (matroska->packets) {
+ int n;
+ for (n = 0; n < matroska->num_packets; n++) {
+ av_free_packet(matroska->packets[n]);
+ av_free(matroska->packets[n]);
+ }
+ av_free(matroska->packets);
+ matroska->packets = NULL;
+ matroska->num_packets = 0;
+ }
+}
+
/*
* Autodetecting...
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown or unsupported track type 0x%x\n",
track->type);
- track->type = 0;
+ track->type = MATROSKA_TRACK_TYPE_NONE;
break;
}
matroska->tracks[matroska->num_tracks - 1] = track;
break;
}
- /* colourspace (only matters for raw video)
+ /* colorspace (only matters for raw video)
* fourcc */
- case MATROSKA_ID_VIDEOCOLOURSPACE: {
+ case MATROSKA_ID_VIDEOCOLORSPACE: {
uint64_t num;
if ((res = ebml_read_uint(matroska, &id,
&num)) < 0)
break;
}
+ case MATROSKA_ID_TRACKCONTENTENCODINGS: {
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ break;
+
+ while (res == 0) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up > 0) {
+ matroska->level_up--;
+ break;
+ }
+
+ switch (id) {
+ case MATROSKA_ID_TRACKCONTENTENCODING: {
+ int encoding_scope = 1;
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ break;
+
+ while (res == 0) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up > 0) {
+ matroska->level_up--;
+ break;
+ }
+
+ switch (id) {
+ case MATROSKA_ID_ENCODINGSCOPE: {
+ uint64_t num;
+ if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
+ break;
+ encoding_scope = num;
+ break;
+ }
+
+ case MATROSKA_ID_ENCODINGTYPE: {
+ uint64_t num;
+ if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
+ break;
+ if (num)
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Unsupported encoding type");
+ break;
+ }
+
+ case MATROSKA_ID_ENCODINGCOMPRESSION: {
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ break;
+
+ while (res == 0) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up > 0) {
+ matroska->level_up--;
+ break;
+ }
+
+ switch (id) {
+ case MATROSKA_ID_ENCODINGCOMPALGO: {
+ uint64_t num;
+ if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
+ break;
+ if (num != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP &&
+#ifdef CONFIG_ZLIB
+ num != MATROSKA_TRACK_ENCODING_COMP_ZLIB &&
+#endif
+#ifdef CONFIG_BZLIB
+ num != MATROSKA_TRACK_ENCODING_COMP_BZLIB &&
+#endif
+ num != MATROSKA_TRACK_ENCODING_COMP_LZO)
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Unsupported compression algo\n");
+ track->encoding_algo = num;
+ break;
+ }
+
+ case MATROSKA_ID_ENCODINGCOMPSETTINGS: {
+ uint8_t *data;
+ int size;
+ if ((res = ebml_read_binary(matroska, &id, &data, &size) < 0))
+ break;
+ track->encoding_settings = data;
+ track->encoding_settings_len = size;
+ break;
+ }
+
+ default:
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "Unknown compression header entry "
+ "0x%x - ignoring\n", id);
+ /* pass-through */
+
+ case EBML_ID_VOID:
+ res = ebml_read_skip(matroska);
+ break;
+ }
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "Unknown content encoding header entry "
+ "0x%x - ignoring\n", id);
+ /* pass-through */
+
+ case EBML_ID_VOID:
+ res = ebml_read_skip(matroska);
+ break;
+ }
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+
+ track->encoding_scope = encoding_scope;
+ break;
+ }
+
+ default:
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "Unknown content encodings header entry "
+ "0x%x - ignoring\n", id);
+ /* pass-through */
+
+ case EBML_ID_VOID:
+ res = ebml_read_skip(matroska);
+ break;
+ }
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+ break;
+ }
+
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown track header entry 0x%x - ignoring\n", id);
/* remember the peeked ID and the current position */
peek_id_cache = matroska->peek_id;
- before_pos = url_ftell(&matroska->ctx->pb);
+ before_pos = url_ftell(matroska->ctx->pb);
/* seek */
if ((res = ebml_read_seek(matroska, seek_pos +
matroska->segment_start)) < 0)
- return res;
+ goto finish;
/* we don't want to lose our seekhead level, so we add
* a dummy. This is a crude hack. */
switch (id) {
case MATROSKA_ID_CUES:
if (!(res = matroska_parse_index(matroska)) ||
- url_feof(&matroska->ctx->pb)) {
+ url_feof(matroska->ctx->pb)) {
matroska->index_parsed = 1;
res = 0;
}
break;
case MATROSKA_ID_TAGS:
if (!(res = matroska_parse_metadata(matroska)) ||
- url_feof(&matroska->ctx->pb)) {
+ url_feof(matroska->ctx->pb)) {
matroska->metadata_parsed = 1;
res = 0;
}
return res;
}
+static int
+matroska_parse_attachments(AVFormatContext *s)
+{
+ MatroskaDemuxContext *matroska = s->priv_data;
+ int res = 0;
+ uint32_t id;
+
+ av_log(matroska->ctx, AV_LOG_DEBUG, "parsing attachments...\n");
+
+ while (res == 0) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+
+ switch (id) {
+ case MATROSKA_ID_ATTACHEDFILE: {
+ char* name = NULL;
+ char* mime = NULL;
+ uint8_t* data = NULL;
+ int i, data_size = 0;
+ AVStream *st;
+
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ break;
+
+ while (res == 0) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+
+ switch (id) {
+ case MATROSKA_ID_FILENAME:
+ res = ebml_read_utf8 (matroska, &id, &name);
+ break;
+
+ case MATROSKA_ID_FILEMIMETYPE:
+ res = ebml_read_ascii (matroska, &id, &mime);
+ break;
+
+ case MATROSKA_ID_FILEDATA:
+ res = ebml_read_binary(matroska, &id, &data, &data_size);
+ break;
+
+ default:
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "Unknown attachedfile ID 0x%x\n", id);
+ case EBML_ID_VOID:
+ res = ebml_read_skip(matroska);
+ break;
+ }
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+
+ if (!(name && mime && data && data_size > 0)) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "incomplete attachment\n");
+ break;
+ }
+
+ st = av_new_stream(s, matroska->num_streams++);
+ if (st == NULL)
+ return AVERROR(ENOMEM);
+ st->filename = av_strdup(name);
+ st->codec->codec_id = CODEC_ID_NONE;
+ st->codec->codec_type = CODEC_TYPE_ATTACHMENT;
+ st->codec->extradata = av_malloc(data_size);
+ if(st->codec->extradata == NULL)
+ return AVERROR(ENOMEM);
+ st->codec->extradata_size = data_size;
+ memcpy(st->codec->extradata, data, data_size);
+
+ for (i=0; ff_mkv_mime_tags[i].id != CODEC_ID_NONE; i++) {
+ if (!strncmp(ff_mkv_mime_tags[i].str, mime,
+ strlen(ff_mkv_mime_tags[i].str))) {
+ st->codec->codec_id = ff_mkv_mime_tags[i].id;
+ break;
+ }
+ }
+
+ av_log(matroska->ctx, AV_LOG_DEBUG, "new attachment: %s, %s, size %d \n", name, mime, data_size);
+ break;
+ }
+
+ default:
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "Unknown attachments ID 0x%x\n", id);
+ /* fall-through */
+
+ case EBML_ID_VOID:
+ res = ebml_read_skip(matroska);
+ break;
+ }
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+
+ return res;
+}
+
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
static int
static int
matroska_aac_sri (int samplerate)
{
- static const int aac_sample_rates[] = {
- 96000, 88200, 64000, 48000, 44100, 32000,
- 24000, 22050, 16000, 12000, 11025, 8000,
- };
int sri;
- for (sri=0; sri<ARRAY_SIZE(aac_sample_rates); sri++)
- if (aac_sample_rates[sri] == samplerate)
+ for (sri=0; sri<ARRAY_SIZE(ff_mpeg4audio_sample_rates); sri++)
+ if (ff_mpeg4audio_sample_rates[sri] == samplerate)
break;
return sri;
}
* after the segment ID/length. */
if ((res = ebml_read_master(matroska, &id)) < 0)
return res;
- matroska->segment_start = url_ftell(&s->pb);
+ matroska->segment_start = url_ftell(s->pb);
matroska->time_scale = 1000000;
/* we've found our segment, start reading the different contents in here */
break;
}
+ case MATROSKA_ID_ATTACHMENTS: {
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ break;
+ res = matroska_parse_attachments(s);
+ break;
+ }
+
case MATROSKA_ID_CLUSTER: {
/* Do not read the master - this will be done in the next
* call to matroska_read_packet. */
if (track->codec_id == NULL)
continue;
- for(j=0; ff_mkv_codec_tags[j].str; j++){
+ for(j=0; ff_mkv_codec_tags[j].id != CODEC_ID_NONE; j++){
if(!strncmp(ff_mkv_codec_tags[j].str, track->codec_id,
strlen(ff_mkv_codec_tags[j].str))){
codec_id= ff_mkv_codec_tags[j].id;
return AVERROR(ENOMEM);
init_put_byte(&b, extradata, extradata_size, 1,
NULL, NULL, NULL, NULL);
- put_buffer(&b, (uint8_t *) "TTA1", 4);
+ put_buffer(&b, "TTA1", 4);
put_le16(&b, 1);
put_le16(&b, audiotrack->channels);
put_le16(&b, audiotrack->bitdepth);
}
}
- else if (codec_id == CODEC_ID_TEXT) {
- MatroskaSubtitleTrack *subtrack=(MatroskaSubtitleTrack *)track;
- if (!strcmp(track->codec_id, "S_TEXT/ASS") ||
- !strcmp(track->codec_id, "S_TEXT/SSA") ||
- !strcmp(track->codec_id, "S_ASS") ||
- !strcmp(track->codec_id, "S_SSA"))
- subtrack->ass = 1;
- }
-
if (codec_id == CODEC_ID_NONE) {
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown/unsupported CodecID %s.\n",
if (strcmp(track->language, "und"))
strcpy(st->language, track->language);
+ if (track->flags & MATROSKA_TRACK_DEFAULT)
+ st->disposition |= AV_DISPOSITION_DEFAULT;
+
if (track->default_duration)
av_reduce(&st->codec->time_base.num, &st->codec->time_base.den,
track->default_duration, 1000000000, 30000);
for (i=0; i<matroska->num_indexes; i++) {
MatroskaDemuxIndex *idx = &matroska->index[i];
track = matroska_find_track_by_num(matroska, idx->track);
+ if (track < 0) continue;
stream = matroska->tracks[track]->stream_index;
- if (stream >= 0)
+ if (stream >= 0 && stream < matroska->ctx->nb_streams)
av_add_index_entry(matroska->ctx->streams[stream],
idx->pos, idx->time/matroska->time_scale,
0, 0, AVINDEX_KEYFRAME);
uint32_t *lace_size = NULL;
int n, flags, laces = 0;
uint64_t num;
+ int stream_index;
/* first byte(s): tracknum */
if ((n = matroska_ebmlnum_uint(data, size, &num)) < 0) {
av_free(origdata);
return res;
}
- if (matroska->tracks[track]->stream_index < 0)
+ stream_index = matroska->tracks[track]->stream_index;
+ if (stream_index < 0 || stream_index >= matroska->ctx->nb_streams) {
+ av_free(origdata);
return res;
- st = matroska->ctx->streams[matroska->tracks[track]->stream_index];
+ }
+ st = matroska->ctx->streams[stream_index];
if (st->discard >= AVDISCARD_ALL) {
av_free(origdata);
return res;
is_keyframe = flags & 0x80 ? PKT_FLAG_KEY : 0;
if (matroska->skip_to_keyframe) {
- if (!is_keyframe || st != matroska->skip_to_stream)
+ if (!is_keyframe || st != matroska->skip_to_stream) {
+ av_free(origdata);
return res;
+ }
matroska->skip_to_keyframe = 0;
}
memcpy(pkt->data, audiotrack->buf
+ a * (h*w / a - audiotrack->pkt_cnt--), a);
pkt->pos = pos;
- pkt->stream_index = matroska->tracks[track]->stream_index;
+ pkt->stream_index = stream_index;
matroska_queue_packet(matroska, pkt);
}
} else {
- int offset = 0;
-
- if (st->codec->codec_id == CODEC_ID_TEXT
- && ((MatroskaSubtitleTrack *)(matroska->tracks[track]))->ass) {
- int i;
- for (i=0; i<8 && data[offset]; offset++)
- if (data[offset] == ',')
- i++;
+ int result, offset = 0, ilen, olen, 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
+ }
}
pkt = av_mallocz(sizeof(AVPacket));
/* XXX: prevent data copy... */
- if (av_new_packet(pkt, lace_size[n]-offset) < 0) {
+ if (av_new_packet(pkt, pkt_size+offset) < 0) {
res = AVERROR(ENOMEM);
n = laces-1;
break;
}
- memcpy (pkt->data, data+offset, lace_size[n]-offset);
+ if (offset)
+ memcpy (pkt->data, matroska->tracks[track]->encoding_settings, offset);
+ memcpy (pkt->data+offset, pkt_data, pkt_size);
if (n == 0)
pkt->flags = is_keyframe;
- pkt->stream_index = matroska->tracks[track]->stream_index;
+ pkt->stream_index = stream_index;
pkt->pts = timecode;
pkt->pos = pos;
* of the harder things, so this code is a bit complicated.
* See http://www.matroska.org/ for documentation. */
case MATROSKA_ID_BLOCK: {
- pos = url_ftell(&matroska->ctx->pb);
+ pos = url_ftell(matroska->ctx->pb);
res = ebml_read_binary(matroska, &id, &data, &size);
break;
}
int size;
av_log(matroska->ctx, AV_LOG_DEBUG,
- "parsing cluster at %"PRId64"\n", url_ftell(&matroska->ctx->pb));
+ "parsing cluster at %"PRId64"\n", url_ftell(matroska->ctx->pb));
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
break;
case MATROSKA_ID_SIMPLEBLOCK:
- pos = url_ftell(&matroska->ctx->pb);
+ pos = url_ftell(matroska->ctx->pb);
res = ebml_read_binary(matroska, &id, &data, &size);
if (res == 0)
res = matroska_parse_block(matroska, data, size, pos,
if (index < 0)
return 0;
+ matroska_clear_queue(matroska);
+
/* do the seek */
- url_fseek(&s->pb, st->index_entries[index].pos, SEEK_SET);
+ url_fseek(s->pb, st->index_entries[index].pos, SEEK_SET);
matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
matroska->skip_to_stream = st;
- matroska->num_packets = 0;
matroska->peek_id = 0;
return 0;
}
av_free(matroska->muxing_app);
av_free(matroska->index);
- if (matroska->packets != NULL) {
- for (n = 0; n < matroska->num_packets; n++) {
- av_free_packet(matroska->packets[n]);
- av_free(matroska->packets[n]);
- }
- av_free(matroska->packets);
- }
+ matroska_clear_queue(matroska);
for (n = 0; n < matroska->num_tracks; n++) {
MatroskaTrack *track = matroska->tracks[n];