X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=libavformat%2Fmatroskadec.c;h=8839654fa25d013a86fe6eead22ef6f6aaaeeef9;hb=28f27e0c0c234ead2742510014132803f2511dc7;hp=0c42b1fc0f54502c3d20d8aa79af9d8cbe2d8e88;hpb=434d496a8a16e016950cd16bc759efb5cf856e32;p=ffmpeg diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 0c42b1fc0f5..8839654fa25 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -1,6 +1,6 @@ /* - * Matroska file demuxer (no muxer yet) - * Copyright (c) 2003-2004 The ffmpeg Project + * Matroska file demuxer + * Copyright (c) 2003-2008 The FFmpeg Project * * This file is part of FFmpeg. * @@ -24,8 +24,8 @@ * Matroska file demuxer * by Ronald Bultje * with a little help from Moritz Bunkus - * Specs available on the matroska project page: - * http://www.matroska.org/. + * totally reworked by Aurelien Jacobs + * Specs available on the Matroska project page: http://www.matroska.org/. */ #include "avformat.h" @@ -88,75 +88,70 @@ typedef struct { uint64_t doctype_version; } Ebml; -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; - uint32_t uid; +typedef struct { + uint64_t algo; + EbmlBin settings; +} MatroskaTrackCompression; - char *name; - char language[4]; +typedef struct { + uint64_t scope; + uint64_t type; + MatroskaTrackCompression compression; +} MatroskaTrackEncoding; - char *codec_id; +typedef struct { + double frame_rate; + uint64_t display_width; + uint64_t display_height; + uint64_t pixel_width; + uint64_t pixel_height; + uint64_t fourcc; +} MatroskaTrackVideo; - unsigned char *codec_priv; - int codec_priv_size; +typedef struct { + double samplerate; + double out_samplerate; + uint64_t bitdepth; + uint64_t channels; + + /* real audio header (extracted from extradata) */ + int coded_framesize; + int sub_packet_h; + int frame_size; + int sub_packet_size; + int sub_packet_cnt; + int pkt_cnt; + uint8_t *buf; +} MatroskaTrackAudio; +typedef struct { + uint64_t num; + uint64_t type; + char *codec_id; + EbmlBin codec_priv; + char *language; double time_scale; uint64_t default_duration; uint64_t flag_default; - - int encoding_scope; - MatroskaTrackEncodingCompAlgo encoding_algo; - uint8_t *encoding_settings; - int encoding_settings_len; + MatroskaTrackVideo video; + MatroskaTrackAudio audio; + EbmlList encodings; AVStream *stream; } MatroskaTrack; -typedef struct MatroskaVideoTrack { - MatroskaTrack track; - - int pixel_width; - int pixel_height; - int display_width; - int display_height; - - uint32_t fourcc; - - //.. -} MatroskaVideoTrack; - -typedef struct MatroskaAudioTrack { - MatroskaTrack track; - - int channels; - int bitdepth; - int internal_samplerate; - int samplerate; - int block_align; - - /* real audio header */ - int coded_framesize; - int sub_packet_h; - int frame_size; - int sub_packet_size; - int sub_packet_cnt; - int pkt_cnt; - uint8_t *buf; - //.. -} MatroskaAudioTrack; - -typedef struct MatroskaSubtitleTrack { - MatroskaTrack track; - //.. -} MatroskaSubtitleTrack; +typedef struct { + char *filename; + char *mime; + EbmlBin bin; +} MatroskaAttachement; -#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; @@ -168,49 +163,66 @@ typedef struct { EbmlList pos; } MatroskaIndex; -typedef struct MatroskaLevel { +typedef struct { + char *name; + char *string; + EbmlList sub; +} MatroskaTag; + +typedef struct { + uint64_t id; + uint64_t pos; +} MatroskaSeekhead; + +typedef struct { uint64_t start; uint64_t length; } MatroskaLevel; -typedef struct MatroskaDemuxContext { +typedef struct { AVFormatContext *ctx; - /* ebml stuff */ + /* EBML stuff */ int num_levels; MatroskaLevel levels[EBML_MAX_DEPTH]; int level_up; - /* timescale in the file */ - int64_t time_scale; + uint64_t time_scale; + double duration; + char *title; + EbmlList tracks; + EbmlList attachments; + 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 num_tracks; - int num_streams; - MatroskaTrack *tracks[MAX_STREAMS]; - - /* cache for ID peeking */ - uint32_t peek_id; + EbmlList tags; + EbmlList seekhead; /* byte position of the segment inside the stream */ offset_t segment_start; - /* The packet queue. */ + /* the packet queue */ AVPacket **packets; int num_packets; - /* have we already parse metadata/cues/clusters? */ - int metadata_parsed; - int index_parsed; int done; + int has_cluster_id; /* What to skip before effectively reading a packet. */ int skip_to_keyframe; AVStream *skip_to_stream; } MatroskaDemuxContext; +typedef struct { + uint64_t duration; + int64_t reference; + EbmlBin bin; +} MatroskaBlock; + +typedef struct { + uint64_t timecode; + EbmlList blocks; +} MatroskaCluster; + #define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x)) static EbmlSyntax ebml_header[] = { @@ -221,7 +233,6 @@ static EbmlSyntax ebml_header[] = { { 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 } }; @@ -230,63 +241,267 @@ static EbmlSyntax ebml_syntax[] = { { 0 } }; +static EbmlSyntax matroska_info[] = { + { MATROSKA_ID_TIMECODESCALE, EBML_UINT, 0, offsetof(MatroskaDemuxContext,time_scale), {.u=1000000} }, + { MATROSKA_ID_DURATION, EBML_FLOAT, 0, offsetof(MatroskaDemuxContext,duration) }, + { MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext,title) }, + { MATROSKA_ID_WRITINGAPP, EBML_NONE }, + { MATROSKA_ID_MUXINGAPP, EBML_NONE }, + { MATROSKA_ID_DATEUTC, EBML_NONE }, + { MATROSKA_ID_SEGMENTUID, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_track_video[] = { + { MATROSKA_ID_VIDEOFRAMERATE, EBML_FLOAT,0, offsetof(MatroskaTrackVideo,frame_rate) }, + { MATROSKA_ID_VIDEODISPLAYWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_width) }, + { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_height) }, + { MATROSKA_ID_VIDEOPIXELWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_width) }, + { MATROSKA_ID_VIDEOPIXELHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_height) }, + { MATROSKA_ID_VIDEOCOLORSPACE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,fourcc) }, + { MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPR, EBML_NONE }, + { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_NONE }, + { MATROSKA_ID_VIDEOFLAGINTERLACED,EBML_NONE }, + { MATROSKA_ID_VIDEOSTEREOMODE, EBML_NONE }, + { MATROSKA_ID_VIDEOASPECTRATIO, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_track_audio[] = { + { MATROSKA_ID_AUDIOSAMPLINGFREQ, EBML_FLOAT,0, offsetof(MatroskaTrackAudio,samplerate), {.f=8000.0} }, + { MATROSKA_ID_AUDIOOUTSAMPLINGFREQ,EBML_FLOAT,0,offsetof(MatroskaTrackAudio,out_samplerate) }, + { MATROSKA_ID_AUDIOBITDEPTH, EBML_UINT, 0, offsetof(MatroskaTrackAudio,bitdepth) }, + { MATROSKA_ID_AUDIOCHANNELS, EBML_UINT, 0, offsetof(MatroskaTrackAudio,channels), {.u=1} }, + { 0 } +}; + +static EbmlSyntax matroska_track_encoding_compression[] = { + { MATROSKA_ID_ENCODINGCOMPALGO, EBML_UINT, 0, offsetof(MatroskaTrackCompression,algo), {.u=0} }, + { MATROSKA_ID_ENCODINGCOMPSETTINGS,EBML_BIN, 0, offsetof(MatroskaTrackCompression,settings) }, + { 0 } +}; + +static EbmlSyntax matroska_track_encoding[] = { + { MATROSKA_ID_ENCODINGSCOPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding,scope), {.u=1} }, + { MATROSKA_ID_ENCODINGTYPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding,type), {.u=0} }, + { MATROSKA_ID_ENCODINGCOMPRESSION,EBML_NEST, 0, offsetof(MatroskaTrackEncoding,compression), {.n=matroska_track_encoding_compression} }, + { MATROSKA_ID_ENCODINGORDER, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_track_encodings[] = { + { MATROSKA_ID_TRACKCONTENTENCODING, EBML_NEST, sizeof(MatroskaTrackEncoding), offsetof(MatroskaTrack,encodings), {.n=matroska_track_encoding} }, + { 0 } +}; + +static EbmlSyntax matroska_track[] = { + { MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack,num) }, + { MATROSKA_ID_TRACKTYPE, EBML_UINT, 0, offsetof(MatroskaTrack,type) }, + { MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack,codec_id) }, + { MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack,codec_priv) }, + { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack,language), {.s="eng"} }, + { MATROSKA_ID_TRACKDEFAULTDURATION, EBML_UINT, 0, offsetof(MatroskaTrack,default_duration) }, + { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT,0, offsetof(MatroskaTrack,time_scale), {.f=1.0} }, + { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack,flag_default), {.u=1} }, + { MATROSKA_ID_TRACKVIDEO, EBML_NEST, 0, offsetof(MatroskaTrack,video), {.n=matroska_track_video} }, + { MATROSKA_ID_TRACKAUDIO, EBML_NEST, 0, offsetof(MatroskaTrack,audio), {.n=matroska_track_audio} }, + { MATROSKA_ID_TRACKCONTENTENCODINGS,EBML_NEST, 0, 0, {.n=matroska_track_encodings} }, + { MATROSKA_ID_TRACKUID, EBML_NONE }, + { MATROSKA_ID_TRACKNAME, EBML_NONE }, + { MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE }, + { MATROSKA_ID_TRACKFLAGFORCED, EBML_NONE }, + { MATROSKA_ID_TRACKFLAGLACING, EBML_NONE }, + { MATROSKA_ID_CODECNAME, EBML_NONE }, + { MATROSKA_ID_CODECDECODEALL, EBML_NONE }, + { MATROSKA_ID_CODECINFOURL, EBML_NONE }, + { MATROSKA_ID_CODECDOWNLOADURL, EBML_NONE }, + { MATROSKA_ID_TRACKMINCACHE, EBML_NONE }, + { MATROSKA_ID_TRACKMAXCACHE, EBML_NONE }, + { MATROSKA_ID_TRACKMAXBLKADDID, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_tracks[] = { + { MATROSKA_ID_TRACKENTRY, EBML_NEST, sizeof(MatroskaTrack), offsetof(MatroskaDemuxContext,tracks), {.n=matroska_track} }, + { 0 } +}; + +static EbmlSyntax matroska_attachment[] = { + { MATROSKA_ID_FILENAME, EBML_UTF8, 0, offsetof(MatroskaAttachement,filename) }, + { MATROSKA_ID_FILEMIMETYPE, EBML_STR, 0, offsetof(MatroskaAttachement,mime) }, + { MATROSKA_ID_FILEDATA, EBML_BIN, 0, offsetof(MatroskaAttachement,bin) }, + { MATROSKA_ID_FILEDESC, EBML_NONE }, + { MATROSKA_ID_FILEUID, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_attachments[] = { + { MATROSKA_ID_ATTACHEDFILE, EBML_NEST, sizeof(MatroskaAttachement), offsetof(MatroskaDemuxContext,attachments), {.n=matroska_attachment} }, + { 0 } +}; + +static EbmlSyntax matroska_chapter_display[] = { + { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter,title) }, + { MATROSKA_ID_CHAPLANG, 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 }, + { MATROSKA_ID_CHAPTERFLAGENABLED, EBML_NONE }, + { MATROSKA_ID_CHAPTERPHYSEQUIV, EBML_NONE }, + { MATROSKA_ID_CHAPTERATOM, 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 }, + { MATROSKA_ID_EDITIONFLAGORDERED, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_chapters[] = { + { MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, {.n=matroska_chapter} }, + { 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 }, + { MATROSKA_ID_CUEBLOCKNUMBER, 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_simpletag[] = { + { MATROSKA_ID_TAGNAME, EBML_UTF8, 0, offsetof(MatroskaTag,name) }, + { MATROSKA_ID_TAGSTRING, EBML_UTF8, 0, offsetof(MatroskaTag,string) }, + { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTag,sub), {.n=matroska_simpletag} }, + { MATROSKA_ID_TAGLANG, EBML_NONE }, + { MATROSKA_ID_TAGDEFAULT, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_tag[] = { + { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), 0, {.n=matroska_simpletag} }, + { MATROSKA_ID_TAGTARGETS, EBML_NONE }, { 0 } }; static EbmlSyntax matroska_tags[] = { - { EBML_ID_VOID, EBML_NONE }, + { MATROSKA_ID_TAG, EBML_NEST, 0, offsetof(MatroskaDemuxContext,tags), {.n=matroska_tag} }, { 0 } }; -/* - * The first few functions handle EBML file parsing. The rest - * is the document interpretation. Matroska really just is a - * EBML file. - */ +static EbmlSyntax matroska_seekhead_entry[] = { + { MATROSKA_ID_SEEKID, EBML_UINT, 0, offsetof(MatroskaSeekhead,id) }, + { MATROSKA_ID_SEEKPOSITION, EBML_UINT, 0, offsetof(MatroskaSeekhead,pos), {.u=-1} }, + { 0 } +}; + +static EbmlSyntax matroska_seekhead[] = { + { MATROSKA_ID_SEEKENTRY, EBML_NEST, sizeof(MatroskaSeekhead), offsetof(MatroskaDemuxContext,seekhead), {.n=matroska_seekhead_entry} }, + { 0 } +}; + +static EbmlSyntax matroska_segment[] = { + { MATROSKA_ID_INFO, EBML_NEST, 0, 0, {.n=matroska_info } }, + { MATROSKA_ID_TRACKS, EBML_NEST, 0, 0, {.n=matroska_tracks } }, + { MATROSKA_ID_ATTACHMENTS, EBML_NEST, 0, 0, {.n=matroska_attachments} }, + { MATROSKA_ID_CHAPTERS, EBML_NEST, 0, 0, {.n=matroska_chapters } }, + { MATROSKA_ID_CUES, EBML_NEST, 0, 0, {.n=matroska_index } }, + { MATROSKA_ID_TAGS, EBML_NEST, 0, 0, {.n=matroska_tags } }, + { MATROSKA_ID_SEEKHEAD, EBML_NEST, 0, 0, {.n=matroska_seekhead } }, + { MATROSKA_ID_CLUSTER, EBML_STOP, 0, offsetof(MatroskaDemuxContext,has_cluster_id) }, + { 0 } +}; + +static EbmlSyntax matroska_segments[] = { + { MATROSKA_ID_SEGMENT, EBML_NEST, 0, 0, {.n=matroska_segment } }, + { 0 } +}; + +static EbmlSyntax matroska_blockgroup[] = { + { MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, + { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, + { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration), {.u=AV_NOPTS_VALUE} }, + { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock,reference) }, + { 0 } +}; + +static EbmlSyntax matroska_cluster[] = { + { MATROSKA_ID_CLUSTERTIMECODE,EBML_UINT,0, offsetof(MatroskaCluster,timecode) }, + { MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster,blocks), {.n=matroska_blockgroup} }, + { MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster,blocks), {.n=matroska_blockgroup} }, + { MATROSKA_ID_CLUSTERPOSITION,EBML_NONE }, + { MATROSKA_ID_CLUSTERPREVSIZE,EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_clusters[] = { + { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, {.n=matroska_cluster} }, + { MATROSKA_ID_INFO, EBML_NONE }, + { MATROSKA_ID_CUES, EBML_NONE }, + { MATROSKA_ID_TAGS, EBML_NONE }, + { MATROSKA_ID_SEEKHEAD, EBML_NONE }, + { 0 } +}; + +#define SIZE_OFF(x) sizeof(((AVFormatContext*)0)->x),offsetof(AVFormatContext,x) +const struct { + const char name[16]; + int size; + int offset; +} metadata[] = { + { "TITLE", SIZE_OFF(title) }, + { "ARTIST", SIZE_OFF(author) }, + { "WRITTEN_BY", SIZE_OFF(author) }, + { "LEAD_PERFORMER", SIZE_OFF(author) }, + { "COPYRIGHT", SIZE_OFF(copyright) }, + { "COMMENT", SIZE_OFF(comment) }, + { "ALBUM", SIZE_OFF(album) }, + { "DATE_WRITTEN", SIZE_OFF(year) }, + { "DATE_RELEASED", SIZE_OFF(year) }, + { "PART_NUMBER", SIZE_OFF(track) }, + { "GENRE", SIZE_OFF(genre) }, +}; /* - * 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. + * Return: Whether we reached the end of a level in the hierarchy or not. */ - -static int -ebml_read_element_level_up (MatroskaDemuxContext *matroska) +static int ebml_level_end(MatroskaDemuxContext *matroska) { ByteIOContext *pb = matroska->ctx->pb; offset_t pos = url_ftell(pb); - int num = 0; - while (matroska->num_levels > 0) { + if (matroska->num_levels > 0) { MatroskaLevel *level = &matroska->levels[matroska->num_levels - 1]; - - if (pos >= level->start + level->length) { + if (pos - level->start >= level->length) { matroska->num_levels--; - num++; - } else { - break; + return 1; } } - - return num; + return 0; } /* @@ -295,19 +510,15 @@ ebml_read_element_level_up (MatroskaDemuxContext *matroska) * 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. + * Returns: number of bytes read, < 0 on error */ - -static int -ebml_read_num (MatroskaDemuxContext *matroska, - int max_size, - uint64_t *number) +static int ebml_read_num(MatroskaDemuxContext *matroska, ByteIOContext *pb, + 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 + /* 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))) { @@ -344,139 +555,18 @@ ebml_read_num (MatroskaDemuxContext *matroska, 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; - - 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) +static int ebml_read_uint(ByteIOContext *pb, int size, uint64_t *num) { - ByteIOContext *pb = matroska->ctx->pb; - int n = 0, size, res; - uint64_t rlength; + int n = 0; - 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 %"PRId64" (0x%"PRIx64")\n", - size, pos, pos); + if (size < 1 || size > 8) return AVERROR_INVALIDDATA; - } - /* big-endian ordening; build up number */ + /* big-endian ordering; build up number */ *num = 0; while (n++ < size) *num = (*num << 8) | get_byte(pb); @@ -484,75 +574,18 @@ ebml_read_uint (MatroskaDemuxContext *matroska, 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 %"PRId64" (0x%"PRIx64")\n", - size, pos, pos); - return AVERROR_INVALIDDATA; - } - if ((*num = get_byte(pb)) & 0x80) { - negative = 1; - *num &= ~0x80; - } - 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) +static int ebml_read_float(ByteIOContext *pb, int size, 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) { *num= av_int2flt(get_be32(pb)); } else if(size==8){ *num= av_int2dbl(get_be64(pb)); - } else{ - offset_t pos = url_ftell(pb); - av_log(matroska->ctx, AV_LOG_ERROR, - "Invalid float element size %d at position %"PRIu64" (0x%"PRIx64")\n", - size, pos, pos); + } else return AVERROR_INVALIDDATA; - } return 0; } @@ -561,31 +594,14 @@ ebml_read_float (MatroskaDemuxContext *matroska, * 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) +static int ebml_read_ascii(ByteIOContext *pb, int size, 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 + av_free(*str); + /* EBML strings are usually not 0-terminated, so we allocate one * byte more, read the string and NULL-terminate it ourselves. */ - if (size < 0 || !(*str = av_malloc(size + 1))) { - av_log(matroska->ctx, AV_LOG_ERROR, "Memory allocation failed\n"); + if (!(*str = av_malloc(size + 1))) return AVERROR(ENOMEM); - } 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. %"PRIu64" (0x%"PRIx64")\n", pos, pos); av_free(*str); return AVERROR(EIO); } @@ -595,16 +611,21 @@ ebml_read_ascii (MatroskaDemuxContext *matroska, } /* - * Read the next element as a UTF-8 string. + * Read the next element as binary data. * 0 is success, < 0 is failure. */ - -static int -ebml_read_utf8 (MatroskaDemuxContext *matroska, - uint32_t *id, - char **str) +static int ebml_read_binary(ByteIOContext *pb, int length, EbmlBin *bin) { - return ebml_read_ascii(matroska, id, str); + av_free(bin->data); + if (!(bin->data = av_malloc(length))) + return AVERROR(ENOMEM); + + bin->size = length; + bin->pos = url_ftell(pb); + if (get_buffer(pb, bin->data, length) != length) + return AVERROR(EIO); + + return 0; } /* @@ -612,28 +633,17 @@ ebml_read_utf8 (MatroskaDemuxContext *matroska, * 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) +static int ebml_read_master(MatroskaDemuxContext *matroska, int length) { 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(ENOSYS); } - /* remember level */ level = &matroska->levels[matroska->num_levels++]; level->start = url_ftell(pb); level->length = length; @@ -642,243 +652,91 @@ ebml_read_master (MatroskaDemuxContext *matroska, } /* - * Read the next element as binary data. - * 0 is success, < 0 is failure. + * Read signed/unsigned "EBML" numbers. + * Return: number of bytes processed, < 0 on error */ +static int matroska_ebmlnum_uint(MatroskaDemuxContext *matroska, + uint8_t *data, uint32_t size, uint64_t *num) +{ + ByteIOContext pb; + init_put_byte(&pb, data, size, 0, NULL, NULL, NULL, NULL); + return ebml_read_num(matroska, &pb, 8, num); +} -static int -ebml_read_binary (MatroskaDemuxContext *matroska, - uint32_t *id, - uint8_t **binary, - int *size) +/* + * Same as above, but signed. + */ +static int matroska_ebmlnum_sint(MatroskaDemuxContext *matroska, + uint8_t *data, uint32_t size, int64_t *num) { - ByteIOContext *pb = matroska->ctx->pb; - uint64_t rlength; + uint64_t unum; int res; - if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 || - (res = ebml_read_element_length(matroska, &rlength)) < 0) + /* read as unsigned number first */ + if ((res = matroska_ebmlnum_uint(matroska, data, size, &unum)) < 0) return res; - *size = rlength; - - if (!(*binary = av_malloc(*size))) { - av_log(matroska->ctx, AV_LOG_ERROR, - "Memory allocation error\n"); - return AVERROR(ENOMEM); - } - if (get_buffer(pb, *binary, *size) != *size) { - 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(EIO); - } + /* make signed (weird way) */ + *num = unum - ((1LL << (7*res - 1)) - 1); - return 0; + return res; } -/* - * Read signed/unsigned "EBML" numbers. - * Return: number of bytes processed, < 0 on error. - * XXX: use ebml_read_num(). - */ +static int ebml_parse_elem(MatroskaDemuxContext *matroska, + EbmlSyntax *syntax, void *data); -static int -matroska_ebmlnum_uint (uint8_t *data, - uint32_t size, - uint64_t *num) +static int ebml_parse_id(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, + uint32_t id, void *data) { - int len_mask = 0x80, read = 1, n = 1, num_ffs = 0; - uint64_t total; - - if (size <= 0) - return AVERROR_INVALIDDATA; + int i; + for (i=0; syntax[i].id; i++) + if (id == syntax[i].id) + break; + if (!syntax[i].id && id != EBML_ID_VOID && id != EBML_ID_CRC32) + av_log(matroska->ctx, AV_LOG_INFO, "Unknown entry 0x%X\n", id); + return ebml_parse_elem(matroska, &syntax[i], data); +} - 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 (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; -} - - -static MatroskaTrack * -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 matroska->tracks[i]; - - av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num); - return NULL; -} - - -/* - * 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) +static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, + void *data) { - 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_freep(&matroska->packets); - } - matroska->num_packets--; - return 0; - } - - return -1; + uint64_t id; + int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id); + id |= 1 << 7*res; + return res < 0 ? res : ebml_parse_id(matroska, syntax, id, data); } -/* - * 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) +static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, + void *data) { - matroska->packets = - av_realloc(matroska->packets, (matroska->num_packets + 1) * - sizeof(AVPacket *)); - matroska->packets[matroska->num_packets] = pkt; - matroska->num_packets++; -} + int i, res = 0; -/* - * 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]); + 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; } - av_free(matroska->packets); - matroska->packets = NULL; - matroska->num_packets = 0; - } -} - - -/* - * 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' }; - - /* ebml header? */ - if (AV_RB32(p->buf) != 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; + while (!res && !ebml_level_end(matroska)) + res = ebml_parse(matroska, syntax, data); - return 0; + return res; } -/* - * 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) { + ByteIOContext *pb = matroska->ctx->pb; uint32_t id = syntax->id; - EbmlBin *bin; + uint64_t length; int res; data = (char *)data + syntax->data_offset; @@ -889,89 +747,30 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, memset(data, 0, syntax->list_elem_size); list->nb_elem++; } - bin = data; + + if (syntax->type != EBML_PASS && syntax->type != EBML_STOP) + if ((res = ebml_read_num(matroska, pb, 8, &length)) < 0) + return res; 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_UINT: res = ebml_read_uint (pb, length, data); break; + case EBML_FLOAT: res = ebml_read_float (pb, length, data); break; 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) + case EBML_UTF8: res = ebml_read_ascii (pb, length, data); break; + case EBML_BIN: res = ebml_read_binary(pb, length, data); break; + case EBML_NEST: if ((res=ebml_read_master(matroska, length)) < 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); + return ebml_parse_nest(matroska, syntax->def.n, data); + case EBML_PASS: return ebml_parse_id(matroska, syntax->def.n, id, data); 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; - } + default: return url_fseek(pb,length,SEEK_CUR)<0 ? AVERROR(EIO) : 0; } - + if (res == AVERROR_INVALIDDATA) + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid element\n"); + else if (res == AVERROR(EIO)) + av_log(matroska->ctx, AV_LOG_ERROR, "Read error\n"); return res; } @@ -998,77 +797,65 @@ static void ebml_free(EbmlSyntax *syntax, void *data) } } -static int -matroska_parse_info (MatroskaDemuxContext *matroska) + +/* + * Autodetecting... + */ +static int matroska_probe(AVProbeData *p) { - int res = 0; - uint32_t id; + uint64_t total = 0; + int len_mask = 0x80, size = 1, n = 1; + char probe_data[] = "matroska"; - av_log(matroska->ctx, AV_LOG_DEBUG, "Parsing info...\n"); + /* EBML header? */ + if (AV_RB32(p->buf) != EBML_ID_HEADER) + return 0; - 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; - } + /* 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++]; - 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; - } + /* Does the probe data contain the whole header? */ + if (p->buf_size < 4 + size + total) + return 0; - case MATROSKA_ID_DURATION: { - double num; - if ((res = ebml_read_float(matroska, &id, &num)) < 0) - break; - matroska->ctx->duration = num * matroska->time_scale * 1000 / AV_TIME_BASE; - break; - } + /* 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)-1); n++) + if (!memcmp(p->buf+n, probe_data, sizeof(probe_data)-1)) + return AVPROBE_SCORE_MAX; - case MATROSKA_ID_TITLE: { - char *text; - if ((res = ebml_read_utf8(matroska, &id, &text)) < 0) - break; - strncpy(matroska->ctx->title, text, - sizeof(matroska->ctx->title)-1); - av_free(text); - break; - } + return 0; +} - 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; - } +static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska, + int num) +{ + MatroskaTrack *tracks = matroska->tracks.elem; + int i; - if (matroska->level_up) { - matroska->level_up--; - break; - } - } + for (i=0; i < matroska->tracks.nb_elem; i++) + if (tracks[i].num == num) + return &tracks[i]; - return res; + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num); + return NULL; } -static int -matroska_decode_buffer(uint8_t** buf, int* buf_size, MatroskaTrack *track) +static int matroska_decode_buffer(uint8_t** buf, int* buf_size, + MatroskaTrack *track) { + MatroskaTrackEncoding *encodings = track->encodings.elem; uint8_t* data = *buf; int isize = *buf_size; uint8_t* pkt_data = NULL; @@ -1076,9 +863,9 @@ matroska_decode_buffer(uint8_t** buf, int* buf_size, MatroskaTrack *track) int result = 0; int olen; - switch (track->encoding_algo) { + switch (encodings[0].compression.algo) { case MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP: - return track->encoding_settings_len; + return encodings[0].compression.settings.size; case MATROSKA_TRACK_ENCODING_COMP_LZO: do { olen = pkt_size *= 3; @@ -1132,1095 +919,92 @@ matroska_decode_buffer(uint8_t** buf, int* buf_size, MatroskaTrack *track) break; } #endif + default: + return -1; } *buf = pkt_data; *buf_size = pkt_size; - return 0; - failed: - av_free(pkt_data); - return -1; -} - -static int -matroska_add_stream (MatroskaDemuxContext *matroska) -{ - int res = 0; - 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. */ - track = av_mallocz(MAX_TRACK_SIZE); - track->time_scale = 1.0; - strcpy(track->language, "eng"); - - /* try reading the trackentry headers */ - 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) { - /* 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 ((res = ebml_read_uint(matroska, &id, &num)) < 0) - break; - if (track->type && track->type != num) { - av_log(matroska->ctx, AV_LOG_INFO, - "More than one tracktype in an entry - skip\n"); - break; - } - track->type = num; - - switch (track->type) { - case MATROSKA_TRACK_TYPE_VIDEO: - case MATROSKA_TRACK_TYPE_AUDIO: - case MATROSKA_TRACK_TYPE_SUBTITLE: - 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 = MATROSKA_TRACK_TYPE_NONE; - break; - } - break; - } - - /* tracktype specific stuff for video */ - case MATROSKA_ID_TRACKVIDEO: { - MatroskaVideoTrack *videotrack; - if (!track->type) - track->type = MATROSKA_TRACK_TYPE_VIDEO; - 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(EIO); - 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; - if (!track->default_duration) - track->default_duration = 1000000000/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; - break; - } - - /* colorspace (only matters for raw video) - * fourcc */ - case MATROSKA_ID_VIDEOCOLORSPACE: { - 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 MATROSKA_ID_VIDEOSTEREOMODE: - case MATROSKA_ID_VIDEOASPECTRATIO: - 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) - track->type = MATROSKA_TRACK_TYPE_AUDIO; - 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; - audiotrack->channels = 1; - audiotrack->samplerate = 8000; - - 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) { - /* samplerate */ - case MATROSKA_ID_AUDIOSAMPLINGFREQ: { - double num; - if ((res = ebml_read_float(matroska, &id, - &num)) < 0) - break; - audiotrack->internal_samplerate = - audiotrack->samplerate = num; - break; - } - - case MATROSKA_ID_AUDIOOUTSAMPLINGFREQ: { - 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 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, *end; - if ((res = ebml_read_utf8(matroska, &id, &text)) < 0) - break; - if ((end = strchr(text, '-'))) - *end = '\0'; - if (strlen(text) == 3) - strcpy(track->language, text); - av_free(text); - break; - } - - /* whether this is actually used */ - case MATROSKA_ID_TRACKFLAGENABLED: { - uint64_t num; - if ((res = ebml_read_uint(matroska, &id, &num)) < 0) - break; - 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; - track->flag_default = num; - 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; - 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; - } - - case MATROSKA_ID_TRACKCONTENTENCODINGS: { - if ((res = ebml_read_master(matroska, &id)) < 0) - break; - - while (res == 0) { - if (!(id = ebml_peek_id(matroska, &matroska->level_up))) { - res = AVERROR(EIO); - break; - } else if (matroska->level_up > 0) { - matroska->level_up--; - break; - } - - switch (id) { - case MATROSKA_ID_TRACKCONTENTENCODING: { - int encoding_scope = 1; - if ((res = ebml_read_master(matroska, &id)) < 0) - break; - - while (res == 0) { - if (!(id = ebml_peek_id(matroska, &matroska->level_up))) { - res = AVERROR(EIO); - break; - } else if (matroska->level_up > 0) { - matroska->level_up--; - break; - } - - switch (id) { - case MATROSKA_ID_ENCODINGSCOPE: { - uint64_t num; - if ((res = ebml_read_uint(matroska, &id, &num)) < 0) - break; - encoding_scope = num; - break; - } - - case MATROSKA_ID_ENCODINGTYPE: { - uint64_t num; - if ((res = ebml_read_uint(matroska, &id, &num)) < 0) - break; - if (num) - av_log(matroska->ctx, AV_LOG_ERROR, - "Unsupported encoding type"); - break; - } - - case MATROSKA_ID_ENCODINGCOMPRESSION: { - if ((res = ebml_read_master(matroska, &id)) < 0) - break; - - while (res == 0) { - if (!(id = ebml_peek_id(matroska, &matroska->level_up))) { - res = AVERROR(EIO); - break; - } else if (matroska->level_up > 0) { - matroska->level_up--; - break; - } - - switch (id) { - case MATROSKA_ID_ENCODINGCOMPALGO: { - uint64_t num; - if ((res = ebml_read_uint(matroska, &id, &num)) < 0) - break; - if (num != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP && -#ifdef CONFIG_ZLIB - num != MATROSKA_TRACK_ENCODING_COMP_ZLIB && -#endif -#ifdef CONFIG_BZLIB - num != MATROSKA_TRACK_ENCODING_COMP_BZLIB && -#endif - num != MATROSKA_TRACK_ENCODING_COMP_LZO) - av_log(matroska->ctx, AV_LOG_ERROR, - "Unsupported compression algo\n"); - track->encoding_algo = num; - break; - } - - case MATROSKA_ID_ENCODINGCOMPSETTINGS: { - uint8_t *data; - int size; - if ((res = ebml_read_binary(matroska, &id, &data, &size) < 0)) - break; - track->encoding_settings = data; - track->encoding_settings_len = size; - break; - } - - default: - av_log(matroska->ctx, AV_LOG_INFO, - "Unknown compression header entry " - "0x%x - ignoring\n", id); - /* pass-through */ - - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; - } - - if (matroska->level_up) { - matroska->level_up--; - break; - } - } - break; - } - - default: - av_log(matroska->ctx, AV_LOG_INFO, - "Unknown content encoding header entry " - "0x%x - ignoring\n", id); - /* pass-through */ - - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; - } - - if (matroska->level_up) { - matroska->level_up--; - break; - } - } - - track->encoding_scope = encoding_scope; - break; - } - - default: - av_log(matroska->ctx, AV_LOG_INFO, - "Unknown content encodings header entry " - "0x%x - ignoring\n", id); - /* pass-through */ - - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; - } - - if (matroska->level_up) { - matroska->level_up--; - break; - } - } - break; - } - - 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 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 (matroska->level_up) { - matroska->level_up--; - 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_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(EIO); - 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) -{ - 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) -{ - 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(EIO); - 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; - 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(EIO); - 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/%"PRIu64")\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) - goto finish; - - /* 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++; - dummy_level = 1; - - /* check ID */ - if (!(id = ebml_peek_id (matroska, - &matroska->level_up))) - goto finish; - if (id != seek_id) { - av_log(matroska->ctx, AV_LOG_INFO, - "We looked for ID=0x%x but got " - "ID=0x%x (pos=%"PRIu64")", - seek_id, id, seek_pos + - matroska->segment_start); - goto finish; - } - - /* read master + parse */ - 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; - } - - finish: - /* remove dummy level */ - 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; - 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_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; - int res = 0; - uint32_t id; - - av_log(s, AV_LOG_DEBUG, "parsing chapters...\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_EDITIONENTRY: { - uint64_t end = AV_NOPTS_VALUE, start = AV_NOPTS_VALUE; - int64_t uid= -1; - char* title = NULL; - /* if there is more than one chapter edition - we take only the first one */ - if(s->chapters) { - ebml_read_skip(matroska); - break; - } - - 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_CHAPTERATOM: - 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_CHAPTERTIMEEND: - res = ebml_read_uint(matroska, &id, &end); - break; - - case MATROSKA_ID_CHAPTERTIMESTART: - res = ebml_read_uint(matroska, &id, &start); - break; - - case MATROSKA_ID_CHAPTERDISPLAY: - 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_CHAPSTRING: - res = ebml_read_utf8(matroska, &id, &title); - break; - - default: - av_log(s, AV_LOG_INFO, "Ignoring unknown Chapter display ID 0x%x\n", id); - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; - } - - if (matroska->level_up) { - matroska->level_up--; - break; - } - } - break; + return 0; + failed: + av_free(pkt_data); + return -1; +} - case MATROSKA_ID_CHAPTERUID: - res = ebml_read_uint(matroska, &id, &uid); - break; - default: - av_log(s, AV_LOG_INFO, "Ignoring unknown Chapter atom ID 0x%x\n", id); - case MATROSKA_ID_CHAPTERFLAGHIDDEN: - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; - } +static void matroska_convert_tags(AVFormatContext *s, EbmlList *list) +{ + MatroskaTag *tags = list->elem; + int i, j; - if (matroska->level_up) { - matroska->level_up--; - break; - } - } + for (i=0; i < list->nb_elem; i++) { + for (j=0; j < ARRAY_SIZE(metadata); j++){ + if (!strcmp(tags[i].name, metadata[j].name)) { + int *ptr = (int *)((char *)s + metadata[j].offset); + if (*ptr) continue; + if (metadata[j].size > sizeof(int)) + av_strlcpy((char *)ptr, tags[i].string, metadata[j].size); + else + *ptr = atoi(tags[i].string); + } + } + if (tags[i].sub.nb_elem) + matroska_convert_tags(s, &tags[i].sub); + } +} - if (start != AV_NOPTS_VALUE && uid != -1) { - if(!ff_new_chapter(s, uid, (AVRational){1, 1000000000}, start, end, title)) - res= AVERROR(ENOMEM); - } - av_free(title); - break; +static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) +{ + EbmlList *seekhead_list = &matroska->seekhead; + MatroskaSeekhead *seekhead = seekhead_list->elem; + uint32_t level_up = matroska->level_up; + offset_t before_pos = url_ftell(matroska->ctx->pb); + MatroskaLevel level; + int i; - default: - av_log(s, AV_LOG_INFO, "Ignoring unknown Edition entry ID 0x%x\n", id); - case MATROSKA_ID_EDITIONUID: - case MATROSKA_ID_EDITIONFLAGHIDDEN: - case MATROSKA_ID_EDITIONFLAGDEFAULT: - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; - } + for (i=0; inb_elem; i++) { + offset_t offset = seekhead[i].pos + matroska->segment_start; + if (seekhead[i].pos <= before_pos + || seekhead[i].id == MATROSKA_ID_SEEKHEAD + || seekhead[i].id == MATROSKA_ID_CLUSTER) + continue; - if (matroska->level_up) { - matroska->level_up--; - break; - } - } - break; - } + /* seek */ + if (url_fseek(matroska->ctx->pb, offset, SEEK_SET) != offset) + continue; - default: - av_log(s, AV_LOG_INFO, "Expected an Edition entry (0x%x), but found 0x%x\n", MATROSKA_ID_EDITIONENTRY, id); - case EBML_ID_VOID: - res = ebml_read_skip(matroska); + /* We don't want to lose our seekhead level, so we add + * a dummy. This is a crude hack. */ + if (matroska->num_levels == EBML_MAX_DEPTH) { + av_log(matroska->ctx, AV_LOG_INFO, + "Max EBML element depth (%d) reached, " + "cannot parse further.\n", EBML_MAX_DEPTH); break; } - if (matroska->level_up) { - matroska->level_up--; - break; + level.start = 0; + level.length = (uint64_t)-1; + matroska->levels[matroska->num_levels] = level; + matroska->num_levels++; + + ebml_parse(matroska, matroska_segment, matroska); + + /* remove dummy level */ + while (matroska->num_levels) { + uint64_t length = matroska->levels[--matroska->num_levels].length; + if (length == (uint64_t)-1) + break; } } - return res; + /* seek back */ + url_fseek(matroska->ctx->pb, before_pos, SEEK_SET); + matroska->level_up = level_up; } -static int -matroska_aac_profile (char *codec_id) +static int matroska_aac_profile(char *codec_id) { - static const char *aac_profiles[] = { - "MAIN", "LC", "SSR" - }; + static const char *aac_profiles[] = { "MAIN", "LC", "SSR" }; int profile; for (profile=0; profilepriv_data; + EbmlList *attachements_list = &matroska->attachments; + MatroskaAttachement *attachements; + EbmlList *chapters_list = &matroska->chapters; + MatroskaChapter *chapters; + MatroskaTrack *tracks; EbmlList *index_list; MatroskaIndex *index; - int i, j, last_level, res = 0; Ebml ebml = { 0 }; - uint32_t id; + AVStream *st; + int i, j; matroska->ctx = s; /* First read the EBML header. */ - if (ebml_parse(matroska, ebml_syntax, &ebml, 0, 1) + if (ebml_parse(matroska, ebml_syntax, &ebml) || ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t) || ebml.id_length > sizeof(uint32_t) || strcmp(ebml.doctype, "matroska") || ebml.doctype_version > 2) { @@ -2267,324 +1053,277 @@ matroska_read_header (AVFormatContext *s, ebml_free(ebml_syntax, &ebml); /* The next thing is a segment. */ - while (1) { - if (!(id = ebml_peek_id(matroska, &last_level))) - return AVERROR(EIO); - 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(EIO); - break; - } else if (matroska->level_up) { - matroska->level_up--; - break; + if (ebml_parse(matroska, matroska_segments, matroska) < 0) + return -1; + matroska_execute_seekhead(matroska); + + if (matroska->duration) + matroska->ctx->duration = matroska->duration * matroska->time_scale + * 1000 / AV_TIME_BASE; + if (matroska->title) + strncpy(matroska->ctx->title, matroska->title, + sizeof(matroska->ctx->title)-1); + matroska_convert_tags(s, &matroska->tags); + + tracks = matroska->tracks.elem; + for (i=0; i < matroska->tracks.nb_elem; i++) { + MatroskaTrack *track = &tracks[i]; + enum CodecID codec_id = CODEC_ID_NONE; + EbmlList *encodings_list = &tracks->encodings; + MatroskaTrackEncoding *encodings = encodings_list->elem; + uint8_t *extradata = NULL; + int extradata_size = 0; + int extradata_offset = 0; + + /* Apply some sanity checks. */ + if (track->type != MATROSKA_TRACK_TYPE_VIDEO && + track->type != MATROSKA_TRACK_TYPE_AUDIO && + track->type != MATROSKA_TRACK_TYPE_SUBTITLE) { + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown or unsupported track type %"PRIu64"\n", + track->type); + continue; } - - switch (id) { - /* stream info */ - case MATROSKA_ID_INFO: { - if ((res = ebml_read_master(matroska, &id)) < 0) - break; - res = matroska_parse_info(matroska); - break; + if (track->codec_id == NULL) + continue; + + if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + if (!track->default_duration) + track->default_duration = 1000000000/track->video.frame_rate; + if (!track->video.display_width) + track->video.display_width = track->video.pixel_width; + if (!track->video.display_height) + track->video.display_height = track->video.pixel_height; + } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { + if (!track->audio.out_samplerate) + track->audio.out_samplerate = track->audio.samplerate; + } + if (encodings_list->nb_elem > 1) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Multiple combined encodings no supported"); + } else if (encodings_list->nb_elem == 1) { + if (encodings[0].type || + (encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP && +#ifdef CONFIG_ZLIB + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB && +#endif +#ifdef CONFIG_BZLIB + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_BZLIB && +#endif + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO)) { + encodings[0].scope = 0; + av_log(matroska->ctx, AV_LOG_ERROR, + "Unsupported encoding type"); + } else if (track->codec_priv.size && encodings[0].scope&2) { + uint8_t *codec_priv = track->codec_priv.data; + int offset = matroska_decode_buffer(&track->codec_priv.data, + &track->codec_priv.size, + track); + if (offset < 0) { + track->codec_priv.data = NULL; + track->codec_priv.size = 0; + av_log(matroska->ctx, AV_LOG_ERROR, + "Failed to decode codec private data\n"); + } else if (offset > 0) { + track->codec_priv.data = av_malloc(track->codec_priv.size + offset); + memcpy(track->codec_priv.data, + encodings[0].compression.settings.data, offset); + memcpy(track->codec_priv.data+offset, codec_priv, + track->codec_priv.size); + track->codec_priv.size += offset; + } + if (codec_priv != track->codec_priv.data) + av_free(codec_priv); } + } - /* track info headers */ - case MATROSKA_ID_TRACKS: { - if ((res = ebml_read_master(matroska, &id)) < 0) - break; - res = matroska_parse_tracks(matroska); + 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; break; } + } - /* stream index */ - case MATROSKA_ID_CUES: { - if (!matroska->index_parsed) { - res = matroska_parse_index(matroska); - } else - res = ebml_read_skip(matroska); - break; + st = track->stream = av_new_stream(s, 0); + if (st == NULL) + return AVERROR(ENOMEM); + + if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") + && track->codec_priv.size >= 40 + && track->codec_priv.data != NULL) { + track->video.fourcc = AV_RL32(track->codec_priv.data + 16); + codec_id = codec_get_id(codec_bmp_tags, track->video.fourcc); + } else if (!strcmp(track->codec_id, "A_MS/ACM") + && track->codec_priv.size >= 18 + && track->codec_priv.data != NULL) { + uint16_t tag = AV_RL16(track->codec_priv.data); + codec_id = codec_get_id(codec_wav_tags, tag); + } else if (!strcmp(track->codec_id, "V_QUICKTIME") + && (track->codec_priv.size >= 86) + && (track->codec_priv.data != NULL)) { + track->video.fourcc = AV_RL32(track->codec_priv.data); + codec_id=codec_get_id(codec_movvideo_tags, track->video.fourcc); + } else if (codec_id == CODEC_ID_PCM_S16BE) { + switch (track->audio.bitdepth) { + case 8: codec_id = CODEC_ID_PCM_U8; break; + case 24: codec_id = CODEC_ID_PCM_S24BE; break; + case 32: codec_id = CODEC_ID_PCM_S32BE; break; } - - /* metadata */ - case MATROSKA_ID_TAGS: { - if (!matroska->metadata_parsed) { - res = matroska_parse_metadata(matroska); - } else - res = ebml_read_skip(matroska); - break; + } else if (codec_id == CODEC_ID_PCM_S16LE) { + switch (track->audio.bitdepth) { + case 8: codec_id = CODEC_ID_PCM_U8; break; + case 24: codec_id = CODEC_ID_PCM_S24LE; break; + case 32: codec_id = CODEC_ID_PCM_S32LE; 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; + } else if (codec_id==CODEC_ID_PCM_F32LE && track->audio.bitdepth==64) { + codec_id = CODEC_ID_PCM_F64LE; + } else if (codec_id == CODEC_ID_AAC && !track->codec_priv.size) { + int profile = matroska_aac_profile(track->codec_id); + int sri = matroska_aac_sri(track->audio.samplerate); + extradata = av_malloc(5); + if (extradata == NULL) + return AVERROR(ENOMEM); + extradata[0] = (profile << 3) | ((sri&0x0E) >> 1); + extradata[1] = ((sri&0x01) << 7) | (track->audio.channels<<3); + if (strstr(track->codec_id, "SBR")) { + sri = matroska_aac_sri(track->audio.out_samplerate); + extradata[2] = 0x56; + extradata[3] = 0xE5; + extradata[4] = 0x80 | (sri<<3); + extradata_size = 5; + } else + extradata_size = 2; + } else if (codec_id == CODEC_ID_TTA) { + ByteIOContext b; + extradata_size = 30; + extradata = av_mallocz(extradata_size); + if (extradata == NULL) + return AVERROR(ENOMEM); + init_put_byte(&b, extradata, extradata_size, 1, + NULL, NULL, NULL, NULL); + put_buffer(&b, "TTA1", 4); + put_le16(&b, 1); + put_le16(&b, track->audio.channels); + put_le16(&b, track->audio.bitdepth); + put_le32(&b, track->audio.out_samplerate); + put_le32(&b, matroska->ctx->duration * track->audio.out_samplerate); + } else if (codec_id == CODEC_ID_RV10 || codec_id == CODEC_ID_RV20 || + codec_id == CODEC_ID_RV30 || codec_id == CODEC_ID_RV40) { + extradata_offset = 26; + track->codec_priv.size -= extradata_offset; + } else if (codec_id == CODEC_ID_RA_144) { + track->audio.out_samplerate = 8000; + track->audio.channels = 1; + } else if (codec_id == CODEC_ID_RA_288 || codec_id == CODEC_ID_COOK || + codec_id == CODEC_ID_ATRAC3) { + ByteIOContext b; + + init_put_byte(&b, track->codec_priv.data,track->codec_priv.size, + 0, NULL, NULL, NULL, NULL); + url_fskip(&b, 24); + track->audio.coded_framesize = get_be32(&b); + url_fskip(&b, 12); + track->audio.sub_packet_h = get_be16(&b); + track->audio.frame_size = get_be16(&b); + track->audio.sub_packet_size = get_be16(&b); + track->audio.buf = av_malloc(track->audio.frame_size * track->audio.sub_packet_h); + if (codec_id == CODEC_ID_RA_288) { + st->codec->block_align = track->audio.coded_framesize; + track->codec_priv.size = 0; + } else { + st->codec->block_align = track->audio.sub_packet_size; + extradata_offset = 78; + track->codec_priv.size -= extradata_offset; } + } - case MATROSKA_ID_ATTACHMENTS: { - if ((res = ebml_read_master(matroska, &id)) < 0) - break; - res = matroska_parse_attachments(s); - break; - } + if (codec_id == CODEC_ID_NONE) + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown/unsupported CodecID %s.\n", track->codec_id); - case MATROSKA_ID_CLUSTER: { - /* Do not read the master - this will be done in the next - * call to matroska_read_packet. */ - res = 1; - break; - } + av_set_pts_info(st, 64, matroska->time_scale*track->time_scale, 1000*1000*1000); /* 64 bit pts in ns */ - case MATROSKA_ID_CHAPTERS: { - if ((res = ebml_read_master(matroska, &id)) < 0) - return res; - res = matroska_parse_chapters(s); - break; - } + st->codec->codec_id = codec_id; + st->start_time = 0; + if (strcmp(track->language, "und")) + av_strlcpy(st->language, track->language, 4); - default: - av_log(matroska->ctx, AV_LOG_INFO, - "Unknown matroska file header ID 0x%x\n", id); - /* fall-through */ + if (track->flag_default) + st->disposition |= AV_DISPOSITION_DEFAULT; - case EBML_ID_VOID: - res = ebml_read_skip(matroska); - break; + if (track->default_duration) + av_reduce(&st->codec->time_base.num, &st->codec->time_base.den, + track->default_duration, 1000000000, 30000); + + if(extradata){ + st->codec->extradata = extradata; + st->codec->extradata_size = extradata_size; + } else if(track->codec_priv.data && track->codec_priv.size > 0){ + st->codec->extradata = av_malloc(track->codec_priv.size); + if(st->codec->extradata == NULL) + return AVERROR(ENOMEM); + st->codec->extradata_size = track->codec_priv.size; + memcpy(st->codec->extradata, + track->codec_priv.data + extradata_offset, + track->codec_priv.size); } - if (matroska->level_up) { - matroska->level_up--; - break; + if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + st->codec->codec_type = CODEC_TYPE_VIDEO; + st->codec->codec_tag = track->video.fourcc; + st->codec->width = track->video.pixel_width; + st->codec->height = track->video.pixel_height; + av_reduce(&st->codec->sample_aspect_ratio.num, + &st->codec->sample_aspect_ratio.den, + st->codec->height * track->video.display_width, + st->codec-> width * track->video.display_height, + 255); + st->need_parsing = AVSTREAM_PARSE_HEADERS; + } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { + st->codec->codec_type = CODEC_TYPE_AUDIO; + st->codec->sample_rate = track->audio.out_samplerate; + st->codec->channels = track->audio.channels; + } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) { + st->codec->codec_type = CODEC_TYPE_SUBTITLE; } } - /* Have we found a cluster? */ - if (ebml_peek_id(matroska, NULL) == MATROSKA_ID_CLUSTER) { - int i, j; - MatroskaTrack *track; - AVStream *st; - - for (i = 0; i < matroska->num_tracks; i++) { - enum CodecID codec_id = CODEC_ID_NONE; - uint8_t *extradata = NULL; - int extradata_size = 0; - int extradata_offset = 0; - track = matroska->tracks[i]; - - /* Apply some sanity checks. */ - if (track->codec_id == NULL) - continue; - - 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; - break; - } - } - - st = track->stream = av_new_stream(s, matroska->num_streams++); + attachements = attachements_list->elem; + for (j=0; jnb_elem; j++) { + if (!(attachements[j].filename && attachements[j].mime && + attachements[j].bin.data && attachements[j].bin.size > 0)) { + av_log(matroska->ctx, AV_LOG_ERROR, "incomplete attachment\n"); + } else { + AVStream *st = av_new_stream(s, 0); 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, - MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC) && - (track->codec_priv_size >= 40) && - (track->codec_priv != NULL)) { - MatroskaVideoTrack *vtrack = (MatroskaVideoTrack *) track; - - /* Offset of biCompression. Stored in LE. */ - vtrack->fourcc = AV_RL32(track->codec_priv + 16); - codec_id = codec_get_id(codec_bmp_tags, vtrack->fourcc); - - } - - /* 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)) { - uint16_t tag; - - /* Offset of wFormatTag. Stored in LE. */ - tag = AV_RL16(track->codec_priv); - codec_id = codec_get_id(codec_wav_tags, tag); - - } - - 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); - int sri = matroska_aac_sri(audiotrack->internal_samplerate); - extradata = av_malloc(5); - if (extradata == NULL) - return AVERROR(ENOMEM); - extradata[0] = (profile << 3) | ((sri&0x0E) >> 1); - extradata[1] = ((sri&0x01) << 7) | (audiotrack->channels<<3); - if (strstr(track->codec_id, "SBR")) { - sri = matroska_aac_sri(audiotrack->samplerate); - extradata[2] = 0x56; - extradata[3] = 0xE5; - extradata[4] = 0x80 | (sri<<3); - extradata_size = 5; - } else { - extradata_size = 2; - } - } - - else if (codec_id == CODEC_ID_TTA) { - MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *) track; - ByteIOContext b; - extradata_size = 30; - extradata = av_mallocz(extradata_size); - if (extradata == NULL) - return AVERROR(ENOMEM); - init_put_byte(&b, extradata, extradata_size, 1, - NULL, NULL, NULL, NULL); - put_buffer(&b, "TTA1", 4); - put_le16(&b, 1); - put_le16(&b, audiotrack->channels); - put_le16(&b, audiotrack->bitdepth); - put_le32(&b, audiotrack->samplerate); - put_le32(&b, matroska->ctx->duration * audiotrack->samplerate); - } - - else if (codec_id == CODEC_ID_RV10 || codec_id == CODEC_ID_RV20 || - codec_id == CODEC_ID_RV30 || codec_id == CODEC_ID_RV40) { - extradata_offset = 26; - track->codec_priv_size -= extradata_offset; - } - - else if (codec_id == CODEC_ID_RA_144) { - MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track; - audiotrack->samplerate = 8000; - audiotrack->channels = 1; - } + break; + st->filename = av_strdup(attachements[j].filename); + st->codec->codec_id = CODEC_ID_NONE; + st->codec->codec_type = CODEC_TYPE_ATTACHMENT; + st->codec->extradata = av_malloc(attachements[j].bin.size); + if(st->codec->extradata == NULL) + break; + st->codec->extradata_size = attachements[j].bin.size; + memcpy(st->codec->extradata, attachements[j].bin.data, attachements[j].bin.size); - else if (codec_id == CODEC_ID_RA_288 || - codec_id == CODEC_ID_COOK || - codec_id == CODEC_ID_ATRAC3) { - MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track; - ByteIOContext b; - - init_put_byte(&b, track->codec_priv, track->codec_priv_size, 0, - NULL, NULL, NULL, NULL); - url_fskip(&b, 24); - audiotrack->coded_framesize = get_be32(&b); - url_fskip(&b, 12); - audiotrack->sub_packet_h = get_be16(&b); - audiotrack->frame_size = get_be16(&b); - audiotrack->sub_packet_size = get_be16(&b); - audiotrack->buf = av_malloc(audiotrack->frame_size * audiotrack->sub_packet_h); - if (codec_id == CODEC_ID_RA_288) { - audiotrack->block_align = audiotrack->coded_framesize; - track->codec_priv_size = 0; - } else { - audiotrack->block_align = audiotrack->sub_packet_size; - extradata_offset = 78; - track->codec_priv_size -= extradata_offset; + for (i=0; ff_mkv_mime_tags[i].id != CODEC_ID_NONE; i++) { + if (!strncmp(ff_mkv_mime_tags[i].str, attachements[j].mime, + strlen(ff_mkv_mime_tags[i].str))) { + st->codec->codec_id = ff_mkv_mime_tags[i].id; + break; } } - - if (codec_id == CODEC_ID_NONE) { - av_log(matroska->ctx, AV_LOG_INFO, - "Unknown/unsupported CodecID %s.\n", - track->codec_id); - } - - 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")) - 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, 1000000000, 30000); - - if(extradata){ - st->codec->extradata = extradata; - st->codec->extradata_size = extradata_size; - } else if(track->codec_priv && track->codec_priv_size > 0){ - st->codec->extradata = av_malloc(track->codec_priv_size); - if(st->codec->extradata == NULL) - return AVERROR(ENOMEM); - st->codec->extradata_size = track->codec_priv_size; - memcpy(st->codec->extradata,track->codec_priv+extradata_offset, - track->codec_priv_size); - } - - 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) - videotrack->display_width= videotrack->pixel_width; - if (videotrack->display_height == 0) - videotrack->display_height= videotrack->pixel_height; - av_reduce(&st->codec->sample_aspect_ratio.num, - &st->codec->sample_aspect_ratio.den, - st->codec->height * videotrack->display_width, - st->codec-> width * videotrack->display_height, - 255); - st->need_parsing = AVSTREAM_PARSE_HEADERS; - } 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; - st->codec->block_align = audiotrack->block_align; - } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) { - st->codec->codec_type = CODEC_TYPE_SUBTITLE; - } - - /* What do we do with private data? E.g. for Vorbis. */ } - res = 0; } + chapters = chapters_list->elem; + for (i=0; inb_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); + index_list = &matroska->index; index = index_list->elem; for (i=0; inb_elem; i++) { @@ -2601,50 +1340,83 @@ matroska_read_header (AVFormatContext *s, } } - return res; + 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_freep(&matroska->packets); + } + matroska->num_packets--; + return 0; + } + + return -1; +} + +/* + * 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_freep(&matroska->packets); + matroska->num_packets = 0; + } } -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) +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) { MatroskaTrack *track; int res = 0; AVStream *st; AVPacket *pkt; - uint8_t *origdata = data; int16_t block_time; uint32_t *lace_size = NULL; int n, flags, laces = 0; uint64_t num; - /* first byte(s): tracknum */ - if ((n = matroska_ebmlnum_uint(data, size, &num)) < 0) { + if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n"); - av_free(origdata); return res; } data += n; size -= n; - /* fetch track from num */ track = matroska_find_track_by_num(matroska, num); if (size <= 3 || !track || !track->stream) { av_log(matroska->ctx, AV_LOG_INFO, "Invalid stream %"PRIu64" or size %u\n", num, size); - av_free(origdata); return res; } st = track->stream; - if (st->discard >= AVDISCARD_ALL) { - av_free(origdata); + if (st->discard >= AVDISCARD_ALL) return res; - } if (duration == AV_NOPTS_VALUE) duration = track->default_duration / matroska->time_scale; - /* block_time (relative to cluster time) */ block_time = AV_RB16(data); data += 2; flags = *data++; @@ -2653,10 +1425,8 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, is_keyframe = flags & 0x80 ? PKT_FLAG_KEY : 0; if (matroska->skip_to_keyframe) { - if (!is_keyframe || st != matroska->skip_to_stream) { - av_free(origdata); + if (!is_keyframe || st != matroska->skip_to_stream) return res; - } matroska->skip_to_keyframe = 0; } @@ -2667,7 +1437,7 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, lace_size[0] = size; break; - case 0x1: /* xiph lacing */ + case 0x1: /* Xiph lacing */ case 0x2: /* fixed-size lacing */ case 0x3: /* EBML lacing */ assert(size>0); // size <=3 is checked before size-=3 above @@ -2677,7 +1447,7 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, lace_size = av_mallocz(laces * sizeof(int)); switch ((flags & 0x06) >> 1) { - case 0x1: /* xiph lacing */ { + case 0x1: /* Xiph lacing */ { uint8_t temp; uint32_t total = 0; for (n = 0; res == 0 && n < laces - 1; n++) { @@ -2706,7 +1476,7 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, case 0x3: /* EBML lacing */ { uint32_t total; - n = matroska_ebmlnum_uint(data, size, &num); + n = matroska_ebmlnum_uint(matroska, data, size, &num); if (n < 0) { av_log(matroska->ctx, AV_LOG_INFO, "EBML block data error\n"); @@ -2718,7 +1488,7 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, for (n = 1; res == 0 && n < laces - 1; n++) { int64_t snum; int r; - r = matroska_ebmlnum_sint (data, size, &snum); + r = matroska_ebmlnum_sint(matroska, data, size, &snum); if (r < 0) { av_log(matroska->ctx, AV_LOG_INFO, "EBML block data error\n"); @@ -2747,45 +1517,44 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, 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 sps = track->audio.sub_packet_size; + int cfs = track->audio.coded_framesize; + int h = track->audio.sub_packet_h; + int y = track->audio.sub_packet_cnt; + int w = track->audio.frame_size; int x; - if (!audiotrack->pkt_cnt) { + if (!track->audio.pkt_cnt) { if (st->codec->codec_id == CODEC_ID_RA_288) for (x=0; xbuf+x*2*w+y*cfs, + memcpy(track->audio.buf+x*2*w+y*cfs, data+x*cfs, cfs); else for (x=0; xbuf+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), data+x*sps, sps); + memcpy(track->audio.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; + if (++track->audio.sub_packet_cnt >= h) { + track->audio.sub_packet_cnt = 0; + track->audio.pkt_cnt = h*w / a; } } - while (audiotrack->pkt_cnt) { + while (track->audio.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); + memcpy(pkt->data, track->audio.buf + + a * (h*w / a - track->audio.pkt_cnt--), a); pkt->pos = pos; pkt->stream_index = st->index; - matroska_queue_packet(matroska, pkt); + dynarray_add(&matroska->packets,&matroska->num_packets,pkt); } } else { + MatroskaTrackEncoding *encodings = track->encodings.elem; 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 (encodings && encodings->scope & 1) { + offset = matroska_decode_buffer(&pkt_data,&pkt_size, track); if (offset < 0) continue; } @@ -2799,7 +1568,7 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, break; } if (offset) - memcpy (pkt->data, track->encoding_settings, offset); + memcpy (pkt->data, encodings->compression.settings.data, offset); memcpy (pkt->data+offset, pkt_data, pkt_size); if (pkt_data != data) @@ -2813,7 +1582,7 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, pkt->pos = pos; pkt->duration = duration; - matroska_queue_packet(matroska, pkt); + dynarray_add(&matroska->packets, &matroska->num_packets, pkt); } if (timecode != AV_NOPTS_VALUE) @@ -2823,251 +1592,81 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, } av_free(lace_size); - av_free(origdata); - return res; -} - -static int -matroska_parse_blockgroup (MatroskaDemuxContext *matroska, - uint64_t cluster_time) -{ - int res = 0; - uint32_t id; - int is_keyframe = PKT_FLAG_KEY, last_num_packets = matroska->num_packets; - uint64_t duration = AV_NOPTS_VALUE; - uint8_t *data; - int size = 0; - int64_t pos = 0; - - av_log(matroska->ctx, AV_LOG_DEBUG, "parsing blockgroup...\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) { - /* 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: { - pos = url_ftell(matroska->ctx->pb); - res = ebml_read_binary(matroska, &id, &data, &size); - break; - } - - case MATROSKA_ID_BLOCKDURATION: { - if ((res = ebml_read_uint(matroska, &id, &duration)) < 0) - break; - break; - } - - case MATROSKA_ID_BLOCKREFERENCE: { - int64_t num; - /* 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; - if ((res = ebml_read_sint(matroska, &id, &num)) < 0) - break; - 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; - } - } - - if (res) - return res; - - if (size > 0) - res = matroska_parse_block(matroska, data, size, pos, cluster_time, - duration, is_keyframe); - return res; } -static int -matroska_parse_cluster (MatroskaDemuxContext *matroska) +static int matroska_parse_cluster(MatroskaDemuxContext *matroska) { - int res = 0; - uint32_t id; - uint64_t cluster_time = 0; - uint8_t *data; - int64_t pos; - int size; - - av_log(matroska->ctx, AV_LOG_DEBUG, - "parsing cluster at %"PRId64"\n", url_ftell(matroska->ctx->pb)); - - 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) { - /* cluster timecode */ - case MATROSKA_ID_CLUSTERTIMECODE: { - uint64_t num; - if ((res = ebml_read_uint(matroska, &id, &num)) < 0) - break; - cluster_time = num; - 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; - - case MATROSKA_ID_SIMPLEBLOCK: - 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); - 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; - } - } - + MatroskaCluster cluster = { 0 }; + EbmlList *blocks_list; + MatroskaBlock *blocks; + int i, res; + if (matroska->has_cluster_id){ + /* For the first cluster we parse, its ID was already read as + part of matroska_read_header(), so don't read it again */ + res = ebml_parse_id(matroska, matroska_clusters, + MATROSKA_ID_CLUSTER, &cluster); + matroska->has_cluster_id = 0; + } else + res = ebml_parse(matroska, matroska_clusters, &cluster); + blocks_list = &cluster.blocks; + blocks = blocks_list->elem; + for (i=0; inb_elem; i++) + if (blocks[i].bin.size > 0) + res=matroska_parse_block(matroska, + blocks[i].bin.data, blocks[i].bin.size, + blocks[i].bin.pos, cluster.timecode, + blocks[i].duration, !blocks[i].reference); + ebml_free(matroska_cluster, &cluster); return res; } -static int -matroska_read_packet (AVFormatContext *s, - AVPacket *pkt) +static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt) { MatroskaDemuxContext *matroska = s->priv_data; - int res; - uint32_t id; - /* Read stream until we have a packet queued. */ while (matroska_deliver_packet(matroska, pkt)) { - - /* Have we already reached the end? */ if (matroska->done) return AVERROR(EIO); - - res = 0; - while (res == 0) { - if (!(id = ebml_peek_id(matroska, &matroska->level_up))) { - return AVERROR(EIO); - } 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) + if (matroska_parse_cluster(matroska) < 0) matroska->done = 1; } return 0; } -static int -matroska_read_seek (AVFormatContext *s, int stream_index, int64_t timestamp, - int flags) +static int matroska_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) { MatroskaDemuxContext *matroska = s->priv_data; AVStream *st = s->streams[stream_index]; int index; - /* find index entry */ index = av_index_search_timestamp(st, timestamp, flags); if (index < 0) return 0; matroska_clear_queue(matroska); - /* do the seek */ 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->peek_id = 0; av_update_cur_dts(s, st, st->index_entries[index].timestamp); return 0; } -static int -matroska_read_close (AVFormatContext *s) +static int matroska_read_close(AVFormatContext *s) { MatroskaDemuxContext *matroska = s->priv_data; - int n = 0; + MatroskaTrack *tracks = matroska->tracks.elem; + int n; 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_priv); - av_free(track->name); - - if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { - MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track; - av_free(audiotrack->buf); - } - - av_free(track); - } - ebml_free(matroska_index, matroska); + for (n=0; n < matroska->tracks.nb_elem; n++) + if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO) + av_free(tracks[n].audio.buf); + ebml_free(matroska_segment, matroska); return 0; }