#include "avformat.h"
/* For codec_get_id(). */
#include "riff.h"
-#include "intfloat_readwrite.h"
+#include "isom.h"
#include "matroska.h"
+#include "libavcodec/mpeg4audio.h"
+#include "libavutil/intfloat_readwrite.h"
+#include "libavutil/avstring.h"
+#include "libavutil/lzo.h"
+#ifdef CONFIG_ZLIB
+#include <zlib.h>
+#endif
+#ifdef CONFIG_BZLIB
+#include <bzlib.h>
+#endif
+
+typedef enum {
+ EBML_NONE,
+ EBML_UINT,
+ EBML_FLOAT,
+ EBML_STR,
+ EBML_UTF8,
+ EBML_BIN,
+ EBML_NEST,
+ EBML_PASS,
+ EBML_STOP,
+} EbmlType;
+
+typedef const struct EbmlSyntax {
+ uint32_t id;
+ EbmlType type;
+ int list_elem_size;
+ int data_offset;
+ union {
+ uint64_t u;
+ double f;
+ const char *s;
+ const struct EbmlSyntax *n;
+ } def;
+} EbmlSyntax;
+
+typedef struct {
+ int nb_elem;
+ void *elem;
+} EbmlList;
+
+typedef struct {
+ int size;
+ uint8_t *data;
+ int64_t pos;
+} EbmlBin;
+
+typedef struct {
+ uint64_t version;
+ uint64_t max_size;
+ uint64_t id_length;
+ char *doctype;
+ uint64_t doctype_version;
+} Ebml;
typedef struct Track {
MatroskaTrackType type;
* the calling app uses for this track. */
uint32_t num;
uint32_t uid;
- int stream_index;
char *name;
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;
+ uint64_t flag_default;
+
+ int encoding_scope;
+ MatroskaTrackEncodingCompAlgo encoding_algo;
+ uint8_t *encoding_settings;
+ int encoding_settings_len;
+
+ AVStream *stream;
} MatroskaTrack;
typedef struct MatroskaVideoTrack {
uint32_t fourcc;
- MatroskaAspectRatioMode ar_mode;
- MatroskaEyeMode eye_mode;
-
//..
} 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 {
+ uint64_t start;
+ uint64_t end;
+ uint64_t uid;
+ char *title;
+} MatroskaChapter;
+
+typedef struct {
+ uint64_t track;
+ uint64_t pos;
+} MatroskaIndexPos;
+
+typedef struct {
+ uint64_t time;
+ EbmlList pos;
+} MatroskaIndex;
+
typedef struct MatroskaLevel {
uint64_t start;
uint64_t length;
} MatroskaLevel;
-typedef struct MatroskaDemuxIndex {
- uint64_t pos; /* of the corresponding *cluster*! */
- uint16_t track; /* reference to 'num' */
- uint64_t time; /* in nanoseconds */
-} MatroskaDemuxIndex;
-
typedef struct MatroskaDemuxContext {
AVFormatContext *ctx;
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;
+ EbmlList chapters;
+ EbmlList index;
/* num_streams is the number of streams that av_new_stream() was called
* for ( = that are available to the calling program). */
int index_parsed;
int done;
- /* The index for seeking. */
- int num_indexes;
- MatroskaDemuxIndex *index;
-
/* What to skip before effectively reading a packet. */
int skip_to_keyframe;
AVStream *skip_to_stream;
} MatroskaDemuxContext;
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+
+static EbmlSyntax ebml_header[] = {
+ { EBML_ID_EBMLREADVERSION, EBML_UINT, 0, offsetof(Ebml,version), {.u=EBML_VERSION} },
+ { EBML_ID_EBMLMAXSIZELENGTH, EBML_UINT, 0, offsetof(Ebml,max_size), {.u=8} },
+ { EBML_ID_EBMLMAXIDLENGTH, EBML_UINT, 0, offsetof(Ebml,id_length), {.u=4} },
+ { EBML_ID_DOCTYPE, EBML_STR, 0, offsetof(Ebml,doctype), {.s="(none)"} },
+ { EBML_ID_DOCTYPEREADVERSION, EBML_UINT, 0, offsetof(Ebml,doctype_version), {.u=1} },
+ { EBML_ID_EBMLVERSION, EBML_NONE },
+ { EBML_ID_DOCTYPEVERSION, EBML_NONE },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax ebml_syntax[] = {
+ { EBML_ID_HEADER, EBML_NEST, 0, 0, {.n=ebml_header} },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapter_display[] = {
+ { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter,title) },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapter_entry[] = {
+ { MATROSKA_ID_CHAPTERTIMESTART, EBML_UINT, 0, offsetof(MatroskaChapter,start), {.u=AV_NOPTS_VALUE} },
+ { MATROSKA_ID_CHAPTERTIMEEND, EBML_UINT, 0, offsetof(MatroskaChapter,end), {.u=AV_NOPTS_VALUE} },
+ { MATROSKA_ID_CHAPTERUID, EBML_UINT, 0, offsetof(MatroskaChapter,uid) },
+ { MATROSKA_ID_CHAPTERDISPLAY, EBML_NEST, 0, 0, {.n=matroska_chapter_display} },
+ { MATROSKA_ID_CHAPTERFLAGHIDDEN, EBML_NONE },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapter[] = {
+ { MATROSKA_ID_CHAPTERATOM, EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaDemuxContext,chapters), {.n=matroska_chapter_entry} },
+ { MATROSKA_ID_EDITIONUID, EBML_NONE },
+ { MATROSKA_ID_EDITIONFLAGHIDDEN, EBML_NONE },
+ { MATROSKA_ID_EDITIONFLAGDEFAULT, EBML_NONE },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapters[] = {
+ { MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, {.n=matroska_chapter} },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_index_pos[] = {
+ { MATROSKA_ID_CUETRACK, EBML_UINT, 0, offsetof(MatroskaIndexPos,track) },
+ { MATROSKA_ID_CUECLUSTERPOSITION, EBML_UINT, 0, offsetof(MatroskaIndexPos,pos) },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_index_entry[] = {
+ { MATROSKA_ID_CUETIME, EBML_UINT, 0, offsetof(MatroskaIndex,time) },
+ { MATROSKA_ID_CUETRACKPOSITION, EBML_NEST, sizeof(MatroskaIndexPos), offsetof(MatroskaIndex,pos), {.n=matroska_index_pos} },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_index[] = {
+ { MATROSKA_ID_POINTENTRY, EBML_NEST, sizeof(MatroskaIndex), offsetof(MatroskaDemuxContext,index), {.n=matroska_index_entry} },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_tags[] = {
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
/*
* The first few functions handle EBML file parsing. The rest
* is the document interpretation. Matroska really just is a
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;
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n",
pos, pos);
}
- return AVERROR_IO; /* EOS or actual I/O error */
+ return AVERROR(EIO); /* EOS or actual I/O error */
}
/* get the length of the EBML number */
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;
offset_t pos = url_ftell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos);
- return AVERROR_IO;
+ 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.
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;
if (matroska->num_levels >= EBML_MAX_DEPTH) {
av_log(matroska->ctx, AV_LOG_ERROR,
"File moves beyond max. allowed depth (%d)\n", EBML_MAX_DEPTH);
- return AVERROR_NOTSUPP;
+ return AVERROR(ENOSYS);
}
/* remember level */
uint8_t **binary,
int *size)
{
- ByteIOContext *pb = &matroska->ctx->pb;
+ ByteIOContext *pb = matroska->ctx->pb;
uint64_t rlength;
int res;
offset_t pos = url_ftell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos);
- return AVERROR_IO;
+ return AVERROR(EIO);
}
return 0;
return res;
}
-/*
- * Read an EBML header.
- * 0 is success, < 0 is failure.
- */
-
-static int
-ebml_read_header (MatroskaDemuxContext *matroska,
- char **doctype,
- int *version)
-{
- uint32_t id;
- int level_up, res = 0;
-
- /* default init */
- if (doctype)
- *doctype = NULL;
- if (version)
- *version = 1;
-
- if (!(id = ebml_peek_id(matroska, &level_up)) ||
- level_up != 0 || id != EBML_ID_HEADER) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "This is not an EBML file (id=0x%x/0x%x)\n", id, EBML_ID_HEADER);
- return AVERROR_INVALIDDATA;
- }
- if ((res = ebml_read_master(matroska, &id)) < 0)
- return res;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &level_up)))
- return AVERROR_IO;
-
- /* end-of-header */
- if (level_up)
- break;
-
- switch (id) {
- /* is our read version uptodate? */
- case EBML_ID_EBMLREADVERSION: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (num > EBML_VERSION) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "EBML version %"PRIu64" (> %d) is not supported\n",
- num, EBML_VERSION);
- return AVERROR_INVALIDDATA;
- }
- break;
- }
-
- /* we only handle 8 byte lengths at max */
- case EBML_ID_EBMLMAXSIZELENGTH: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (num > sizeof(uint64_t)) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "Integers of size %"PRIu64" (> %zd) not supported\n",
- num, sizeof(uint64_t));
- return AVERROR_INVALIDDATA;
- }
- break;
- }
-
- /* we handle 4 byte IDs at max */
- case EBML_ID_EBMLMAXIDLENGTH: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (num > sizeof(uint32_t)) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "IDs of size %"PRIu64" (> %zu) not supported\n",
- num, sizeof(uint32_t));
- return AVERROR_INVALIDDATA;
- }
- break;
- }
-
- case EBML_ID_DOCTYPE: {
- char *text;
-
- if ((res = ebml_read_ascii(matroska, &id, &text)) < 0)
- return res;
- if (doctype) {
- if (*doctype)
- av_free(*doctype);
- *doctype = text;
- } else
- av_free(text);
- break;
- }
-
- case EBML_ID_DOCTYPEREADVERSION: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (version)
- *version = num;
- break;
- }
-
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown data type 0x%x in EBML header", id);
- /* pass-through */
-
- case EBML_ID_VOID:
- /* we ignore these two, as they don't tell us anything we
- * care about */
- case EBML_ID_EBMLVERSION:
- case EBML_ID_DOCTYPEVERSION:
- res = ebml_read_skip (matroska);
- break;
- }
- }
-
- return 0;
-}
-
-static int
+static MatroskaTrack *
matroska_find_track_by_num (MatroskaDemuxContext *matroska,
int num)
{
for (i = 0; i < matroska->num_tracks; i++)
if (matroska->tracks[i]->num == num)
- return i;
+ return matroska->tracks[i];
- return -1;
+ av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num);
+ return NULL;
}
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...
* From here on, it's all XML-style DTD stuff... Needs no comments.
*/
+static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
+ void *data, uint32_t expected_id, int once);
+
+static int ebml_parse_elem(MatroskaDemuxContext *matroska,
+ EbmlSyntax *syntax, void *data)
+{
+ uint32_t id = syntax->id;
+ EbmlBin *bin;
+ int res;
+
+ 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);
+ data = (char*)list->elem + list->nb_elem*syntax->list_elem_size;
+ memset(data, 0, syntax->list_elem_size);
+ list->nb_elem++;
+ }
+ bin = data;
+
+ switch (syntax->type) {
+ case EBML_UINT: return ebml_read_uint (matroska, &id, data);
+ case EBML_FLOAT: return ebml_read_float(matroska, &id, data);
+ case EBML_STR:
+ case EBML_UTF8: av_free(*(char **)data);
+ return ebml_read_ascii(matroska, &id, data);
+ case EBML_BIN: av_free(bin->data);
+ bin->pos = url_ftell(matroska->ctx->pb);
+ return ebml_read_binary(matroska, &id, &bin->data,
+ &bin->size);
+ case EBML_NEST: if ((res=ebml_read_master(matroska, &id)) < 0)
+ return res;
+ if (id == MATROSKA_ID_SEGMENT)
+ matroska->segment_start = url_ftell(matroska->ctx->pb);
+ return ebml_parse(matroska, syntax->def.n, data, 0, 0);
+ case EBML_PASS: return ebml_parse(matroska, syntax->def.n, data, 0, 1);
+ case EBML_STOP: *(int *)data = 1; return 1;
+ default: return ebml_read_skip(matroska);
+ }
+}
+
+static int ebml_parse_id(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
+ uint32_t id, void *data)
+{
+ int i;
+ for (i=0; syntax[i].id; i++)
+ if (id == syntax[i].id)
+ break;
+ if (!syntax[i].id)
+ av_log(matroska->ctx, AV_LOG_INFO, "Unknown entry 0x%X\n", id);
+ return ebml_parse_elem(matroska, &syntax[i], data);
+}
+
+static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
+ void *data, uint32_t expected_id, int once)
+{
+ int i, res = 0;
+ uint32_t id = 0;
+
+ for (i=0; syntax[i].id; i++)
+ switch (syntax[i].type) {
+ case EBML_UINT:
+ *(uint64_t *)((char *)data+syntax[i].data_offset) = syntax[i].def.u;
+ break;
+ case EBML_FLOAT:
+ *(double *)((char *)data+syntax[i].data_offset) = syntax[i].def.f;
+ break;
+ case EBML_STR:
+ case EBML_UTF8:
+ *(char **)((char *)data+syntax[i].data_offset) = av_strdup(syntax[i].def.s);
+ break;
+ }
+
+ if (expected_id) {
+ res = ebml_read_master(matroska, &id);
+ if (id != expected_id)
+ return AVERROR_INVALIDDATA;
+ if (id == MATROSKA_ID_SEGMENT)
+ matroska->segment_start = url_ftell(matroska->ctx->pb);
+ }
+
+ while (!res) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+
+ res = ebml_parse_id(matroska, syntax, id, data);
+ if (once)
+ break;
+
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static void ebml_free(EbmlSyntax *syntax, void *data)
+{
+ int i, j;
+ for (i=0; syntax[i].id; i++) {
+ void *data_off = (char *)data + syntax[i].data_offset;
+ switch (syntax[i].type) {
+ case EBML_STR:
+ case EBML_UTF8: av_freep(data_off); break;
+ case EBML_BIN: av_freep(&((EbmlBin *)data_off)->data); break;
+ case EBML_NEST:
+ if (syntax[i].list_elem_size) {
+ EbmlList *list = data_off;
+ char *ptr = list->elem;
+ for (j=0; j<list->nb_elem; j++, ptr+=syntax[i].list_elem_size)
+ ebml_free(syntax[i].def.n, ptr);
+ av_free(list->elem);
+ } else
+ ebml_free(syntax[i].def.n, data_off);
+ default: break;
+ }
+ }
+}
+
static int
matroska_parse_info (MatroskaDemuxContext *matroska)
{
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
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))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up > 0) {
matroska->level_up--;
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;
}
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up > 0) {
matroska->level_up--;
if ((res = ebml_read_uint (matroska, &id,
&num)) < 0)
break;
- track->default_duration = num/matroska->time_scale;
+ track->default_duration = num;
break;
}
if ((res = ebml_read_float(matroska, &id,
&num)) < 0)
break;
- track->default_duration = 1000000000/(matroska->time_scale*num);
+ if (!track->default_duration)
+ track->default_duration = 1000000000/num;
break;
}
if ((res = ebml_read_uint(matroska, &id,
&num)) < 0)
break;
- if (num)
- track->flags |=
- MATROSKA_VIDEOTRACK_INTERLACED;
- else
- track->flags &=
- ~MATROSKA_VIDEOTRACK_INTERLACED;
- 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;
}
- /* 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)
"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;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up > 0) {
matroska->level_up--;
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;
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- if (num)
- track->flags |= MATROSKA_TRACK_ENABLED;
- else
- track->flags &= ~MATROSKA_TRACK_ENABLED;
break;
}
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- if (num)
- track->flags |= MATROSKA_TRACK_DEFAULT;
- else
- track->flags &= ~MATROSKA_TRACK_DEFAULT;
+ track->flag_default = num;
break;
}
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- if (num)
- track->flags |= MATROSKA_TRACK_LACING;
- else
- track->flags &= ~MATROSKA_TRACK_LACING;
break;
}
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- track->default_duration = num / matroska->time_scale;
+ track->default_duration = num;
break;
}
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown track header entry 0x%x - ignoring\n", id);
- /* pass-through */
+ case MATROSKA_ID_TRACKCONTENTENCODINGS: {
+ if ((res = ebml_read_master(matroska, &id)) < 0)
+ break;
- case EBML_ID_VOID:
- /* we ignore these because they're nothing useful. */
- case MATROSKA_ID_CODECINFOURL:
- case MATROSKA_ID_CODECDOWNLOADURL:
- case MATROSKA_ID_TRACKMINCACHE:
- case MATROSKA_ID_TRACKMAXCACHE:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
-
- return res;
-}
-
-static int
-matroska_parse_tracks (MatroskaDemuxContext *matroska)
-{
- int res = 0;
- uint32_t id;
-
- av_log(matroska->ctx, AV_LOG_DEBUG, "parsing tracks...\n");
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- /* one track within the "all-tracks" header */
- case MATROSKA_ID_TRACKENTRY:
- res = matroska_add_stream(matroska);
- break;
-
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in track header\n", id);
- /* fall-through */
-
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
-
- return res;
-}
-
-static int
-matroska_parse_index (MatroskaDemuxContext *matroska)
-{
- int res = 0;
- uint32_t id;
- MatroskaDemuxIndex idx;
-
- av_log(matroska->ctx, AV_LOG_DEBUG, "parsing index...\n");
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- /* one single index entry ('point') */
- case MATROSKA_ID_POINTENTRY:
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
-
- /* in the end, we hope to fill one entry with a
- * timestamp, a file position and a tracknum */
- idx.pos = (uint64_t) -1;
- idx.time = (uint64_t) -1;
- idx.track = (uint16_t) -1;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- 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) {
- /* one single index entry ('point') */
- case MATROSKA_ID_CUETIME: {
- uint64_t time;
- if ((res = ebml_read_uint(matroska, &id,
- &time)) < 0)
- break;
- idx.time = time * matroska->time_scale;
- break;
- }
-
- /* position in the file + track to which it
- * belongs */
- case MATROSKA_ID_CUETRACKPOSITION:
+ 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_IO;
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
break;
- } else if (matroska->level_up) {
+ } else if (matroska->level_up > 0) {
matroska->level_up--;
break;
}
switch (id) {
- /* track number */
- case MATROSKA_ID_CUETRACK: {
+ case MATROSKA_ID_ENCODINGSCOPE: {
uint64_t num;
- if ((res = ebml_read_uint(matroska,
- &id, &num)) < 0)
+ if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- idx.track = num;
+ encoding_scope = num;
break;
}
- /* position in file */
- case MATROSKA_ID_CUECLUSTERPOSITION: {
+ case MATROSKA_ID_ENCODINGTYPE: {
uint64_t num;
- if ((res = ebml_read_uint(matroska,
- &id, &num)) < 0)
+ if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- idx.pos = num+matroska->segment_start;
+ 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 entry 0x%x in "
- "CuesTrackPositions\n", id);
- /* fall-through */
+ "Unknown content encoding header entry "
+ "0x%x - ignoring\n", id);
+ /* pass-through */
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
}
}
+ track->encoding_scope = encoding_scope;
break;
+ }
default:
av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in cuespoint "
- "index\n", id);
- /* fall-through */
+ "Unknown content encodings header entry "
+ "0x%x - ignoring\n", id);
+ /* pass-through */
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
}
}
+ break;
+ }
- /* so let's see if we got what we wanted */
- if (idx.pos != (uint64_t) -1 &&
- idx.time != (uint64_t) -1 &&
- idx.track != (uint16_t) -1) {
- if (matroska->num_indexes % 32 == 0) {
- /* re-allocate bigger index */
- matroska->index =
- av_realloc(matroska->index,
- (matroska->num_indexes + 32) *
- sizeof(MatroskaDemuxIndex));
- }
- matroska->index[matroska->num_indexes] = idx;
- matroska->num_indexes++;
- }
+ 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 entry 0x%x in cues header\n", id);
- /* fall-through */
+ "Unknown track header entry 0x%x - ignoring\n", id);
+ /* pass-through */
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:
+ case MATROSKA_ID_TRACKMAXCACHE:
res = ebml_read_skip(matroska);
break;
}
}
}
+ 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;
}
static int
-matroska_parse_metadata (MatroskaDemuxContext *matroska)
+matroska_parse_tracks (MatroskaDemuxContext *matroska)
{
int res = 0;
uint32_t id;
+ av_log(matroska->ctx, AV_LOG_DEBUG, "parsing tracks...\n");
+
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
}
switch (id) {
- /* Hm, this is unsupported... */
+ /* one track within the "all-tracks" header */
+ case MATROSKA_ID_TRACKENTRY:
+ res = matroska_add_stream(matroska);
+ break;
+
default:
av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in metadata header\n", id);
+ "Unknown entry 0x%x in track header\n", id);
/* fall-through */
case EBML_ID_VOID:
return res;
}
+static int
+matroska_parse_index (MatroskaDemuxContext *matroska)
+{
+ return ebml_parse(matroska, matroska_index, matroska, MATROSKA_ID_CUES, 0);
+}
+
+static int
+matroska_parse_metadata (MatroskaDemuxContext *matroska)
+{
+ return ebml_parse(matroska, matroska_tags, matroska, MATROSKA_ID_TAGS, 0);
+}
+
static int
matroska_parse_seekhead (MatroskaDemuxContext *matroska)
{
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
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;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
/* 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. */
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,
}
/* read master + parse */
- if ((res = ebml_read_master(matroska, &id)) < 0)
- goto finish;
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;
}
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)
return res;
}
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+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 MATROSKA_ID_FILEUID:
+ 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;
+}
+
+static int
+matroska_parse_chapters(AVFormatContext *s)
+{
+ MatroskaDemuxContext *matroska = s->priv_data;
+ EbmlList *chapters_list = &matroska->chapters;
+ MatroskaChapter *chapters;
+ int i, res;
+
+ res = ebml_parse(matroska, matroska_chapters, matroska, MATROSKA_ID_CHAPTERS, 0);
+
+ chapters = chapters_list->elem;
+ for (i=0; i<chapters_list->nb_elem; i++)
+ if (chapters[i].start != AV_NOPTS_VALUE && chapters[i].uid)
+ ff_new_chapter(s, chapters[i].uid, (AVRational){1, 1000000000},
+ chapters[i].start, chapters[i].end,
+ chapters[i].title);
+
+ return res;
+}
static int
matroska_aac_profile (char *codec_id)
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;
}
AVFormatParameters *ap)
{
MatroskaDemuxContext *matroska = s->priv_data;
- char *doctype;
- int version, last_level, res = 0;
+ EbmlList *index_list;
+ MatroskaIndex *index;
+ int i, j, last_level, res = 0;
+ Ebml ebml = { 0 };
uint32_t id;
matroska->ctx = s;
/* First read the EBML header. */
- doctype = NULL;
- if ((res = ebml_read_header(matroska, &doctype, &version)) < 0)
- return res;
- if ((doctype == NULL) || strcmp(doctype, "matroska")) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "Wrong EBML doctype ('%s' != 'matroska').\n",
- doctype ? doctype : "(none)");
- if (doctype)
- av_free(doctype);
- return AVERROR_NOFMT;
- }
- av_free(doctype);
- if (version > 2) {
+ if (ebml_parse(matroska, ebml_syntax, &ebml, 0, 1)
+ || ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t)
+ || ebml.id_length > sizeof(uint32_t) || strcmp(ebml.doctype, "matroska")
+ || ebml.doctype_version > 2) {
av_log(matroska->ctx, AV_LOG_ERROR,
- "Matroska demuxer version 2 too old for file version %d\n",
- version);
+ "EBML header using unsupported features\n"
+ "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
+ ebml.version, ebml.doctype, ebml.doctype_version);
return AVERROR_NOFMT;
}
+ ebml_free(ebml_syntax, &ebml);
/* The next thing is a segment. */
while (1) {
if (!(id = ebml_peek_id(matroska, &last_level)))
- return AVERROR_IO;
+ return AVERROR(EIO);
if (id == MATROSKA_ID_SEGMENT)
break;
* 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 */
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
/* stream index */
case MATROSKA_ID_CUES: {
if (!matroska->index_parsed) {
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
res = matroska_parse_index(matroska);
} else
res = ebml_read_skip(matroska);
/* metadata */
case MATROSKA_ID_TAGS: {
if (!matroska->metadata_parsed) {
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
res = matroska_parse_metadata(matroska);
} else
res = ebml_read_skip(matroska);
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. */
break;
}
+ case MATROSKA_ID_CHAPTERS: {
+ res = matroska_parse_chapters(s);
+ break;
+ }
+
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown matroska file header ID 0x%x\n", id);
int extradata_size = 0;
int extradata_offset = 0;
track = matroska->tracks[i];
- track->stream_index = -1;
/* Apply some sanity checks. */
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;
}
}
+ st = track->stream = av_new_stream(s, matroska->num_streams++);
+ if (st == NULL)
+ return AVERROR(ENOMEM);
+
/* Set the FourCC from the CodecID. */
/* This is the MS compatibility mode which stores a
* BITMAPINFOHEADER in the CodecPrivate. */
}
+ if (!strcmp(track->codec_id, "V_QUICKTIME") &&
+ (track->codec_priv_size >= 86) &&
+ (track->codec_priv != NULL)) {
+ MatroskaVideoTrack *vtrack = (MatroskaVideoTrack *) track;
+
+ vtrack->fourcc = AV_RL32(track->codec_priv);
+ codec_id = codec_get_id(codec_movvideo_tags, vtrack->fourcc);
+ }
+
else if (codec_id == CODEC_ID_AAC && !track->codec_priv_size) {
MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *) track;
int profile = matroska_aac_profile(track->codec_id);
} else {
extradata_size = 2;
}
- track->default_duration = 1024*1000 / audiotrack->internal_samplerate;
}
else if (codec_id == CODEC_ID_TTA) {
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);
codec_id == CODEC_ID_RV30 || codec_id == CODEC_ID_RV40) {
extradata_offset = 26;
track->codec_priv_size -= extradata_offset;
- track->flags |= MATROSKA_TRACK_REAL_V;
}
else if (codec_id == CODEC_ID_RA_144) {
}
}
- 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",
track->codec_id);
}
- track->stream_index = matroska->num_streams;
-
- matroska->num_streams++;
- 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;
if (strcmp(track->language, "und"))
- strcpy(st->language, track->language);
+ av_strlcpy(st->language, track->language, 4);
+
+ if (track->flag_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, 1000, 30000);
+ track->default_duration, 1000000000, 30000);
if(extradata){
st->codec->extradata = extradata;
res = 0;
}
- if (matroska->index_parsed) {
- int i, track, stream;
- for (i=0; i<matroska->num_indexes; i++) {
- MatroskaDemuxIndex *idx = &matroska->index[i];
- track = matroska_find_track_by_num(matroska, idx->track);
- stream = matroska->tracks[track]->stream_index;
- if (stream >= 0)
- av_add_index_entry(matroska->ctx->streams[stream],
- idx->pos, idx->time/matroska->time_scale,
+ index_list = &matroska->index;
+ index = index_list->elem;
+ 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*matroska->time_scale/AV_TIME_BASE,
0, 0, AVINDEX_KEYFRAME);
}
}
return res;
}
-static inline int
-rv_offset(uint8_t *data, int slice, int slices)
-{
- return AV_RL32(data+8*slice+4) + 8*slices;
-}
-
static int
matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size,
int64_t pos, uint64_t cluster_time, uint64_t duration,
- int is_keyframe, int is_bframe)
+ int is_keyframe)
{
+ MatroskaTrack *track;
int res = 0;
- int track;
AVStream *st;
AVPacket *pkt;
uint8_t *origdata = data;
/* fetch track from num */
track = matroska_find_track_by_num(matroska, num);
- if (size <= 3 || track < 0 || track >= matroska->num_tracks) {
+ if (size <= 3 || !track || !track->stream) {
av_log(matroska->ctx, AV_LOG_INFO,
- "Invalid stream %d or size %u\n", track, size);
+ "Invalid stream %"PRIu64" or size %u\n", num, size);
av_free(origdata);
return res;
}
- if (matroska->tracks[track]->stream_index < 0)
- return res;
- st = matroska->ctx->streams[matroska->tracks[track]->stream_index];
+ st = track->stream;
if (st->discard >= AVDISCARD_ALL) {
av_free(origdata);
return res;
}
if (duration == AV_NOPTS_VALUE)
- duration = matroska->tracks[track]->default_duration;
+ duration = track->default_duration / matroska->time_scale;
/* block_time (relative to cluster time) */
block_time = AV_RB16(data);
flags = *data++;
size -= 3;
if (is_keyframe == -1)
- is_keyframe = flags & 1 ? PKT_FLAG_KEY : 0;
+ 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;
}
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;
}
if (res == 0) {
- int real_v = matroska->tracks[track]->flags & MATROSKA_TRACK_REAL_V;
uint64_t timecode = AV_NOPTS_VALUE;
- if (cluster_time != (uint64_t)-1 && cluster_time + block_time >= 0)
+ if (cluster_time != (uint64_t)-1
+ && (block_time >= 0 || cluster_time >= -block_time))
timecode = cluster_time + block_time;
for (n = 0; n < laces; n++) {
- int slice, slices = 1;
-
- if (real_v) {
- slices = *data++ + 1;
- lace_size[n]--;
- }
-
- for (slice=0; slice<slices; slice++) {
- int slice_size, slice_offset = 0;
- if (real_v)
- slice_offset = rv_offset(data, slice, slices);
- if (slice+1 == slices)
- slice_size = lace_size[n] - slice_offset;
- else
- slice_size = rv_offset(data, slice+1, slices) - slice_offset;
-
- if (st->codec->codec_id == CODEC_ID_RA_288 ||
- st->codec->codec_id == CODEC_ID_COOK ||
- st->codec->codec_id == CODEC_ID_ATRAC3) {
- MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)matroska->tracks[track];
- int a = st->codec->block_align;
- int sps = audiotrack->sub_packet_size;
- int cfs = audiotrack->coded_framesize;
- int h = audiotrack->sub_packet_h;
- int y = audiotrack->sub_packet_cnt;
- int w = audiotrack->frame_size;
- int x;
-
- if (!audiotrack->pkt_cnt) {
- if (st->codec->codec_id == CODEC_ID_RA_288)
- for (x=0; x<h/2; x++)
- memcpy(audiotrack->buf+x*2*w+y*cfs,
- data+x*cfs, cfs);
- else
- for (x=0; x<w/sps; x++)
- memcpy(audiotrack->buf+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), data+x*sps, sps);
-
- if (++audiotrack->sub_packet_cnt >= h) {
- audiotrack->sub_packet_cnt = 0;
- audiotrack->pkt_cnt = h*w / a;
- }
- }
- while (audiotrack->pkt_cnt) {
- pkt = av_mallocz(sizeof(AVPacket));
- av_new_packet(pkt, a);
- memcpy(pkt->data, audiotrack->buf
- + a * (h*w / a - audiotrack->pkt_cnt--), a);
- pkt->pos = pos;
- pkt->stream_index = matroska->tracks[track]->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[slice_offset+offset]; offset++)
- if (data[slice_offset+offset] == ',')
- i++;
+ if (st->codec->codec_id == CODEC_ID_RA_288 ||
+ st->codec->codec_id == CODEC_ID_COOK ||
+ st->codec->codec_id == CODEC_ID_ATRAC3) {
+ MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track;
+ int a = st->codec->block_align;
+ int sps = audiotrack->sub_packet_size;
+ int cfs = audiotrack->coded_framesize;
+ int h = audiotrack->sub_packet_h;
+ int y = audiotrack->sub_packet_cnt;
+ int w = audiotrack->frame_size;
+ int x;
+
+ if (!audiotrack->pkt_cnt) {
+ if (st->codec->codec_id == CODEC_ID_RA_288)
+ for (x=0; x<h/2; x++)
+ memcpy(audiotrack->buf+x*2*w+y*cfs,
+ data+x*cfs, cfs);
+ else
+ for (x=0; x<w/sps; x++)
+ memcpy(audiotrack->buf+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), data+x*sps, sps);
+
+ if (++audiotrack->sub_packet_cnt >= h) {
+ audiotrack->sub_packet_cnt = 0;
+ audiotrack->pkt_cnt = h*w / a;
}
-
+ }
+ while (audiotrack->pkt_cnt) {
pkt = av_mallocz(sizeof(AVPacket));
- /* XXX: prevent data copy... */
- if (av_new_packet(pkt, slice_size-offset) < 0) {
- res = AVERROR(ENOMEM);
- n = laces-1;
- break;
- }
- memcpy (pkt->data, data+slice_offset+offset, slice_size-offset);
-
- if (n == 0)
- pkt->flags = is_keyframe;
- pkt->stream_index = matroska->tracks[track]->stream_index;
-
- pkt->pts = timecode;
+ av_new_packet(pkt, a);
+ memcpy(pkt->data, audiotrack->buf
+ + a * (h*w / a - audiotrack->pkt_cnt--), a);
pkt->pos = pos;
- pkt->duration = duration;
-
+ pkt->stream_index = st->index;
matroska_queue_packet(matroska, pkt);
}
+ } else {
+ int offset = 0, pkt_size = lace_size[n];
+ uint8_t *pkt_data = data;
+
+ if (track->encoding_scope & 1) {
+ offset = matroska_decode_buffer(&pkt_data, &pkt_size,
+ 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;
+ }
+ if (offset)
+ memcpy (pkt->data, track->encoding_settings, offset);
+ memcpy (pkt->data+offset, pkt_data, pkt_size);
- if (timecode != AV_NOPTS_VALUE)
- timecode = duration ? timecode + duration : AV_NOPTS_VALUE;
+ if (pkt_data != data)
+ av_free(pkt_data);
+
+ if (n == 0)
+ pkt->flags = is_keyframe;
+ pkt->stream_index = st->index;
+
+ pkt->pts = timecode;
+ pkt->pos = pos;
+ pkt->duration = duration;
+
+ matroska_queue_packet(matroska, pkt);
}
+
+ if (timecode != AV_NOPTS_VALUE)
+ timecode = duration ? timecode + duration : AV_NOPTS_VALUE;
data += lace_size[n];
}
}
{
int res = 0;
uint32_t id;
- int is_bframe = 0;
int is_keyframe = PKT_FLAG_KEY, last_num_packets = matroska->num_packets;
uint64_t duration = AV_NOPTS_VALUE;
uint8_t *data;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
* 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;
}
matroska->packets[last_num_packets]->flags = 0;
if ((res = ebml_read_sint(matroska, &id, &num)) < 0)
break;
- if (num > 0)
- is_bframe = 1;
break;
}
if (size > 0)
res = matroska_parse_block(matroska, data, size, pos, cluster_time,
- duration, is_keyframe, is_bframe);
+ duration, is_keyframe);
return res;
}
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))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
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,
cluster_time, AV_NOPTS_VALUE,
- -1, 0);
+ -1);
break;
default:
/* Have we already reached the end? */
if (matroska->done)
- return AVERROR_IO;
+ return AVERROR(EIO);
res = 0;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- return AVERROR_IO;
+ return AVERROR(EIO);
} else if (matroska->level_up) {
matroska->level_up--;
break;
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;
+ 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);
-
- 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];
av_free(track->codec_id);
- av_free(track->codec_name);
av_free(track->codec_priv);
av_free(track->name);
av_free(track);
}
+ ebml_free(matroska_index, matroska);
return 0;
}
AVInputFormat matroska_demuxer = {
"matroska",
- "Matroska file format",
+ NULL_IF_CONFIG_SMALL("Matroska file format"),
sizeof(MatroskaDemuxContext),
matroska_probe,
matroska_read_header,