📄 matroskaparser.c
字号:
QPut(&mf->Queues[track],qe);
}
if (add_ref) { mf->cache->releaseref(mf->cache,add_ref); add_ref = NULL; } //Picard
break;
ENDFOR(mf);
}
static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode) {
ulonglong v;
ulonglong duration = 0;
ulonglong dpos;
//unsigned add_id = 0; //Picard
struct QueueEntry *qe,*qf = NULL;
unsigned char have_duration = 0, have_block = 0;
unsigned char gap = 0;
unsigned char lacing = 0;
unsigned char ref = 0;
unsigned char trackid;
unsigned tracknum = 0;
int c;
unsigned nframes = 0,i;
unsigned *sizes;
signed short block_timecode;
FOREACH(mf,toplen)
case 0xfb: // ReferenceBlock
readSInt(mf,(unsigned)len);
ref = 1;
break;
case 0xa1: // Block
have_block = 1;
dpos = filepos(mf);
v = readVLUInt(mf);
if (v>255)
errorjmp(mf,"Invalid track number in Block: %d",(int)v);
trackid = (unsigned char)v;
for (tracknum=0;tracknum<mf->nTracks;++tracknum)
if (mf->Tracks[tracknum]->Number == trackid)
goto found;
errorjmp(mf,"Invalid track number in Block: %u",trackid);
found:
if (mf->trackMask & (1<<tracknum)) { // ignore this block
skipbytes(mf,start + tmplen - filepos(mf)); // shortcut
return;
}
block_timecode = (signed short)readSInt(mf,2);
// recalculate this block's timecode to final timecode in ns
timecode = mul3(mf->Tracks[tracknum]->TimecodeScale,
(timecode - mf->firstTimecode + block_timecode) * mf->Seg.TimecodeScale);
c = readch(mf);
if (c==EOF)
errorjmp(mf,"Unexpected EOF while reading Block flags");
gap = c & 0x1;
lacing = (c >> 1) & 3;
if (lacing) {
c = readch(mf);
if (c == EOF)
errorjmp(mf,"Unexpected EOF while reading lacing data");
nframes = c+1;
} else
nframes = 1;
sizes = alloca(nframes*sizeof(*sizes));
switch (lacing) {
case 0: // No lacing
sizes[0] = (unsigned)(len - filepos(mf) + dpos);
break;
case 1: // Xiph lacing
sizes[nframes-1] = 0;
for (i=0;i<nframes-1;++i) {
sizes[i] = 0;
do {
c = readch(mf);
if (c==EOF)
errorjmp(mf,"Unexpected EOF while reading lacing data");
sizes[i] += c;
} while (c==255);
sizes[nframes-1] += sizes[i];
}
sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[nframes-1];
break;
case 3: // EBML lacing
sizes[nframes-1] = 0;
sizes[0] = (unsigned)readVLUInt(mf);
for (i=1;i<nframes-1;++i) {
sizes[i] = sizes[i-1] + (int)readVLSInt(mf);
sizes[nframes-1] += sizes[i];
}
if (nframes>1)
sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[0] - sizes[nframes-1];
break;
case 2: // Fixed lacing
sizes[0] = (unsigned)(len - filepos(mf) + dpos)/nframes;
for (i=1;i<nframes;++i)
sizes[i] = sizes[0];
break;
}
v = filepos(mf);
qf = NULL;
for (i=0;i<nframes;++i) {
qe = QAlloc(mf);
if (!qf)
qf = qe;
qe->Start = timecode;
qe->End = timecode;
qe->Position = v;
qe->Length = sizes[i];
qe->Ref = mf->cache->makeref(mf->cache,sizes[i]); //Picard
qe->flags = FRAME_UNKNOWN_END | FRAME_KF;
if (i == nframes-1 && gap)
qe->flags |= FRAME_GAP;
if (i > 0)
qe->flags |= FRAME_UNKNOWN_START;
QPut(&mf->Queues[tracknum],qe);
v += sizes[i];
}
// we want to still load these bytes into cache
//Picard, not needed for TCPMP
/*
for (v = filepos(mf) & ~0x3fff; v < len + dpos; v += 0x4000)
mf->cache->read(mf->cache,v,NULL,0); // touch page
*/
skipbytes(mf,len - filepos(mf) + dpos);
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);
if (!have_block)
errorjmp(mf,"Found a BlockGroup without Block");
if (nframes > 1) {
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;
qe->End = v;
qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
}
qe->Start = v;
qe->End = v + duration;
qe->flags &= ~(FRAME_UNKNOWN_START|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;
qe->End = v;
qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
}
}
} else if (nframes == 1) {
if (have_duration) {
qf->End = qf->Start + mul3(mf->Tracks[tracknum]->TimecodeScale,
duration * mf->Seg.TimecodeScale);
qf->flags &= ~FRAME_UNKNOWN_END;
} else if (mf->Tracks[tracknum]->DefaultDuration) {
qf->End = qf->Start + mf->Tracks[tracknum]->DefaultDuration;
qf->flags &= ~FRAME_UNKNOWN_END;
}
}
if (ref)
while (qf) {
qf->flags &= ~FRAME_KF;
qf = qf->next;
}
}
static void ClearQueue(MatroskaFile *mf,struct Queue *q) {
struct QueueEntry *qe,*qn;
for (qe=q->head;qe;qe=qn) {
qn = qe->next;
QFree(mf,qe); //Picard
/*
qe->next = mf->QFreeList;
mf->QFreeList = qe;
*/
}
q->head = NULL;
q->tail = NULL;
}
static void EmptyQueues(MatroskaFile *mf) {
unsigned i;
for (i=0;i<mf->nTracks;++i)
ClearQueue(mf,&mf->Queues[i]);
}
static int readMoreBlocks(MatroskaFile *mf) {
ulonglong toplen, cstop;
longlong cp;
int cid, ret = 0;
jmp_buf jb;
volatile unsigned retries = 0;
if (mf->readPosition >= mf->pSegmentTop)
return EOF;
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)
goto ex;
cp = mf->cache->scan(mf->cache,filepos(mf),0x1f43b675); // cluster
if (cp < 0 || (ulonglong)cp >= mf->pSegmentTop)
goto ex;
seek(mf,cp);
cid = readID(mf);
if (cid == EOF)
goto ex;
if (cid == 0x1f43b675) {
toplen = readSize(mf);
if (toplen < MAXCLUSTER) {
// reset error flags
mf->flags &= ~MPF_ERROR;
ret = RBRESYNC;
break;
}
}
}
mf->readPosition = cp;
}
cstop = mf->cache->getsize(mf->cache)>>1;
if (cstop > MAX_READAHEAD)
cstop = MAX_READAHEAD;
cstop += mf->readPosition;
seek(mf,mf->readPosition);
while (filepos(mf) < mf->pSegmentTop) {
cid = readID(mf);
if (cid == EOF) {
ret = EOF;
break;
}
toplen = readSize(mf);
if (cid == 0x1f43b675) { // Cluster
unsigned char have_timecode = 0;
FOREACH(mf,toplen)
case 0xe7: // Timecode
mf->tcCluster = readUInt(mf,(unsigned)len);
have_timecode = 1;
break;
case 0xa7: // Position
readUInt(mf,(unsigned)len);
break;
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);
goto out;
ENDFOR(mf);
out:;
} else {
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);
else
skipbytes(mf,toplen);
}
if ((mf->readPosition = filepos(mf)) > cstop)
break;
}
mf->readPosition = filepos(mf);
ex:
memcpy(&mf->jb,&jb,sizeof(jb));
return ret;
}
// this is almost the same as readMoreBlocks, except it ensures
// there are no partial frames queued, however empty queues are ok
static int fillQueues(MatroskaFile *mf,unsigned int mask) {
unsigned i,j;
int ret = 0;
for (;;) {
j = 0;
for (i=0;i<mf->nTracks;++i)
if (mf->Queues[i].head && !(mask & (1<<i)))
++j;
if (j>0) // have at least some frames
return ret;
if ((ret = readMoreBlocks(mf)) < 0) {
j = 0;
for (i=0;i<mf->nTracks;++i)
if (mf->Queues[i].head && !(mask & (1<<i)))
++j;
if (j) // we adjusted some blocks
return 0;
return EOF;
}
}
}
static void reindex(MatroskaFile *mf) {
jmp_buf jb;
ulonglong pos = mf->pCluster;
ulonglong step = 10*1024*1024;
ulonglong size, tc=0, isize; //Picard
longlong next_cluster;
int id, have_tc, bad;
struct Cue *cue;
if (pos >= mf->pSegmentTop)
return;
if (pos + step * 10 > mf->pSegmentTop)
step = (mf->pSegmentTop - pos) / 10;
if (step == 0)
step = 1;
memcpy(&jb,&mf->jb,sizeof(jb));
// remove all cues
mf->nCues = 0;
bad = 0;
while (pos < mf->pSegmentTop) {
if (!mf->cache->progress(mf->cache,pos,mf->pSegmentTop))
break;
if (++bad > 50) {
pos += step;
bad = 0;
continue;
}
// find next cluster header
next_cluster = mf->cache->scan(mf->cache,pos,0x1f43b675); // cluster
if (next_cluster < 0 || (ulonglong)next_cluster >= mf->pSegmentTop)
break;
pos = next_cluster + 4; // prevent endless loops
if (setjmp(mf->jb)) // something evil happened while reindexing
continue;
seek(mf,next_cluster);
id = readID(mf);
if (id == EOF)
break;
if (id != 0x1f43b675) // shouldn't happen
continue;
size = readVLUInt(mf);
if (size >= MAXCLUSTER || size < 1024)
continue;
have_tc = 0;
size += filepos(mf);
while (filepos(mf) < (ulonglong)next_cluster + 1024) {
id = readID(mf);
if (id == EOF)
break;
isize = readVLUInt(mf);
if (id == 0xe7) { // cluster timecode
tc = readUInt(mf,(unsigned)isize);
have_tc = 1;
break;
}
skipbytes(mf,isize);
}
if (!have_tc)
continue;
seek(mf,size);
id = readID(mf);
if (id == EOF)
break;
if (id != 0x1f43b675) // cluster
continue;
// good cluster, remember it
cue = AGET(mf,Cues);
cue->Time = tc;
cue->Position = next_cluster - mf->pSegment;
cue->Block = 0;
cue->Track = 0;
// advance to the next point
pos = next_cluster + step;
if (pos < size)
pos = size;
bad = 0;
}
fixupCues(mf);
if (mf->nCues == 0) {
cue = AGET(mf,Cues);
cue->Time = mf->firstTimecode;
cue->Position = mf->pCluster - mf->pSegment;
cue->Block = 0;
cue->Track = 0;
}
mf->cache->progress(mf->cache,0,0);
memcpy(&mf->jb,&jb,sizeof(jb));
}
static void fixupChapter(ulonglong adj, struct Chapter *ch) {
unsigned i;
if (ch->Start != 0)
ch->Start -= adj;
if (ch->End != 0)
ch->End -= adj;
for (i=0;i<ch->nChildren;++i)
fixupChapter(adj,&ch->Children[i]);
}
static longlong findLastTimecode(MatroskaFile *mf) {
ulonglong nd = 0;
unsigned n,vtrack;
if (mf->nCues == 0 || mf->nTracks == 0)
return -1;
for (n=vtrack=0;n<mf->nTracks;++n)
if (mf->Tracks[n]->Type == TT_VIDEO) {
vtrack = n;
goto ok;
}
return -1;
ok:
EmptyQueues(mf);
mf->trackMask = ~(1 << vtrack);
mf->readPosition = mf->Cues[mf->nCues - 1].Position + mf->pSegme
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -