Simple MKV splitter

I finished off this task which was left a couple of weeks ago. This splitter, virtually a primitive parser,  draws recoginizable tracks out from MKV files, esp. helpful to get the subtitles. Reference: http://www.matroska.org/
The code is as follows:

#include <stdio.h>


typedef enum
{
    /* Table 1 */
    k_EMBLVersion = 0x4286,
    k_EMBLReadVersion = 0x42f7,
    k_EMBLMaxIdLength = 0x42f2,
    k_EMBLMaxSizeLength = 0x42f3,
    k_DocType = 0x4282,
    k_DocTypeVersion = 0x4287,
    k_DocTypeReadVersion = 0x4285,
    /* Table 2 */
    k_SegmentInfo = 0x1549a966,
    k_SeekHead = 0x114d9b74,
    k_Cluster = 0x1f43b675,
    k_Tracks = 0x1654ae6b,
    k_Cues = 0x1c53bb6b,
    k_Attachments = 0x1941a469,
    k_Chapters = 0x1043a770,
    k_Tags = 0x1254c367,
    /* Table 3 */
    k_SegmentUID = 0x73a4,
    k_SegmentFileName = 0x7384,
    k_PrevUID = 0x3cb923,
    k_PrevFileName = 0x3c83ab,
    k_NextUID = 0x3eb923,
    k_NextFileName = 0x3e83bb,
    k_TimeCodeScale = 0x2ad7b1,
    k_Duration = 0x4489,
    k_Title = 0x7ba9,
    k_MuxingApp = 0x4d80,
    k_DateUTC = 0x4461,
    /* Table 4 */
    k_Seek = 0x4dbb,
    /* Table 5 */
    k_SeekId = 0x53ab,
    k_SeekPosition = 0x53ac,
    /* Table 6 */
    k_TrackEntry = 0xae,
    /* Table 7 */
    k_TrackNumber = 0xd7,
    k_TrackUID = 0x73c5,
    k_TrackType = 0x83,
    k_FlagEnabled = 0xb9,
    k_FlagDefault = 0x88,
    k_FlagForced = 0x55aa,
    k_FlagLacing = 0x9c,
    k_MinCache = 0x6de7,
    k_MaxCache = 0x6df8,
    k_DefaultDuration = 0x23e383,
    k_TrackTimeCodeScale = 0x23314f,
    k_Name = 0x536e,
    k_Language = 0x22b59c,
    k_CodecId = 0x86,
    k_CodecPrivate = 0x63a2,
    k_CodecName = 0x258688,
    k_AttachmentLink = 0x7446,
    k_Video = 0xe0,
    k_Audio = 0xe1,
    k_ContentEncodings = 0x6d80,
    /* Table 8 */
    k_PixelWidth = 0xb0,
    k_PixelHeight = 0xba,
    k_PixelCropBottom = 0x54aa,
    k_PixelCropTop = 0x54bb,
    k_PixelCropLeft = 0x54cc,
    k_PixelCropRight = 0x54dd,
    k_DisplayWidth = 0x54b0,
    k_DisplayHeight = 0x54ba,
    k_DisplayUnit = 0x54b2,
    /* Table 9 */
    k_SampleFrequency = 0xb5,
    k_OutputSamplingFrequency = 0x78b5,
    k_Channels = 0x9f,
    k_BitDepth = 0x6264,
    /* Table 10 */
    k_ContentEncoding = 0x6240,
    /* Table 11 */
    k_ContentEncodingOrder = 0x5031,
    k_ContentEncodingScope = 0x5032,
    k_ContentEncodingType = 0x5033,
    k_ContentCompression = 0x5034,
    k_ContentEncryption = 0x5035,
    /* Table 12 */
    k_ContentCompAlgo = 0x4254,
    k_ContentCompSettings = 0x4255,
    /* Table 16 */
    k_TimeCode = 0xe7,
    k_Position = 0xa7,
    k_PrevSize = 0xab,
    k_BlockGroup = 0xa0,
    k_SimpleBlock = 0xa3,
    /* Table 17 */
    k_Block = 0xa1,
    k_ReferenceBlock = 0xfb,
    k_BlockDuration = 0x9b,
    /* Table 18 */
    k_CuePoint = 0xbb,
    /* Table 19 */
    k_CueTime = 0xb3,
    k_CueTrackPositions = 0xb7,
    /* Table 20 */
    k_CueTrack = 0xf7,
    k_CueClusterPosition = 0xf1,
    k_CueBlockNumber = 0x5378,
    /* Table 21 */
    k_EditionEntry = 0x45b9,
    /* Table 22: */
    k_EditionUID = 0x45bc,
    k_EditionFlagHidden = 0x45bd,
    k_EditionFlagDefault = 0x45db,
    k_EditionFlagOrdered = 0x45dd,
    k_ChapterAtom = 0xb6,
    /* Table 23 */
    k_ChapterUID = 0x73c4,
    k_ChapterTimeStart = 0x91,
    k_ChapterTimeEnd = 0x92,
    k_ChapterFlagHidden = 0x98,
    k_ChapterFlagEnabled = 0x4598,
    k_ChapterSegmentUID = 0x6e67,
    k_ChapterSegmentEditionUID = 0x6ebc,
    k_ChapterTracks = 0x8f,
    k_ChapterDisplay = 0x80,
    /* Table 24 */
    k_ChapterTrackNumber = 0x89,
    /* Table 25 */
    k_ChapString = 0x85,
    k_ChapLanguage = 0x437c,
    k_ChapCountry = 0x437e,
    /* Table 26 */
    k_AttachedFile = 0x61a7,
    /* Table 27 */  
    k_FileDescription = 0x467e,
    k_FileName = 0x466e,
    k_FileMimeType = 0x4660,
    k_FileData = 0x465c,
    k_FileUID = 0x46ae,
    /* Table 28 */  
    k_Tag = 0x7373,
    /* Table 29 */
    k_Targets = 0x63c0,
    k_SimpleTag = 0x67c8,
    /* Table 30 */  
    k_TargetTypeValue = 0x68ca,
    k_TargetType = 0x63ca,
    k_TargetUID = 0x63c5,
    k_TargetEditionUID = 0x63c9,
    k_TargetChapterUID = 0x63c4,
    /* Table 31 */
    k_TagName = 0x45a3,
    k_TagLanguage = 0x447a,
    k_TagOriginal = 0x4484,
    k_TagString = 0x4487,
    k_TagBinary = 0x4485,
} ElemId;


typedef unsigned char   uint8_t;
typedef unsigned char   sint8_t;
typedef unsigned char   uint16_t;
typedef unsigned char   sint16_t;
typedef unsigned long   uint32_t;
typedef signed long        sint32_t;

typedef int (*mkv_srng_proc)(void *, int start, int *end);
typedef int (*mkv_read_proc)(void *, int size, uint8_t *buf);
typedef int (*mkv_seek_proc)(void *, int offset);

typedef struct _mkvparser_t
{
    void *          read_info;
    mkv_read_proc   read;
    mkv_seek_proc   seek;
    mkv_srng_proc    set_range;
} mkvparser_t;

typedef enum _track_type_t
{
    k_VideoTrack    = 0x01,
    k_AudioTrack    = 0x02,
    k_ComplexTrack    = 0x03,
    k_LogoTrack        = 0x10,
    k_SubtitleTrack = 0x11,
    k_ButtonTrack    = 0x12,
    k_ControlTrack    = 0x20,
} track_type_t;


#define CHECK_READ(size, buf)   if(parser->read(parser->read_info, size, buf) != size){return -1;}

typedef struct _uint64_t
{
    uint32_t hi;
    uint32_t lo;
} uint64_t;

typedef struct _sint64_t
{
    sint32_t hi;
    uint32_t lo;
} sint64_t;

typedef struct _mkv_elem_t
{
    uint32_t        id;             /* elem id */

    uint8_t         id_len;         /* number of bytes occupied by elem id */
    uint8_t         size_buf_len;   /* number of bytes occupied by size */

    uint32_t        data_size;      /* size of data */
    uint32_t        data_addr;      /* absolute offset of the data from the file beginning */

} mkv_elem_t;


//////////////////////////////
// util

int read_sint16 (mkvparser_t *parser, sint16_t *val)
{
    uint8_t buf[2];
    uint16_t tmp;
    if (parser->read(parser->read_info, 2, buf) != 2)
    {
        return -1;
    }
    tmp = buf[0]<<8;
    tmp |= buf[1];
    *val = (sint16_t)tmp;
    return 2;
}

int read_int_withlen (mkvparser_t *parser, int len, uint32_t *val)
{
    uint8_t        buf[4];
    uint32_t    tmp = 0;
    int            i;
    len = len < 4? len : 4;
    len = parser->read(parser->read_info, len, buf);
    for (i = 0; i < len; i++)
    {
        tmp <<= 8;
        tmp |= buf[i];
    }
    *val = tmp;
    return len;
}

int read_int8 (mkvparser_t *parser, uint8_t *val)
{
    if (parser->read(parser->read_info, 1, val) != 1)
    {
        return -1;
    }
    return 1;
}

int read_vint (mkvparser_t *parser, uint64_t *val)
{
    int i;
    int code_len;
    uint8_t id_buf[4];

    CHECK_READ(1, id_buf);
    if (id_buf[0] & 0x80)
    {   /* class A */
        code_len = 1;
        id_buf[0] &= 0x7f;
    }
    else if (id_buf[0] & 0x40)
    {   /* class B */
        CHECK_READ(1, id_buf + 1);
        code_len = 2;
        id_buf[0] &= 0x3f;
    }
    else if (id_buf[0] & 0x20)
    {   /* class C */
        CHECK_READ(2, id_buf + 1);
        code_len = 3;
        id_buf[0] &= 0x1f;
    }
    else if (id_buf[0] & 0x10)
    {   /* class D */
        CHECK_READ(3, id_buf + 1);
        code_len = 4;
        id_buf[0] &= 0x0f;
    }
    else if (id_buf[0] & 0x08)
    {   /* class B */
        CHECK_READ(4, id_buf + 1);
        code_len = 5;
        id_buf[0] &= 0x07;
    }
    else if (id_buf[0] & 0x04)
    {   /* class C */
        CHECK_READ(5, id_buf + 1);
        code_len = 6;
        id_buf[0] &= 0x03;
    }
    else if (id_buf[0] & 0x02)
    {   /* class D */
        CHECK_READ(6, id_buf + 1);
        code_len = 7;
        id_buf[0] &= 0x01;
    }
    else if (id_buf[0] & 0x01)
    {   /* class D */
        CHECK_READ(7, id_buf + 1);
        code_len = 8;
        id_buf[0] = 0;
    }
    else
    {    /* error */
        return -2;
    }
    
    val->hi = 0;
    val->lo = 0;
    i = 0;
    if (code_len > 4)
    {
        for ( ; i < code_len - 4; i++)
        {
            val->hi <<= 8;
            val->hi |= id_buf[i];
        }
    }
    for (; i < code_len; i++)
    {
        val->lo <<= 8;
        val->lo |= id_buf[i];
    }

    return code_len;
}

int read_svint (mkvparser_t *parser, sint64_t *val)
{
    static sint32_t vsint_subtr_lo[] = {0x3f, 0x1fff, 0xfffff, 0x7ffffff};
    static sint32_t vsint_subtr_hi[] = {0x3, 0x1ff, 0xffff, 0x7fffff};
    int code_len = read_vint(parser, (uint64_t *)val);
    if (code_len <= 0)
    {
        return code_len;
    }
    if (code_len < 4)
    {
        val->lo -= vsint_subtr_lo[code_len];
        if (val->lo & 0x80000000)
        {
            val->hi = -1;
        }
    }
    else
    {
        val->hi -= vsint_subtr_hi[code_len - 4];
        val->lo -= 0xffffffff;
        if (val->lo != 0)
        {
            val->hi--;
        }
    }
    return code_len;
}

//////////////////////////////
// elem

char * get_elem_name (mkv_elem_t *elem)
{
    switch (elem->id)
    {
        /* Table 1 */
        case k_EMBLVersion: return "EMBLVersion";
        case k_EMBLReadVersion: return "EMBLReadVersion";
        case k_EMBLMaxIdLength: return "EMBLMaxIdLength";
        case k_EMBLMaxSizeLength: return "EMBLMaxSizeLength";
        case k_DocType: return "DocType";
        case k_DocTypeVersion: return "DocTypeVersion";
        case k_DocTypeReadVersion: return "DocTypeReadVersion";
        /* Table 2 */
        case k_SegmentInfo: return "SegmentInfo";
        case k_SeekHead: return "SeekHead";
        case k_Cluster: return "Cluster";
        case k_Tracks: return "Tracks";
        case k_Cues: return "Cues";
        case k_Attachments: return "Attachments";
        case k_Chapters: return "Chapters";
        case k_Tags: return "Tags";
        /* Table 3 */
        case k_SegmentUID: return "SegmentUID";
        case k_SegmentFileName: return "SegmentFileName";
        case k_PrevUID: return "PrevUID";
        case k_PrevFileName: return "PrevFileName";
        case k_NextUID: return "NextUID";
        case k_NextFileName: return "NextFileName";
        case k_TimeCodeScale: return "TimeCodeScale";
        case k_Duration: return "Duration";
        case k_Title: return "Title";
        case k_MuxingApp: return "MuxingApp";
        case k_DateUTC: return "DateUTC";
        /* Table 4 */
        case k_Seek: return "Seek";
        /* Table 5 */
        case k_SeekId: return "SeekId";
        case k_SeekPosition: return "SeekPosition";
        /* Table 6 */
        case k_TrackEntry: return "TrackEntry";
        /* Table 7 */
        case k_TrackNumber: return "TrackNumber";
        case k_TrackUID: return "TrackUID";
        case k_TrackType: return "TrackType";
        case k_FlagEnabled: return "FlagEnabled";
        case k_FlagDefault: return "FlagDefault";
        case k_FlagForced: return "FlagForced";
        case k_FlagLacing: return "FlagLacing";
        case k_MinCache: return "MinCache";
        case k_MaxCache: return "MaxCache";
        case k_DefaultDuration: return "DefaultDuration";
        case k_TrackTimeCodeScale: return "TrackTimeCodeScale";
        case k_Name: return "Name";
        case k_Language: return "Language";
        case k_CodecId: return "CodecId";
        case k_CodecPrivate: return "CodecPrivate";
        case k_CodecName: return "CodecName";
        case k_AttachmentLink: return "AttachmentLink";
        case k_Video: return "Video";
        case k_Audio: return "Audio";
        case k_ContentEncodings: return "ContentEncodings";
        /* Table 8 */
        case k_PixelWidth: return "PixelWidth";
        case k_PixelHeight: return "PixelHeight";
        case k_PixelCropBottom: return "PixelCropBottom";
        case k_PixelCropTop: return "PixelCropTop";
        case k_PixelCropLeft: return "PixelCropLeft";
        case k_PixelCropRight: return "PixelCropRight";
        case k_DisplayWidth: return "DisplayWidth";
        case k_DisplayHeight: return "DisplayHeight";
        case k_DisplayUnit: return "DisplayUnit";
        /* Table 9 */
        case k_SampleFrequency: return "SampleFrequency";
        case k_OutputSamplingFrequency: return "OutputSamplingFrequency";
        case k_Channels: return "Channels";
        case k_BitDepth: return "BitDepth";
        /* Table 10 */
        case k_ContentEncoding: return "ContentEncoding";
        /* Table 11 */
        case k_ContentEncodingOrder: return "ContentEncodingOrder";
        case k_ContentEncodingScope: return "ContentEncodingScope";
        case k_ContentEncodingType: return "ContentEncodingType";
        case k_ContentCompression: return "ContentCompression";
        case k_ContentEncryption: return "ContentEncryption";
        /* Table 12 */
        case k_ContentCompAlgo: return "ContentCompAlgo";
        case k_ContentCompSettings: return "ContentCompSettings";
        /* Table 16 */
        case k_TimeCode: return "TimeCode";
        case k_Position: return "Position";
        case k_PrevSize: return "PrevSize";
        case k_BlockGroup: return "BlockGroup";
        case k_SimpleBlock: return "SimpleBlock";
        /* Table 17 */
        case k_Block: return "Block";
        case k_ReferenceBlock: return "ReferenceBlock";
        case k_BlockDuration: return "BlockDuration";
        /* Table 18 */
        case k_CuePoint: return "CuePoint";
        /* Table 19 */
        case k_CueTime: return "CueTime";
        case k_CueTrackPositions: return "CueTrackPositions";
        /* Table 20 */
        case k_CueTrack: return "CueTrack";
        case k_CueClusterPosition: return "CueClusterPosition";
        case k_CueBlockNumber: return "CueBlockNumber";
        /* Table 21 */
        case k_EditionEntry: return "EditionEntry";
        /* Table 22: */
        case k_EditionUID: return "EditionUID";
        case k_EditionFlagHidden: return "EditionFlagHidden";
        case k_EditionFlagDefault: return "EditionFlagDefault";
        case k_EditionFlagOrdered: return "EditionFlagOrdered";
        case k_ChapterAtom: return "ChapterAtom";
        /* Table 23 */
        case k_ChapterUID: return "ChapterUID";
        case k_ChapterTimeStart: return "ChapterTimeStart";
        case k_ChapterTimeEnd: return "ChapterTimeEnd";
        case k_ChapterFlagHidden: return "ChapterFlagHidden";
        case k_ChapterFlagEnabled: return "ChapterFlagEnabled";
        case k_ChapterSegmentUID: return "ChapterSegmentUID";
        case k_ChapterSegmentEditionUID: return "ChapterSegmentEditionUID";
        case k_ChapterTracks: return "ChapterTracks";
        case k_ChapterDisplay: return "ChapterDisplay";
        /* Table 24 */
        case k_ChapterTrackNumber: return "ChapterTrackNumber";
        /* Table 25 */
        case k_ChapString: return "ChapString";
        case k_ChapLanguage: return "ChapLanguage";
        case k_ChapCountry: return "ChapCountry";
        /* Table 26 */
        case k_AttachedFile: return "AttachedFile";
        /* Table 27 */  
        case k_FileDescription: return "FileDescription";
        case k_FileName: return "FileName";
        case k_FileMimeType: return "FileMimeType";
        case k_FileData: return "FileData";
        case k_FileUID: return "FileUID";
        /* Table 28 */  
        case k_Tag: return "Tag";
        /* Table 29 */
        case k_Targets: return "Targets";
        case k_SimpleTag: return "SimpleTag";
        /* Table 30 */  
        case k_TargetTypeValue: return "TargetTypeValue";
        case k_TargetType: return "TargetType";
        case k_TargetUID: return "TargetUID";
        case k_TargetEditionUID: return "TargetEditionUID";
        case k_TargetChapterUID: return "TargetChapterUID";
        /* Table 31 */
        case k_TagName: return "TagName";
        case k_TagLanguage: return "TagLanguage";
        case k_TagOriginal: return "TagOriginal";
        case k_TagString: return "TagString";
        case k_TagBinary: return "TagBinary";
        default: return 0;
    }
}

char * get_track_type_name (track_type_t type)
{
    switch (type)
    {
        case k_VideoTrack: return "Video";
        case k_AudioTrack: return "Audio";
        case k_ComplexTrack: return "Complex";
        case k_LogoTrack: return "Logo";
        case k_SubtitleTrack: return "Subtitle";
        case k_ButtonTrack: return "Button";
        case k_ControlTrack: return "Control";
        default: return "Unknown";
    }
}

//////////////////////////////
// mkvparser

#define MAX_TRACKS    16
typedef struct _mkv_track_t
{
    track_type_t    type;
    uint32_t    language;
    uint32_t    tcodescale;
    uint32_t    block_tcode;
    uint32_t    timecode_scale;
    FILE        *fout;
    int            err;
} mkv_track_t;

typedef struct _mkv_t
{
    uint32_t    cluster_tcode;
    mkv_track_t    tracks[MAX_TRACKS];
    uint32_t    trackidx;

} mkv_t;


static char * get_indent (int stackdepth)
{
    static char indent[1024];
    static int old = 0;
    if (indent[0] != ' ' && indent[1] != ' ')
    {
        int i;
        for (i = 0; i < 64; i++)
        {
            indent[i] = ' ';
        }
        indent[0] = 0;
    }
    if (old != stackdepth)
    {
        indent[old*2] = ' ';
        old = stackdepth;
        indent[old*2] = 0;
    }

    return indent;
}

static int mkv_get_elem (mkvparser_t *parser, int start, int end, mkv_elem_t *elem)
{
    uint8_t local_buf[8];
    uint8_t id_buf[4];
    int     size_buf_len = 0;
    int     i;

    /* parse Element ID */
    CHECK_READ(1, id_buf);
    if (id_buf[0] & 0x80)
    {   /* class A */
        elem->id_len = 1;
    }
    else if (id_buf[0] & 0x40)
    {   /* class B */
        CHECK_READ(1, id_buf + 1);
        elem->id_len = 2;
    }
    else if (id_buf[0] & 0x20)
    {   /* class C */
        CHECK_READ(2, id_buf + 1);
        elem->id_len = 3;
    }
    else if (id_buf[0] & 0x10)
    {   /* class D */
        CHECK_READ(3, id_buf + 1);
        elem->id_len = 4;
    }
    else
    {
        return -2;  /* syntax error */
    }

    /* parse size */
    CHECK_READ(1, local_buf);

    if (local_buf[0] & 0x80)
    {
        size_buf_len = 1;   
    }
    else if (local_buf[0] & 0x40)
    {
        CHECK_READ(1, local_buf + 1);
        size_buf_len = 2;   
    }
    else if (local_buf[0] & 0x20)
    {
        CHECK_READ(2, local_buf + 1);
        size_buf_len = 3;   
    }
    else if (local_buf[0] & 0x10)
    {
        CHECK_READ(3, local_buf + 1);
        size_buf_len = 4;   
    }
    else if (local_buf[0] & 0x08)
    {
        CHECK_READ(4, local_buf + 1);
        size_buf_len = 5;   
    }
    else if (local_buf[0] & 0x04)
    {
        CHECK_READ(5, local_buf + 1);
        size_buf_len = 6;   
    }
    else if (local_buf[0] & 0x02)
    {
        CHECK_READ(6, local_buf + 1);
        size_buf_len = 7;   
    }
    else if (local_buf[0] & 0x01)
    {
        CHECK_READ(7, local_buf + 1);
        size_buf_len = 8;   
    }
    else
    {
        return -2;  /* syntax error */
    }

    elem->data_size = local_buf[0] & (0xff >> size_buf_len);
    for (i = 1; i < size_buf_len; i++)
    {
        elem->data_size <<= 8;
        elem->data_size += local_buf[i];
    }

    elem->data_addr = start + elem->id_len + size_buf_len;
    elem->size_buf_len = size_buf_len;

    if (elem->data_addr + elem->data_size > end)
    {    /* invalid */
        return -1;
    }

    // form the id
    elem->id = 0;
    for (i = 0; i < elem->id_len; i++)
    {
        elem->id <<= 8;
        elem->id |= id_buf[i];
    }

    return 0;   /* success */
}

static void elem_checkin  (mkv_elem_t *elem, int stackdepth)
{
    char *indent = get_indent(stackdepth);
    char *elem_name = get_elem_name(elem);
    
    if (elem_name)
    {
        printf("%s%s () { 0x%08x /n", indent, elem_name, elem->data_addr - elem->id_len - elem->size_buf_len);
    }
    else
    {
        printf("%s[%X] () { 0x%08x /n", indent, elem->id, elem->data_addr - elem->id_len - elem->size_buf_len);
    }
}

static void elem_checkout (mkv_elem_t *elem, int stackdepth)
{
    char *indent = get_indent(stackdepth);
    char *elem_name = get_elem_name(elem);

    if (elem_name)
    {
        printf("%s} %s () /n", indent, elem_name);
    }
    else
    {
        printf("%s} [%X] () /n", indent, elem->id);
    }
}

static void mkv_init (mkv_t *mkv)
{
    memset(mkv, 0, sizeof(mkv_t));
    mkv->cluster_tcode = 0;
}

void mkvparser_init (mkvparser_t *parser, mkv_read_proc read, mkv_seek_proc seek,
               mkv_srng_proc srng, void *read_info)
{
    parser->read = read;
    parser->seek = seek;
    parser->set_range = srng;
    parser->read_info = read_info;
}


void mkvparser_general_parse (mkvparser_t *parser)
{
#define STACK_SIZE  32
    int         depth = 0;
    int            start, end;
    int            top_end;
    int            file_end;
    int            res;
    int            deeper = 1;
    int            i;
    mkv_elem_t  *cur, *top;
    mkv_elem_t  stack[STACK_SIZE];
    uint8_t        buf[16];

    // mkv syntax related
    mkv_t        mkv;

    mkv_init(&mkv);

    start = 0, end = -1;
    if (parser->set_range(parser->read_info, start, &end) != 0)
    {
        return;
    }
    file_end = end;

    while (1)
    {
        if (deeper && depth < STACK_SIZE)
        {
            cur = stack + depth;
            res = mkv_get_elem(parser, start, end, cur);
        }
        else
        {   // no deeper or stack overflow to be concealed
            res = -1;
        }

        if (res == 0)
        {   // elem found

            start = cur->data_addr;
            end = cur->data_addr + cur->data_size;

            /* validate `end' */
            top = stack + depth - 1;
            top_end = top->data_addr + top->data_size;
            if (end > top_end)
            {   // invalid stream
                end = top_end; 
            }
            if (parser->set_range(parser->read_info, cur->data_addr, &end) != 0)
            {
                return;
            }
            cur->data_size = end - cur->data_addr;    // correct mandatorily

            elem_checkin(cur, depth);

            // Process the Tag
            {
                char *indent = get_indent(depth + 1);
                if (cur->id == k_Block)
                {    // Block
                    int bytesgone = 0;
                    int readsize = 0;
                    union
                    {
                        uint64_t    u64;
                        sint16_t    s16;
                    } val;
                    uint8_t        lacing;

                    readsize = read_vint(parser, &val.u64);
                    if (bytesgone != -1 && readsize > 0) { bytesgone += readsize; }
                    printf("%sTrackNumber = %d/n", indent, val.u64.lo);
                    mkv.trackidx = val.u64.lo - 1;

                    if (mkv.trackidx < 0 || mkv.trackidx >= MAX_TRACKS)
                    {
                        mkv.trackidx = -1;
                    }
                   
                    val.s16 = 0;
                    readsize = read_sint16(parser, &val.s16);
                    if (bytesgone != -1 && readsize > 0) { bytesgone += readsize; }
                    if (mkv.trackidx >= 0)
                    {
                        printf("%sTrackType = %s/n", indent, get_track_type_name(mkv.tracks[mkv.trackidx].type));

                        mkv.tracks[mkv.trackidx].block_tcode = mkv.cluster_tcode + val.s16;
                        printf("%sTimeCode = %d * %f/n", indent,
                            mkv.tracks[mkv.trackidx].block_tcode,
                            mkv.tracks[mkv.trackidx].timecode_scale);
                    }

                    readsize = read_int8(parser, &lacing);
                    if (bytesgone != -1 && readsize > 0) { bytesgone += readsize; }

                    if (lacing != 0)
                    {
                        printf("%sFlags = 0x%04x/n", indent, lacing);
                    }

                    if (lacing & 0x06)    //lacing
                    {
                        printf("%sLacing not supported yet/n");
                    }
                    else if (mkv.trackidx >= 0)
                    {
                        #define OUTBUFSIZE    4096
                        unsigned char outbuf[OUTBUFSIZE];
                        int rem = cur->data_size - bytesgone;

                        printf("outputing track %d with size %d/n", mkv.trackidx + 1, rem);

                        FILE *f = mkv.tracks[mkv.trackidx].fout;
                        if (f) while (rem > 0)
                        {
                            int outsize = rem<OUTBUFSIZE? rem : OUTBUFSIZE;
                            rem -= outsize;

                            readsize = parser->read(parser->read_info, outsize, outbuf);
                            if (readsize < outsize)
                            {    // fatal error
                                mkv.tracks[mkv.trackidx].err = 1;
                                break;
                            }
                            else
                            {
                                fwrite(outbuf, 1, outsize, f);
                            }
                        }

                        if (mkv.tracks[mkv.trackidx].type == k_SubtitleTrack)
                        {
                            char retchar[] = {0xd, 0xa};
                            fwrite(retchar, 1, 2, f);
                        }
                    }

                    deeper = 0;
                }
                else if (cur->id == k_ReferenceBlock)
                {
                    uint32_t tcode;
                    if (cur->data_size <= 4 && read_int_withlen(parser, cur->data_size, &tcode) > 0)
                    {
                        if (mkv.trackidx >= 0)
                        {
                            printf("%sTimeCode = %d * %d/n", indent,
                                mkv.tracks[mkv.trackidx].block_tcode + tcode,
                                mkv.tracks[mkv.trackidx].timecode_scale);
                        }
                    }
                }
                else if (cur->id == k_TrackNumber || cur->id == k_TrackType || cur->id == k_Language)
                {    // TrackName, TrackType, Language
                    if (cur->id == k_TrackNumber)
                    {
                        mkv.trackidx = -1;
                        if (cur->data_size <= 4)
                        {
                            uint32_t trackidx;
                            if (read_int_withlen(parser, cur->data_size, &trackidx) > 0
                                && trackidx <= MAX_TRACKS)
                            {
                                mkv.trackidx = trackidx - 1;
                            }
                        }
                    }
                    else if (cur->id == k_TrackType && cur->data_size <= 4)
                    {
                        uint32_t type;
                        if (read_int_withlen(parser, cur->data_size, &type) > 0)
                        {
                            printf("%sTrackType = %s/n", indent, get_track_type_name(type));
                            if (mkv.trackidx >= 0)
                            {
                                mkv.tracks[mkv.trackidx].type = type;
                               
                                char fnbuf[32];
                                sprintf(fnbuf, "track%02d.dat", mkv.trackidx + 1);
                                mkv.tracks[mkv.trackidx].fout = fopen(fnbuf, "wb");
                            }
                        }
                    }
                    else
                    {
                        int read_size = (16<cur->data_size)?16:cur->data_size;
                        parser->read(parser->read_info, read_size, buf);
                        printf("%s", indent);
                        for (i = 0; i < read_size; i++)
                        {
                            printf("%02x ", buf[i]);
                        }
                        printf("/n");
                    }

                    deeper = 0;
                }
                else if (cur->id == k_TimeCode)
                {    // cluster timecode
                    if (cur->data_size <= 4)
                    {
                        uint32_t    tcode;
                        if (read_int_withlen(parser, cur->data_size, &tcode) > 0)
                        {
                            mkv.cluster_tcode = tcode;
                            printf("%sTimeCode = %d/n", indent, tcode);
                        }
                    }
                    deeper = 0;
                }
                else if (cur->id == k_TrackTimeCodeScale)
                {
                    uint32_t tcscale;
                    printf("tcodescale datasize = %d/n", cur->data_size);
                    if (cur->data_size <= 4 && read_int_withlen(parser, cur->data_size, &tcscale) > 0)
                    {
                        printf("%sTrackTimeCodeScale = %d/n", indent, tcscale);
                        if (mkv.trackidx >= 0)
                        {  
                            *(uint32_t*)(&mkv.tracks[mkv.trackidx].timecode_scale)
                                = tcscale;    // it's a floating number!
                        }
                    }
                    deeper = 0;
                }
            }

            depth++;
        }
        else
        {   // no more sub-element in current element
            // move to the end
            --depth;
            if (depth < 0)
            {
                break;
            }

            // move on to the next element on the same level
            cur = stack + depth;
            start = cur->data_addr + cur->data_size;

            if (depth > 0)
            {
                top = stack + depth - 1;
                end = top->data_addr + top->data_size;
            }
            else
            {
                end = file_end;
            }

            parser->set_range(parser->read_info, start, &end);

            // TODO:
            // ...
            deeper = 1;

            elem_checkout(cur, depth);
        }
    }
    for (i = 0; i < MAX_TRACKS; i++)
    {
        if (mkv.tracks[i].fout)
        {
            fclose(mkv.tracks[i].fout);
            if (mkv.tracks[i].err)
            {    // clear the file
                char fnbuf[32];
                sprintf(fnbuf, "track%02d.dat", mkv.trackidx + 1);
                mkv.tracks[i].fout = fopen(fnbuf, "wb");
                fclose(mkv.tracks[i].fout);
            }
        }
    }
}


//////////////////////////////
// main

typedef struct _mkv_read_info
{
    FILE    *fIn;
    int     end;
    int        file_end;
} mkv_read_info;

void mkv_file_init (void *info, FILE *fIn)
{
    mkv_read_info *mfr_info = (mkv_read_info *)info;
    mfr_info->fIn = fIn;
    fseek(fIn, 0, SEEK_END);
    mfr_info->file_end = ftell(fIn);
    fseek(fIn, 0, SEEK_SET);
}

int mkv_file_read (void *info, int size, uint8_t *buf)
{
    mkv_read_info *mfr_info = (mkv_read_info *)info;
    FILE *fIn = mfr_info->fIn;
    int start = ftell(fIn);
    int end = mfr_info->end;
    if (end >= 0 && start + size > end)
    {
        size = end - start;
    }
    if (size <= 0)
    {
        return 0;
    }

    return fread(buf, 1, size, fIn);
}

int mkv_file_seek (void *info, int offset)
{
    mkv_read_info   *mfr_info = (mkv_read_info *)info;
    FILE *fIn = mfr_info->fIn;
    int end = mfr_info->end;

    if (end >= 0 && offset > end)
    {
        return 0;
    }
    fseek(fIn, offset, SEEK_SET);
    return (offset == ftell(fIn));
}

int mkv_file_setrange (void *info, int start, int *end)
{
    mkv_read_info   *mfr_info = (mkv_read_info *)info;

    if (!mkv_file_seek(mfr_info, start))
    {
        return -1;
    }
    if (*end > mfr_info->file_end || *end <= 0)
    {
        *end = mfr_info->file_end;
    }
    mfr_info->end = *end;
    return 0;
}

int main (void)
{
    FILE            *fIn = fopen("in.mkv", "rb");
    mkv_read_info    rinfo;
    mkvparser_t    parser;

    if (!fIn)
    {
        goto bail;
    }

    mkv_file_init(&rinfo, fIn);
    mkvparser_init(&parser, mkv_file_read, mkv_file_seek, mkv_file_setrange, &rinfo);
    mkvparser_general_parse(&parser);

    fclose(fIn);
bail:
    return 0;
}


posted @ 2008-04-20 03:23  quanben  阅读(301)  评论(0编辑  收藏  举报