From 08abe0fd66a03b50238654c93a172a059d09ac18 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Thu, 11 Mar 2004 16:26:42 +0000 Subject: [PATCH] matroska demuxer by ("Ronald S. Bultje" ) Originally committed as revision 2872 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/Makefile | 2 +- libavformat/allformats.c | 1 + libavformat/avformat.h | 3 + libavformat/matroska.c | 2689 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 2694 insertions(+), 1 deletion(-) create mode 100644 libavformat/matroska.c diff --git a/libavformat/Makefile b/libavformat/Makefile index a20a7eb764..0b8161b047 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -16,7 +16,7 @@ OBJS+=mpeg.o mpegts.o mpegtsenc.o ffm.o crc.o img.o raw.o rm.o \ avienc.o avidec.o wav.o swf.o au.o gif.o mov.o mpjpeg.o dv.o \ yuv4mpeg.o 4xm.o flvenc.o flvdec.o movenc.o psxstr.o idroq.o ipmovie.o \ nut.o wc3movie.o mp3.o westwood.o segafilm.o idcin.o flic.o \ - sierravmd.o + sierravmd.o matroska.o ifeq ($(CONFIG_RISKY),yes) OBJS+= asf.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 156f9783b7..b0b0e64372 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -96,6 +96,7 @@ void av_register_all(void) #endif nut_init(); + matroska_init(); #ifdef CONFIG_ENCODERS /* image formats */ diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 58109627f9..5d87ac7e22 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -483,6 +483,9 @@ int flic_init(void); /* sierravmd.c */ int vmd_init(void); +/* matroska.c */ +int matroska_init(void); + #include "rtp.h" #include "rtsp.h" diff --git a/libavformat/matroska.c b/libavformat/matroska.c new file mode 100644 index 0000000000..7fcc3c91f9 --- /dev/null +++ b/libavformat/matroska.c @@ -0,0 +1,2689 @@ +/* + * Matroska file demuxer (no muxer yet) + * Copyright (c) 2003-2004 The ffmpeg Project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file matroska.c + * Matroska file demuxer + * by Ronald Bultje + * with a little help from Moritz Bunkus + * Specs available on the matroska project page: + * http://www.matroska.org/. + */ + +#include "avformat.h" +/* For codec_get_bmp_id and codec_get_wav_id. */ +#include "avi.h" + +/* EBML version supported */ +#define EBML_VERSION 1 + +/* top-level master-IDs */ +#define EBML_ID_HEADER 0x1A45DFA3 + +/* IDs in the HEADER master */ +#define EBML_ID_EBMLVERSION 0x4286 +#define EBML_ID_EBMLREADVERSION 0x42F7 +#define EBML_ID_EBMLMAXIDLENGTH 0x42F2 +#define EBML_ID_EBMLMAXSIZELENGTH 0x42F3 +#define EBML_ID_DOCTYPE 0x4282 +#define EBML_ID_DOCTYPEVERSION 0x4287 +#define EBML_ID_DOCTYPEREADVERSION 0x4285 + +/* general EBML types */ +#define EBML_ID_VOID 0xEC + +/* + * Matroska element IDs. max. 32-bit. + */ + +/* toplevel segment */ +#define MATROSKA_ID_SEGMENT 0x18538067 + +/* matroska top-level master IDs */ +#define MATROSKA_ID_INFO 0x1549A966 +#define MATROSKA_ID_TRACKS 0x1654AE6B +#define MATROSKA_ID_CUES 0x1C53BB6B +#define MATROSKA_ID_TAGS 0x1254C367 +#define MATROSKA_ID_SEEKHEAD 0x114D9B74 +#define MATROSKA_ID_CLUSTER 0x1F43B675 + +/* IDs in the info master */ +#define MATROSKA_ID_TIMECODESCALE 0x2AD7B1 +#define MATROSKA_ID_DURATION 0x4489 +#define MATROSKA_ID_WRITINGAPP 0x5741 +#define MATROSKA_ID_MUXINGAPP 0x4D80 +#define MATROSKA_ID_DATEUTC 0x4461 + +/* ID in the tracks master */ +#define MATROSKA_ID_TRACKENTRY 0xAE + +/* IDs in the trackentry master */ +#define MATROSKA_ID_TRACKNUMBER 0xD7 +#define MATROSKA_ID_TRACKUID 0x73C5 +#define MATROSKA_ID_TRACKTYPE 0x83 +#define MATROSKA_ID_TRACKAUDIO 0xE1 +#define MATROSKA_ID_TRACKVIDEO 0xE0 +#define MATROSKA_ID_CODECID 0x86 +#define MATROSKA_ID_CODECPRIVATE 0x63A2 +#define MATROSKA_ID_CODECNAME 0x258688 +#define MATROSKA_ID_CODECINFOURL 0x3B4040 +#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240 +#define MATROSKA_ID_TRACKNAME 0x536E +#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C +#define MATROSKA_ID_TRACKFLAGENABLED 0xB9 +#define MATROSKA_ID_TRACKFLAGDEFAULT 0x88 +#define MATROSKA_ID_TRACKFLAGLACING 0x9C +#define MATROSKA_ID_TRACKMINCACHE 0x6DE7 +#define MATROSKA_ID_TRACKMAXCACHE 0x6DF8 +#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383 + +/* IDs in the trackvideo master */ +#define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3 +#define MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0 +#define MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA +#define MATROSKA_ID_VIDEOPIXELWIDTH 0xB0 +#define MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA +#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A +#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B9 +#define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3 +#define MATROSKA_ID_VIDEOCOLOURSPACE 0x2EB524 + +/* IDs in the trackaudio master */ +#define MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5 +#define MATROSKA_ID_AUDIOBITDEPTH 0x6264 +#define MATROSKA_ID_AUDIOCHANNELS 0x9F + +/* ID in the cues master */ +#define MATROSKA_ID_POINTENTRY 0xBB + +/* IDs in the pointentry master */ +#define MATROSKA_ID_CUETIME 0xB3 +#define MATROSKA_ID_CUETRACKPOSITION 0xB7 + +/* IDs in the cuetrackposition master */ +#define MATROSKA_ID_CUETRACK 0xF7 +#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1 + +/* IDs in the tags master */ +/* TODO */ + +/* IDs in the seekhead master */ +#define MATROSKA_ID_SEEKENTRY 0x4DBB + +/* IDs in the seekpoint master */ +#define MATROSKA_ID_SEEKID 0x53AB +#define MATROSKA_ID_SEEKPOSITION 0x53AC + +/* IDs in the cluster master */ +#define MATROSKA_ID_CLUSTERTIMECODE 0xE7 +#define MATROSKA_ID_BLOCKGROUP 0xA0 + +/* IDs in the blockgroup master */ +#define MATROSKA_ID_BLOCK 0xA1 +#define MATROSKA_ID_BLOCKDURATION 0x9B +#define MATROSKA_ID_BLOCKREFERENCE 0xFB + +typedef enum { + MATROSKA_TRACK_TYPE_VIDEO = 0x1, + MATROSKA_TRACK_TYPE_AUDIO = 0x2, + MATROSKA_TRACK_TYPE_COMPLEX = 0x3, + MATROSKA_TRACK_TYPE_LOGO = 0x10, + MATROSKA_TRACK_TYPE_SUBTITLE = 0x11, + MATROSKA_TRACK_TYPE_CONTROL = 0x20, +} MatroskaTrackType; + +typedef enum { + MATROSKA_EYE_MODE_MONO = 0x0, + MATROSKA_EYE_MODE_RIGHT = 0x1, + MATROSKA_EYE_MODE_LEFT = 0x2, + MATROSKA_EYE_MODE_BOTH = 0x3, +} MatroskaEyeMode; + +typedef enum { + MATROSKA_ASPECT_RATIO_MODE_FREE = 0x0, + MATROSKA_ASPECT_RATIO_MODE_KEEP = 0x1, + MATROSKA_ASPECT_RATIO_MODE_FIXED = 0x2, +} MatroskaAspectRatioMode; + +/* + * These aren't in any way "matroska-form" things, + * it's just something I use in the muxer/demuxer. + */ + +typedef enum { + MATROSKA_TRACK_ENABLED = (1<<0), + MATROSKA_TRACK_DEFAULT = (1<<1), + MATROSKA_TRACK_LACING = (1<<2), + MATROSKA_TRACK_SHIFT = (1<<16) +} MatroskaTrackFlags; + +typedef enum { + MATROSKA_VIDEOTRACK_INTERLACED = (MATROSKA_TRACK_SHIFT<<0) +} MatroskaVideoTrackFlags; + +/* + * Matroska Codec IDs. Strings. + */ + +#define MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC "V_MS/VFW/FOURCC" +#define MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED "V_UNCOMPRESSED" +#define MATROSKA_CODEC_ID_VIDEO_MPEG4_SP "V_MPEG4/ISO/SP" +#define MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP "V_MPEG4/ISO/ASP" +#define MATROSKA_CODEC_ID_VIDEO_MPEG4_AP "V_MPEG4/ISO/AP" +#define MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3 "V_MPEG4/MS/V3" +#define MATROSKA_CODEC_ID_VIDEO_MPEG1 "V_MPEG1" +#define MATROSKA_CODEC_ID_VIDEO_MPEG2 "V_MPEG2" +#define MATROSKA_CODEC_ID_VIDEO_MJPEG "V_MJPEG" +/* TODO: Real/Quicktime */ + +#define MATROSKA_CODEC_ID_AUDIO_ACM "A_MS/ACM" +#define MATROSKA_CODEC_ID_AUDIO_MPEG1_L1 "A_MPEG/L1" +#define MATROSKA_CODEC_ID_AUDIO_MPEG1_L2 "A_MPEG/L2" +#define MATROSKA_CODEC_ID_AUDIO_MPEG1_L3 "A_MPEG/L3" +#define MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE "A_PCM/INT/BIG" +#define MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE "A_PCM/INT/LIT" +#define MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT "A_PCM/FLOAT/IEEE" +#define MATROSKA_CODEC_ID_AUDIO_AC3 "A_AC3" +#define MATROSKA_CODEC_ID_AUDIO_DTS "A_DTS" +#define MATROSKA_CODEC_ID_AUDIO_VORBIS "A_VORBIS" +#define MATROSKA_CODEC_ID_AUDIO_ACM "A_MS/ACM" +#define MATROSKA_CODEC_ID_AUDIO_MPEG2 "A_AAC/MPEG2/" +#define MATROSKA_CODEC_ID_AUDIO_MPEG4 "A_AAC/MPEG4/" +/* TODO: AC3-9/10 (?), Real, Musepack, Quicktime */ + +/* max. depth in the EBML tree structure */ +#define EBML_MAX_DEPTH 16 + +typedef struct Track { + MatroskaTrackType type; + + /* Unique track number and track ID. stream_index is the index that + * the calling app uses for this track. */ + uint32_t num, + uid, + stream_index; + + char *name, + *language; + + char *codec_id, + *codec_name; + + unsigned char *codec_priv; + int codec_priv_size; + + int64_t default_duration; + MatroskaTrackFlags flags; +} MatroskaTrack; + +typedef struct MatroskaVideoTrack { + MatroskaTrack track; + + int pixel_width, + pixel_height, + display_width, + display_height; + + uint32_t fourcc; + + MatroskaAspectRatioMode ar_mode; + MatroskaEyeMode eye_mode; + + //.. +} MatroskaVideoTrack; + +typedef struct MatroskaAudioTrack { + MatroskaTrack track; + + int channels, + bitdepth, + samplerate; + //.. +} MatroskaAudioTrack; + +typedef struct MatroskaSubtitleTrack { + MatroskaTrack track; + + //.. +} MatroskaSubtitleTrack; + +typedef struct MatroskaLevel { + uint64_t start, 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; + + /* ebml stuff */ + int num_levels; + MatroskaLevel levels[EBML_MAX_DEPTH]; + int level_up; + + /* matroska stuff */ + char *writing_app, + *muxing_app; + int64_t created; + + /* timescale in the file */ + int64_t time_scale; + + /* length, position (time, ns) */ + int64_t duration, + pos; + + /* num_streams is the number of streams that av_new_stream() was called + * for ( = that are available to the calling program). */ + int num_tracks, num_streams; + MatroskaTrack *tracks[MAX_STREAMS]; + + /* cache for ID peeking */ + uint32_t peek_id; + + /* byte position of the segment inside the stream */ + offset_t segment_start; + + /* The packet queue. */ + AVPacket **packets; + int num_packets; + + /* have we already parse metadata/cues/clusters? */ + int metadata_parsed, + index_parsed, + done; + + /* The index for seeking. */ + int num_indexes; + MatroskaDemuxIndex *index; +} MatroskaDemuxContext; + +/* + * The first few functions handle EBML file parsing. The rest + * is the document interpretation. Matroska really just is a + * EBML file. + */ + +/* + * Return: the amount of levels in the hierarchy that the + * current element lies higher than the previous one. + * The opposite isn't done - that's auto-done using master + * element reading. + */ + +static int +ebml_read_element_level_up (MatroskaDemuxContext *matroska) +{ + ByteIOContext *pb = &matroska->ctx->pb; + offset_t pos = url_ftell(pb); + int num = 0; + + while (matroska->num_levels > 0) { + MatroskaLevel *level = &matroska->levels[matroska->num_levels - 1]; + + if (pos >= level->start + level->length) { + matroska->num_levels--; + num++; + } else { + break; + } + } + + return num; +} + +/* + * Read: an "EBML number", which is defined as a variable-length + * array of bytes. The first byte indicates the length by giving a + * number of 0-bits followed by a one. The position of the first + * "one" bit inside the first byte indicates the length of this + * number. + * Returns: num. of bytes read. < 0 on error. + */ + +static int +ebml_read_num (MatroskaDemuxContext *matroska, + int max_size, + uint64_t *number) +{ + ByteIOContext *pb = &matroska->ctx->pb; + int len_mask = 0x80, read = 1, n = 1; + int64_t total = 0; + + /* the first byte tells us the length in bytes - get_byte() can normally + * return 0, but since that's not a valid first ebmlID byte, we can + * use it safely here to catch EOS. */ + if (!(total = get_byte(pb))) { + /* we might encounter EOS here */ + if (!url_feof(pb)) { + offset_t pos = url_ftell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Read error at pos. %llu (0x%llx)\n", + pos, pos); + } + return AVERROR_IO; /* EOS or actual I/O error */ + } + + /* get the length of the EBML number */ + while (read <= max_size && !(total & len_mask)) { + read++; + len_mask >>= 1; + } + if (read > max_size) { + offset_t pos = url_ftell(pb) - 1; + av_log(matroska->ctx, AV_LOG_ERROR, + "Invalid EBML number size tag 0x%02x at pos %llu (0x%llx)\n", + (uint8_t) total, pos, pos); + return AVERROR_INVALIDDATA; + } + + /* read out length */ + total &= ~len_mask; + while (n++ < read) + total = (total << 8) | get_byte(pb); + + *number = total; + + return read; +} + +/* + * Read: the element content data ID. + * Return: the number of bytes read or < 0 on error. + */ + +static int +ebml_read_element_id (MatroskaDemuxContext *matroska, + uint32_t *id, + int *level_up) +{ + int read; + uint64_t total; + + /* if we re-call this, use our cached ID */ + if (matroska->peek_id != 0) { + if (level_up) + *level_up = 0; + *id = matroska->peek_id; + return 0; + } + + /* read out the "EBML number", include tag in ID */ + if ((read = ebml_read_num(matroska, 4, &total)) < 0) + return read; + *id = matroska->peek_id = total | (1 << (read * 7)); + + /* level tracking */ + if (level_up) + *level_up = ebml_read_element_level_up(matroska); + + return read; +} + +/* + * Read: element content length. + * Return: the number of bytes read or < 0 on error. + */ + +static int +ebml_read_element_length (MatroskaDemuxContext *matroska, + uint64_t *length) +{ + /* clear cache since we're now beyond that data point */ + matroska->peek_id = 0; + + /* read out the "EBML number", include tag in ID */ + return ebml_read_num(matroska, 8, length); +} + +/* + * Return: the ID of the next element, or 0 on error. + * Level_up contains the amount of levels that this + * next element lies higher than the previous one. + */ + +static uint32_t +ebml_peek_id (MatroskaDemuxContext *matroska, + int *level_up) +{ + uint32_t id; + + assert(level_up != NULL); + + if (ebml_read_element_id(matroska, &id, level_up) < 0) + return 0; + + return id; +} + +/* + * Seek to a given offset. + * 0 is success, -1 is failure. + */ + +static int +ebml_read_seek (MatroskaDemuxContext *matroska, + offset_t offset) +{ + ByteIOContext *pb = &matroska->ctx->pb; + + /* clear ID cache, if any */ + matroska->peek_id = 0; + + return (url_fseek(pb, offset, SEEK_SET) == offset) ? 0 : -1; +} + +/* + * Skip the next element. + * 0 is success, -1 is failure. + */ + +static int +ebml_read_skip (MatroskaDemuxContext *matroska) +{ + ByteIOContext *pb = &matroska->ctx->pb; + uint32_t id; + uint64_t length; + int res; + + if ((res = ebml_read_element_id(matroska, &id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &length)) < 0) + return res; + + url_fskip(pb, length); + + return 0; +} + +/* + * Read the next element as an unsigned int. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_uint (MatroskaDemuxContext *matroska, + uint32_t *id, + uint64_t *num) +{ + ByteIOContext *pb = &matroska->ctx->pb; + int n = 0, size, res; + uint64_t rlength; + + if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &rlength)) < 0) + return res; + size = rlength; + if (size < 1 || size > 8) { + offset_t pos = url_ftell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Invalid uint element size %d at position %lld (0x%llx)\n", + size, pos, pos); + return AVERROR_INVALIDDATA; + } + + /* big-endian ordening; build up number */ + *num = 0; + while (n++ < size) + *num = (*num << 8) | get_byte(pb); + + return 0; +} + +/* + * Read the next element as a signed int. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_sint (MatroskaDemuxContext *matroska, + uint32_t *id, + int64_t *num) +{ + ByteIOContext *pb = &matroska->ctx->pb; + int size, n = 1, negative = 0, res; + uint64_t rlength; + + if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &rlength)) < 0) + return res; + size = rlength; + if (size < 1 || size > 8) { + offset_t pos = url_ftell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Invalid sint element size %d at position %lld (0x%llx)\n", + size, pos, pos); + return AVERROR_INVALIDDATA; + } + if ((*num = get_byte(pb)) & 0x80) { + negative = 1; + *num &= ~0x80; + } + *num = 0; + while (n++ < size) + *num = (*num << 8) | get_byte(pb); + + /* make signed */ + if (negative) + *num = *num - (1LL << ((8 * size) - 1)); + + return 0; +} + +/* + * Read the next element as a float. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_float (MatroskaDemuxContext *matroska, + uint32_t *id, + double *num) +{ + ByteIOContext *pb = &matroska->ctx->pb; + int size, res; + uint64_t rlength; + + if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &rlength)) < 0) + return res; + size = rlength; + + if (size != 4 && size != 8 && size != 10) { + offset_t pos = url_ftell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Invalid float element size %d at position %llu (0x%llx)\n", + size, pos, pos); + return AVERROR_INVALIDDATA; + } + if (size == 10) { + av_log(matroska->ctx, AV_LOG_ERROR, + "FIXME! 10-byte floats unimplemented\n"); + return AVERROR_UNKNOWN; + } + + if (size == 4) { + float f; + +#ifdef WORDS_BIGENDIAN + f = * (float *) data; +#else + while (size-- > 0) + ((uint8_t *) &f)[size] = get_byte(pb); +#endif + + *num = f; + } else { + double d; + +#ifdef WORDS_BIGENDIAN + d = * (double *) data; +#else + while (size-- > 0) + ((uint8_t *) &d)[size] = get_byte(pb); +#endif + + *num = d; + } + + return 0; +} + +/* + * Read the next element as an ASCII string. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_ascii (MatroskaDemuxContext *matroska, + uint32_t *id, + char **str) +{ + ByteIOContext *pb = &matroska->ctx->pb; + int size, res; + uint64_t rlength; + + if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &rlength)) < 0) + return res; + size = rlength; + + /* 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))) { + av_log(matroska->ctx, AV_LOG_ERROR, "Memory allocation failed\n"); + return AVERROR_NOMEM; + } + if (get_buffer(pb, (uint8_t *) *str, size) != size) { + offset_t pos = url_ftell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Read error at pos. %llu (0x%llx)\n", pos, pos); + return AVERROR_IO; + } + (*str)[size] = '\0'; + + return 0; +} + +/* + * Read the next element as a UTF-8 string. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_utf8 (MatroskaDemuxContext *matroska, + uint32_t *id, + char **str) +{ + 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. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_master (MatroskaDemuxContext *matroska, + uint32_t *id) +{ + ByteIOContext *pb = &matroska->ctx->pb; + uint64_t length; + MatroskaLevel *level; + int res; + + if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &length)) < 0) + return res; + + /* protect... (Heaven forbids that the '>' is true) */ + 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; + } + + /* remember level */ + level = &matroska->levels[matroska->num_levels++]; + level->start = url_ftell(pb); + level->length = length; + + return 0; +} + +/* + * Read the next element as binary data. + * 0 is success, < 0 is failure. + */ + +static int +ebml_read_binary (MatroskaDemuxContext *matroska, + uint32_t *id, + uint8_t **binary, + int *size) +{ + ByteIOContext *pb = &matroska->ctx->pb; + uint64_t rlength; + int res; + + if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || + (res = ebml_read_element_length(matroska, &rlength)) < 0) + return res; + *size = rlength; + + if (!(*binary = av_malloc(*size))) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Memory allocation error\n"); + return AVERROR_NOMEM; + } + + if (get_buffer(pb, *binary, *size) != *size) { + offset_t pos = url_ftell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Read error at pos. %llu (0x%llx)\n", pos, pos); + return AVERROR_IO; + } + + return 0; +} + +/* + * Read signed/unsigned "EBML" numbers. + * Return: number of bytes processed, < 0 on error. + * XXX: use ebml_read_num(). + */ + +static int +matroska_ebmlnum_uint (uint8_t *data, + uint32_t size, + uint64_t *num) +{ + int len_mask = 0x80, read = 1, n = 1, num_ffs = 0; + uint64_t total; + + if (size <= 0) + return AVERROR_INVALIDDATA; + + total = data[0]; + while (read <= 8 && !(total & len_mask)) { + read++; + len_mask >>= 1; + } + if (read > 8) + return AVERROR_INVALIDDATA; + + if ((total &= (len_mask - 1)) == len_mask - 1) + num_ffs++; + if (size < read) + return AVERROR_INVALIDDATA; + while (n < read) { + if (data[n] == 0xff) + num_ffs++; + total = (total << 8) | data[n]; + n++; + } + + if (!total) + return AVERROR_INVALIDDATA; + + if (read == num_ffs) + *num = (uint64_t)-1; + else + *num = total; + + return read; +} + +/* + * Same as above, but signed. + */ + +static int +matroska_ebmlnum_sint (uint8_t *data, + uint32_t size, + int64_t *num) +{ + uint64_t unum; + int res; + + /* read as unsigned number first */ + if ((res = matroska_ebmlnum_uint(data, size, &unum)) < 0) + return res; + + /* make signed (weird way) */ + if (unum == (uint64_t)-1) + *num = INT64_MAX; + else + *num = unum - ((1LL << ((7 * res) - 1)) - 1); + + 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 %llu (> %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 %llu (> %d) 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 %llu (> %d) 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; +} + +/* + * Put one packet in an application-supplied AVPacket struct. + * Returns 0 on success or -1 on failure. + */ + +static int +matroska_deliver_packet (MatroskaDemuxContext *matroska, + AVPacket *pkt) +{ + if (matroska->num_packets > 0) { + memcpy(pkt, matroska->packets[0], sizeof(AVPacket)); + av_free(matroska->packets[0]); + if (matroska->num_packets > 1) { + 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 *)); + } else { + av_free(matroska->packets); + matroska->packets = NULL; + } + matroska->num_packets--; + return 0; + } + + return -1; +} + +/* + * Put a packet into our internal queue. Will be delivered to the + * user/application during the next get_packet() call. + */ + +static void +matroska_queue_packet (MatroskaDemuxContext *matroska, + AVPacket *pkt) +{ + matroska->packets = + av_realloc(matroska->packets, (matroska->num_packets + 1) * + sizeof(AVPacket *)); + matroska->packets[matroska->num_packets] = pkt; + matroska->num_packets++; +} + +/* + * Autodetecting... + */ + +static int +matroska_probe (AVProbeData *p) +{ + uint64_t total = 0; + int len_mask = 0x80, size = 1, n = 1; + uint8_t probe_data[] = { 'm', 'a', 't', 'r', 'o', 's', 'k', 'a' }; + + if (p->buf_size < 5) + return 0; + + /* ebml header? */ + if ((p->buf[0] << 24 | p->buf[1] << 16 | + p->buf[2] << 8 | p->buf[3]) != EBML_ID_HEADER) + return 0; + + /* length of header */ + total = p->buf[4]; + while (size <= 8 && !(total & len_mask)) { + size++; + len_mask >>= 1; + } + if (size > 8) + return 0; + total &= (len_mask - 1); + while (n < size) + total = (total << 8) | p->buf[4 + n++]; + + /* does the probe data contain the whole header? */ + if (p->buf_size < 4 + size + total) + return 0; + + /* the header must contain the document type 'matroska'. For now, + * we don't parse the whole header but simply check for the + * availability of that array of characters inside the header. + * Not fully fool-proof, but good enough. */ + for (n = 4 + size; n < 4 + size + total - sizeof(probe_data); n++) + if (!memcmp (&p->buf[n], probe_data, sizeof(probe_data))) + return AVPROBE_SCORE_MAX; + + return 0; +} + +/* + * From here on, it's all XML-style DTD stuff... Needs no comments. + */ + +static int +matroska_parse_info (MatroskaDemuxContext *matroska) +{ + int res = 0; + uint32_t id; + + av_log(matroska->ctx, AV_LOG_DEBUG, "Parsing info...\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) { + /* cluster timecode */ + case MATROSKA_ID_TIMECODESCALE: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + matroska->time_scale = num; + break; + } + + case MATROSKA_ID_DURATION: { + double num; + if ((res = ebml_read_float(matroska, &id, &num)) < 0) + break; + matroska->duration = num * matroska->time_scale; + 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 EBML_ID_VOID: + res = ebml_read_skip(matroska); + break; + } + + if (matroska->level_up) { + matroska->level_up--; + break; + } + } + + return res; +} + +static int +matroska_add_stream (MatroskaDemuxContext *matroska) +{ + int res = 0; + uint32_t id; + MatroskaTrack *track; + + 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. */ + track = av_mallocz(sizeof(MatroskaTrack)); + matroska->num_tracks++; + + /* 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; + break; + } else if (matroska->level_up > 0) { + matroska->level_up--; + break; + } + + switch (id) { + /* track number (unique stream ID) */ + case MATROSKA_ID_TRACKNUMBER: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + track->num = num; + break; + } + + /* track UID (unique identifier) */ + case MATROSKA_ID_TRACKUID: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + track->uid = num; + break; + } + + /* track type (video, audio, combined, subtitle, etc.) */ + case MATROSKA_ID_TRACKTYPE: { + uint64_t num; + if (track->type != 0) { + av_log(matroska->ctx, AV_LOG_INFO, + "More than one tracktype in an entry - skip\n"); + break; + } + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + track->type = num; + + /* ok, so we're actually going to reallocate this thing */ + switch (track->type) { + case MATROSKA_TRACK_TYPE_VIDEO: + track = (MatroskaTrack *) + av_realloc(track, sizeof(MatroskaVideoTrack)); + break; + case MATROSKA_TRACK_TYPE_AUDIO: + track = (MatroskaTrack *) + av_realloc(track, sizeof(MatroskaAudioTrack)); + ((MatroskaAudioTrack *)track)->channels = 1; + ((MatroskaAudioTrack *)track)->samplerate = 8000; + break; + case MATROSKA_TRACK_TYPE_SUBTITLE: + track = (MatroskaTrack *) + av_realloc(track, sizeof(MatroskaSubtitleTrack)); + break; + case MATROSKA_TRACK_TYPE_COMPLEX: + case MATROSKA_TRACK_TYPE_LOGO: + case MATROSKA_TRACK_TYPE_CONTROL: + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown or unsupported track type 0x%x\n", + track->type); + track->type = 0; + break; + } + matroska->tracks[matroska->num_tracks - 1] = track; + break; + } + + /* tracktype specific stuff for video */ + case MATROSKA_ID_TRACKVIDEO: { + MatroskaVideoTrack *videotrack; + if (track->type != MATROSKA_TRACK_TYPE_VIDEO) { + av_log(matroska->ctx, AV_LOG_INFO, + "video data in non-video track - ignoring\n"); + res = AVERROR_INVALIDDATA; + break; + } else if ((res = ebml_read_master(matroska, &id)) < 0) + break; + videotrack = (MatroskaVideoTrack *)track; + + while (res == 0) { + if (!(id = ebml_peek_id(matroska, &matroska->level_up))) { + res = AVERROR_IO; + break; + } else if (matroska->level_up > 0) { + matroska->level_up--; + break; + } + + switch (id) { + /* fixme, this should be one-up, but I get it here */ + case MATROSKA_ID_TRACKDEFAULTDURATION: { + uint64_t num; + if ((res = ebml_read_uint (matroska, &id, + &num)) < 0) + break; + track->default_duration = num; + break; + } + + /* video framerate */ + case MATROSKA_ID_VIDEOFRAMERATE: { + double num; + if ((res = ebml_read_float(matroska, &id, + &num)) < 0) + break; + track->default_duration = 1000000000 * (1. / num); + break; + } + + /* width of the size to display the video at */ + case MATROSKA_ID_VIDEODISPLAYWIDTH: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + videotrack->display_width = num; + break; + } + + /* height of the size to display the video at */ + case MATROSKA_ID_VIDEODISPLAYHEIGHT: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + videotrack->display_height = num; + break; + } + + /* width of the video in the file */ + case MATROSKA_ID_VIDEOPIXELWIDTH: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + videotrack->pixel_width = num; + break; + } + + /* height of the video in the file */ + case MATROSKA_ID_VIDEOPIXELHEIGHT: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + videotrack->pixel_height = num; + break; + } + + /* whether the video is interlaced */ + case MATROSKA_ID_VIDEOFLAGINTERLACED: { + uint64_t num; + 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) + * fourcc */ + case MATROSKA_ID_VIDEOCOLOURSPACE: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + videotrack->fourcc = num; + break; + } + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown video track 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; + } + + /* tracktype specific stuff for audio */ + case MATROSKA_ID_TRACKAUDIO: { + MatroskaAudioTrack *audiotrack; + if (track->type != MATROSKA_TRACK_TYPE_AUDIO) { + av_log(matroska->ctx, AV_LOG_INFO, + "audio data in non-audio track - ignoring\n"); + res = AVERROR_INVALIDDATA; + break; + } else if ((res = ebml_read_master(matroska, &id)) < 0) + break; + audiotrack = (MatroskaAudioTrack *)track; + + while (res == 0) { + if (!(id = ebml_peek_id(matroska, &matroska->level_up))) { + res = AVERROR_IO; + break; + } else if (matroska->level_up > 0) { + matroska->level_up--; + break; + } + + switch (id) { + /* samplerate */ + case MATROSKA_ID_AUDIOSAMPLINGFREQ: { + double num; + if ((res = ebml_read_float(matroska, &id, + &num)) < 0) + break; + audiotrack->samplerate = num; + break; + } + + /* bitdepth */ + case MATROSKA_ID_AUDIOBITDEPTH: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + audiotrack->bitdepth = num; + break; + } + + /* channels */ + case MATROSKA_ID_AUDIOCHANNELS: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, + &num)) < 0) + break; + audiotrack->channels = num; + break; + } + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown audio track 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; + } + + /* codec identifier */ + case MATROSKA_ID_CODECID: { + char *text; + if ((res = ebml_read_ascii(matroska, &id, &text)) < 0) + break; + track->codec_id = text; + break; + } + + /* codec private data */ + case MATROSKA_ID_CODECPRIVATE: { + uint8_t *data; + int size; + if ((res = ebml_read_binary(matroska, &id, &data, &size) < 0)) + break; + track->codec_priv = data; + track->codec_priv_size = size; + 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; + if ((res = ebml_read_utf8(matroska, &id, &text)) < 0) + break; + track->name = text; + break; + } + + /* language (matters for audio/subtitles, mostly) */ + case MATROSKA_ID_TRACKLANGUAGE: { + char *text; + if ((res = ebml_read_utf8(matroska, &id, &text)) < 0) + break; + track->language = text; + break; + } + + /* whether this is actually used */ + case MATROSKA_ID_TRACKFLAGENABLED: { + 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; + } + + /* whether it's the default for this track type */ + case MATROSKA_ID_TRACKFLAGDEFAULT: { + 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; + break; + } + + /* lacing (like MPEG, where blocks don't end/start on frame + * boundaries) */ + case MATROSKA_ID_TRACKFLAGLACING: { + 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; + } + + /* default length (in time) of one data block in this track */ + case MATROSKA_ID_TRACKDEFAULTDURATION: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + 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 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; + } + + switch (id) { + /* one single index entry ('point') */ + case MATROSKA_ID_CUETIME: { + int64_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: + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + + 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) { + /* track number */ + case MATROSKA_ID_CUETRACK: { + uint64_t num; + if ((res = ebml_read_uint(matroska, + &id, &num)) < 0) + break; + idx.track = num; + break; + } + + /* position in file */ + case MATROSKA_ID_CUECLUSTERPOSITION: { + uint64_t num; + if ((res = ebml_read_uint(matroska, + &id, &num)) < 0) + break; + idx.pos = num; + break; + } + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown entry 0x%x in " + "CuesTrackPositions\n", id); + /* fall-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 cuespoint " + "index\n", id); + /* fall-through */ + + case EBML_ID_VOID: + res = ebml_read_skip(matroska); + break; + } + + if (matroska->level_up) { + matroska->level_up--; + 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++; + } + break; + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown entry 0x%x in cues 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_metadata (MatroskaDemuxContext *matroska) +{ + int res = 0; + uint32_t id; + + 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) { + /* Hm, this is unsupported... */ + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown entry 0x%x in metadata 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_seekhead (MatroskaDemuxContext *matroska) +{ + int res = 0; + uint32_t id; + + av_log(matroska->ctx, AV_LOG_DEBUG, "parsing seekhead...\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) { + case MATROSKA_ID_SEEKENTRY: { + uint32_t seek_id = 0, peek_id_cache = 0; + uint64_t seek_pos = (uint64_t) -1, t; + + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + + 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) { + case MATROSKA_ID_SEEKID: + res = ebml_read_uint(matroska, &id, &t); + seek_id = t; + break; + + case MATROSKA_ID_SEEKPOSITION: + res = ebml_read_uint(matroska, &id, &seek_pos); + break; + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown seekhead 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; + } + } + + if (!seek_id || seek_pos == (uint64_t) -1) { + av_log(matroska->ctx, AV_LOG_INFO, + "Incomplete seekhead entry (0x%x/%llu)\n", + seek_id, seek_pos); + break; + } + + switch (seek_id) { + case MATROSKA_ID_CUES: + case MATROSKA_ID_TAGS: { + uint32_t level_up = matroska->level_up; + offset_t before_pos; + uint64_t length; + MatroskaLevel level; + + /* remember the peeked ID and the current position */ + peek_id_cache = matroska->peek_id; + before_pos = url_ftell(&matroska->ctx->pb); + + /* seek */ + if ((res = ebml_read_seek(matroska, seek_pos + + matroska->segment_start)) < 0) + return res; + + /* 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); + return AVERROR_UNKNOWN; + } + + level.start = 0; + level.length = (uint64_t)-1; + matroska->levels[matroska->num_levels] = level; + matroska->num_levels++; + + /* check ID */ + if (!(id = ebml_peek_id (matroska, + &matroska->level_up))) + break; + if (id != seek_id) { + av_log(matroska->ctx, AV_LOG_INFO, + "We looked for ID=0x%x but got " + "ID=0x%x (pos=%llu)", + seek_id, id, seek_pos + + matroska->segment_start); + goto finish; + } + + /* read master + parse */ + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + switch (id) { + case MATROSKA_ID_CUES: + if (!(res = matroska_parse_index(matroska)) || + 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)) { + matroska->metadata_parsed = 1; + res = 0; + } + break; + } + if (res < 0) + break; + + finish: + /* remove 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; + matroska->peek_id = peek_id_cache; + matroska->level_up = level_up; + break; + } + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Ignoring seekhead entry for ID=0x%x\n", + seek_id); + break; + } + + break; + } + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown seekhead 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_read_header (AVFormatContext *s, + AVFormatParameters *ap) +{ + MatroskaDemuxContext *matroska = s->priv_data; + char *doctype; + int version, last_level, res = 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 != 1) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Matroska demuxer version 1 too old for file version %d\n", + version); + return AVERROR_NOFMT; + } + + /* The next thing is a segment. */ + while (1) { + if (!(id = ebml_peek_id(matroska, &last_level))) + return AVERROR_IO; + if (id == MATROSKA_ID_SEGMENT) + break; + + /* oi! */ + av_log(matroska->ctx, AV_LOG_INFO, + "Expected a Segment ID (0x%x), but received 0x%x!\n", + MATROSKA_ID_SEGMENT, id); + if ((res = ebml_read_skip(matroska)) < 0) + return res; + } + + /* We now have a Matroska segment. + * Seeks are from the beginning of the segment, + * after the segment ID/length. */ + if ((res = ebml_read_master(matroska, &id)) < 0) + return res; + 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; + break; + } else if (matroska->level_up) { + matroska->level_up--; + break; + } + + switch (id) { + /* stream info */ + case MATROSKA_ID_INFO: { + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + res = matroska_parse_info(matroska); + break; + } + + /* track info headers */ + case MATROSKA_ID_TRACKS: { + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + res = matroska_parse_tracks(matroska); + break; + } + + /* 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); + break; + } + + /* 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; + } + + /* file index (if seekable, seek to Cues/Tags to parse it) */ + case MATROSKA_ID_SEEKHEAD: { + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + res = matroska_parse_seekhead(matroska); + break; + } + + case MATROSKA_ID_CLUSTER: { + /* Do not read the master - this will be done in the next + * call to matroska_read_packet. */ + res = 1; + break; + } + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown matroska file header 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; + } + } + + if (res < 0) + return res; + + /* Have we found a cluster? */ + if (res == 1) { + int i; + enum CodecID codec_id; + MatroskaTrack *track; + AVStream *st; + + av_set_pts_info(s, 24, 1, 1000); /* 24 bit pts in ms */ + + for (i = 0; i < matroska->num_tracks; i++) { + track = matroska->tracks[i]; + + /* libavformat does not really support subtitles. + * Also apply some sanity checks. */ + if ((track->type == MATROSKA_TRACK_TYPE_SUBTITLE) || + (track->codec_id == NULL)) + continue; + + /* Set the FourCC from the CodecID. */ + /* This is the MS compatibility mode which stores a + * BITMAPINFOHEADER in the CodecPrivate. */ + if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC) && + (track->codec_priv_size >= 40) && + (track->codec_priv != NULL)) { + unsigned char *p; + + /* Offset of biCompression. Stored in LE. */ + p = (unsigned char *)track->codec_priv + 16; + ((MatroskaVideoTrack *)track)->fourcc = (p[3] << 24) | + (p[2] << 16) | (p[1] << 8) | p[0]; + codec_id = codec_get_bmp_id(((MatroskaVideoTrack *)track)->fourcc); + + } else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_MPEG4_SP) || + !strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP) || + !strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_MPEG4_AP)) + codec_id = CODEC_ID_MPEG4; +/* else if (!strcmp(track->codec_id, */ +/* MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED)) */ +/* codec_id = CODEC_ID_???; */ + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3)) + codec_id = CODEC_ID_MSMPEG4V3; + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_MPEG1) || + !strcmp(track->codec_id, + MATROSKA_CODEC_ID_VIDEO_MPEG2)) + codec_id = CODEC_ID_MPEG2VIDEO; + + /* This is the MS compatibility mode which stores a + * WAVEFORMATEX in the CodecPrivate. */ + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_ACM) && + (track->codec_priv_size >= 18) && + (track->codec_priv != NULL)) { + unsigned char *p; + uint16_t tag; + + /* Offset of wFormatTag. Stored in LE. */ + p = (unsigned char *)track->codec_priv; + tag = (p[1] << 8) | p[0]; + codec_id = codec_get_wav_id(tag); + + } else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_MPEG1_L1) || + !strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_MPEG1_L2) || + !strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_MPEG1_L3)) + codec_id = CODEC_ID_MP3; + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE)) + codec_id = CODEC_ID_PCM_U16BE; + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE)) + codec_id = CODEC_ID_PCM_U16LE; +/* else if (!strcmp(track->codec_id, */ +/* MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT)) */ +/* codec_id = CODEC_ID_PCM_???; */ + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_AC3)) + codec_id = CODEC_ID_AC3; + /* No such codec id so far. */ +/* else if (!strcmp(track->codec_id, */ +/* MATROSKA_CODEC_ID_AUDIO_DTS)) */ +/* codec_id = CODEC_ID_DTS; */ + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_VORBIS)) + codec_id = CODEC_ID_VORBIS; + else if (!strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_MPEG2) || + !strcmp(track->codec_id, + MATROSKA_CODEC_ID_AUDIO_MPEG4)) + codec_id = CODEC_ID_AAC; + else + codec_id = CODEC_ID_NONE; + + 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_NOMEM; + + st->codec.codec_id = codec_id; + + if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + MatroskaVideoTrack *videotrack = (MatroskaVideoTrack *)track; + + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_tag = videotrack->fourcc; + st->codec.width = videotrack->pixel_width; + st->codec.height = videotrack->pixel_height; + if (videotrack->display_width == 0) + st->codec.sample_aspect_ratio.num = + videotrack->pixel_width; + else + st->codec.sample_aspect_ratio.num = + videotrack->display_width; + if (videotrack->display_height == 0) + st->codec.sample_aspect_ratio.num = + videotrack->pixel_height; + else + st->codec.sample_aspect_ratio.num = + videotrack->display_height; + + } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { + MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track; + + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.sample_rate = audiotrack->samplerate; + st->codec.channels = audiotrack->channels; + } + + /* What do we do with private data? E.g. for Vorbis. */ + } + } + + return 0; +} + +static int +matroska_find_track_by_num (MatroskaDemuxContext *matroska, + int num) +{ + int i; + + for (i = 0; i < matroska->num_tracks; i++) + if (matroska->tracks[i]->num == num) + return i; + + return -1; +} + +static int +matroska_parse_blockgroup (MatroskaDemuxContext *matroska, + uint64_t cluster_time) +{ + int res = 0; + uint32_t id; + AVPacket *pkt; + int is_keyframe = PKT_FLAG_KEY, last_num_packets = matroska->num_packets; + + av_log(matroska->ctx, AV_LOG_DEBUG, "parsing blockgroup...\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 block inside the group. Note, block parsing is one + * of the harder things, so this code is a bit complicated. + * See http://www.matroska.org/ for documentation. */ + case MATROSKA_ID_BLOCK: { + uint8_t *data, *origdata; + int size; + uint64_t time; + uint32_t *lace_size = NULL; + int n, track, flags, laces = 0; + uint64_t num; + + if ((res = ebml_read_binary(matroska, &id, &data, &size)) < 0) + break; + origdata = data; + + /* first byte(s): blocknum */ + if ((n = matroska_ebmlnum_uint(data, size, &num)) < 0) { + av_log(matroska->ctx, AV_LOG_ERROR, + "EBML block data error\n"); + av_free(origdata); + break; + } + data += n; + size -= n; + + /* fetch track from num */ + track = matroska_find_track_by_num(matroska, num); + if (size <= 3 || track < 0 || track >= matroska->num_tracks) { + av_log(matroska->ctx, AV_LOG_INFO, + "Invalid stream %d or size %u\n", track, size); + av_free(origdata); + break; + } + + /* time (relative to cluster time) */ + time = ((data[0] << 8) | data[1]) * matroska->time_scale; + data += 2; + size -= 2; + flags = *data; + data += 1; + size -= 1; + switch ((flags & 0x06) >> 1) { + case 0x0: /* no lacing */ + laces = 1; + lace_size = av_mallocz(sizeof(int)); + lace_size[0] = size; + break; + + case 0x1: /* xiph lacing */ + case 0x2: /* fixed-size lacing */ + case 0x3: /* EBML lacing */ + if (size == 0) { + res = -1; + break; + } + laces = (*data) + 1; + data += 1; + size -= 1; + lace_size = av_mallocz(laces * sizeof(int)); + + switch ((flags & 0x06) >> 1) { + case 0x1: /* xiph lacing */ { + uint8_t temp; + uint32_t total = 0; + for (n = 0; res == 0 && n < laces - 1; n++) { + while (1) { + if (size == 0) { + res = -1; + break; + } + temp = *data; + lace_size[n] += temp; + data += 1; + size -= 1; + if (temp != 0xff) + break; + } + total += lace_size[n]; + } + lace_size[n] = size - total; + break; + } + + case 0x2: /* fixed-size lacing */ + for (n = 0; n < laces; n++) + lace_size[n] = size / laces; + break; + + case 0x3: /* EBML lacing */ { + uint32_t total; + n = matroska_ebmlnum_uint(data, size, &num); + if (n < 0) { + av_log(matroska->ctx, AV_LOG_INFO, + "EBML block data error\n"); + break; + } + data += n; + size -= n; + total = lace_size[0] = num; + for (n = 1; res == 0 && n < laces - 1; n++) { + int64_t snum; + int r; + r = matroska_ebmlnum_sint (data, size, + &snum); + if (r < 0) { + av_log(matroska->ctx, AV_LOG_INFO, + "EBML block data error\n"); + break; + } + data += r; + size -= r; + lace_size[n] = lace_size[n - 1] + snum; + total += lace_size[n]; + } + lace_size[n] = size - total; + break; + } + } + break; + } + + if (res == 0) { + for (n = 0; n < laces; n++) { + uint64_t timecode = 0; + + pkt = av_mallocz(sizeof(AVPacket)); + /* XXX: prevent data copy... */ + if (av_new_packet(pkt,lace_size[n]) < 0) { + res = AVERROR_NOMEM; + break; + } + if (cluster_time != (uint64_t)-1) { + if (time < 0 && (-time) > cluster_time) + timecode = cluster_time; + else + timecode = cluster_time + time; + } + /* FIXME: duration */ + + memcpy(pkt->data, data, lace_size[n]); + data += lace_size[n]; + if (n == 0) + pkt->flags = is_keyframe; + pkt->stream_index = + matroska->tracks[track]->stream_index; + + pkt->pts = timecode / 1000000; /* ns to ms */ + + matroska_queue_packet(matroska, pkt); + } + } + + av_free(lace_size); + av_free(origdata); + break; + } + + case MATROSKA_ID_BLOCKDURATION: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + av_log(matroska->ctx, AV_LOG_INFO, + "FIXME: implement support for BlockDuration\n"); + break; + } + + case MATROSKA_ID_BLOCKREFERENCE: + /* We've found a reference, so not even the first frame in + * the lace is a key frame. */ + is_keyframe = 0; + if (last_num_packets != matroska->num_packets) + matroska->packets[last_num_packets]->flags = 0; + res = ebml_read_skip(matroska); + break; + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown entry 0x%x in blockgroup data\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_cluster (MatroskaDemuxContext *matroska) +{ + int res = 0; + uint32_t id; + uint64_t cluster_time = 0; + + av_log(matroska->ctx, AV_LOG_DEBUG, + "parsing cluster at %lld\n", url_ftell(&matroska->ctx->pb)); + + 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) { + /* cluster timecode */ + case MATROSKA_ID_CLUSTERTIMECODE: { + uint64_t num; + if ((res = ebml_read_uint(matroska, &id, &num)) < 0) + break; + cluster_time = num * matroska->time_scale; + break; + } + + /* a group of blocks inside a cluster */ + case MATROSKA_ID_BLOCKGROUP: + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + res = matroska_parse_blockgroup(matroska, cluster_time); + break; + + default: + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown entry 0x%x in cluster data\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_read_packet (AVFormatContext *s, + AVPacket *pkt) +{ + MatroskaDemuxContext *matroska = s->priv_data; + int res = 0; + uint32_t id; + + /* Do we still have a packet queued? */ + if (matroska_deliver_packet(matroska, pkt) == 0) + return 0; + + /* Have we already reached the end? */ + if (matroska->done) + return AVERROR_IO; + + 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) { + case MATROSKA_ID_CLUSTER: + if ((res = ebml_read_master(matroska, &id)) < 0) + break; + if ((res = matroska_parse_cluster(matroska)) == 0) + res = 1; /* Parsed one cluster, let's get out. */ + break; + + default: + case EBML_ID_VOID: + res = ebml_read_skip(matroska); + break; + } + + if (matroska->level_up) { + matroska->level_up--; + break; + } + } + + if (res == -1) + matroska->done = 1; + + return matroska_deliver_packet(matroska, pkt); +} + +static int +matroska_read_close (AVFormatContext *s) +{ + MatroskaDemuxContext *matroska = s->priv_data; + int n = 0; + + if (matroska->writing_app) + av_free(matroska->writing_app); + if (matroska->muxing_app) + av_free(matroska->muxing_app); + if (matroska->index) + 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); + } + + for (n = 0; n < matroska->num_tracks; n++) { + MatroskaTrack *track = matroska->tracks[n]; + if (track->codec_id) + av_free(track->codec_id); + if (track->codec_name) + av_free(track->codec_name); + if (track->codec_priv) + av_free(track->codec_priv); + if (track->name) + av_free(track->name); + if (track->language) + av_free(track->language); + + av_free(track); + } + + memset(matroska, 0, sizeof(MatroskaDemuxContext)); + + return 0; +} + +static AVInputFormat matroska_iformat = { + "matroska", + "Matroska file format", + sizeof(MatroskaDemuxContext), + matroska_probe, + matroska_read_header, + matroska_read_packet, + matroska_read_close, +}; + +int +matroska_init(void) +{ + av_register_input_format(&matroska_iformat); + return 0; +}