Aegisub/src/MatroskaParser.c

3334 lines
78 KiB
C
Raw Permalink Normal View History

/*
* Copyright (c) 2004-2009 Mike Matsnev. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Absolutely no warranty of function or purpose is made by the author
* Mike Matsnev.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
/// @file MatroskaParser.c
/// @brief Haali's low-level Matroska-parsing library
/// @ingroup video_input
///
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#ifdef _WIN32
// MS names some functions differently
#define alloca _alloca
#define inline __inline
#include <tchar.h>
#endif
#ifndef EVCBUG
#define EVCBUG
#endif
#include "MatroskaParser.h"
#ifdef MATROSKA_COMPRESSION_SUPPORT
#include <zlib.h>
#endif
#define EBML_VERSION 1
#define EBML_MAX_ID_LENGTH 4
#define EBML_MAX_SIZE_LENGTH 8
#define MATROSKA_VERSION 2
#define MATROSKA_DOCTYPE "matroska"
#define MAX_STRING_LEN 1023
#define QSEGSIZE 512
#define MAX_TRACKS 32
#define MAX_READAHEAD (256*1024)
#define MAXCLUSTER (64*1048576)
#define MAXFRAME (4*1048576)
#ifdef WIN32
#define LL(x) x##i64
#define ULL(x) x##ui64
#else
#define LL(x) x##ll
#define ULL(x) x##ull
#endif
#define MAXU64 ULL(0xffffffffffffffff)
#define ONE ULL(1)
// compatibility
static char *mystrdup(struct InputStream *is,const char *src) {
size_t len;
char *dst;
if (src==NULL)
return NULL;
len = strlen(src);
dst = is->memalloc(is,len+1);
if (dst==NULL)
return NULL;
memcpy(dst,src,len+1);
return dst;
}
static void mystrlcpy(char *dst,const char *src,unsigned size) {
unsigned i;
for (i=0;i+1<size && src[i];++i)
dst[i] = src[i];
if (i<size)
dst[i] = 0;
}
struct Cue {
2014-03-23 17:01:25 +01:00
uint64_t Time;
uint64_t Position;
uint64_t Block;
unsigned char Track;
};
struct QueueEntry {
struct QueueEntry *next;
unsigned int Length;
2014-03-23 17:01:25 +01:00
uint64_t Start;
uint64_t End;
uint64_t Position;
unsigned int flags;
};
struct Queue {
struct QueueEntry *head;
struct QueueEntry *tail;
};
#define MPF_ERROR 0x10000
#define IBSZ 1024
#define RBRESYNC 1
struct MatroskaFile {
// parser config
unsigned flags;
// input
InputStream *cache;
// internal buffering
char inbuf[IBSZ];
2014-03-23 17:01:25 +01:00
uint64_t bufbase; // file offset of the first byte in buffer
int bufpos; // current read position in buffer
int buflen; // valid bytes in buffer
2014-05-20 18:44:20 +02:00
void *cpbuf;
// error reporting
char errmsg[128];
jmp_buf jb;
// pointers to key elements
2014-03-23 17:01:25 +01:00
uint64_t pSegment;
uint64_t pSeekHead;
uint64_t pSegmentInfo;
uint64_t pCluster;
uint64_t pTracks;
uint64_t pCues;
uint64_t pAttachments;
uint64_t pChapters;
uint64_t pTags;
// flags for key elements
struct {
unsigned int SegmentInfo:1;
unsigned int Cluster:1;
unsigned int Tracks:1;
unsigned int Cues:1;
unsigned int Attachments:1;
unsigned int Chapters:1;
unsigned int Tags:1;
} seen;
// file info
2014-03-23 17:01:25 +01:00
uint64_t firstTimecode;
// SegmentInfo
struct SegmentInfo Seg;
// Tracks
unsigned int nTracks,nTracksSize;
struct TrackInfo **Tracks;
// Queues
struct QueueEntry *QFreeList;
unsigned int nQBlocks,nQBlocksSize;
struct QueueEntry **QBlocks;
struct Queue *Queues;
2014-03-23 17:01:25 +01:00
uint64_t readPosition;
unsigned int trackMask;
2014-03-23 17:01:25 +01:00
uint64_t pSegmentTop; // offset of next byte after the segment
uint64_t tcCluster; // current cluster timecode
// Cues
unsigned int nCues,nCuesSize;
struct Cue *Cues;
// Attachments
unsigned int nAttachments,nAttachmentsSize;
struct Attachment *Attachments;
// Chapters
unsigned int nChapters,nChaptersSize;
struct Chapter *Chapters;
// Tags
unsigned int nTags,nTagsSize;
struct Tag *Tags;
};
///////////////////////////////////////////////////////////////////////////
// error reporting
static void myvsnprintf_string(char **pdest,char *de,const char *str) {
char *dest = *pdest;
while (dest < de && *str)
*dest++ = *str++;
*pdest = dest;
}
static void myvsnprintf_uint_impl(char **pdest,char *de,int width,int zero,
int neg,unsigned base,int letter,
2014-03-23 17:01:25 +01:00
int ms,uint64_t val)
{
char *dest = *pdest;
char tmp[21]; /* enough for 64 bit ints */
char *np = tmp + sizeof(tmp);
int rw,pad,trail;
char pc = zero ? '0' : ' ';
*--np = '\0';
if (val == 0)
*--np = '0';
else
while (val != 0) {
int rem = (int)(val % base);
val = val / base;
*--np = (char)(rem < 10 ? rem + '0' : rem - 10 + letter);
}
rw = (int)(tmp - np + sizeof(tmp) - 1);
if (ms)
++rw;
pad = trail = 0;
if (rw < width)
pad = width - rw;
if (neg)
trail = pad, pad = 0;
if (dest < de && ms)
*dest++ = '-';
while (dest < de && pad--)
*dest++ = pc;
while (dest < de && *np)
*dest++ = *np++;
while (dest < de && trail--)
*dest++ = ' ';
*pdest = dest;
}
static void myvsnprintf_uint(char **pdest,char *de,int width,int zero,
int neg,unsigned base,int letter,
2014-03-23 17:01:25 +01:00
uint64_t val)
{
myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,0,val);
}
static void myvsnprintf_int(char **pdest,char *de,int width,int zero,
int neg,unsigned base,int letter,
2014-03-23 17:01:25 +01:00
int64_t val)
{
if (val < 0)
myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,1,-val);
else
myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,0,val);
}
static void myvsnprintf(char *dest,unsigned dsize,const char *fmt,va_list ap) {
// s,d,x,u,ll
char *de = dest + dsize - 1;
int state = 0, width, zero, neg, ll;
if (dsize <= 1) {
if (dsize > 0)
*dest = '\0';
return;
}
while (*fmt && dest < de)
switch (state) {
case 0:
if (*fmt == '%') {
++fmt;
state = 1;
width = zero = neg = ll = 0;
} else
*dest++ = *fmt++;
break;
case 1:
if (*fmt == '-') {
neg = 1;
++fmt;
state = 2;
break;
}
if (*fmt == '0')
zero = 1;
state = 2;
case 2:
if (*fmt >= '0' && *fmt <= '9') {
width = width * 10 + *fmt++ - '0';
break;
}
state = 3;
case 3:
if (*fmt == 'l') {
++ll;
++fmt;
break;
}
state = 4;
case 4:
switch (*fmt) {
case 's':
myvsnprintf_string(&dest,de,va_arg(ap,const char *));
break;
case 'd':
switch (ll) {
case 0:
myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,int));
break;
case 1:
myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,long));
break;
case 2:
2014-03-23 17:01:25 +01:00
myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,int64_t));
break;
}
break;
case 'u':
switch (ll) {
case 0:
myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned int));
break;
case 1:
myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned long));
break;
case 2:
2014-03-23 17:01:25 +01:00
myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,uint64_t));
break;
}
break;
case 'x':
switch (ll) {
case 0:
myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned int));
break;
case 1:
myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned long));
break;
case 2:
2014-03-23 17:01:25 +01:00
myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,uint64_t));
break;
}
break;
case 'X':
switch (ll) {
case 0:
myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned int));
break;
case 1:
myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned long));
break;
case 2:
2014-03-23 17:01:25 +01:00
myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,uint64_t));
break;
}
break;
default:
break;
}
++fmt;
state = 0;
break;
default:
state = 0;
break;
}
*dest = '\0';
}
static void errorjmp(MatroskaFile *mf,const char *fmt, ...) {
va_list ap;
2014-05-20 18:44:20 +02:00
mf->cache->memfree(mf->cache, mf->cpbuf);
mf->cpbuf = NULL;
va_start(ap, fmt);
myvsnprintf(mf->errmsg,sizeof(mf->errmsg),fmt,ap);
va_end(ap);
mf->flags |= MPF_ERROR;
longjmp(mf->jb,1);
}
///////////////////////////////////////////////////////////////////////////
// arrays
static void *ArrayAlloc(MatroskaFile *mf,void **base,
unsigned *cur,unsigned *max,unsigned elem_size)
{
if (*cur>=*max) {
void *np;
unsigned newsize = *max * 2;
if (newsize==0)
newsize = 1;
np = mf->cache->memrealloc(mf->cache,*base,newsize*elem_size);
if (np==NULL)
errorjmp(mf,"Out of memory in ArrayAlloc");
*base = np;
*max = newsize;
}
return (char*)*base + elem_size * (*cur)++;
}
static void ArrayReleaseMemory(MatroskaFile *mf,void **base,
unsigned cur,unsigned *max,unsigned elem_size)
{
if (cur<*max) {
void *np = mf->cache->memrealloc(mf->cache,*base,cur*elem_size);
*base = np;
*max = cur;
}
}
#define ASGET(f,s,name) ArrayAlloc((f),(void**)&(s)->name,&(s)->n##name,&(s)->n##name##Size,sizeof(*((s)->name)))
#define AGET(f,name) ArrayAlloc((f),(void**)&(f)->name,&(f)->n##name,&(f)->n##name##Size,sizeof(*((f)->name)))
#define ARELEASE(f,s,name) ArrayReleaseMemory((f),(void**)&(s)->name,(s)->n##name,&(s)->n##name##Size,sizeof(*((s)->name)))
///////////////////////////////////////////////////////////////////////////
// queues
static struct QueueEntry *QPut(struct Queue *q,struct QueueEntry *qe) {
if (q->tail)
q->tail->next = qe;
qe->next = NULL;
q->tail = qe;
if (q->head==NULL)
q->head = qe;
return qe;
}
static struct QueueEntry *QGet(struct Queue *q) {
struct QueueEntry *qe = q->head;
if (qe == NULL)
return NULL;
q->head = qe->next;
if (q->tail == qe)
q->tail = NULL;
return qe;
}
static struct QueueEntry *QAlloc(MatroskaFile *mf) {
struct QueueEntry *qe,**qep;
if (mf->QFreeList == NULL) {
unsigned i;
qep = AGET(mf,QBlocks);
*qep = mf->cache->memalloc(mf->cache,QSEGSIZE * sizeof(*qe));
if (*qep == NULL)
errorjmp(mf,"Ouf of memory");
qe = *qep;
for (i=0;i<QSEGSIZE-1;++i)
qe[i].next = qe+i+1;
qe[QSEGSIZE-1].next = NULL;
mf->QFreeList = qe;
}
qe = mf->QFreeList;
mf->QFreeList = qe->next;
return qe;
}
static inline void QFree(MatroskaFile *mf,struct QueueEntry *qe) {
qe->next = mf->QFreeList;
mf->QFreeList = qe;
}
// fill the buffer at current position
static void fillbuf(MatroskaFile *mf) {
int rd;
// advance buffer pointers
mf->bufbase += mf->buflen;
mf->buflen = mf->bufpos = 0;
// get the relevant page
rd = mf->cache->read(mf->cache, mf->bufbase, mf->inbuf, IBSZ);
if (rd<0)
errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache));
mf->buflen = rd;
}
// fill the buffer and return next char
static int nextbuf(MatroskaFile *mf) {
fillbuf(mf);
if (mf->bufpos < mf->buflen)
return (unsigned char)(mf->inbuf[mf->bufpos++]);
return EOF;
}
static inline int readch(MatroskaFile *mf) {
return mf->bufpos < mf->buflen ? (unsigned char)(mf->inbuf[mf->bufpos++]) : nextbuf(mf);
}
2014-03-23 17:01:25 +01:00
static inline uint64_t filepos(MatroskaFile *mf) {
return mf->bufbase + mf->bufpos;
}
static void readbytes(MatroskaFile *mf,void *buffer,int len) {
char *cp = buffer;
int nb = mf->buflen - mf->bufpos;
if (nb > len)
nb = len;
memcpy(cp, mf->inbuf + mf->bufpos, nb);
mf->bufpos += nb;
len -= nb;
cp += nb;
if (len>0) {
mf->bufbase += mf->buflen;
mf->bufpos = mf->buflen = 0;
nb = mf->cache->read(mf->cache, mf->bufbase, cp, len);
if (nb<0)
errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache));
if (nb != len)
errorjmp(mf,"Short read: got %d bytes of %d",nb,len);
mf->bufbase += len;
}
}
2014-03-23 17:01:25 +01:00
static void skipbytes(MatroskaFile *mf,uint64_t len) {
unsigned int nb = mf->buflen - mf->bufpos;
if (nb > len)
nb = (int)len;
mf->bufpos += nb;
len -= nb;
if (len>0) {
mf->bufbase += mf->buflen;
mf->bufpos = mf->buflen = 0;
mf->bufbase += len;
}
}
2014-03-23 17:01:25 +01:00
static void seek(MatroskaFile *mf,uint64_t pos) {
// see if pos is inside buffer
if (pos>=mf->bufbase && pos<mf->bufbase+mf->buflen)
mf->bufpos = (unsigned)(pos - mf->bufbase);
else {
// invalidate buffer and set pointer
mf->bufbase = pos;
mf->buflen = mf->bufpos = 0;
}
}
///////////////////////////////////////////////////////////////////////////
// floating point
static inline MKFLOAT mkfi(int i) {
#ifdef MATROSKA_INTEGER_ONLY
MKFLOAT f;
2014-03-23 17:01:25 +01:00
f.v = (int64_t)i << 32;
return f;
#else
return i;
#endif
}
2014-03-23 17:01:25 +01:00
static inline int64_t mul3(MKFLOAT scale,int64_t tc) {
#ifdef MATROSKA_INTEGER_ONLY
// x1 x0
// y1 y0
// --------------
// x0*y0
// x1*y0
// x0*y1
// x1*y1
// --------------
// .. r1 r0 ..
//
// r = ((x0*y0) >> 32) + (x1*y0) + (x0*y1) + ((x1*y1) << 32)
unsigned x0,x1,y0,y1;
2014-03-23 17:01:25 +01:00
uint64_t p;
char sign = 0;
if (scale.v < 0)
sign = !sign, scale.v = -scale.v;
if (tc < 0)
sign = !sign, tc = -tc;
x0 = (unsigned)scale.v;
2014-03-23 17:01:25 +01:00
x1 = (unsigned)((uint64_t)scale.v >> 32);
y0 = (unsigned)tc;
2014-03-23 17:01:25 +01:00
y1 = (unsigned)((uint64_t)tc >> 32);
2014-03-23 17:01:25 +01:00
p = (uint64_t)x0*y0 >> 32;
p += (uint64_t)x0*y1;
p += (uint64_t)x1*y0;
p += (uint64_t)(x1*y1) << 32;
return p;
#else
2014-03-23 17:01:25 +01:00
return (int64_t)(scale * tc);
#endif
}
///////////////////////////////////////////////////////////////////////////
// EBML support
static int readID(MatroskaFile *mf) {
int c1,c2,c3,c4;
c1 = readch(mf);
if (c1 == EOF)
return EOF;
if (c1 & 0x80)
return c1;
if ((c1 & 0xf0) == 0)
errorjmp(mf,"Invalid first byte of EBML ID: %02X",c1);
c2 = readch(mf);
if (c2 == EOF)
fail:
errorjmp(mf,"Got EOF while reading EBML ID");
if ((c1 & 0xc0) == 0x40)
return (c1<<8) | c2;
c3 = readch(mf);
if (c3 == EOF)
goto fail;
if ((c1 & 0xe0) == 0x20)
return (c1<<16) | (c2<<8) | c3;
c4 = readch(mf);
if (c4 == EOF)
goto fail;
if ((c1 & 0xf0) == 0x10)
return (c1<<24) | (c2<<16) | (c3<<8) | c4;
return 0; // NOT REACHED
}
2014-03-23 17:01:25 +01:00
static uint64_t readVLUIntImp(MatroskaFile *mf,int *mask) {
int c,d,m;
2014-03-23 17:01:25 +01:00
uint64_t v = 0;
c = readch(mf);
if (c == EOF)
return 0; // XXX should errorjmp()?
if (c == 0)
errorjmp(mf,"Invalid first byte of EBML integer: 0");
for (m=0;;++m) {
if (c & (0x80 >> m)) {
c &= 0x7f >> m;
if (mask)
*mask = m;
2014-03-23 17:01:25 +01:00
return v | ((uint64_t)c << m*8);
}
d = readch(mf);
if (d == EOF)
errorjmp(mf,"Got EOF while reading EBML unsigned integer");
v = (v<<8) | d;
}
// NOT REACHED
}
2014-03-23 17:01:25 +01:00
static inline uint64_t readVLUInt(MatroskaFile *mf) {
return readVLUIntImp(mf,NULL);
}
2014-03-23 17:01:25 +01:00
static uint64_t readSize(MatroskaFile *mf) {
int m;
2014-03-23 17:01:25 +01:00
uint64_t v = readVLUIntImp(mf,&m);
// see if it's unspecified
if (v == (MAXU64 >> (57-m*7)))
errorjmp(mf,"Unspecified element size is not supported here.");
return v;
}
2014-03-23 17:01:25 +01:00
static inline int64_t readVLSInt(MatroskaFile *mf) {
static int64_t bias[8] = { (ONE<<6)-1, (ONE<<13)-1, (ONE<<20)-1, (ONE<<27)-1,
(ONE<<34)-1, (ONE<<41)-1, (ONE<<48)-1, (ONE<<55)-1 };
int m;
2014-03-23 17:01:25 +01:00
int64_t v = readVLUIntImp(mf,&m);
return v - bias[m];
}
2014-03-23 17:01:25 +01:00
static uint64_t readUInt(MatroskaFile *mf,unsigned int len) {
int c;
unsigned int m = len;
2014-03-23 17:01:25 +01:00
uint64_t v = 0;
if (len==0)
return v;
if (len>8)
errorjmp(mf,"Unsupported integer size in readUInt: %u",len);
do {
c = readch(mf);
if (c == EOF)
errorjmp(mf,"Got EOF while reading EBML unsigned integer");
v = (v<<8) | c;
} while (--m);
return v;
}
2014-03-23 17:01:25 +01:00
static inline int64_t readSInt(MatroskaFile *mf,unsigned int len) {
int64_t v = readUInt(mf,(unsigned)len);
int s = 64 - (len<<3);
return (v << s) >> s;
}
static MKFLOAT readFloat(MatroskaFile *mf,unsigned int len) {
#ifdef MATROSKA_INTEGER_ONLY
MKFLOAT f;
int shift;
#else
union {
unsigned int ui;
2014-03-23 17:01:25 +01:00
uint64_t ull;
float f;
double d;
} u;
#endif
if (len!=4 && len!=8)
errorjmp(mf,"Invalid float size in readFloat: %u",len);
#ifdef MATROSKA_INTEGER_ONLY
if (len == 4) {
unsigned ui = (unsigned)readUInt(mf,(unsigned)len);
f.v = (ui & 0x7fffff) | 0x800000;
if (ui & 0x80000000)
f.v = -f.v;
shift = (ui >> 23) & 0xff;
if (shift == 0) // assume 0
zero:
shift = 0, f.v = 0;
else if (shift == 255)
inf:
if (ui & 0x80000000)
f.v = LL(0x8000000000000000);
else
f.v = LL(0x7fffffffffffffff);
else {
shift += -127 + 9;
if (shift > 39)
goto inf;
shift:
if (shift < 0)
f.v = f.v >> -shift;
else if (shift > 0)
f.v = f.v << shift;
}
} else if (len == 8) {
2014-03-23 17:01:25 +01:00
uint64_t ui = readUInt(mf,(unsigned)len);
f.v = (ui & LL(0xfffffffffffff)) | LL(0x10000000000000);
if (ui & 0x80000000)
f.v = -f.v;
shift = (int)((ui >> 52) & 0x7ff);
if (shift == 0) // assume 0
goto zero;
else if (shift == 2047)
goto inf;
else {
shift += -1023 - 20;
if (shift > 10)
goto inf;
goto shift;
}
}
return f;
#else
if (len==4) {
u.ui = (unsigned int)readUInt(mf,(unsigned)len);
return u.f;
}
if (len==8) {
u.ull = readUInt(mf,(unsigned)len);
return u.d;
}
return 0;
#endif
}
2014-03-23 17:01:25 +01:00
static void readString(MatroskaFile *mf,uint64_t len,char *buffer,int buflen) {
unsigned int nread;
if (buflen<1)
errorjmp(mf,"Invalid buffer size in readString: %d",buflen);
nread = buflen - 1;
if (nread > len)
nread = (int)len;
readbytes(mf,buffer,nread);
len -= nread;
if (len>0)
skipbytes(mf,len);
buffer[nread] = '\0';
}
2014-03-23 17:01:25 +01:00
static void readLangCC(MatroskaFile *mf, uint64_t len, char lcc[4]) {
unsigned todo = len > 3 ? 3 : (int)len;
lcc[0] = lcc[1] = lcc[2] = lcc[3] = 0;
readbytes(mf, lcc, todo);
skipbytes(mf, len - todo);
}
///////////////////////////////////////////////////////////////////////////
// file parser
#define FOREACH(f,tl) \
{ \
2014-03-23 17:01:25 +01:00
uint64_t tmplen = (tl); \
{ \
2014-03-23 17:01:25 +01:00
uint64_t start = filepos(f); \
uint64_t cur,len; \
int id; \
for (;;) { \
cur = filepos(mf); \
if (cur == start + tmplen) \
break; \
id = readID(f); \
if (id==EOF) \
errorjmp(mf,"Unexpected EOF while reading EBML container"); \
len = readSize(mf); \
switch (id) {
#define ENDFOR1(f) \
default: \
skipbytes(f,len); \
break; \
}
#define ENDFOR2() \
} \
} \
}
#define ENDFOR(f) ENDFOR1(f) ENDFOR2()
#define myalloca(f,c) alloca(c)
#define STRGETF(f,v,len,func) \
{ \
char *TmpVal; \
unsigned TmpLen = (len)>MAX_STRING_LEN ? MAX_STRING_LEN : (unsigned)(len); \
TmpVal = func(f->cache,TmpLen+1); \
if (TmpVal == NULL) \
errorjmp(mf,"Out of memory"); \
readString(f,len,TmpVal,TmpLen+1); \
(v) = TmpVal; \
}
#define STRGETA(f,v,len) STRGETF(f,v,len,myalloca)
#define STRGETM(f,v,len) STRGETF(f,v,len,f->cache->memalloc)
/* Removed because it's not used.
static int IsWritingApp(MatroskaFile *mf,const char *str) {
const char *cp = mf->Seg.WritingApp;
if (!cp)
return 0;
while (*str && *str++==*cp++) ;
return !*str;
}
*/
2014-03-23 17:01:25 +01:00
static void parseEBML(MatroskaFile *mf,uint64_t toplen) {
uint64_t v;
char buf[32];
FOREACH(mf,toplen)
case 0x4286: // Version
v = readUInt(mf,(unsigned)len);
break;
case 0x42f7: // ReadVersion
v = readUInt(mf,(unsigned)len);
if (v > EBML_VERSION)
errorjmp(mf,"File requires version %d EBML parser",(int)v);
break;
case 0x42f2: // MaxIDLength
v = readUInt(mf,(unsigned)len);
if (v > EBML_MAX_ID_LENGTH)
errorjmp(mf,"File has identifiers longer than %d",(int)v);
break;
case 0x42f3: // MaxSizeLength
v = readUInt(mf,(unsigned)len);
if (v > EBML_MAX_SIZE_LENGTH)
errorjmp(mf,"File has integers longer than %d",(int)v);
break;
case 0x4282: // DocType
readString(mf,len,buf,sizeof(buf));
if (strcmp(buf,MATROSKA_DOCTYPE))
errorjmp(mf,"Unsupported DocType: %s",buf);
break;
case 0x4287: // DocTypeVersion
v = readUInt(mf,(unsigned)len);
break;
case 0x4285: // DocTypeReadVersion
v = readUInt(mf,(unsigned)len);
if (v > MATROSKA_VERSION)
errorjmp(mf,"File requires version %d Matroska parser",(int)v);
break;
ENDFOR(mf);
}
2014-03-23 17:01:25 +01:00
static void parseSeekEntry(MatroskaFile *mf,uint64_t toplen) {
int seekid = 0;
2014-03-23 17:01:25 +01:00
uint64_t pos = (uint64_t)-1;
FOREACH(mf,toplen)
case 0x53ab: // SeekID
if (len>EBML_MAX_ID_LENGTH)
errorjmp(mf,"Invalid ID size in parseSeekEntry: %d\n",(int)len);
seekid = (int)readUInt(mf,(unsigned)len);
break;
case 0x53ac: // SeekPos
pos = readUInt(mf,(unsigned)len);
break;
ENDFOR(mf);
2014-03-23 17:01:25 +01:00
if (pos == (uint64_t)-1)
errorjmp(mf,"Invalid element position in parseSeekEntry");
pos += mf->pSegment;
switch (seekid) {
case 0x114d9b74: // next SeekHead
if (mf->pSeekHead)
errorjmp(mf,"SeekHead contains more than one SeekHead pointer");
mf->pSeekHead = pos;
break;
case 0x1549a966: // SegmentInfo
mf->pSegmentInfo = pos;
break;
case 0x1f43b675: // Cluster
if (!mf->pCluster)
mf->pCluster = pos;
break;
case 0x1654ae6b: // Tracks
mf->pTracks = pos;
break;
case 0x1c53bb6b: // Cues
mf->pCues = pos;
break;
case 0x1941a469: // Attachments
mf->pAttachments = pos;
break;
case 0x1043a770: // Chapters
mf->pChapters = pos;
break;
case 0x1254c367: // tags
mf->pTags = pos;
break;
}
}
2014-03-23 17:01:25 +01:00
static void parseSeekHead(MatroskaFile *mf,uint64_t toplen) {
FOREACH(mf,toplen)
case 0x4dbb:
parseSeekEntry(mf,len);
break;
ENDFOR(mf);
}
2014-03-23 17:01:25 +01:00
static void parseSegmentInfo(MatroskaFile *mf,uint64_t toplen) {
MKFLOAT duration = mkfi(0);
if (mf->seen.SegmentInfo) {
skipbytes(mf,toplen);
return;
}
mf->seen.SegmentInfo = 1;
mf->Seg.TimecodeScale = 1000000; // Default value
FOREACH(mf,toplen)
case 0x73a4: // SegmentUID
if (len!=sizeof(mf->Seg.UID))
errorjmp(mf,"SegmentUID size is not %d bytes",mf->Seg.UID);
readbytes(mf,mf->Seg.UID,sizeof(mf->Seg.UID));
break;
case 0x7384: // SegmentFilename
STRGETM(mf,mf->Seg.Filename,len);