/*
 * Matroska file demuxer
 * Original copyright (c) 2003-2011 The FFmpeg Project
 *
 * This file is part of Mpxplay/FFmpeg
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * Matroska file demuxer
 * by Ronald Bultje <rbultje@ronald.bitfreak.net>
 * with a little help from Moritz Bunkus <moritz@bunkus.org>
 * totally reworked by Aurelien Jacobs <aurel@gnuage.org>
 * strongly rewritten for Mpxplay by Attila Padar
 * Specs available on the Matroska project page: http://www.matroska.org/.
 */

//#define MPXPLAY_USE_DEBUGF 1
//#define MPXPLAY_USE_DEBUGMSG 1
#define MPXPLAY_DEBUG_OUTPUT stdout

#ifdef MPXPLAY_USE_DEBUGF
 //#define MPXPLAY_MKVDEBUG_ERROR 1
 //#define MPXPLAY_MKVDEBUG_HEADER 1
 //#define MPXPLAY_MKVDEBUG_INFO 1
 //#define MPXPLAY_MKVDEBUG_DEMUX 1
 #define MPXPLAY_MKVDEBUG_SEEK 1
#endif

#include "mpxplay.h"

#ifdef MPXPLAY_LINK_INFILE_FFMPG

#include "ffutils.h"
#include <string.h>

#define MATROSKA_TIME_BASE 1000000000.0 // ns
#define MATROSKA_MAX_BLOCKSIZE 0x4000000 // was 0x10000000 (67108864 vs. 268435456)
#define MATROSKA_BLOCKDATA_INITIAL_BUFSIZE 0x40000 // 262144 (this is usually enough)
#define MATROSKA_LASTERROR_SEEK 1

/* EBML version supported */
#define EBML_VERSION 1

/* top-level master-IDs */
#define EBML_ID_HEADER             0x1A45DFA3
#define EBML_ID_INVALID            0xffffffff

/* IDs in the HEADER master */
#define EBML_ID_EBMLVERSION        0x4286
#define EBML_ID_EBMLREADVERSION    0x42F7
#define EBML_ID_EBMLMAXIDLENGTH    0x42F2
#define EBML_ID_EBMLMAXSIZELENGTH  0x42F3
#define EBML_ID_DOCTYPE            0x4282
#define EBML_ID_DOCTYPEVERSION     0x4287
#define EBML_ID_DOCTYPEREADVERSION 0x4285

/* general EBML types */
#define EBML_ID_VOID               0xEC
#define EBML_ID_CRC32              0xBF

/* toplevel segment */
#define MATROSKA_ID_SEGMENT    0x18538067

/* Matroska top-level master IDs */
#define MATROSKA_ID_INFO       0x1549A966
#define MATROSKA_ID_TRACKS     0x1654AE6B
#define MATROSKA_ID_CUES       0x1C53BB6B
#define MATROSKA_ID_TAGS       0x1254C367
#define MATROSKA_ID_SEEKHEAD   0x114D9B74
#define MATROSKA_ID_ATTACHMENTS 0x1941A469
#define MATROSKA_ID_CLUSTER    0x1F43B675
#define MATROSKA_ID_CHAPTERS   0x1043A770

/* IDs in the info master */
#define MATROSKA_ID_TIMECODESCALE 0x2AD7B1
#define MATROSKA_ID_DURATION   0x4489
#define MATROSKA_ID_TITLE      0x7BA9
#define MATROSKA_ID_WRITINGAPP 0x5741
#define MATROSKA_ID_MUXINGAPP  0x4D80
#define MATROSKA_ID_DATEUTC    0x4461
#define MATROSKA_ID_SEGMENTUID 0x73A4

/* ID in the tracks master */
#define MATROSKA_ID_TRACKENTRY 0xAE

/* IDs in the trackentry master */
#define MATROSKA_ID_TRACKNUMBER 0xD7
#define MATROSKA_ID_TRACKUID   0x73C5
#define MATROSKA_ID_TRACKTYPE  0x83
#define MATROSKA_ID_TRACKAUDIO 0xE1
#define MATROSKA_ID_TRACKVIDEO 0xE0
#define MATROSKA_ID_CODECID    0x86
#define MATROSKA_ID_CODECPRIVATE 0x63A2
#define MATROSKA_ID_CODECNAME  0x258688
#define MATROSKA_ID_CODECINFOURL 0x3B4040
#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240
#define MATROSKA_ID_CODECDECODEALL 0xAA
#define MATROSKA_ID_TRACKNAME  0x536E
#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C
#define MATROSKA_ID_TRACKFLAGENABLED 0xB9
#define MATROSKA_ID_TRACKFLAGDEFAULT 0x88
#define MATROSKA_ID_TRACKFLAGFORCED 0x55AA
#define MATROSKA_ID_TRACKFLAGLACING 0x9C
#define MATROSKA_ID_TRACKMINCACHE 0x6DE7
#define MATROSKA_ID_TRACKMAXCACHE 0x6DF8
#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383
#define MATROSKA_ID_TRACKCONTENTENCODINGS 0x6D80
#define MATROSKA_ID_TRACKCONTENTENCODING 0x6240
#define MATROSKA_ID_TRACKTIMECODESCALE 0x23314F
#define MATROSKA_ID_TRACKOFFSET 0x537F
#define MATROSKA_ID_TRACKMAXBLKADDID 0x55EE

/* IDs in the trackvideo master */
#define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
#define MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0
#define MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA
#define MATROSKA_ID_VIDEOPIXELWIDTH 0xB0
#define MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA
#define MATROSKA_ID_VIDEOPIXELCROPB 0x54AA
#define MATROSKA_ID_VIDEOPIXELCROPT 0x54BB
#define MATROSKA_ID_VIDEOPIXELCROPL 0x54CC
#define MATROSKA_ID_VIDEOPIXELCROPR 0x54DD
#define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2
#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A
#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B9
#define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3
#define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524

/* IDs in the trackaudio master */
#define MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5
#define MATROSKA_ID_AUDIOOUTSAMPLINGFREQ 0x78B5

#define MATROSKA_ID_AUDIOBITDEPTH 0x6264
#define MATROSKA_ID_AUDIOCHANNELS 0x9F

/* IDs in the content encoding master */
#define MATROSKA_ID_ENCODINGORDER 0x5031
#define MATROSKA_ID_ENCODINGSCOPE 0x5032
#define MATROSKA_ID_ENCODINGTYPE 0x5033
#define MATROSKA_ID_ENCODINGCOMPRESSION 0x5034
#define MATROSKA_ID_ENCODINGCOMPALGO 0x4254
#define MATROSKA_ID_ENCODINGCOMPSETTINGS 0x4255

/* ID in the cues master */
#define MATROSKA_ID_POINTENTRY 0xBB

/* IDs in the pointentry master */
#define MATROSKA_ID_CUETIME    0xB3
#define MATROSKA_ID_CUETRACKPOSITION 0xB7

/* IDs in the cuetrackposition master */
#define MATROSKA_ID_CUETRACK   0xF7
#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1
#define MATROSKA_ID_CUEBLOCKNUMBER 0x5378

/* IDs in the tags master */
#define MATROSKA_ID_TAG                 0x7373
#define MATROSKA_ID_SIMPLETAG           0x67C8
#define MATROSKA_ID_TAGNAME             0x45A3
#define MATROSKA_ID_TAGSTRING           0x4487
#define MATROSKA_ID_TAGLANG             0x447A
#define MATROSKA_ID_TAGDEFAULT          0x4484
#define MATROSKA_ID_TAGDEFAULT_BUG      0x44B4
#define MATROSKA_ID_TAGTARGETS          0x63C0
#define MATROSKA_ID_TAGTARGETS_TYPE       0x63CA
#define MATROSKA_ID_TAGTARGETS_TYPEVALUE  0x68CA
#define MATROSKA_ID_TAGTARGETS_TRACKUID   0x63C5
#define MATROSKA_ID_TAGTARGETS_CHAPTERUID 0x63C4
#define MATROSKA_ID_TAGTARGETS_ATTACHUID  0x63C6

/* IDs in the seekhead master */
#define MATROSKA_ID_SEEKENTRY  0x4DBB

/* IDs in the seekpoint master */
#define MATROSKA_ID_SEEKID     0x53AB
#define MATROSKA_ID_SEEKPOSITION 0x53AC

/* IDs in the cluster master */
#define MATROSKA_ID_CLUSTERTIMECODE 0xE7
#define MATROSKA_ID_CLUSTERPOSITION 0xA7
#define MATROSKA_ID_CLUSTERPREVSIZE 0xAB
#define MATROSKA_ID_BLOCKGROUP 0xA0
#define MATROSKA_ID_SIMPLEBLOCK 0xA3

/* IDs in the blockgroup master */
#define MATROSKA_ID_BLOCK      0xA1
#define MATROSKA_ID_BLOCKDURATION 0x9B
#define MATROSKA_ID_BLOCKREFERENCE 0xFB

/* IDs in the attachments master */
/*#define MATROSKA_ID_ATTACHEDFILE        0x61A7
#define MATROSKA_ID_FILEDESC            0x467E
#define MATROSKA_ID_FILENAME            0x466E
#define MATROSKA_ID_FILEMIMETYPE        0x4660
#define MATROSKA_ID_FILEDATA            0x465C
#define MATROSKA_ID_FILEUID             0x46AE*/

/* IDs in the chapters master */
/*#define MATROSKA_ID_EDITIONENTRY        0x45B9
#define MATROSKA_ID_CHAPTERATOM         0xB6
#define MATROSKA_ID_CHAPTERTIMESTART    0x91
#define MATROSKA_ID_CHAPTERTIMEEND      0x92
#define MATROSKA_ID_CHAPTERDISPLAY      0x80
#define MATROSKA_ID_CHAPSTRING          0x85
#define MATROSKA_ID_CHAPLANG            0x437C
#define MATROSKA_ID_EDITIONUID          0x45BC
#define MATROSKA_ID_EDITIONFLAGHIDDEN   0x45BD
#define MATROSKA_ID_EDITIONFLAGDEFAULT  0x45DB
#define MATROSKA_ID_EDITIONFLAGORDERED  0x45DD
#define MATROSKA_ID_CHAPTERUID          0x73C4
#define MATROSKA_ID_CHAPTERFLAGHIDDEN   0x98
#define MATROSKA_ID_CHAPTERFLAGENABLED  0x4598
#define MATROSKA_ID_CHAPTERPHYSEQUIV    0x63C3*/

typedef enum {
  MATROSKA_TRACK_TYPE_NONE     = 0x0,
  MATROSKA_TRACK_TYPE_VIDEO    = 0x1,
  MATROSKA_TRACK_TYPE_AUDIO    = 0x2,
  MATROSKA_TRACK_TYPE_COMPLEX  = 0x3,
  MATROSKA_TRACK_TYPE_LOGO     = 0x10,
  MATROSKA_TRACK_TYPE_SUBTITLE = 0x11,
  MATROSKA_TRACK_TYPE_CONTROL  = 0x20,
} MatroskaTrackType;

typedef enum {
  MATROSKA_TRACK_ENCODING_COMP_ZLIB        = 0,
  MATROSKA_TRACK_ENCODING_COMP_BZLIB       = 1,
  MATROSKA_TRACK_ENCODING_COMP_LZO         = 2,
  MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP = 3,
} MatroskaTrackEncodingCompAlgo;

typedef struct CodecTags{
    char *str;
    enum CodecID id;
}CodecTags;

#define EBML_MAX_DEPTH 16

static const int ff_mpeg4audio_sample_rates[16] = {96000, 88200, 64000, 48000, 44100, 32000,24000, 22050, 16000, 12000, 11025, 8000, 7350};

static const CodecTags ff_mkv_codec_tags[]={
    {"A_AAC"            , CODEC_ID_AAC},
    {"A_AC3"            , CODEC_ID_AC3},
    {"A_DTS"            , CODEC_ID_DTS},
    //{"A_EAC3"           , CODEC_ID_EAC3},
    {"A_FLAC"           , CODEC_ID_FLAC},
    //{"A_MLP"            , CODEC_ID_MLP},
    {"A_MPEG/L2"        , CODEC_ID_MP2},
    //{"A_MPEG/L1"        , CODEC_ID_MP2},
    {"A_MPEG/L3"        , CODEC_ID_MP3},
    {"A_PCM/FLOAT/IEEE" , CODEC_ID_PCM_F32LE},
    {"A_PCM/FLOAT/IEEE" , CODEC_ID_PCM_F64LE},
    {"A_PCM/INT/BIG"    , CODEC_ID_PCM_S16BE},
    {"A_PCM/INT/BIG"    , CODEC_ID_PCM_S24BE},
    {"A_PCM/INT/BIG"    , CODEC_ID_PCM_S32BE},
    {"A_PCM/INT/LIT"    , CODEC_ID_PCM_S16LE},
    {"A_PCM/INT/LIT"    , CODEC_ID_PCM_S24LE},
    {"A_PCM/INT/LIT"    , CODEC_ID_PCM_S32LE},
    {"A_PCM/INT/LIT"    , CODEC_ID_PCM_U8},
    //{"A_QUICKTIME/QDM2" , CODEC_ID_QDM2},
    //{"A_REAL/14_4"      , CODEC_ID_RA_144},
    //{"A_REAL/28_8"      , CODEC_ID_RA_288},
    //{"A_REAL/ATRC"      , CODEC_ID_ATRAC3},
    //{"A_REAL/COOK"      , CODEC_ID_COOK},
    //{"A_REAL/SIPR"      , CODEC_ID_SIPR},
    //{"A_TRUEHD"         , CODEC_ID_TRUEHD},
    //{"A_TTA1"           , CODEC_ID_TTA},
    {"A_VORBIS"         , CODEC_ID_VORBIS},
    {"A_OPUS"           , CODEC_ID_OPUS},
    {"A_WAVPACK4"       , CODEC_ID_WAVPACK},

#ifdef MPXPLAY_LINK_VIDEO
    {"S_TEXT/UTF8"      , CODEC_ID_TEXT},
    //{"S_TEXT/UTF8"      , CODEC_ID_SRT},
    {"S_TEXT/ASCII"     , CODEC_ID_TEXT},
    {"S_TEXT/ASS"       , CODEC_ID_SSA},
    {"S_TEXT/SSA"       , CODEC_ID_SSA},
    {"S_ASS"            , CODEC_ID_SSA},
    {"S_SSA"            , CODEC_ID_SSA},
    {"S_VOBSUB"         , CODEC_ID_DVD_SUBTITLE},
    {"S_HDMV/PGS"       , CODEC_ID_HDMV_PGS_SUBTITLE},

    {"V_DIRAC"          , CODEC_ID_DIRAC},
    {"V_MJPEG"          , CODEC_ID_MJPEG},
    {"V_MPEG1"          , CODEC_ID_MPEG1VIDEO},
    {"V_MPEG2"          , CODEC_ID_MPEG2VIDEO},
    {"V_MPEG4/ISO/ASP"  , CODEC_ID_MPEG4},
    {"V_MPEG4/ISO/AP"   , CODEC_ID_MPEG4},
    {"V_MPEG4/ISO/SP"   , CODEC_ID_MPEG4},
    {"V_MPEG4/ISO/AVC"  , CODEC_ID_H264},
    {"V_MPEG4/MS/V3"    , CODEC_ID_MSMPEG4V3},
    {"V_REAL/RV10"      , CODEC_ID_RV10},
    {"V_REAL/RV20"      , CODEC_ID_RV20},
    {"V_REAL/RV30"      , CODEC_ID_RV30},
    {"V_REAL/RV40"      , CODEC_ID_RV40},
    {"V_SNOW"           , CODEC_ID_SNOW},
    {"V_THEORA"         , CODEC_ID_THEORA},
    {"V_UNCOMPRESSED"   , CODEC_ID_RAWVIDEO},
    //{"V_VP8"            , CODEC_ID_VP8},
#endif

    {""                 , CODEC_ID_NONE}
};

#if 0
static const AVMetadataConv ff_mkv_metadata_conv[] = {
    { "LEAD_PERFORMER", "performer" },
    { "PART_NUMBER"   , "track"  },
    { 0 }
};
#endif

//matr_dec.c
typedef enum {
    EBML_NONE,
    EBML_UINT,
//    EBML_SINT,
    EBML_FLOAT,
    EBML_STR,
    EBML_UTF8,
    EBML_BIN,
    EBML_NEST,
    EBML_PASS,
    EBML_STOP,
    EBML_TYPE_COUNT
} EbmlType;

#ifdef __WATCOMC__
#pragma pack(push,1)
typedef const struct EbmlSyntax{
  uint32_t id;
  uint8_t type;
  uint16_t list_elem_size;
  uint8_t data_offset;
  union{
   uint64_t    u;
   double      f;
   const char *s;
   const struct EbmlSyntax *n;
  }def;
}EbmlSyntax;
#pragma pack(pop)
#else
typedef const struct EbmlSyntax{
  uint32_t id;
  EbmlType type;
  int list_elem_size;
  int data_offset;
  union{
   uint64_t    u;
   double      f;
   const char *s;
   const struct EbmlSyntax *n;
  }def;
}EbmlSyntax;
#endif

typedef struct {
    int nb_elem;
    void *elem;
} EbmlList;

typedef struct {
    int      size;
    uint8_t *data;
    int64_t  pos;
} EbmlBin;

typedef struct {
    uint64_t version;
    uint64_t max_size;
    uint64_t id_length;
    char    *doctype;
    uint64_t doctype_version;
} Ebml;

typedef struct {
    uint64_t algo;
    EbmlBin  settings;
} MatroskaTrackCompression;

typedef struct {
    uint64_t scope;
    uint64_t type;
    MatroskaTrackCompression compression;
} MatroskaTrackEncoding;

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 {
    double   frame_rate;
    uint64_t display_width;
    uint64_t display_height;
    uint64_t pixel_width;
    uint64_t pixel_height;
    uint64_t fourcc;
} MatroskaTrackVideo;

typedef struct {
    uint64_t num;
    uint64_t uid;
    uint64_t type;
    char    *name;
    char    *codec_id;
    EbmlBin  codec_priv;
    char    *language;
    double time_scale;
    uint64_t default_duration;
    uint64_t flag_default;
    uint64_t flag_forced;
    EbmlList encodings;
    AVStream *stream;
    int64_t end_timecode;
//    int64_t time_offset;
    int ms_compat;
    MatroskaTrackAudio audio;
#ifdef MPXPLAY_LINK_VIDEO
    MatroskaTrackVideo video;
#endif
} MatroskaTrack;

/*typedef struct {
    uint64_t start;
    uint64_t end;
    uint64_t uid;
    char    *title;

    AVChapter *chapter;
} MatroskaChapter;*/

typedef struct {
    uint64_t track;
    uint64_t pos;
} MatroskaIndexPos;

typedef struct {
    uint64_t time;
    EbmlList pos;
} MatroskaIndex;

typedef struct {
    char *name;
    char *string;
    char *lang;
    uint64_t def;
    EbmlList sub;
} MatroskaTag;

typedef struct {
    char    *type;
    uint64_t typevalue;
    uint64_t trackuid;
    uint64_t chapteruid;
    uint64_t attachuid;
} MatroskaTagTarget;

typedef struct {
    MatroskaTagTarget target;
    EbmlList tag;
} MatroskaTags;

typedef struct {
    uint64_t id;
    uint64_t pos;
} MatroskaSeekhead;

typedef struct {
    uint64_t start;
    uint64_t length;
} MatroskaLevel;

typedef struct {
    AVFormatContext *ctx;

    int level_up;
    uint32_t current_id;
    uint32_t lasterror;

    uint64_t time_scale;
    double   duration;
    char    *title;
    EbmlList tracks;
    //EbmlList chapters;
    EbmlList index;
    EbmlList tags;
    EbmlList seekhead;

    /* byte position of the segment inside the stream */
    int64_t segment_start;

    /* What to skip before effectively reading a packet. */
    int seek_reset_bufs;
    int skip_to_keyframe;
    uint64_t skip_to_timecode;

    // for new block handling by PDS
    uint64_t firstcluster_filepos;
    uint64_t cluster_filepos;
    uint64_t cluster_timecode;
    int64_t  cluster_size;
    int64_t  blockgroup_size;
    uint64_t block_filepos;
    uint64_t block_duration;
    int64_t  block_fref;
    int64_t  block_bref;
    uint64_t block_timecode;
    uint64_t block_length;
    uint8_t *block_data;
    uint32_t blockdata_bufsize;
    uint32_t is_keyframe;

#ifdef INFFMPG_LINK_LZO
    uint8_t  *block_decode_buffer;
    uint32_t block_decode_bufsize;
#endif

    MatroskaTrack *block_track; // stream info of current block
    uint8_t *lace_data;
    int32_t  lace_length;
    uint32_t lace_counter;
    uint32_t nb_laces;        // number of frames per block
    uint32_t lace_sizes[256]; // frame sizes

    int num_levels;
    MatroskaLevel levels[EBML_MAX_DEPTH];
} MatroskaDemuxContext;

typedef struct {
    uint64_t duration;
    int64_t  reference;
    uint64_t non_simple;
    EbmlBin  bin;
} MatroskaBlock;

typedef struct {
    uint64_t timecode;
    EbmlList blocks;
} MatroskaCluster;

static EbmlSyntax ebml_header[] = {
    { EBML_ID_EBMLREADVERSION,        EBML_UINT, 0, offsetof(Ebml,version), {.u=EBML_VERSION} },
    { EBML_ID_EBMLMAXSIZELENGTH,      EBML_UINT, 0, offsetof(Ebml,max_size), {.u=8} },
    { EBML_ID_EBMLMAXIDLENGTH,        EBML_UINT, 0, offsetof(Ebml,id_length), {.u=4} },
    { EBML_ID_DOCTYPE,                EBML_STR,  0, offsetof(Ebml,doctype), {.s="(none)"} },
    { EBML_ID_DOCTYPEREADVERSION,     EBML_UINT, 0, offsetof(Ebml,doctype_version), {.u=1} },
    { EBML_ID_EBMLVERSION,            EBML_NONE },
    { EBML_ID_DOCTYPEVERSION,         EBML_NONE },
    { 0 }
};

static EbmlSyntax ebml_syntax[] = {
    { EBML_ID_HEADER,                 EBML_NEST, 0, 0, {.n=ebml_header} },
    { 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 }
};

#ifdef MPXPLAY_LINK_VIDEO
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 }
};
#endif

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_TRACKNAME,            EBML_UTF8, 0, offsetof(MatroskaTrack,name) },
    { MATROSKA_ID_TRACKUID,             EBML_UINT, 0, offsetof(MatroskaTrack,uid) },
    { 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_TRACKFLAGFORCED,      EBML_UINT, 0, offsetof(MatroskaTrack,flag_forced), {.u=0} },
//    { MATROSKA_ID_TRACKOFFSET,          EBML_SINT, 0, offsetof(MatroskaTrack,time_offset) },
#ifdef MPXPLAY_LINK_VIDEO
    { MATROSKA_ID_TRACKVIDEO,           EBML_NEST, 0, offsetof(MatroskaTrack,video), {.n=matroska_track_video} },
#endif
    { 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_TRACKFLAGENABLED,     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_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)   },
    { 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} },
    { 0 }
};

static EbmlSyntax matroska_index[] = {
    { MATROSKA_ID_POINTENTRY,         EBML_NEST, sizeof(MatroskaIndex), offsetof(MatroskaDemuxContext,index), {.n=matroska_index_entry} },
    { 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_TAGLANG,            EBML_STR,  0, offsetof(MatroskaTag,lang), {.s="und"} },
    { MATROSKA_ID_TAGDEFAULT,         EBML_UINT, 0, offsetof(MatroskaTag,def) },
    { MATROSKA_ID_TAGDEFAULT_BUG,     EBML_UINT, 0, offsetof(MatroskaTag,def) },
    { MATROSKA_ID_SIMPLETAG,          EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTag,sub), {.n=matroska_simpletag} },
    { 0 }
};

static EbmlSyntax matroska_tagtargets[] = {
    { MATROSKA_ID_TAGTARGETS_TYPE,      EBML_STR,  0, offsetof(MatroskaTagTarget,type) },
    { MATROSKA_ID_TAGTARGETS_TYPEVALUE, EBML_UINT, 0, offsetof(MatroskaTagTarget,typevalue), {.u=50} },
    { MATROSKA_ID_TAGTARGETS_TRACKUID,  EBML_UINT, 0, offsetof(MatroskaTagTarget,trackuid) },
    { MATROSKA_ID_TAGTARGETS_CHAPTERUID,EBML_UINT, 0, offsetof(MatroskaTagTarget,chapteruid) },
    { MATROSKA_ID_TAGTARGETS_ATTACHUID, EBML_UINT, 0, offsetof(MatroskaTagTarget,attachuid) },
    { 0 }
};

static EbmlSyntax matroska_tag[] = {
    { MATROSKA_ID_SIMPLETAG,          EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTags,tag), {.n=matroska_simpletag} },
    { MATROSKA_ID_TAGTARGETS,         EBML_NEST, 0, offsetof(MatroskaTags,target), {.n=matroska_tagtargets} },
    { 0 }
};

static EbmlSyntax matroska_tags[] = {
    { MATROSKA_ID_TAG,                EBML_NEST, sizeof(MatroskaTags), offsetof(MatroskaDemuxContext,tags), {.n=matroska_tag} },
    { 0 }
};

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_head[] = {
    { MATROSKA_ID_INFO,           EBML_NEST, 0, 0, {.n=matroska_info       } },
    { MATROSKA_ID_TRACKS,         EBML_NEST, 0, 0, {.n=matroska_tracks     } },
    { MATROSKA_ID_ATTACHMENTS,    EBML_PASS, 0, 0,                           },
    { MATROSKA_ID_CHAPTERS,       EBML_PASS, 0, 0,                           },
    { MATROSKA_ID_CUES,           EBML_PASS, 0, 0,                           },
    { 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,firstcluster_filepos) },
    { 0 }
};

static EbmlSyntax matroska_segments_head[] = {
    { MATROSKA_ID_SEGMENT,        EBML_NEST, 0, 0, {.n=matroska_segment_head    } },
    { 0 }
};

static EbmlSyntax matroska_segment_full[] = {
    { MATROSKA_ID_INFO,           EBML_NEST, 0, 0, {.n=matroska_info       } },
    { MATROSKA_ID_TRACKS,         EBML_NEST, 0, 0, {.n=matroska_tracks     } },
    { MATROSKA_ID_ATTACHMENTS,    EBML_PASS, 0, 0,                           },
    { MATROSKA_ID_CHAPTERS,       EBML_PASS, 0, 0,                           },
    //{ 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,firstcluster_filepos) },
    { 0 }
};

static EbmlSyntax matroska_segments_full[] = {
    { MATROSKA_ID_SEGMENT,        EBML_NEST, 0, 0, {.n=matroska_segment_full    } },
    { 0 }
};

#ifdef MPXPLAY_MKVDEBUG_ERROR
static const char *matroska_doctypes[] = { "matroska", "webm" };
#endif
#ifdef __DOS__
extern unsigned int intsoundcontrol;
#endif

static int ebml_level_end(MatroskaDemuxContext *matroska)
{
 AVFormatContext *s = matroska->ctx;
 int64_t pos=s->fbfs->ftell(s->fbds);

 if(matroska->num_levels > 0){
  MatroskaLevel *level = &matroska->levels[matroska->num_levels - 1];
  if(pos - level->start >= level->length || matroska->current_id){
   matroska->num_levels--;
   return 1;
  }
 }
 return 0;
}

static int32_t ffmkv_bitstream_get_byte(MatroskaDemuxContext *matroska, struct mpxplay_bitstreambuf_s *bs)
{
 if(!bs){
  AVFormatContext *s = matroska->ctx;
  uint8_t rbuf[4];
  if(s->fbfs->fread(s->fbds,&rbuf[0],1)!=1)
   return AVERROR(EIO);
  return (uint32_t)(rbuf[0]);
 }
 if(mpxplay_bitstream_leftbytes(bs)<1)
  return AVERROR(EIO);
 return mpxplay_bitstream_get_byte(bs);
}

static int ebml_read_num(MatroskaDemuxContext *matroska, struct mpxplay_bitstreambuf_s *bs,
                         int max_size, uint64_t *number)
{
 AVFormatContext *s = matroska->ctx;
 int32_t rb,bpos,len,lenm1;
 uint8_t total[8];

 rb=ffmkv_bitstream_get_byte(matroska,bs);
 if(rb<0){
#ifdef MPXPLAY_MKVDEBUG_ERROR
  if(!s->fbfs->eof(s->fbds)){
   int64_t pos = s->fbfs->ftell(s->fbds);
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Read error at pos. %"PRIu64" (0x%"PRIx64")",pos, pos);
  }
#endif
  return rb;
 }
 if(!rb)
  return AVERROR_INVALIDDATA;

 bpos= mpxplay_ff_log2_tab[rb];
 len = 8-bpos;
 if(len>max_size){
#ifdef MPXPLAY_MKVDEBUG_ERROR
  int64_t pos = s->fbfs->ftell(s->fbds) - 1;
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Invalid EBML number size tag 0x%2.2x at pos %"PRIu64" (0x%"PRIx64")",rb, pos, pos);
#endif
  return AVERROR_INVALIDDATA;
 }

 *((uint64_t *)&total[0])=0ULL;
 total[bpos]= rb ^ (1 << bpos);
 lenm1=len-1;
 if(lenm1){
  bpos++;
  if(bs){
   if(mpxplay_bitstream_readbytes(bs,&total[bpos],lenm1)!=lenm1)
    return AVERROR(EIO);
  }else{
   if(s->fbfs->fread(s->fbds,&total[bpos],lenm1)!=lenm1)
    return AVERROR(EIO);
  }
 }

 PDS_GETBD_BEU64(number,&total[0]);

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_read_num bs:%x l:%d rb:%d n:%"PRIx64" t1:%"PRIx64"",
 // (long)bs,lenm1,rb,*number,*((uint64_t *)&total[0]));
 return len;
}

static int ebml_read_id(MatroskaDemuxContext *matroska)
{
 uint64_t id;
 int res=ebml_read_num(matroska,NULL,4,&id);
 if(res<0)
  return res;
 matroska->current_id = id | 1 << 7*res;
 return res;
}

static int ebml_read_length(MatroskaDemuxContext *matroska, uint64_t *number)
{
 int res = ebml_read_num(matroska, NULL, 8, number);
 if((res>0) && (*number+1 == 1ULL << (7 * res)))
  *number = 0xffffffffffffffULL;
 return res;
}

static int ebml_read_length_uint(MatroskaDemuxContext *matroska, uint64_t *number)
{
 int n = 0;
 int ll;
 uint64_t size=0,locnum;

 *number = 0;
 ll=ebml_read_length(matroska,&size);
 if(ll<=0)
  return ll;
 if((size==0) || (size>8))
  return AVERROR_INVALIDDATA;

 ll+=size;
 locnum=0;
 while(n++ < size){
  int32_t rb=ffmkv_bitstream_get_byte(matroska,NULL);
  if(rb<0)
   return rb;
  locnum = (locnum << 8) | (uint64_t)rb;
 }
 *number=locnum;

 return ll;
}

static int ebml_read_uint(MatroskaDemuxContext *matroska, int size, uint64_t *number)
{
 int n = 0;
 uint64_t locnum;

 *number = 0;
 if(size > 8)
  return AVERROR_INVALIDDATA;

 locnum=0;
 while(n++ < size){
  int32_t rb=ffmkv_bitstream_get_byte(matroska,NULL);
  if(rb<0)
   return rb;
  locnum = (locnum << 8) | (uint64_t)rb;
 }
 *number=locnum;

 return 0;
}

static int ebml_read_float(MatroskaDemuxContext *matroska, int size, double *num)
{
 AVFormatContext *s = matroska->ctx;
 switch(size){
  case 0:*num=0;break;
  case 4:*num=mpxplay_av_int2flt(s->fbfs->get_be32(s->fbds));break;
  case 8:*num=mpxplay_av_int2dbl(s->fbfs->get_be64(s->fbds));break;
  default:return AVERROR_INVALIDDATA;
 }
 return 0;
}

static int ebml_read_ascii(MatroskaDemuxContext *matroska, int size, char **str)
{
 AVFormatContext *s=matroska->ctx;
 mpxplay_av_free(*str);
 if(!(*str = pds_malloc(size + 1)))
  return AVERROR(ENOMEM);
 if(s->fbfs->fread(s->fbds,(uint8_t *)*str,size)!=size){
  mpxplay_av_freep(str);
  return AVERROR(EIO);
 }
 (*str)[size] = '\0';
 return 0;
}

static int ebml_read_binary(MatroskaDemuxContext *matroska, int length, EbmlBin *bin)
{
 AVFormatContext *s=matroska->ctx;

 mpxplay_av_free(bin->data);
 if(!(bin->data = pds_malloc(length)))
  return AVERROR(ENOMEM);

 bin->size = length;
 bin->pos  = s->fbfs->ftell(s->fbds);
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"embl_read_binary size:%d pos:%d",(long)bin->size,(long)bin->pos);

 if(s->fbfs->fread(s->fbds,bin->data,length)!=length){
  mpxplay_av_freep(&bin->data);
  return AVERROR(EIO);
 }
 return 0;
}

static int ebml_read_skip(MatroskaDemuxContext *matroska)
{
 AVFormatContext *s = matroska->ctx;
 uint64_t skip_bytes=0;
 int64_t res;
 int l;

 l=ebml_read_length(matroska,&skip_bytes);
 if(l<=0)
  return l;

 res=s->fbfs->fseek(s->fbds,skip_bytes,SEEK_CUR);
 if(res<=0)
  return (int)res;

 l+=skip_bytes;

 return l;
}

static int ebml_read_master(MatroskaDemuxContext *matroska, uint64_t length)
{
 AVFormatContext *s = matroska->ctx;
 MatroskaLevel *level;

 if(matroska->num_levels >= EBML_MAX_DEPTH){
#ifdef MPXPLAY_MKVDEBUG_ERROR
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"File moves beyond max. allowed depth (%d)", EBML_MAX_DEPTH);
#endif
  return AVERROR(ENOSYS);
 }

 level = &matroska->levels[matroska->num_levels++];
 level->start = s->fbfs->ftell(s->fbds);
 level->length = length;
 return 0;
}

static int matroska_ebmlnum_uint(MatroskaDemuxContext *matroska,
                                 uint8_t *data, uint32_t size, uint64_t *num)
{
 struct mpxplay_bitstreambuf_s bs;
 mpxplay_bitstream_init(&bs,data,size);
 return ebml_read_num(matroska,&bs,FFMIN(size,8),num);
}

static int matroska_ebmlnum_sint(MatroskaDemuxContext *matroska,
                                 uint8_t *data, uint32_t size, int64_t *num)
{
 uint64_t unum;
 int res;

 if((res = matroska_ebmlnum_uint(matroska, data, size, &unum)) < 0)
  return res;

 *num = unum - ((1LL << (7*res - 1)) - 1);

 return res;
}

#ifdef MPXPLAY_USE_DEBUGF
static int epe_level;
#endif

static int ebml_parse_elem(MatroskaDemuxContext *matroska,
                           EbmlSyntax *syntax, void *data);

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){
  if((syntax[0].id==EBML_ID_HEADER) || (syntax[0].id==MATROSKA_ID_SEGMENT))
   return AVERROR(EINVAL);
  if((id==MATROSKA_ID_CLUSTER) && (matroska->num_levels>0) && (matroska->levels[matroska->num_levels-1].length==0xffffffffffffff))
   return 0;  // we reached the end of an unknown size cluster
#ifdef MPXPLAY_MKVDEBUG_ERROR
  if((id!=EBML_ID_VOID) && (id!=EBML_ID_CRC32))
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Unknown entry 0x%X", id);
#endif
 }
 return ebml_parse_elem(matroska, &syntax[i], data);
}

static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,void *data)
{
 if(!matroska->current_id){
  int res=ebml_read_id(matroska);
  if(res<0)
   return res;
 }
 return ebml_parse_id(matroska, syntax, matroska->current_id, data);
}

static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,void *data)
{
 int i, res = 0;
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_parse_nest lev:%d",epe_level);

 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:
    if(syntax[i].def.s)
     *(char    **)((char *)data+syntax[i].data_offset) = strdup(syntax[i].def.s);
    break;
  }

 while(!res && !ebml_level_end(matroska))
  res = ebml_parse(matroska, syntax, data);

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_parse_nest end res:%d",res);
 return res;
}

static int ebml_parse_elem(MatroskaDemuxContext *matroska,EbmlSyntax *syntax, void *data)
{
 static const uint64_t max_lengths[EBML_TYPE_COUNT] = {
        [EBML_UINT]  = 8,
        [EBML_FLOAT] = 8,
        // max. 16 MB for strings
        [EBML_STR]   = 0x1000000,
        [EBML_UTF8]  = 0x1000000,
        // max. 256 MB for binary data
        [EBML_BIN]   = MATROSKA_MAX_BLOCKSIZE,
        // no limits for anything else
    };
 AVFormatContext *s=matroska->ctx;
 EbmlList *list;
 uint32_t id = syntax->id;
 uint64_t length;
 int res=-100;

#ifdef MPXPLAY_USE_DEBUGF
 epe_level++;
#endif

 if(!syntax || !data)
  goto err_out_epe;
 data = (char *)data + syntax->data_offset;
 list = data;

 if(syntax->list_elem_size && (list->nb_elem<0)){
  res=-101;
  goto err_out_epe;
 }

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_parse_elem start nb:%d id:%8.8X do:%d lev:%d",list->nb_elem,id,syntax->data_offset,epe_level);

 if(syntax->list_elem_size) {
  list->elem=pds_realloc(list->elem,(list->nb_elem+1)*syntax->list_elem_size);
  if(!list->elem){
   res=AVERROR(ENOMEM);
   goto err_out_epe;
  }
  data = (char*)list->elem + list->nb_elem*syntax->list_elem_size;
  memset(data, 0, syntax->list_elem_size);
  list->nb_elem++;
 }
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_parse_elem 2.");

 if((syntax->type!=EBML_PASS) && (syntax->type!=EBML_STOP)){
  matroska->current_id = 0;
  if((res = ebml_read_length(matroska, &length)) < 0)
   goto err_out_epe;
  if(max_lengths[syntax->type] && (length>max_lengths[syntax->type])){
#ifdef MPXPLAY_MKVDEBUG_ERROR
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Invalid length 0x%"PRIx64" > 0x%"PRIx64" for syntax element %i\n",
                  length, max_lengths[syntax->type], syntax->type);
#endif
   return AVERROR_INVALIDDATA;
  }
 }
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_parse_elem 3. id:%d type:%d len:%d pos:%d",(long)id,(long)syntax->type,(long)length,(long)s->fbfs->ftell(s->fbds));

 switch(syntax->type) {
    case EBML_UINT:  res = ebml_read_uint  (matroska, length, data);  break;
//    case EBML_SINT:  res = ebml_read_uint  (matroska, length, data);
//                     mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"EBML_SINT res:%d d:"PRIx64"",res,data);
//                     if(res>0)
//                      *((int64_t *)data) = *((uint64_t *)data) - ((1LL << (7*res - 1)) - 1);
//                     break;
    case EBML_FLOAT: res = ebml_read_float (matroska, length, data);  break;
    case EBML_STR:
    case EBML_UTF8:  res = ebml_read_ascii (matroska, length, data);  break;
    case EBML_BIN:   res = ebml_read_binary(matroska, length, data);  break;
    case EBML_NEST:  if((res=ebml_read_master(matroska, length)) < 0)
                      return res;
                     if(id == MATROSKA_ID_SEGMENT)
                      matroska->segment_start = s->fbfs->ftell(s->fbds);
                     res=ebml_parse_nest(matroska, syntax->def.n, data);
                     break;
    case EBML_PASS:  res= 1;break;
    case EBML_STOP:  res= 1;
                     if(data)
                      *((uint64_t *)data)=s->fbfs->ftell(s->fbds)-4; // matroska->firstcluster_filepos
                     break;
    default:         res=((s->fbfs->fseek(s->fbds,length,SEEK_CUR)<0)? AVERROR(EIO):0);
 }
#ifdef MPXPLAY_MKVDEBUG_ERROR
 if(res == AVERROR_INVALIDDATA)
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Invalid element");
 else if (res == AVERROR(EIO))
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Read error");
#endif
err_out_epe:
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ebml_parse_elem end id:%8.8X len:%d res:%d lev:%d pos:%d",id,(long)length,res,epe_level,(long)s->fbfs->ftell(s->fbds));
#ifdef MPXPLAY_USE_DEBUGF
 epe_level--;
#endif
 return res;
}

static void ebml_free(EbmlSyntax *syntax, void *data)
{
 int i, j;
 for(i=0; syntax[i].id; i++){
  void *data_off = (char *)data + syntax[i].data_offset;
  switch (syntax[i].type){
   case EBML_STR:
   case EBML_UTF8:  mpxplay_av_freep(data_off);                      break;
   case EBML_BIN:   mpxplay_av_freep(&((EbmlBin *)data_off)->data);  break;
   case EBML_NEST:
    if(syntax[i].list_elem_size){
     EbmlList *list = data_off;
     char *ptr = list->elem;
     for(j=0; j<list->nb_elem; j++, ptr+=syntax[i].list_elem_size)
      ebml_free(syntax[i].def.n, ptr);
     mpxplay_av_free(list->elem);
    }else
     ebml_free(syntax[i].def.n, data_off);
   default:  break;
  }
 }
}

static int matroska_decode_buffer(MatroskaDemuxContext *matroska,uint8_t** buf, int* buf_size,MatroskaTrack *track)
{
 MatroskaTrackEncoding *encodings = track->encodings.elem;
#ifdef INFFMPG_LINK_LZO
 uint8_t* data = *buf;
 int isize = *buf_size;
 int result = 0;
 int olen;
 uint8_t* pkt_data = NULL;
 int pkt_size = isize;

 if(pkt_size >= 10000000)
  return -1;
#endif

 switch ((uint32_t)encodings[0].compression.algo) {
  case MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP:
   return encodings[0].compression.settings.size;
#ifdef INFFMPG_LINK_LZO
  case MATROSKA_TRACK_ENCODING_COMP_LZO:
   do{
    olen = pkt_size *= 3;
    pkt_data = pds_realloc(pkt_data, pkt_size+AV_LZO_OUTPUT_PADDING);
    result = mpxplay_av_lzo1x_decode(pkt_data, &olen, data, &isize);
   }while (result==AV_LZO_OUTPUT_FULL && pkt_size<10000000);
   if(result)
    goto failed;
   pkt_size -= olen;
   break;
#endif
  default:
   return -1;
 }

#ifdef INFFMPG_LINK_LZO
 *buf = pkt_data;
 *buf_size = pkt_size;
#endif
 return 0;
#ifdef INFFMPG_LINK_LZO
failed:
 mpxplay_av_free(pkt_data);
 return -1;
#endif
}

#ifdef INFFMPG_LINK_LZO
static int matroska_decode_buffer_block(MatroskaDemuxContext *matroska,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 = matroska->block_decode_buffer;
 int pkt_size = isize;
 int result = 0;
 int olen;

 switch ((uint32_t)encodings[0].compression.algo) {
  case MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP:
   return encodings[0].compression.settings.size;
  case MATROSKA_TRACK_ENCODING_COMP_LZO:
   olen = matroska->block_decode_bufsize;
   result = mpxplay_av_lzo1x_decode(pkt_data, &olen, data, &isize);
   if(result)
    goto failed;
   pkt_size -= olen;
   break;
  default:
   return -1;
 }

 *buf = pkt_data;
 *buf_size = pkt_size;
 return 0;
failed:
 return -1;
}
#else
#define matroska_decode_buffer_block(m,b,s,t) matroska_decode_buffer(m,b,s,t)
#endif

static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
                                 AVMetadata **metadata, char *prefix)
{
    MatroskaTag *tags = list->elem;
    char key[1024];
    int i;

    for (i=0; i < list->nb_elem; i++) {
        const char *lang = strcmp(tags[i].lang, "und") ? tags[i].lang : NULL;
        if (prefix)
         snprintf(key, sizeof(key), "%s/%s", prefix, tags[i].name);
        else
         mpxplay_av_strlcpy(key, tags[i].name, sizeof(key));
        if (tags[i].def || !lang) {
        //mpxplay_av_metadata_set2(metadata, key, tags[i].string, 0);
        if (tags[i].sub.nb_elem)
            matroska_convert_tag(s, &tags[i].sub, metadata, key);
        }
        if (lang) {
            mpxplay_av_strlcat(key, "-", sizeof(key));
            mpxplay_av_strlcat(key, lang, sizeof(key));
            //mpxplay_av_metadata_set2(metadata, key, tags[i].string, 0);
            if (tags[i].sub.nb_elem)
                matroska_convert_tag(s, &tags[i].sub, metadata, key);
        }
    }
    //ff_metadata_conv(metadata, NULL, ff_mkv_metadata_conv);
}

static void matroska_convert_tags(AVFormatContext *s)
{
    MatroskaDemuxContext *matroska = s->priv_data;
    MatroskaTags *tags = matroska->tags.elem;
    int i, j;

    for (i=0; i < matroska->tags.nb_elem; i++) {
        /*if(tags[i].target.chapteruid) {
            MatroskaChapter *chapter = matroska->chapters.elem;
            for (j=0; j<matroska->chapters.nb_elem; j++)
                if (chapter[j].uid == tags[i].target.chapteruid)
                    matroska_convert_tag(s, &tags[i].tag,
                                         &chapter[j].chapter->metadata, NULL);
        } else*/ if (tags[i].target.trackuid) {
            MatroskaTrack *track = matroska->tracks.elem;
            for (j=0; j<matroska->tracks.nb_elem; j++)
                if (track[j].uid == tags[i].target.trackuid)
                    matroska_convert_tag(s, &tags[i].tag,
                                         &track[j].stream->metadata, NULL);
        } else {
            matroska_convert_tag(s, &tags[i].tag, &s->metadata,
                                 tags[i].target.type);
        }
    }
}

static void matroska_execute_seekhead(MatroskaDemuxContext *matroska, EbmlSyntax *matroska_segment_select)
{
    EbmlList *seekhead_list = &matroska->seekhead;
    MatroskaSeekhead *seekhead = seekhead_list->elem;
    AVFormatContext *s = matroska->ctx;
    uint32_t level_up = matroska->level_up;
    int64_t before_pos = s->fbfs->ftell(s->fbds);
    uint32_t saved_id = matroska->current_id;
    MatroskaLevel level;
    int i;

    for (i=0; i<seekhead_list->nb_elem; i++) {
        int64_t offset = seekhead[i].pos + matroska->segment_start;

        if (seekhead[i].pos <= before_pos
            || seekhead[i].id == MATROSKA_ID_SEEKHEAD
            || seekhead[i].id == MATROSKA_ID_CLUSTER)
            continue;

        if (s->fbfs->fseek(s->fbds, offset, SEEK_SET) != offset)
            continue;

        /* We don't want to lose our seekhead level, so we add
         * a dummy. This is a crude hack. */
        if (matroska->num_levels == EBML_MAX_DEPTH){
#ifdef MPXPLAY_MKVDEBUG_ERROR
            mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Max EBML element depth (%d) reached,cannot parse further.", EBML_MAX_DEPTH);
#endif
            break;
        }

        level.start = 0;
        level.length = (uint64_t)-1;
        matroska->levels[matroska->num_levels] = level;
        matroska->num_levels++;
        matroska->current_id = 0;

        ebml_parse(matroska, matroska_segment_select, matroska);

        /* remove dummy level */
        while (matroska->num_levels) {
            uint64_t length = matroska->levels[--matroska->num_levels].length;
            if (length == (uint64_t)-1)
                break;
        }
    }

    s->fbfs->fseek(s->fbds, before_pos, SEEK_SET);
    matroska->level_up = level_up;
    matroska->current_id = saved_id;
}

static int matroska_aac_profile(char *codec_id)
{
 static const char * const aac_profiles[] = { "MAIN", "LC", "SSR" };
 int profile;

 for(profile=0; profile<FF_ARRAY_ELEMS(aac_profiles); profile++)
  if(strstr(codec_id, aac_profiles[profile]))
   break;
 return (profile+1);
}

static int matroska_aac_sri(int samplerate)
{
 int sri;

 for(sri=0; sri<FF_ARRAY_ELEMS(ff_mpeg4audio_sample_rates); sri++)
  if(ff_mpeg4audio_sample_rates[sri] == samplerate)
   break;
 return sri;
}

static int matroska_read_header(AVFormatContext *s, AVPacket *pkt)
{
    MatroskaDemuxContext *matroska = s->priv_data;
    MatroskaTrack *tracks;
#if 0
    EbmlList *chapters_list = &matroska->chapters;
    MatroskaChapter *chapters;
    uint64_t max_start = 0;
    EbmlList *index_list;
    MatroskaIndex *index;
    int index_scale = 1;
#endif
    Ebml ebml;
    AVStream *st;
    int i, j, res;

    memset(&ebml,0,sizeof(ebml));

    matroska->ctx = s;

    //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"read header start");

    if((res=ebml_parse(matroska, ebml_syntax, &ebml))<0)
     return res;
    //if(!ebml.doctype)
    // return AVERROR(EINVAL);
    if((ebml.version>EBML_VERSION) || (ebml.max_size>sizeof(uint64_t))
     || (ebml.id_length>sizeof(uint32_t)) || (ebml.doctype_version>2)){
#ifdef MPXPLAY_MKVDEBUG_ERROR
     mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"EBML header using unsupported features\n"
      "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")",
      ebml.version, ((ebml.doctype)? ebml.doctype:"n/a"), ebml.doctype_version);
#endif
     ebml_free(ebml_syntax, &ebml);
     return AVERROR_PATCHWELCOME;
    }
#ifdef MPXPLAY_MKVDEBUG_ERROR
    if(ebml.doctype){
     for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++)
        if (!strcmp(ebml.doctype, matroska_doctypes[i]))
            break;
     if(i>=FF_ARRAY_ELEMS(matroska_doctypes))
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Unknown EBML doctype '%s'", ebml.doctype);
    }
#endif
    ebml_free(ebml_syntax, &ebml);
#ifdef MPXPLAY_MKVDEBUG_HEADER
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"read header 4. f:%8.8X d:%8.8X",(long)s->fbfs,(long)s->fbds);
#endif
    if(s->flags&ADFMT_FLAG_LOADHEADONLY){
     if((res = ebml_parse(matroska, matroska_segments_head, matroska)) < 0)
      return res;
     matroska_execute_seekhead(matroska, matroska_segment_head);
    }else{
     if((res = ebml_parse(matroska, matroska_segments_full, matroska)) < 0)
      return res;
     matroska_execute_seekhead(matroska, matroska_segment_full);
    }

#ifdef MPXPLAY_MKVDEBUG_HEADER
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"read header 6. nb:%d ts:%d dur:%d",matroska->tracks.nb_elem,(long)matroska->time_scale,(long)matroska->duration);
#endif
    if (!matroska->time_scale)
        matroska->time_scale = 1000000;
    if (matroska->duration)
        matroska->ctx->duration = matroska->duration * matroska->time_scale
                                  * 1000 / AV_TIME_BASE;
    //mpxplay_av_metadata_set2(&s->metadata, "title", matroska->title, 0);

    tracks = matroska->tracks.elem;
    for (i=0; i < matroska->tracks.nb_elem; i++) {
        MatroskaTrack *track = &tracks[i];
        enum CodecID codec_id = CODEC_ID_NONE;
        EbmlList *encodings_list = &tracks->encodings;
        MatroskaTrackEncoding *encodings = encodings_list->elem;
        uint8_t *extradata = NULL;
        int extradata_size = 0;
        int extradata_offset = 0;
        struct mpxplay_bitstreambuf_s bs;

        /* Apply some sanity checks. */
        if (track->type != MATROSKA_TRACK_TYPE_VIDEO &&
            track->type != MATROSKA_TRACK_TYPE_AUDIO &&
            track->type != MATROSKA_TRACK_TYPE_SUBTITLE){
#ifdef MPXPLAY_MKVDEBUG_ERROR
            mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Unknown or unsupported track type %"PRIu64"",track->type);
#endif
            continue;
        }
        if (track->codec_id == NULL)
            continue;

#ifdef MPXPLAY_LINK_VIDEO
        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
#endif
        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){
#ifdef MPXPLAY_MKVDEBUG_ERROR
         mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Multiple combined encodings no supported");
#endif
        }else if (encodings_list->nb_elem==1){
#ifdef INFFMPG_LINK_LZO
         if(!encodings->type && (encodings->scope&1) && (encodings->compression.algo!=MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP))
          if(!matroska->block_decode_bufsize){
           matroska->block_decode_buffer=pds_malloc(MATROSKA_BLOCKDATA_INITIAL_BUFSIZE);
           if(!matroska->block_decode_buffer)
            return AVERROR_NOMEM;
           matroska->block_decode_bufsize=MATROSKA_BLOCKDATA_INITIAL_BUFSIZE;
          }
#endif
         if (encodings[0].type ||
                (encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP &&
#if CONFIG_ZLIB
                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB &&
#endif
#if 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;
#ifdef MPXPLAY_MKVDEBUG_ERROR
                mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Unsupported encoding type");
#endif
         } else if(track->codec_priv.size && encodings[0].scope&2) {
                uint8_t *codec_priv = track->codec_priv.data;
                int offset = matroska_decode_buffer(matroska,&track->codec_priv.data,
                                                    &track->codec_priv.size,
                                                    track);
                if (offset < 0) {
                    track->codec_priv.data = NULL;
                    track->codec_priv.size = 0;
#ifdef MPXPLAY_MKVDEBUG_ERROR
                    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Failed to decode codec private data");
#endif
                } else if (offset > 0) {
                    track->codec_priv.data = pds_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)
                    mpxplay_av_free(codec_priv);
         }
        }

        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 = mpxplay_av_new_stream(s, 0);
        if (st == NULL)
            return AVERROR(ENOMEM);

#ifdef MPXPLAY_LINK_VIDEO
        if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC")
            && track->codec_priv.size >= 40
            && track->codec_priv.data != NULL) {
            track->ms_compat = 1;
            track->video.fourcc = AV_RL32(track->codec_priv.data + 16);
            codec_id = ff_codec_get_id(ff_codec_bmp_tags, track->video.fourcc);
            extradata_offset = 40;
        } else
#endif
        if (!strcmp(track->codec_id, "A_MS/ACM")
                   && track->codec_priv.size >= 14
                   && track->codec_priv.data != NULL) {
            mpxplay_bitstream_init(&bs,track->codec_priv.data, track->codec_priv.size);
            mpxplay_ff_get_wav_header(&bs, st->codec, track->codec_priv.size);
            codec_id = st->codec->codec_id;
            extradata_offset = FFMIN(track->codec_priv.size, 18);
        } else
#ifdef MPXPLAY_LINK_VIDEO
        if (!strcmp(track->codec_id, "V_QUICKTIME")
                   && (track->codec_priv.size >= 86)
                   && (track->codec_priv.data != NULL)) {
            track->video.fourcc = AV_RL32(track->codec_priv.data);
            codec_id=ff_codec_get_id(codec_movvideo_tags, track->video.fourcc);
        } else
#endif
        if (codec_id == CODEC_ID_PCM_S16BE) {
            switch ((uint32_t)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;
            }
        } else if (codec_id == CODEC_ID_PCM_S16LE) {
            switch ((uint32_t)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;
            }
        } 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 = pds_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;
#if 0   // Mpxplay doesn't support these
        } else if (codec_id == CODEC_ID_TTA) {
            extradata_size = 30;
            extradata = pds_calloc(1,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;
        } 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 || codec_id == CODEC_ID_SIPR) {
            int flavor;
            init_put_byte(&b, track->codec_priv.data,track->codec_priv.size,
                          0, NULL, NULL, NULL, NULL);
            url_fskip(&b, 22);
            flavor                       = get_be16(&b);
            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 = pds_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 {
                /*if (codec_id == CODEC_ID_SIPR && flavor < 4) {
                    const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 };
                    track->audio.sub_packet_size = ff_sipr_subpk_size[flavor];
                    st->codec->bit_rate = sipr_bit_rate[flavor];
                }*/
                st->codec->block_align = track->audio.sub_packet_size;
                extradata_offset = 78;
            }
#endif
        }

        track->codec_priv.size -= extradata_offset;

#ifdef MPXPLAY_MKVDEBUG_ERROR
        if (codec_id == CODEC_ID_NONE)
         mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Unknown/unsupported CodecID %s.", track->codec_id);
#endif

        if (track->time_scale < 0.01)
            track->time_scale = 1.0;
        mpxplay_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"))
        //    mpxplay_av_metadata_set2(&st->metadata, "language", track->language, 0);
        //mpxplay_av_metadata_set2(&st->metadata, "title", track->name, 0);

        if (track->flag_default)
            st->disposition |= AV_DISPOSITION_DEFAULT;
        if (track->flag_forced)
            st->disposition |= AV_DISPOSITION_FORCED;

        //if (track->default_duration)
        //    mpxplay_av_reduce(&st->codec->time_base.num, &st->codec->time_base.den,
        //              track->default_duration, 1000000000, 30000);

        if (!st->codec->extradata) {
            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 = pds_calloc(1,track->codec_priv.size +
                                                  FF_INPUT_BUFFER_PADDING_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);
            }
        }

#ifdef MPXPLAY_LINK_VIDEO
        if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
            st->codec->codec_tag  = track->video.fourcc;
            st->codec->width  = track->video.pixel_width;
            st->codec->height = track->video.pixel_height;
            //mpxplay_av_reduce(&st->sample_aspect_ratio.num,
            //          &st->sample_aspect_ratio.den,
            //          st->codec->height * track->video.display_width,
            //          st->codec-> width * track->video.display_height,
            //          255);
            if (st->codec->codec_id != CODEC_ID_H264)
            st->need_parsing = AVSTREAM_PARSE_HEADERS;
            //if (track->default_duration)
            //    st->avg_frame_rate = mpxplay_av_d2q(1000000000.0/track->default_duration, INT_MAX);
        } else
#endif
        if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
            st->codec->sample_rate = track->audio.out_samplerate;
            st->codec->channels = track->audio.channels;
            if (st->codec->codec_id != CODEC_ID_AAC)
            st->need_parsing = AVSTREAM_PARSE_HEADERS;
        }
#ifdef MPXPLAY_LINK_VIDEO
        else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
            st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
        }
#endif
    }

#if 0
    chapters = chapters_list->elem;
    for (i=0; i<chapters_list->nb_elem; i++)
        if (chapters[i].start != AV_NOPTS_VALUE && chapters[i].uid
            && (max_start==0 || chapters[i].start > max_start)) {
            /*chapters[i].chapter =
            ff_new_chapter(s, chapters[i].uid, (AVRational){1, 1000000000},
                           chapters[i].start, chapters[i].end,
                           chapters[i].title);
            mpxplay_av_metadata_set2(&chapters[i].chapter->metadata,
                             "title", chapters[i].title, 0);*/
            max_start = chapters[i].start;
        }
#endif

 matroska_convert_tags(s);

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"read header end pos:%X ts:%d dur:%d",(long)s->fbfs->ftell(s->fbds),(long)matroska->time_scale,(long)matroska->duration);

 return 0;
}

//-------------------------------------------------------------------------
// frame demuxing

static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska,
                                                 int num)
{
 MatroskaTrack *tracks = matroska->tracks.elem;
 int i;

 for(i=0; i < matroska->tracks.nb_elem; i++)
  if(tracks[i].num == num)
   return &tracks[i];
#ifdef MPXPLAY_MKVDEBUG_ERROR
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "Invalid track number %d", num);
#endif
 return NULL;
}

static int matroska_parse_block(MatroskaDemuxContext *matroska,int is_keyframe)
{
 MatroskaTrack *track;
 uint32_t *lace_size = &matroska->lace_sizes[0];
 uint8_t *data=matroska->block_data;
 int size=matroska->block_length;
 int res = 0;
 AVStream *st;
 int16_t block_time;
 int n, flags;
 uint64_t num;

 matroska->block_timecode=AV_NOPTS_VALUE;
 matroska->nb_laces=0;
 memset(lace_size,0,sizeof(matroska->lace_sizes));

 if((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0){
#ifdef MPXPLAY_MKVDEBUG_ERROR
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "EBML block data error");
#endif
  return res;
 }

 data += n;
 size -= n;

 matroska->block_track = track = matroska_find_track_by_num(matroska, num);
 if((size<=3) || !track || !track->stream){
#ifdef MPXPLAY_MKVDEBUG_ERROR
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Invalid stream %"PRIu64" or size %u", num, size);
#endif
  return res;
 }
 st = track->stream;
 if(st->discard >= AVDISCARD_ALL)
  return res;
#ifndef MPXPLAY_LINK_VIDEO
 if(st->codec && (st->codec->codec_type!=CODEC_TYPE_AUDIO))
  return res;
#endif

 if(matroska->block_duration == AV_NOPTS_VALUE)
  matroska->block_duration = track->default_duration / matroska->time_scale;

 block_time = AV_RB16(data);
 data += 2;
 flags = *data++;
 size -= 3;
 if(is_keyframe == -1)
  is_keyframe = (flags&0x80)? AV_PKT_FLAG_KEY:0;

 if((matroska->cluster_timecode!=(uint64_t)-1) && ((block_time>=0) || (matroska->cluster_timecode>=-block_time))){
  matroska->block_timecode = matroska->cluster_timecode + block_time;
  if((track->type==MATROSKA_TRACK_TYPE_SUBTITLE) && (matroska->block_timecode<track->end_timecode))
   is_keyframe = 0;
  //if(is_keyframe)
  // mpxplay_av_add_index_entry(st, matroska->cluster_filepos, matroska->block_timecode, 0,0,AVINDEX_KEYFRAME);
  track->end_timecode = FFMAX(track->end_timecode,(matroska->block_timecode+matroska->block_duration));
 }

 if(!matroska->seek_reset_bufs && matroska->skip_to_keyframe && (track->type!=MATROSKA_TRACK_TYPE_SUBTITLE)){
  if(!is_keyframe || (matroska->block_timecode<matroska->skip_to_timecode))
   return res;
  matroska->skip_to_keyframe = 0;
 }

 switch((flags & 0x06) >> 1) {
  case 0x0: // no lacing
   matroska->nb_laces = 1;
   lace_size[0] = size;
   break;
  case 0x1: // Xiph lacing
  case 0x2: // fixed-size lacing
  case 0x3: // EBML lacing
   if(size<1)
    return res;
   matroska->nb_laces = (*data) + 1;
   data += 1;
   size -= 1;
   switch((flags & 0x06) >> 1) {
    case 0x1:
    {
     uint8_t temp;
     uint32_t total = 0;
     for(n = 0; (res==0) && (n < matroska->nb_laces - 1); n++){
      while(1){
       if(size == 0){
        res = -1;
        break;
       }
       temp = *data;
       lace_size[n] += temp;
       data += 1;
       size -= 1;
       if(temp != 0xff)
        break;
      }
      total += lace_size[n];
     }
     lace_size[n] = size - total;
     break;
    }
    case 0x2:
     for (n = 0; n < matroska->nb_laces; n++)
      lace_size[n] = size / matroska->nb_laces;
     break;
    case 0x3:
    {
     uint32_t total;
     n = matroska_ebmlnum_uint(matroska, data, size, &num);
     if (n < 0){
#ifdef MPXPLAY_MKVDEBUG_ERROR
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"EBML block data error");
#endif
      break;
     }
     data += n;
     size -= n;
     total = lace_size[0] = num;
     for (n = 1; res == 0 && n < matroska->nb_laces - 1; n++) {
      int64_t snum;
      int r = matroska_ebmlnum_sint(matroska, data, size, &snum);
      if(r < 0){
#ifdef MPXPLAY_MKVDEBUG_ERROR
       mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"EBML block data error");
#endif
       break;
      }
      data += r;
      size -= r;
      lace_size[n] = lace_size[n - 1] + snum;
      total += lace_size[n];
     }
     lace_size[n] = size - total;
     break;
    }
   }
   break;
 }

 matroska->lace_data=data;
 matroska->lace_length=size;
 matroska->is_keyframe=is_keyframe;
 matroska->lace_counter=0;
 return ((res<0)? res:1);
}

static int matroska_read_block(MatroskaDemuxContext *matroska)
{
 AVFormatContext *s = matroska->ctx;
 int len=0,idln=0;
 mpxp_filesize_t lastgoodfilepos=s->fbfs->ftell(s->fbds);
 char tempbuf[8];

 do{
  while(matroska->cluster_size>0){
   matroska->block_duration=AV_NOPTS_VALUE;
   matroska->block_fref=0;
   matroska->block_length=0;
   len=0;
   while(matroska->blockgroup_size>0){
    lastgoodfilepos=s->fbfs->ftell(s->fbds);
    idln=ebml_read_id(matroska);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"bg id:%8.8X idln:%d %d",matroska->current_id,idln,lastgoodfilepos);
#endif
    if(idln<=0)
     goto err_out_idln;
    matroska->lasterror=0;
    switch(matroska->current_id){
     case EBML_ID_INVALID:
#ifdef MPXPLAY_MKVDEBUG_ERROR
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"EBML_INVALID clusterpos:%d",(long)matroska->cluster_filepos);
#endif
      goto err_out_data;
     case MATROSKA_ID_BLOCKDURATION:
      len=ebml_read_length_uint(matroska,&matroska->block_duration);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"blockdur: %d",(long)matroska->block_duration);
#endif
      if(len<=0)
       goto err_out_len;
      if(!matroska->block_duration || (matroska->block_duration==AV_NOPTS_VALUE))
       goto err_out_data;
      matroska->block_duration *= matroska->time_scale / 1000000.0;
      break;
     case MATROSKA_ID_BLOCK:
      len = ebml_read_length(matroska,&matroska->block_length);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"block: %d",(long)matroska->block_length);
#endif
      if(len<=0)
       goto err_out_len;
      if(!matroska->block_length || (matroska->block_length>MATROSKA_MAX_BLOCKSIZE))
       goto err_out_data;
      if(matroska->blockdata_bufsize<(matroska->block_length+AV_LZO_INPUT_PADDING)){
#ifdef __DOS__
       if(intsoundcontrol&INTSOUND_DECODER){ // we don't use inline malloc on INT08 (program can crash)
        len+=matroska->block_length;
        goto err_out_data;
       }
#endif
       mpxplay_av_free(matroska->block_data);
       matroska->block_data=pds_malloc(matroska->block_length+AV_LZO_INPUT_PADDING);
       if(!matroska->block_data){
        matroska->blockdata_bufsize=0;
        goto err_out_data;
       }
       matroska->blockdata_bufsize=matroska->block_length+AV_LZO_INPUT_PADDING;
      }
      matroska->block_filepos = s->fbfs->ftell(s->fbds);
      if(s->fbfs->fread(s->fbds,matroska->block_data,matroska->block_length)!=matroska->block_length)
       goto err_out_read;
      len += matroska->block_length;
      break;
     case MATROSKA_ID_BLOCKREFERENCE:
     {
      uint64_t uval;
      int64_t num;
      len = ebml_read_length_uint(matroska,&uval);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"blockref");
#endif
      if(len<=0)
       goto err_out_len;
      num = uval - ((1LL << (7*len - 1)) - 1);
      //if(num <= 0)
      // matroska->block_bref = num;
      //else
       matroska->block_fref = num;
      break;
     }
     default:
      len=ebml_read_skip(matroska);
      if(len<=0)
       goto err_out_skip;
      break;
    }
    matroska->blockgroup_size -= len + idln;
    matroska->cluster_size -= len + idln;
   }

   if(matroska->block_length){
    int res=matroska_parse_block(matroska,matroska->block_fref);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"parse block 1 res:%d",res);
#endif
    if(res<0)
     return 0;
    if(res)
     return 1;
   }

   len=0;

   if(matroska->cluster_size>0){
    lastgoodfilepos=s->fbfs->ftell(s->fbds);
    idln=ebml_read_id(matroska);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"cl ln:%d id:%8.8X",idln,matroska->current_id);
#endif
    if(idln<=0)
     goto err_out_idln;
    matroska->lasterror=0;
    switch(matroska->current_id){
     case EBML_ID_INVALID:
      goto err_out_data;
     case MATROSKA_ID_CLUSTERTIMECODE:
     {
      uint64_t num;
      len = ebml_read_length_uint(matroska,&num);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ctc ln:%d num:%8.8X",len,(long)num);
#endif
      if(len<=0)
       goto err_out_len;
      //if(!mkv_d->has_first_tc) {
      // mkv_d->first_tc = num * mkv_d->tc_scale / 1000000.0;
      // mkv_d->has_first_tc = 1;
      //}
      matroska->cluster_timecode = num * matroska->time_scale;
      break;
     }

     case MATROSKA_ID_BLOCKGROUP:
      len = ebml_read_length(matroska,(uint64_t *)&matroska->blockgroup_size);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"cbg ln:%d bl:%d",len,(long)matroska->blockgroup_size);
#endif
      if(len<=0)
       goto err_out_len;
      if(!matroska->blockgroup_size)
       goto err_out_data;
      break;

     case MATROSKA_ID_SIMPLEBLOCK:
     {
      int res;
      len = ebml_read_length(matroska,&matroska->block_length);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"csb ln:%d bl:%d",len,(long)matroska->block_length);
#endif
      if(len<=0)
       goto err_out_len;
      if(!matroska->block_length || (matroska->block_length>MATROSKA_MAX_BLOCKSIZE))
       goto err_out_data;
      if(matroska->blockdata_bufsize<(matroska->block_length+AV_LZO_INPUT_PADDING)){
#ifdef __DOS__
       if(intsoundcontrol&INTSOUND_DECODER){
        len+=matroska->block_length;
        goto err_out_data;
       }
#endif
       mpxplay_av_free(matroska->block_data);
       matroska->block_data=pds_malloc(matroska->block_length+AV_LZO_INPUT_PADDING);
       if(!matroska->block_data){
        matroska->blockdata_bufsize=0;
        goto err_out_data;
       }
       matroska->blockdata_bufsize=matroska->block_length+AV_LZO_INPUT_PADDING;
      }
      matroska->block_filepos=s->fbfs->ftell(s->fbds);
      if(s->fbfs->fread(s->fbds,matroska->block_data,matroska->block_length)!=matroska->block_length)
       goto err_out_read;
      len += matroska->block_length;
      res = matroska_parse_block(matroska,-1);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"parse block 2 res:%d",res);
#endif
      matroska->cluster_size -= len + idln;
      if(res<0)
       return 0;
      else if(res)
       return 1;
      else
       matroska->cluster_size += len + idln;
      break;
     }
     default:
      len=ebml_read_skip(matroska);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"cskip ln:%d",len);
#endif
      if(len<=0)
       goto err_out_skip;
      break;
    }
    matroska->cluster_size -= len + idln;
   }
  }

  lastgoodfilepos=s->fbfs->ftell(s->fbds);
  if(s->fbfs->fread(s->fbds,&tempbuf[0],4)!=4)
   goto err_out_read;
  matroska->lasterror=0;
  idln=0;
  do{
   matroska->current_id=PDS_GETB_BE32(&tempbuf[0]);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"cid:%8.8X fp:%d (%X)",(unsigned long)matroska->current_id,lastgoodfilepos,lastgoodfilepos);
#endif
   switch(matroska->current_id){
    case 0: // skip 4 bytes (search for valid id) (faster)
     if(s->fbfs->fread(s->fbds,&tempbuf[0],4)!=4)
      goto err_out_read;
     lastgoodfilepos+=4;
     break;
    case MATROSKA_ID_CLUSTER:
     idln=ebml_read_length(matroska, (uint64_t *)&matroska->cluster_size);
     if(idln<=0)
      goto err_out_idln;
     matroska->cluster_filepos=s->fbfs->ftell(s->fbds);
     break;
    case MATROSKA_ID_INFO:
    case MATROSKA_ID_TRACKS:
    case MATROSKA_ID_CUES:
    case MATROSKA_ID_TAGS:
    case MATROSKA_ID_SEEKHEAD:
    case MATROSKA_ID_ATTACHMENTS:
    case MATROSKA_ID_CHAPTERS:
     idln=ebml_read_skip(matroska);
     if(idln<=0)
      goto err_out_skip;
     break;
    default: // unknown id, skip 1 byte (search for valid id)
     PDS_PUTB_LE32(&tempbuf[0],PDS_GETB_LE32(&tempbuf[1]));
     if(s->fbfs->fread(s->fbds,&tempbuf[3],1)!=1)
      goto err_out_read;
     lastgoodfilepos++;
   }
  }while(idln==0);
#ifdef MPXPLAY_MKVDEBUG_DEMUX
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"CLUSTER lp:%d id:%8.8X pos:%d (%8.8X) size:%d",(long)lastgoodfilepos,matroska->current_id,
    (long)matroska->cluster_filepos,(long)matroska->cluster_filepos,(long)matroska->cluster_size);
#endif
 }while(1);

 return 0;

//Mpxplay can run out of file-prebuffer -> restores filepos of block
err_out_idln:
 if(idln==AVERROR(EIO) && !s->fbfs->eof(s->fbds) && (matroska->lasterror!=MATROSKA_LASTERROR_SEEK))
  if(s->fbfs->fseek(s->fbds,lastgoodfilepos,SEEK_SET)<0)
   matroska->lasterror=MATROSKA_LASTERROR_SEEK;
 if(idln==AVERROR(EINVAL)){
  if(matroska->blockgroup_size>0)
   matroska->cluster_size-=matroska->blockgroup_size;
  else
   matroska->cluster_size=0;
  matroska->blockgroup_size=0;
 }
 return 0;
err_out_len:
 if(len==AVERROR(EIO) && !s->fbfs->eof(s->fbds))
  if(s->fbfs->fseek(s->fbds,lastgoodfilepos,SEEK_SET)<0)
   matroska->lasterror=MATROSKA_LASTERROR_SEEK;
 if(len==AVERROR(EINVAL)){
  if(matroska->blockgroup_size>0)
   matroska->cluster_size-=matroska->blockgroup_size;
  else
   matroska->cluster_size=0;
  matroska->blockgroup_size=0;
 }
 return 0;
err_out_read:
 if(!s->fbfs->eof(s->fbds) && (matroska->lasterror!=MATROSKA_LASTERROR_SEEK))
  if(s->fbfs->fseek(s->fbds,lastgoodfilepos,SEEK_SET)<0)
   matroska->lasterror=MATROSKA_LASTERROR_SEEK;
#ifdef MPXPLAY_MKVDEBUG_DEMUX
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"err_out_read %d",(long)matroska->cluster_filepos);
#endif
 return 0;
err_out_skip:
 if(!s->fbfs->eof(s->fbds))
  matroska->lasterror=MATROSKA_LASTERROR_SEEK;
 return 0;
err_out_data:
 matroska->blockgroup_size -= len + idln;
 matroska->cluster_size -= len + idln;
 return 0;
}

static int matroska_deliver_packet(MatroskaDemuxContext *matroska,AVPacket *pkt)
{
 MatroskaTrack *track;
 MatroskaTrackEncoding *encodings;
 AVStream *st;

 if(!matroska->block_track)
  return 0;
 track=matroska->block_track;
 encodings = track->encodings.elem;
 st=track->stream;

 do{
  int offset,lace_size,pkt_size,is_keyframe;
  uint8_t *pkt_data,*lacedata;

  if((matroska->lace_counter>=matroska->nb_laces) || (matroska->lace_length<=0))
   break;

  offset=0;
  lace_size=matroska->lace_sizes[matroska->lace_counter];
  if(!lace_size){
#ifdef MPXPLAY_MKVDEBUG_ERROR
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Invalid packet size: 0");
#endif
   break;
  }
  pkt_size=lace_size;
  pkt_data=lacedata=matroska->lace_data;
  is_keyframe=matroska->is_keyframe;

  matroska->lace_counter++;
  matroska->is_keyframe=0;

  if(pkt_size>matroska->lace_length){
#ifdef MPXPLAY_MKVDEBUG_ERROR
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Invalid packet size");
#endif
   break;
  }
  matroska->lace_data += lace_size;
  matroska->lace_length -= lace_size;

  if(encodings && (encodings->scope&1)){
   offset = matroska_decode_buffer_block(matroska,&pkt_data,&pkt_size,track);
   if(offset<0)
    continue;
  }
  if((offset+pkt_size)>pkt->bufsize)
   pkt_size=pkt->bufsize-offset;

  if(offset)
   memcpy(pkt->data, encodings->compression.settings.data, offset);
  memcpy(pkt->data+offset, pkt_data, pkt_size);

  //if(pkt_data != lacedata)
  // mpxplay_av_free(pkt_data);

  pkt->size = offset+pkt_size;
  pkt->flags = is_keyframe;
  pkt->stream_index = st->index;

  if(track->ms_compat)
   pkt->dts = matroska->block_timecode;
  else
   pkt->pts = matroska->block_timecode;
  pkt->pos = matroska->block_filepos; // ???
  if(st->codec->codec_id==CODEC_ID_TEXT)
   pkt->convergence_duration = matroska->block_duration;
  else if(track->type!=MATROSKA_TRACK_TYPE_SUBTITLE)
   pkt->duration = matroska->block_duration;

  //if(st->codec->codec_id == CODEC_ID_SSA)
  // matroska_fix_ass_packet(matroska, pkt, matroska->block_duration);

  if(matroska->block_timecode != AV_NOPTS_VALUE)
   matroska->block_timecode=(matroska->block_duration)? (matroska->block_timecode+matroska->block_duration):AV_NOPTS_VALUE;

  return 1;
 }while(1);

 return 0;
}

static void matroska_clear_queue(MatroskaDemuxContext *matroska)
{
 matroska->cluster_size=0;
 matroska->blockgroup_size=0;
 matroska->nb_laces=0;
}

static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt)
{
 MatroskaDemuxContext *matroska = s->priv_data;

 if(matroska->seek_reset_bufs){
  matroska_clear_queue(matroska);
  matroska->current_id=0;
  matroska->seek_reset_bufs=0;
 }

 while(!matroska_deliver_packet(matroska, pkt)){
  if(!matroska_read_block(matroska))
   return AVERROR_EOF;
 }

 return 0;
}

static int64_t matroska_index_track_filepos(EbmlList *pos_list,unsigned int track_index)
{
 int64_t streamfilepos;
 unsigned int j;
 MatroskaIndexPos *pos = pos_list->elem;
 streamfilepos=pos[0].pos;
 for(j=0; j<pos_list->nb_elem; j++){
  if(pos[j].track==track_index){
   streamfilepos=pos[j].pos;
   break;
  }
 }
 return streamfilepos;
}

static int64_t matroska_read_seek(AVFormatContext *s, int stream_index,
                              int64_t timestamp_av, int flags)
{
 MatroskaDemuxContext *matroska = s->priv_data;
 MatroskaTrack *tracks = matroska->tracks.elem, *track=NULL;
 EbmlList *index_list;
 MatroskaIndex *index;
 long i,indnum=0,newtimediff,track_index=0;
 int64_t timestamp,newfilepos,streamfilepos=0,newtimepos=0,tms4000,skiptime;
 float matroskatrack_timescale;

 for(i=0;i<matroska->tracks.nb_elem;i++){
  track = &tracks[i];
#ifdef MPXPLAY_MKVDEBUG_SEEK2
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"i:%d ts:%8.8X ti:%d si:%d tri:%d",i,track->stream,
   ((track->stream)? track->stream->index:-1),stream_index,track->num);
#endif
  if(track->stream && (track->stream->index==stream_index)){
   track_index=track->num;
   break;
  }
 }
 if(!track || (i>=matroska->tracks.nb_elem))
  return -1;

 matroskatrack_timescale=MATROSKA_TIME_BASE/(float)matroska->time_scale*track->time_scale;
 tms4000=(int64_t)(4.0*matroskatrack_timescale);

 timestamp=(int64_t)((float)timestamp_av/AV_TIME_BASE*matroskatrack_timescale);

 index_list =&matroska->index;
 index = index_list->elem;

 if(index_list->nb_elem && (index[0].time<(int64_t)((float)s->duration*matroskatrack_timescale/AV_TIME_BASE))){
#ifdef MPXPLAY_MKVDEBUG_SEEK
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ts:%"PRIi64" i0t:%"PRIi64" i1t:%"PRIi64" iet:%"PRIi64" i0f:%"PRIi64" i1f:%"PRIi64"",
   timestamp,index[0].time,index[1].time,index[index_list->nb_elem-1].time,matroska_index_track_filepos(&index[0].pos,track_index),
   matroska_index_track_filepos(&index[1].pos,track_index));
#endif
  if(!timestamp_av){
   if(!index[0].time)
    streamfilepos=matroska_index_track_filepos(&index[0].pos,track_index);
   goto do_seek;
  }
  i=index_list->nb_elem-1;
  if(flags&AVSEEK_FLAG_BACKWARD){
   do{
    if(timestamp>=index[i].time)
     break;
    i--;
   }while(i>=0);
  }else{
   if(timestamp<index[i].time){
    for(i=0; i<index_list->nb_elem; i++)
     if(index[i].time>=timestamp)
      break;
   }
  }
  if((i>=0) && (i<index_list->nb_elem)){
   streamfilepos=matroska_index_track_filepos(&index[i].pos,track_index);
   newtimepos=index[i].time;
   indnum=i;
  }

do_seek:
  newtimediff=newtimepos-timestamp;
  if(newtimediff<0)
   newtimediff=-newtimediff;
  skiptime=0;
  if(flags&AVSEEK_FLAG_RELATIVE){
   if(flags&AVSEEK_FLAG_BACKWARD){
    if(timestamp>=index[0].time)
     skiptime=7.0*matroskatrack_timescale;
   }else if(timestamp<=index[(index_list->nb_elem-1)].time)
    skiptime=4.0*matroskatrack_timescale;
  }
  if(newtimediff>skiptime){ // if seek points are too far OR we seek absolute OR we seek before the first or after the last index-pos
   int64_t time_prev,time_next=0,stfilepos_prev,stfilepos_next;
   if(newtimepos>timestamp){
    time_next=index[indnum].time;
    stfilepos_next=matroska_index_track_filepos(&index[indnum].pos,track_index);
    if(indnum){
     time_prev=index[indnum-1].time;
     stfilepos_prev=matroska_index_track_filepos(&index[indnum-1].pos,track_index);
    }else{ // bof
     time_prev=0;
     stfilepos_prev=matroska->firstcluster_filepos;
    }
   }else if((newtimepos<timestamp) && (newtimepos>=index[indnum].time)){
    time_prev=index[indnum].time;
    stfilepos_prev=matroska_index_track_filepos(&index[indnum].pos,track_index);
    if((indnum+1)<index_list->nb_elem){
     time_next=index[indnum+1].time;
     stfilepos_next=matroska_index_track_filepos(&index[indnum+1].pos,track_index);
    }else{// if(!(flags&AVSEEK_FLAG_BACKWARD)){ // eof
     time_next=(float)s->duration*matroskatrack_timescale/AV_TIME_BASE;
     stfilepos_next=s->fbfs->filelength(s->fbds);
    }
   }
   if(time_next && (time_next>time_prev) && (stfilepos_next>stfilepos_prev)){
    newtimepos=timestamp;
    if(flags&AVSEEK_FLAG_RELATIVE){
     if(flags&AVSEEK_FLAG_BACKWARD){
      newtimepos-=tms4000;   // to not skip too small
      if(newtimepos<time_prev)
       newtimepos=time_prev;
     }else{
      newtimepos+=tms4000;   //
      if(newtimepos>time_next)
       newtimepos=time_next;
     }
    }
    if(newtimepos==time_next)
     streamfilepos=stfilepos_next;
    else{
     streamfilepos=newtimepos-matroskatrack_timescale; // hidden -1 sec skip (to find the begin of searched cluster)
     if(streamfilepos<=time_prev)
      streamfilepos=stfilepos_prev;
     else
      streamfilepos=(int64_t)(((float)stfilepos_next-(float)stfilepos_prev)
                   *((float)streamfilepos-(float)time_prev)
                   /((float)time_next-(float)time_prev))+stfilepos_prev;
    }
#ifdef MPXPLAY_MKVDEBUG_SEEK
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"fp:%"PRIu64" fn:%"PRIu64" ntp:%"PRIu64" tp:%"PRIu64" tn:%"PRIu64"",
     streamfilepos,stfilepos_prev,stfilepos_next,newtimepos,time_prev,time_next);
#endif
   }
  }
  newfilepos=matroska->segment_start+streamfilepos;
 }else{
  newtimepos=timestamp;
  if(flags&AVSEEK_FLAG_RELATIVE){
   if(flags&AVSEEK_FLAG_BACKWARD){
    newtimepos-=tms4000;
    if(newtimepos<0)
     newtimepos=0;
   }else
    newtimepos+=tms4000;
  }
  streamfilepos=newtimepos-matroskatrack_timescale; // hidden -1 sec skip
  if(streamfilepos<=0)
   newfilepos=matroska->firstcluster_filepos;
  else{
   newfilepos=s->fbfs->filelength(s->fbds)-matroska->firstcluster_filepos;
   newfilepos=(int64_t)((float)newfilepos*(float)streamfilepos/matroskatrack_timescale/(float)s->duration*AV_TIME_BASE);
   newfilepos+=matroska->firstcluster_filepos;
  }
 }

 matroska->skip_to_keyframe = 0;//!(flags & AVSEEK_FLAG_ANY);
 matroska->skip_to_timecode = newtimepos;
 matroska->seek_reset_bufs=1;

 newtimepos=(int64_t)((float)newtimepos/matroskatrack_timescale*(float)AV_TIME_BASE);

#ifdef MPXPLAY_MKVDEBUG_SEEK
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mrs si:%d ti:%d nf:%lld fs:%"PRIi64" ne:%d i:%d ts:%"PRIi64" nt:%"PRIi64" l:%"PRIi64" sp:%"PRIi64"",
  stream_index,track_index,newfilepos,(int64_t)s->fbfs->filelength(s->fbds),index_list->nb_elem,indnum,timestamp,
  newtimepos,s->duration/1000,(int64_t)streamfilepos);
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"st:%"PRIu64" cp:%"PRIu64"",matroska->segment_start,matroska->firstcluster_filepos);

/*for(i=0;i<10;i++){
  EbmlList *pos_list = &index[i].pos;
  MatroskaIndexPos *pos = pos_list->elem;
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ti%d:%d %d tr0:%d p0:%d tr1:%d p1:%d",i,(long)(index[i].time),(long)pos_list->nb_elem,
   (long)pos[0].track,(long)(pos[0].pos),(long)pos[1].track,(long)(pos[1].pos));
 }*/
#endif

 if(!matroska->blockdata_bufsize){
  matroska->block_data=pds_malloc(MATROSKA_BLOCKDATA_INITIAL_BUFSIZE);
  if(matroska->block_data)
   matroska->blockdata_bufsize=MATROSKA_BLOCKDATA_INITIAL_BUFSIZE;
 }

 streamfilepos=s->fbfs->fseek(s->fbds,newfilepos,SEEK_SET);
 if(streamfilepos<0){
#ifdef MPXPLAY_MKVDEBUG_SEEK
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"seek failed: %"PRIi64" %"PRIi64"",newfilepos,streamfilepos);
#endif
  return streamfilepos;
 }

 return newtimepos;
}

static int matroska_read_close(AVFormatContext *s)
{
 MatroskaDemuxContext *matroska = s->priv_data;
 MatroskaTrack *tracks = matroska->tracks.elem;
 int n;

 matroska_clear_queue(matroska);

 for(n=0; n < matroska->tracks.nb_elem; n++){
  if(tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
   mpxplay_av_free(tracks[n].audio.buf);
  //mpxplay_av_free(tracks[n].codec_priv.data);
 }
 ebml_free(matroska_segment_full, matroska);

 mpxplay_av_freep(&matroska->block_data);
 matroska->blockdata_bufsize=0;
#ifdef INFFMPG_LINK_LZO
 mpxplay_av_freep(&matroska->block_decode_buffer);
 matroska->block_decode_bufsize=0;
#endif

 return 0;
}

AVInputFormat mpxplay_ff_matroska_demuxer = {
 sizeof(MatroskaDemuxContext),
 matroska_read_header,
 matroska_read_packet,
 matroska_read_close,
 matroska_read_seek
};

#endif // MPXPLAY_LINK_INFILE_FFMPG
