Update to v1.61 of MatroskaParser library

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30942 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
David McPaul
2009-06-02 09:15:14 +00:00
parent 656341901e
commit 938fd5a7e0
3 changed files with 388 additions and 167 deletions
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2004-2005 Mike Matsnev. All Rights Reserved.
* Copyright (c) 2004-2006 Mike Matsnev. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,7 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: MatroskaParser.c,v 1.31 2005/03/07 11:22:16 mike Exp $
* $Id: MatroskaParser.c,v 1.61 2006/10/28 10:39:45 mike Exp $
*
*/
@@ -50,23 +50,20 @@
#include "MatroskaParser.h"
#ifdef MATROSKA_COMPRESSION_SUPPORT
#include <zlib.h>
#include "zlib.h"
#endif
#define EBML_VERSION 1
#define EBML_MAX_ID_LENGTH 4
#define EBML_MAX_SIZE_LENGTH 8
#define MATROSKA_VERSION 1
#define MATROSKA_VERSION 2
#define MATROSKA_DOCTYPE "matroska"
#define MAX_STRING_LEN 1023
#define QSEGSIZE 512
#define MAX_TRACKS 32
#define MAX_READAHEAD (512*1024)
#define MAX_READAHEAD (256*1024)
#define DEFAULT_PAGE_SIZE 65536
#define MIN_PAGE_SIZE 4096
#define MIN_PAGES 2
#define MAXCLUSTER (64*1048576)
#define MAXFRAME (4*1048576)
@@ -90,7 +87,7 @@ static char *mystrdup(struct InputStream *is,const char *src) {
return NULL;
len = strlen(src);
dst = (char *)is->memalloc(is,len+1);
dst = is->memalloc(is,len+1);
if (dst==NULL)
return NULL;
@@ -236,6 +233,9 @@ static void myvsnprintf_uint_impl(char **pdest,char *de,int width,int zero,
char pc = zero ? '0' : ' ';
*--np = '\0';
if (val == 0)
*--np = '0';
else
while (val != 0) {
int rem = (int)(val % base);
val = val / base;
@@ -290,7 +290,7 @@ static void myvsnprintf_int(char **pdest,char *de,int width,int zero,
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;
int state = 0, width = 0, zero = 0, neg = 0, ll = 0;
if (dsize <= 1) {
if (dsize > 0)
@@ -315,6 +315,8 @@ static void myvsnprintf(char *dest,unsigned dsize,const char *fmt,va_list ap)
state = 2;
break;
}
if (*fmt == '0')
zero = 1;
state = 2;
case 2:
if (*fmt >= '0' && *fmt <= '9') {
@@ -537,7 +539,7 @@ static inline ulonglong filepos(MatroskaFile *mf) {
}
static void readbytes(MatroskaFile *mf,void *buffer,int len) {
char *cp = (char *)buffer;
char *cp = buffer;
int nb = mf->buflen - mf->bufpos;
if (nb > len)
@@ -852,6 +854,14 @@ static void readString(MatroskaFile *mf,ulonglong len,char *buffer,int buflen) {
buffer[nread] = '\0';
}
static void readLangCC(MatroskaFile *mf, ulonglong 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) \
@@ -888,7 +898,7 @@ static void readString(MatroskaFile *mf,ulonglong len,char *buffer,int buflen) {
{ \
char *TmpVal; \
unsigned TmpLen = (len)>MAX_STRING_LEN ? MAX_STRING_LEN : (unsigned)(len); \
TmpVal = (char *)func(f->cache,TmpLen+1); \
TmpVal = func(f->cache,TmpLen+1); \
if (TmpVal == NULL) \
errorjmp(mf,"Out of memory"); \
readString(f,len,TmpVal,TmpLen+1); \
@@ -1006,7 +1016,7 @@ static void parseSeekHead(MatroskaFile *mf,ulonglong toplen) {
}
static void parseSegmentInfo(MatroskaFile *mf,ulonglong toplen) {
MKFLOAT duration;
MKFLOAT duration = mkfi(0);
if (mf->seen.SegmentInfo) {
skipbytes(mf,toplen);
@@ -1051,6 +1061,7 @@ static void parseSegmentInfo(MatroskaFile *mf,ulonglong toplen) {
break;
case 0x4461: // DateUTC
mf->Seg.DateUTC = readUInt(mf,(unsigned)len);
mf->Seg.DateUTCValid = 1;
break;
case 0x7ba9: // Title
STRGETM(mf,mf->Seg.Title,len);
@@ -1074,6 +1085,12 @@ static void parseFirstCluster(MatroskaFile *mf,ulonglong toplen) {
case 0xe7: // Timecode
mf->firstTimecode += readUInt(mf,(unsigned)len);
break;
case 0xa3: // BlockEx
readVLUInt(mf); // track number
mf->firstTimecode += readSInt(mf, 2);
skipbytes(mf,start + toplen - filepos(mf));
return;
case 0xa0: // BlockGroup
FOREACH(mf,len)
case 0xa1: // Block
@@ -1093,61 +1110,85 @@ static void parseVideoInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *t
FOREACH(mf,toplen)
case 0x9a: // FlagInterlaced
ti->Video.Interlaced = readUInt(mf,(unsigned)len)!=0;
ti->AV.Video.Interlaced = readUInt(mf,(unsigned)len)!=0;
break;
case 0x53b8: // StereoMode
v = readUInt(mf,(unsigned)len);
if (v>3)
errorjmp(mf,"Invalid stereo mode");
ti->Video.StereoMode = (unsigned char)v;
ti->AV.Video.StereoMode = (unsigned char)v;
break;
case 0xb0: // PixelWidth
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"PixelWidth is too large");
ti->Video.PixelWidth = (unsigned)v;
ti->AV.Video.PixelWidth = (unsigned)v;
if (!dW)
ti->Video.DisplayWidth = ti->Video.PixelWidth;
ti->AV.Video.DisplayWidth = ti->AV.Video.PixelWidth;
break;
case 0xba: // PixelHeight
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"PixelHeight is too large");
ti->Video.PixelHeight = (unsigned)v;
ti->AV.Video.PixelHeight = (unsigned)v;
if (!dH)
ti->Video.DisplayHeight = ti->Video.PixelHeight;
ti->AV.Video.DisplayHeight = ti->AV.Video.PixelHeight;
break;
case 0x54b0: // DisplayWidth
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"DisplayWidth is too large");
ti->Video.DisplayWidth = (unsigned)v;
ti->AV.Video.DisplayWidth = (unsigned)v;
dW = 1;
break;
case 0x54ba: // DisplayHeight
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"DisplayHeight is too large");
ti->Video.DisplayHeight = (unsigned)v;
ti->AV.Video.DisplayHeight = (unsigned)v;
dH = 1;
break;
case 0x54b2: // DisplayUnit
v = readUInt(mf,(unsigned)len);
if (v>2)
errorjmp(mf,"Invalid DisplayUnit: %d",(int)v);
ti->Video.DisplayUnit = (unsigned char)v;
ti->AV.Video.DisplayUnit = (unsigned char)v;
break;
case 0x54b3: // AspectRatioType
v = readUInt(mf,(unsigned)len);
if (v>2)
errorjmp(mf,"Invalid AspectRatioType: %d",(int)v);
ti->Video.AspectRatioType = (unsigned char)v;
ti->AV.Video.AspectRatioType = (unsigned char)v;
break;
case 0x54aa: // PixelCropBottom
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"PixelCropBottom is too large");
ti->AV.Video.CropB = (unsigned)v;
break;
case 0x54bb: // PixelCropTop
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"PixelCropTop is too large");
ti->AV.Video.CropT = (unsigned)v;
break;
case 0x54cc: // PixelCropLeft
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"PixelCropLeft is too large");
ti->AV.Video.CropL = (unsigned)v;
break;
case 0x54dd: // PixelCropRight
v = readUInt(mf,(unsigned)len);
if (v>0xffffffff)
errorjmp(mf,"PixelCropRight is too large");
ti->AV.Video.CropR = (unsigned)v;
break;
case 0x2eb524: // ColourSpace
ti->Video.ColourSpace = (unsigned)readUInt(mf,4);
ti->AV.Video.ColourSpace = (unsigned)readUInt(mf,4);
break;
case 0x2fb523: // GammaValue
ti->Video.GammaValue = readFloat(mf,(unsigned)len);
ti->AV.Video.GammaValue = readFloat(mf,(unsigned)len);
break;
ENDFOR(mf);
}
@@ -1157,30 +1198,36 @@ static void parseAudioInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *t
FOREACH(mf,toplen)
case 0xb5: // SamplingFrequency
ti->Audio.SamplingFreq = readFloat(mf,(unsigned)len);
ti->AV.Audio.SamplingFreq = readFloat(mf,(unsigned)len);
break;
case 0x78b5: // OutputSamplingFrequency
ti->Audio.OutputSamplingFreq = readFloat(mf,(unsigned)len);
ti->AV.Audio.OutputSamplingFreq = readFloat(mf,(unsigned)len);
break;
case 0x9f: // Channels
v = readUInt(mf,(unsigned)len);
if (v<1 || v>255)
errorjmp(mf,"Invalid Channels value");
ti->Audio.Channels = (unsigned char)v;
ti->AV.Audio.Channels = (unsigned char)v;
break;
case 0x7d7b: // ChannelPositions
skipbytes(mf,len);
break;
case 0x6264: // BitDepth
v = readUInt(mf,(unsigned)len);
#if 0
if ((v<1 || v>255) && !IsWritingApp(mf,"AVI-Mux GUI"))
errorjmp(mf,"Invalid BitDepth: %d",(int)v);
ti->Audio.BitDepth = (unsigned char)v;
#endif
ti->AV.Audio.BitDepth = (unsigned char)v;
break;
ENDFOR(mf);
if (mkv_TruncFloat(ti->Audio.OutputSamplingFreq)==0)
ti->Audio.OutputSamplingFreq = ti->Audio.SamplingFreq;
if (ti->AV.Audio.Channels == 0)
ti->AV.Audio.Channels = 1;
if (mkv_TruncFloat(ti->AV.Audio.SamplingFreq) == 0)
ti->AV.Audio.SamplingFreq = mkfi(8000);
if (mkv_TruncFloat(ti->AV.Audio.OutputSamplingFreq)==0)
ti->AV.Audio.OutputSamplingFreq = ti->AV.Audio.SamplingFreq;
}
static void CopyStr(char **src,char **dst) {
@@ -1198,8 +1245,9 @@ static void CopyStr(char **src,char **dst) {
static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
struct TrackInfo t,*tp,**tpp;
ulonglong v;
char *cp = NULL;
size_t cplen = 0, cpadd = 0;
char *cp = NULL, *cs = NULL;
size_t cplen = 0, cslen = 0, cpadd = 0;
unsigned CompScope = 0, num_comp = 0;
if (mf->nTracks >= MAX_TRACKS)
errorjmp(mf,"Too many tracks.");
@@ -1229,10 +1277,6 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
if (v<1 || v>254)
errorjmp(mf,"Invalid track type: %d",(int)v);
t.Type = (unsigned char)v;
if (t.Type == 2) { // fill defaults for audio tracks
t.Audio.Channels = 1;
t.Audio.SamplingFreq = mkfi(8000);
}
break;
case 0xb9: // Enabled
t.Enabled = readUInt(mf,(unsigned)len)!=0;
@@ -1261,15 +1305,16 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
case 0x23314f: // TrackTimecodeScale
t.TimecodeScale = readFloat(mf,(unsigned)len);
break;
case 0x55ee: // MaxBlockAdditionID
t.MaxBlockAdditionID = (unsigned)readUInt(mf,(unsigned)len);
break;
case 0x536e: // Name
if (t.Name)
errorjmp(mf,"Duplicate Track Name");
STRGETA(mf,t.Name,len);
break;
case 0x22b59c: // Language
if (t.Language)
errorjmp(mf,"Duplicate Track Language");
STRGETA(mf,t.Language,len);
readLangCC(mf, len, t.Language);
break;
case 0x86: // CodecID
if (t.CodecID)
@@ -1282,7 +1327,7 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
if (len>262144) // 256KB
errorjmp(mf,"CodecPrivate is too large: %d",(int)len);
cplen = (unsigned)len;
cp = (char *)alloca(cplen);
cp = alloca(cplen);
readbytes(mf,cp,(int)cplen);
break;
case 0x258688: // CodecName
@@ -1315,30 +1360,45 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
case 0x6d80: // ContentEncodings
FOREACH(mf,len)
case 0x6240: // ContentEncoding
// fill in defaults
t.CompEnabled = 1;
t.CompMethod = COMP_ZLIB;
CompScope = 1;
if (++num_comp > 1)
return; // only one compression layer supported
FOREACH(mf,len)
case 0x5031: // ContentEncodingOrder
readUInt(mf,(unsigned)len);
break;
case 0x5032: // ContentEncodingScope
readUInt(mf,(unsigned)len);
CompScope = (unsigned)readUInt(mf,(unsigned)len);
break;
case 0x5033: // ContentEncodingType
readUInt(mf,(unsigned)len);
if (readUInt(mf,(unsigned)len) != 0)
return; // encryption is not supported
break;
case 0x5034: // ContentCompression
// fill in defaults
t.CompEnabled = 1;
t.CompMethod = COMP_ZLIB;
FOREACH(mf,len)
case 0x4254: // ContentCompAlgo
v = readUInt(mf,(unsigned)len);
if (v != 0)
errorjmp(mf,"Unsupported compression algorithm: %d",(int)v);
t.CompEnabled = 1;
switch (v) {
case 0: // Zlib
t.CompMethod = COMP_ZLIB;
break;
case 3: // prepend fixed data
t.CompMethod = COMP_PREPEND;
break;
default:
return; // unsupported compression, skip track
}
break;
case 0x4255: // ContentCompSettings
skipbytes(mf,len);
if (len > 256)
return;
cslen = (unsigned)len;
cs = alloca(cslen);
readbytes(mf, cs, (int)cslen);
break;
ENDFOR(mf);
break;
@@ -1353,36 +1413,95 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
if (!t.CodecID)
errorjmp(mf,"Track has no Codec ID");
if (t.UID != 0) {
unsigned i;
for (i = 0; i < mf->nTracks; ++i)
if (mf->Tracks[i]->UID == t.UID) // duplicate track entry
return;
}
#ifdef MATROSKA_COMPRESSION_SUPPORT
// handle compressed CodecPrivate
if (t.CompEnabled && t.CompMethod == COMP_ZLIB && (CompScope & 2) && cplen > 0) {
z_stream zs;
char tmp[64], *ncp;
int code;
uLong ncplen;
memset(&zs,0,sizeof(zs));
if (inflateInit(&zs) != Z_OK)
errorjmp(mf, "inflateInit failed");
zs.next_in = cp;
zs.avail_in = cplen;
do {
zs.next_out = tmp;
zs.avail_out = sizeof(tmp);
code = inflate(&zs, Z_NO_FLUSH);
} while (code == Z_OK);
if (code != Z_STREAM_END)
errorjmp(mf, "invalid compressed data in CodecPrivate");
ncplen = zs.total_out;
ncp = alloca(ncplen);
inflateReset(&zs);
zs.next_in = cp;
zs.avail_in = cplen;
zs.next_out = ncp;
zs.avail_out = ncplen;
if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
errorjmp(mf, "inflate failed");
inflateEnd(&zs);
cp = ncp;
cplen = ncplen;
}
#endif
if (t.CompEnabled && !(CompScope & 1)) {
t.CompEnabled = 0;
cslen = 0;
}
// allocate new track
tpp = (TrackInfo **)AGET(mf,Tracks);
tpp = AGET(mf,Tracks);
// copy strings
if (t.Name)
cpadd += strlen(t.Name)+1;
if (t.Language)
cpadd += strlen(t.Language)+1;
if (t.CodecID)
cpadd += strlen(t.CodecID)+1;
tp = (TrackInfo *)mf->cache->memalloc(mf->cache,sizeof(*tp) + cplen + cpadd);
tp = mf->cache->memalloc(mf->cache,sizeof(*tp) + cplen + cslen + cpadd);
if (tp == NULL)
errorjmp(mf,"Out of memory");
memcpy(tp,&t,sizeof(*tp));
memcpy(tp+1,cp,cplen);
if (cplen) {
tp->CodecPrivate = tp+1;
tp->CodecPrivateSize = (unsigned)cplen;
memcpy(tp->CodecPrivate,cp,cplen);
}
if (cslen) {
tp->CompMethodPrivate = (char *)(tp+1) + cplen;
tp->CompMethodPrivateSize = cslen;
memcpy(tp->CompMethodPrivate, cs, cslen);
}
cp = (char*)(tp+1) + cplen;
cp = (char*)(tp+1) + cplen + cslen;
CopyStr(&tp->Name,&cp);
CopyStr(&tp->Language,&cp);
CopyStr(&tp->CodecID,&cp);
// set default language
if (!tp->Language)
tp->Language="eng";
if (!tp->Language[0])
memcpy(tp->Language, "eng", 4);
*tpp = tp;
}
@@ -1498,8 +1617,6 @@ static void parseCues(MatroskaFile *mf,ulonglong toplen) {
mf->Cues[j] = tmp;
++k;
}
fixupCues(mf);
}
static void parseAttachment(MatroskaFile *mf,ulonglong toplen) {
@@ -1554,7 +1671,7 @@ static void parseChapter(MatroskaFile *mf,ulonglong toplen,struct Chapter *paren
struct ChapterDisplay *disp;
struct ChapterProcess *proc;
struct ChapterCommand *cmd;
struct Chapter *ch = (Chapter *)ASGET(mf,parent,Children);
struct Chapter *ch = ASGET(mf,parent,Children);
memset(ch,0,sizeof(*ch));
@@ -1564,6 +1681,12 @@ static void parseChapter(MatroskaFile *mf,ulonglong toplen,struct Chapter *paren
case 0x73c4: // ChapterUID
ch->UID = readUInt(mf,(unsigned)len);
break;
case 0x6e67: // ChapterSegmentUID
if (len != sizeof(ch->SegmentUID))
skipbytes(mf, len);
else
readbytes(mf, ch->SegmentUID, sizeof(ch->SegmentUID));
break;
case 0x91: // ChapterTimeStart
ch->Start = readUInt(mf,(unsigned)len);
break;
@@ -1602,28 +1725,19 @@ static void parseChapter(MatroskaFile *mf,ulonglong toplen,struct Chapter *paren
disp = ASGET(mf,ch,Display);
memset(disp, 0, sizeof(*disp));
}
if (disp->Language)
skipbytes(mf,len);
else
STRGETM(mf,disp->Language,len);
readLangCC(mf, len, disp->Language);
break;
case 0x437e: // ChapterCountry
if (disp==NULL) {
disp = ASGET(mf,ch,Display);
memset(disp, 0, sizeof(*disp));
}
if (disp->Country)
skipbytes(mf,len);
else
STRGETM(mf,disp->Country,len);
readLangCC(mf, len, disp->Country);
break;
ENDFOR(mf);
if (disp && !disp->String) {
mf->cache->memfree(mf->cache,disp->Language);
mf->cache->memfree(mf->cache,disp->Country);
if (disp && !disp->String)
--ch->nDisplay;
}
break;
case 0x6944: // ChapProcess
proc = NULL;
@@ -1703,7 +1817,7 @@ static void parseChapters(MatroskaFile *mf,ulonglong toplen) {
FOREACH(mf,toplen)
case 0x45b9: // EditionEntry
ch = (Chapter *)AGET(mf,Chapters);
ch = AGET(mf,Chapters);
memset(ch, 0, sizeof(*ch));
FOREACH(mf,len)
case 0x45bc: // EditionUID
@@ -1735,7 +1849,7 @@ static void parseTags(MatroskaFile *mf,ulonglong toplen) {
FOREACH(mf,toplen)
case 0x7373: // Tag
tag = (Tag *)AGET(mf,Tags);
tag = AGET(mf,Tags);
memset(tag,0,sizeof(*tag));
FOREACH(mf,len)
@@ -1781,10 +1895,7 @@ static void parseTags(MatroskaFile *mf,ulonglong toplen) {
STRGETM(mf,st->Value,len);
break;
case 0x447a: // TagLanguage
if (st->Language)
skipbytes(mf,len);
else
STRGETM(mf,st->Language,len);
readLangCC(mf, len, st->Language);
break;
case 0x4484: // TagDefault
st->Default = readUInt(mf,(unsigned)len)!=0;
@@ -1841,18 +1952,22 @@ static void parseContainerPos(MatroskaFile *mf,ulonglong pos) {
}
static void parsePointers(MatroskaFile *mf) {
jmp_buf jb;
if (mf->pSegmentInfo && !mf->seen.SegmentInfo)
parseContainerPos(mf,mf->pSegmentInfo);
if (mf->pCluster && !mf->seen.Cluster)
parseContainerPos(mf,mf->pCluster);
if (mf->pTracks && !mf->seen.Tracks)
parseContainerPos(mf,mf->pTracks);
if (mf->pCues && !mf->seen.Cues) {
parseContainerPos(mf,mf->pCues);
// ignore errors
mf->flags &= ~MPF_ERROR;
}
memcpy(&jb,&mf->jb,sizeof(jb));
if (setjmp(mf->jb))
mf->flags &= ~MPF_ERROR; // ignore errors
else {
if (mf->pCues && !mf->seen.Cues)
parseContainerPos(mf,mf->pCues);
if (mf->pAttachments && !mf->seen.Attachments)
parseContainerPos(mf,mf->pAttachments);
if (mf->pChapters && !mf->seen.Chapters)
@@ -1861,6 +1976,9 @@ static void parsePointers(MatroskaFile *mf) {
parseContainerPos(mf,mf->pTags);
}
memcpy(&mf->jb,&jb,sizeof(jb));
}
static void parseSegment(MatroskaFile *mf,ulonglong toplen) {
ulonglong nextpos;
unsigned nSeekHeads = 0, dontstop = 0;
@@ -1882,9 +2000,9 @@ static void parseSegment(MatroskaFile *mf,ulonglong toplen) {
seek(mf,mf->pSeekHead);
id = readID(mf);
if (id==EOF) // chained SeekHead points to EOF?
goto resume;
break;
if (id != 0x114d9b74) // chained SeekHead doesnt point to a SeekHead?
goto resume;
break;
len = readSize(mf);
} else if (mf->pSegmentInfo && mf->pTracks && mf->pCues && mf->pCluster) { // we have pointers to all key elements
// XXX EVIL HACK
@@ -1899,10 +2017,6 @@ static void parseSegment(MatroskaFile *mf,ulonglong toplen) {
dontstop = 1;
}
} while (mf->pSeekHead);
resume:
// XXX workaround for AVI-Mux GUI
if (mf->pSegmentInfo && !mf->seen.SegmentInfo)
parseContainerPos(mf,mf->pSegmentInfo);
seek(mf,nextpos); // resume reading segment
break;
case 0x1549a966: // SegmentInfo
@@ -1945,10 +2059,43 @@ resume:
parsePointers(mf);
}
static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode) {
static void parseBlockAdditions(MatroskaFile *mf, ulonglong toplen, ulonglong timecode, unsigned track) {
ulonglong add_id = 1, add_pos = 0, add_len = 0;
unsigned char have_add;
FOREACH(mf, toplen)
case 0xa6: // BlockMore
have_add = 0;
FOREACH(mf, len)
case 0xee: // BlockAddId
add_id = readUInt(mf, (unsigned)len);
break;
case 0xa5: // BlockAddition
add_pos = filepos(mf);
add_len = len;
skipbytes(mf, len);
++have_add;
break;
ENDFOR(mf);
if (have_add == 1 && id > 0 && id < 255) {
struct QueueEntry *qe = QAlloc(mf);
qe->Start = qe->End = timecode;
qe->Position = add_pos;
qe->Length = (unsigned)add_len;
qe->flags = FRAME_UNKNOWN_START | FRAME_UNKNOWN_END |
(((unsigned)add_id << FRAME_STREAM_SHIFT) & FRAME_STREAM_MASK);
QPut(&mf->Queues[track],qe);
}
break;
ENDFOR(mf);
}
static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode, int blockex) {
ulonglong v;
ulonglong duration = 0;
ulonglong dpos;
// unsigned add_id = 0;
struct QueueEntry *qe,*qf = NULL;
unsigned char have_duration = 0, have_block = 0;
unsigned char gap = 0;
@@ -1961,11 +2108,17 @@ static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode
unsigned *sizes;
signed short block_timecode;
if (blockex)
goto blockex;
FOREACH(mf,toplen)
case 0xfb: // ReferenceBlock
readSInt(mf,(unsigned)len);
ref = 1;
break;
blockex:
cur = start = filepos(mf);
len = tmplen = toplen;
case 0xa1: // Block
have_block = 1;
@@ -1977,15 +2130,16 @@ static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode
trackid = (unsigned char)v;
for (tracknum=0;tracknum<mf->nTracks;++tracknum)
if (mf->Tracks[tracknum]->Number == trackid)
if (mf->Tracks[tracknum]->Number == trackid) {
if (mf->trackMask & (1<<tracknum)) // ignore this block
break;
goto found;
errorjmp(mf,"Invalid track number in Block: %u",trackid);
found:
}
if (mf->trackMask & (1<<tracknum)) { // ignore this block
// bad trackid/unsupported track
skipbytes(mf,start + tmplen - filepos(mf)); // shortcut
return;
}
found:
block_timecode = (signed short)readSInt(mf,2);
@@ -1997,6 +2151,9 @@ found:
if (c==EOF)
errorjmp(mf,"Unexpected EOF while reading Block flags");
if (blockex)
ref = !(c & 0x80);
gap = c & 0x1;
lacing = (c >> 1) & 3;
@@ -2007,7 +2164,7 @@ found:
nframes = c+1;
} else
nframes = 1;
sizes = (unsigned *)alloca(nframes*sizeof(*sizes));
sizes = alloca(nframes*sizeof(*sizes));
switch (lacing) {
case 0: // No lacing
@@ -2071,39 +2228,50 @@ found:
mf->cache->read(mf->cache,v,NULL,0); // touch page
skipbytes(mf,len - filepos(mf) + dpos);
if (blockex)
goto out;
break;
case 0x9b: // BlockDuration
duration = readUInt(mf,(unsigned)len);
have_duration = 1;
break;
case 0x75a1: // BlockAdditions
if (nframes > 0) // have some frames
parseBlockAdditions(mf, len, timecode, tracknum);
else
skipbytes(mf, len);
break;
ENDFOR(mf);
out:
if (!have_block)
errorjmp(mf,"Found a BlockGroup without Block");
if (nframes > 1) {
ulonglong defd = mf->Tracks[tracknum]->DefaultDuration;
v = qf->Start;
if (have_duration) {
duration = mul3(mf->Tracks[tracknum]->TimecodeScale,
duration * mf->Seg.TimecodeScale);
dpos = duration / nframes;
v = qf->Start;
for (qe = qf; nframes > 1; --nframes, qe = qe->next) {
qe->Start = v;
v += dpos;
duration -= dpos;
v += defd;
duration -= defd;
qe->End = v;
#if 0
qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
#endif
}
qe->Start = v;
qe->End = v + duration;
qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
qe->flags &= ~FRAME_UNKNOWN_END;
} else if (mf->Tracks[tracknum]->DefaultDuration) {
dpos = mf->Tracks[tracknum]->DefaultDuration;
v = qf->Start;
for (qe = qf; nframes > 0; --nframes, qe = qe->next) {
qe->Start = v;
v += dpos;
v += defd;
qe->End = v;
qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
}
@@ -2159,16 +2327,18 @@ static int readMoreBlocks(MatroskaFile *mf) {
memcpy(&jb,&mf->jb,sizeof(jb));
if (setjmp(mf->jb)) { // something evil happened here, try to resync
// always advance read position no matter what so
// we don't get caught in an endless loop
mf->readPosition = filepos(mf);
ret = EOF;
if (++retries > 3) // don't try too hard
goto ex;
for (;;) {
if (filepos(mf) >= mf->pSegmentTop) {
mf->readPosition = filepos(mf);
if (filepos(mf) >= mf->pSegmentTop)
goto ex;
}
cp = mf->cache->scan(mf->cache,filepos(mf),0x1f43b675); // cluster
@@ -2194,7 +2364,7 @@ static int readMoreBlocks(MatroskaFile *mf) {
mf->readPosition = cp;
}
cstop = mf->cache->getsize(mf->cache)>>1;
cstop = mf->cache->getcachesize(mf->cache)>>1;
if (cstop > MAX_READAHEAD)
cstop = MAX_READAHEAD;
cstop += mf->readPosition;
@@ -2223,10 +2393,29 @@ static int readMoreBlocks(MatroskaFile *mf) {
case 0xab: // PrevSize
readUInt(mf,(unsigned)len);
break;
case 0x5854: { // SilentTracks
unsigned stmask = 0, i, trk;
FOREACH(mf, len)
case 0x58d7: // SilentTrackNumber
trk = (unsigned)readUInt(mf, (unsigned)len);
for (i = 0; i < mf->nTracks; ++i)
if (mf->Tracks[i]->Number == trk) {
stmask |= 1 << i;
break;
}
break;
ENDFOR(mf);
// TODO pass stmask to reading app
break; }
case 0xa0: // BlockGroup
if (!have_timecode)
errorjmp(mf,"Found BlockGroup before cluster TimeCode");
parseBlockGroup(mf,len,mf->tcCluster);
parseBlockGroup(mf,len,mf->tcCluster, 0);
goto out;
case 0xa3: // BlockEx
if (!have_timecode)
errorjmp(mf,"Found BlockGroup before cluster TimeCode");
parseBlockGroup(mf, len, mf->tcCluster, 1);
goto out;
ENDFOR(mf);
out:;
@@ -2234,7 +2423,9 @@ out:;
if (toplen > MAXFRAME)
errorjmp(mf,"Element in a cluster is too large around %llu, %X [%u]",filepos(mf),cid,(unsigned)toplen);
if (cid == 0xa0) // BlockGroup
parseBlockGroup(mf,toplen,mf->tcCluster);
parseBlockGroup(mf,toplen,mf->tcCluster, 0);
else if (cid == 0xa3) // BlockEx
parseBlockGroup(mf, toplen, mf->tcCluster, 1);
else
skipbytes(mf,toplen);
}
@@ -2283,7 +2474,7 @@ static void reindex(MatroskaFile *mf) {
jmp_buf jb;
ulonglong pos = mf->pCluster;
ulonglong step = 10*1024*1024;
ulonglong size, tc, isize;
ulonglong size, tc = 0, isize;
longlong next_cluster;
int id, have_tc, bad;
struct Cue *cue;
@@ -2412,7 +2603,7 @@ static longlong findLastTimecode(MatroskaFile *mf) {
ulonglong nd = 0;
unsigned n,vtrack;
if (mf->nCues == 0 || mf->nTracks == 0)
if (mf->nTracks == 0)
return -1;
for (n=vtrack=0;n<mf->nTracks;++n)
@@ -2424,9 +2615,16 @@ static longlong findLastTimecode(MatroskaFile *mf) {
return -1;
ok:
mf->trackMask = ~(1 << vtrack);
EmptyQueues(mf);
if (mf->nCues == 0) {
mf->readPosition = mf->pCluster + 13000000 > mf->pSegmentTop ? mf->pCluster : mf->pSegmentTop - 13000000;
mf->tcCluster = 0;
} else {
mf->readPosition = mf->Cues[mf->nCues - 1].Position + mf->pSegment;
mf->tcCluster = mf->Cues[mf->nCues - 1].Time / mf->Seg.TimecodeScale;
}
mf->trackMask = ~(1 << vtrack);
do
while (mf->Queues[vtrack].head)
@@ -2437,10 +2635,19 @@ ok:
nd = tc;
QFree(mf,QGet(&mf->Queues[vtrack]));
}
while (readMoreBlocks(mf) != EOF);
while (fillQueues(mf,0) != EOF);
mf->trackMask = 0;
EmptyQueues(mf);
// there may have been an error, but at this point we will ignore it
if (mf->flags & MPF_ERROR) {
mf->flags &= ~MPF_ERROR;
if (nd == 0)
return -1;
}
return nd;
}
@@ -2480,22 +2687,30 @@ segment:
// found it
mf->pSegment = filepos(mf);
mf->pSegmentTop = len == MAXU64 ? MAXU64 : mf->pSegment + len;
if (len == MAXU64) {
mf->pSegmentTop = MAXU64;
if (mf->cache->getfilesize) {
longlong seglen = mf->cache->getfilesize(mf->cache);
if (seglen > 0)
mf->pSegmentTop = seglen;
}
} else
mf->pSegmentTop = mf->pSegment + len;
parseSegment(mf,len);
// check if we got all data
if (!mf->seen.SegmentInfo)
errorjmp(mf,"Couldn't find SegmentInfo");
if (!mf->seen.Tracks)
errorjmp(mf,"Couldn't find Tracks");
if (!mf->seen.Cluster)
errorjmp(mf,"Couldn't find any Clusters");
mf->pCluster = mf->pSegmentTop;
adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
for (i=0;i<mf->nChapters;++i)
fixupChapter(adjust, &mf->Chapters[i]);
fixupCues(mf);
// release extra memory
ARELEASE(mf,mf,Tracks);
@@ -2520,11 +2735,8 @@ segment:
static void DeleteChapter(MatroskaFile *mf,struct Chapter *ch) {
unsigned i,j;
for (i=0;i<ch->nDisplay;++i) {
for (i=0;i<ch->nDisplay;++i)
mf->cache->memfree(mf->cache,ch->Display[i].String);
mf->cache->memfree(mf->cache,ch->Display[i].Language);
mf->cache->memfree(mf->cache,ch->Display[i].Country);
}
mf->cache->memfree(mf->cache,ch->Display);
mf->cache->memfree(mf->cache,ch->Tracks);
@@ -2548,7 +2760,7 @@ MatroskaFile *mkv_OpenEx(InputStream *io,
unsigned flags,
char *err_msg,unsigned msgsize)
{
MatroskaFile *mf = (MatroskaFile *)io->memalloc(io,sizeof(*mf));
MatroskaFile *mf = io->memalloc(io,sizeof(*mf));
if (mf == NULL) {
strlcpy(err_msg,"Out of memory",msgsize);
return NULL;
@@ -2744,7 +2956,7 @@ void mkv_Seek(MatroskaFile *mf,ulonglong timecode,unsigned flags) {
QFree(mf,QGet(&mf->Queues[n]));
}
if (mf->Queues[n].head)
if (mf->Queues[n].head && (mf->Tracks[n]->Type != TT_AUDIO || mf->Queues[n].head->Start<=timecode))
if (!IS_DELTA(mf->Queues[n].head))
m_kftime[n] = mf->Queues[n].head->Start;
}
@@ -3031,6 +3243,7 @@ int mkv_ReadData(MatroskaFile *mf,ulonglong FilePos, void *Buffer,unsigned
return (mf->cache->read(mf->cache, FilePos, Buffer, Count) != Count);
}
/* read and decode more data from current frame, return number of bytes decoded,
* 0 on end of frame, or -1 on error */
int cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize)
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2004-2005 Mike Matsnev. All Rights Reserved.
* Copyright (c) 2004-2006 Mike Matsnev. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,7 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: MatroskaParser.h,v 1.10 2005/01/10 04:52:34 mike Exp $
* $Id: MatroskaParser.h,v 1.19 2006/03/11 10:57:13 mike Exp $
*
*/
@@ -84,15 +84,17 @@ struct InputStream {
/* scan for a four byte signature, bytes must be nonzero */
longlong (*scan)(struct InputStream *cc,ulonglong start,unsigned signature);
/* get cache size, this is used to cap readahead */
unsigned (*getsize)(struct InputStream *cc);
unsigned (*getcachesize)(struct InputStream *cc);
/* fetch last error message */
const char *(*geterror)(struct InputStream *cc);
/* memory allocation */
void *(*memalloc)(struct InputStream *cc,size_t size);
void *(*memrealloc)(struct InputStream *cc,void *mem,size_t newsize);
void (*memfree)(struct InputStream *cc,void *mem);
// zero return causes parser to abort open
/* zero return causes parser to abort open */
int (*progress)(struct InputStream *cc,ulonglong cur,ulonglong max);
/* get file size, optional, can be NULL or return -1 if filesize is unknown */
longlong (*getfilesize)(struct InputStream *cc);
};
typedef struct InputStream InputStream;
@@ -103,6 +105,9 @@ struct MatroskaFile; /* opaque */
typedef struct MatroskaFile MatroskaFile;
#define COMP_ZLIB 0
#define COMP_BZIP 1
#define COMP_LZO1X 2
#define COMP_PREPEND 3
#define TT_VIDEO 1
#define TT_AUDIO 2
@@ -120,6 +125,9 @@ struct TrackInfo {
void *CodecPrivate;
unsigned CodecPrivateSize;
unsigned CompMethod;
void *CompMethodPrivate;
unsigned CompMethodPrivateSize;
unsigned MaxBlockAdditionID;
struct {
unsigned int Enabled:1;
unsigned int Default:1;
@@ -137,6 +145,7 @@ struct TrackInfo {
unsigned int PixelHeight;
unsigned int DisplayWidth;
unsigned int DisplayHeight;
unsigned int CropL, CropT, CropR, CropB;
unsigned int ColourSpace;
MKFLOAT GammaValue;
struct {
@@ -149,11 +158,11 @@ struct TrackInfo {
unsigned char Channels;
unsigned char BitDepth;
} Audio;
};
} AV;
/* various strings */
char *Name;
char *Language;
char Language[4];
char *CodecID;
};
@@ -171,7 +180,8 @@ struct SegmentInfo {
char *WritingApp;
ulonglong TimecodeScale;
ulonglong Duration;
ulonglong DateUTC;
longlong DateUTC;
char DateUTCValid;
};
typedef struct SegmentInfo SegmentInfo;
@@ -189,20 +199,20 @@ typedef struct Attachment Attachment;
struct ChapterDisplay {
char *String;
char *Language;
char *Country;
char Language[4];
char Country[4];
};
struct ChapterCommand {
unsigned Time;
unsigned CommandLength;
char *Command;
void *Command;
};
struct ChapterProcess {
unsigned CodecID;
unsigned CodecPrivateLength;
char *CodecPrivate;
void *CodecPrivate;
unsigned nCommands,nCommandsSize;
struct ChapterCommand *Commands;
};
@@ -221,6 +231,8 @@ struct Chapter {
unsigned nProcess,nProcessSize;
struct ChapterProcess *Process;
char SegmentUID[16];
struct {
unsigned int Hidden:1;
unsigned int Enabled:1;
@@ -245,7 +257,7 @@ struct Target {
struct SimpleTag {
char *Name;
char *Value;
char *Language;
char Language[4];
unsigned Default:1;
};
@@ -324,10 +336,12 @@ X int mkv_TruncFloat(MKFLOAT f);
*/
/* frame flags */
#define FRAME_KF 0x00000001
#define FRAME_UNKNOWN_START 0x00000002
#define FRAME_UNKNOWN_END 0x00000004
#define FRAME_GAP 0x00000008
#define FRAME_UNKNOWN_START 0x00000001
#define FRAME_UNKNOWN_END 0x00000002
#define FRAME_KF 0x00000004
#define FRAME_GAP 0x00800000
#define FRAME_STREAM_MASK 0xff000000
#define FRAME_STREAM_SHIFT 24
/* This sets the masking flags for the parser,
* masked tracks [with 1s in their bit positions]
@@ -350,14 +364,8 @@ X int mkv_ReadFrame(/* in */ MatroskaFile *mf,
/* out */ unsigned int *FrameSize /* in bytes */,
/* out */ unsigned int *FrameFlags);
/* Read raw frame data, invokes underlying io methods,
* but keeps track of the file pointer for internal buffering.
* Returns the -1 if error occurred, or 0 on success
*/
X int mkv_ReadData(/* in */ MatroskaFile *mf,
/* in */ ulonglong FilePos,
/* out */ void *Buffer,
/* in */ unsigned int Count);
int mkv_ReadData(MatroskaFile *mf,ulonglong FilePos, void *Buffer,unsigned int Count);
#ifdef MATROSKA_COMPRESSION_SUPPORT
/* Compressed streams support */
@@ -107,7 +107,7 @@ CreateFileCache(BDataIO *dataio)
io->filecache.read = &stream_read;
io->filecache.scan = &stream_scan;
// io->filecache.close = &stream_close;
io->filecache.getsize = &stream_getsize;
io->filecache.getcachesize = &stream_getsize;
io->filecache.geterror = &stream_geterror;
io->filecache.memalloc = &stream_memalloc;
io->filecache.memrealloc = &stream_memrealloc;