1
0
Fork 0

matroskadec_haali: defer parsing of the full MKV header until required

This allows quickly scanning the file header for the segment UID instead
of parsing the full headers for this information.
This commit is contained in:
Hendrik Leppkes 2013-04-28 12:30:41 +02:00
parent a73ab4b2e4
commit deb07a272a
Signed by: hendrik
GPG Key ID: 846079A4B0A7C1B5
3 changed files with 165 additions and 6 deletions

View File

@ -2813,6 +2813,63 @@ segment:
mf->tcCluster = mf->firstTimecode;
}
static void parseFileSparse(MatroskaFile *mf) {
ulonglong len = filepos(mf), adjust;
unsigned i;
int id = readID(mf);
int found_uid = 0;
if (id==EOF)
errorjmp(mf,"Unexpected EOF at start of file");
// files with multiple concatenated segments can have only
// one EBML prolog
if (len > 0 && id == 0x18538067)
goto segment;
if (id!=0x1a45dfa3)
errorjmp(mf,"First element in file is not EBML");
parseEBML(mf,readSize(mf));
// next we need to find the first segment
for (;;) {
id = readID(mf);
if (id==EOF)
errorjmp(mf,"No segments found in the file");
segment:
len = readSizeUnspec(mf);
if (id == 0x18538067) // Segment
break;
if (len == MAXU64)
errorjmp(mf,"No segments found in the file");
skipbytes(mf,len);
}
// found it
mf->pSegment = filepos(mf);
// we want to read data until we find a seekhead or a trackinfo
FOREACH2(mf,len,0x1f43b675)
case 0x1549a966: // SegmentInfo
mf->pSegmentInfo = cur;
FOREACH(mf,len)
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));
return;
ENDFOR(mf);
break;
ENDFOR1(mf);
// if we found our segment info
if (mf->pSegmentInfo)
break;
ENDFOR2();
errorjmp(mf,"Couldn't find SegmentInfo");
}
static void DeleteChapter(MatroskaFile *mf,struct Chapter *ch) {
unsigned i,j;
@ -2865,6 +2922,33 @@ MatroskaFile *mkv_OpenEx(InputStream *io,
return mf;
}
MatroskaFile *mkv_OpenSparse(InputStream *io,
char *err_msg,unsigned msgsize)
{
MatroskaFile *mf = io->memalloc(io,sizeof(*mf));
if (mf == NULL) {
mystrlcpy(err_msg,"Out of memory",msgsize);
return NULL;
}
memset(mf,0,sizeof(*mf));
mf->cache = io;
mf->flags = MKVF_AVOID_SEEKS;
io->progress(io,0,0);
if (setjmp(mf->jb)==0) {
seek(mf,0);
parseFileSparse(mf);
} else { // parser error
mystrlcpy(err_msg,mf->errmsg,msgsize);
mkv_Close(mf);
return NULL;
}
return mf;
}
MatroskaFile *mkv_Open(InputStream *io,
char *err_msg,unsigned msgsize)
{

View File

@ -294,6 +294,11 @@ X MatroskaFile *mkv_OpenEx(/* in */ InputStream *io,
/* out */ char *err_msg,
/* in */ unsigned msgsize);
/* Open the file and only parse enough information to find the segment uid */
X MatroskaFile *mkv_OpenSparse(/* in */ InputStream *io,
/* out */ char *err_msg,
/* in */ unsigned msgsize);
/* Close and deallocate mf
* NULL pointer is ok and is simply ignored
*/

View File

@ -65,7 +65,9 @@ typedef struct MatroskaSegment {
AVIOStream *iostream;
MatroskaFile *matroska;
SegmentInfo *info;
int free_avio;
char UID[16];
int free_avio;
int failed;
} MatroskaSegment;
typedef struct VirtualTimelineEntry {
@ -307,6 +309,55 @@ static MatroskaSegment* mkv_open_segment(AVFormatContext *s, AVIOContext *pb, ul
}
segment->info = mkv_GetFileInfo(segment->matroska);
memcpy(segment->UID, segment->info->UID, 16);
av_dynarray_add(&ctx->segments, &ctx->num_segments, segment);
return segment;
}
static void mkv_reopen_segment(AVFormatContext *s, MatroskaSegment *segment)
{
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
char ErrorMessage[256];
/* reset packet size */
segment->iostream->pb->max_packet_size = 0;
ffio_set_buf_size(segment->iostream->pb, IO_BUFFER_SIZE * 4);
segment->matroska = mkv_OpenEx(&segment->iostream->base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (!segment->matroska) {
av_log(s, AV_LOG_ERROR, "mkv_OpenEx returned error: %s\n", ErrorMessage);
segment->failed = 1;
}
segment->info = mkv_GetFileInfo(segment->matroska);
}
static MatroskaSegment* mkv_discover_segment(AVFormatContext *s, AVIOContext *pb)
{
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
char ErrorMessage[256];
SegmentInfo *info;
MatroskaSegment *segment = av_mallocz(sizeof(*segment));
segment->index = ctx->num_segments;
segment->iostream = aviostream_create(pb);
pb->max_packet_size = IO_BUFFER_SIZE;
segment->matroska = mkv_OpenSparse(&segment->iostream->base, ErrorMessage, sizeof(ErrorMessage));
if (!segment->matroska) {
av_log(s, AV_LOG_ERROR, "mkv_OpenEx returned error: %s\n", ErrorMessage);
av_freep(&segment->iostream);
av_freep(&segment);
return NULL;
}
info = mkv_GetFileInfo(segment->matroska);
memcpy(segment->UID, info->UID, 16);
mkv_Close(segment->matroska);
segment->matroska = NULL;
av_dynarray_add(&ctx->segments, &ctx->num_segments, segment);
return segment;
@ -318,13 +369,16 @@ static int mkv_find_segment_avio(AVFormatContext *s, AVIOContext *pb, ulonglong
av_log(s, AV_LOG_INFO, "Scanning for Segment at %I64d\n", base);
segment = mkv_open_segment(s, pb, base);
if (base == 0)
segment = mkv_discover_segment(s, pb);
else
segment = mkv_open_segment(s, pb, base);
if (!segment)
return 0;
av_log(s, AV_LOG_INFO, "Found Segment with UID: %08x%08x%08x%08x\n",
*(unsigned int*)&segment->info->UID[0], *(unsigned int*)&segment->info->UID[4], *(unsigned int*)&segment->info->UID[8], *(unsigned int*)&segment->info->UID[12]);
*(unsigned int*)&segment->UID[0], *(unsigned int*)&segment->UID[4], *(unsigned int*)&segment->UID[8], *(unsigned int*)&segment->UID[12]);
if (base == 0) {
segment->free_avio = 1;
@ -395,7 +449,7 @@ static MatroskaSegment* mkv_get_segment(AVFormatContext *s, char uid[16])
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
int i;
if (mkv_uid_zero(uid) || mkv_uid_compare(ctx->segments[0]->info->UID, uid))
if (mkv_uid_zero(uid) || mkv_uid_compare(ctx->segments[0]->UID, uid))
return ctx->segments[0];
if (!ctx->segments_scanned) {
@ -418,7 +472,12 @@ static MatroskaSegment* mkv_get_segment(AVFormatContext *s, char uid[16])
}
for (i = 1; i < ctx->num_segments; i++) {
if (mkv_uid_compare(ctx->segments[i]->info->UID, uid)) {
if (!ctx->segments[i]->failed && mkv_uid_compare(ctx->segments[i]->UID, uid)) {
if (!ctx->segments[i]->matroska) {
mkv_reopen_segment(s, ctx->segments[i]);
if (ctx->segments[i]->failed)
break;
}
return ctx->segments[i];
}
}
@ -1130,7 +1189,8 @@ static int mkv_read_header(AVFormatContext *s)
}
for (i = 0; i < ctx->num_segments; i++) {
mkv_process_attachments(s, ctx->segments[i]);
if (ctx->segments[i]->matroska)
mkv_process_attachments(s, ctx->segments[i]);
}
if (tagCount > 0 && tags) {
@ -1140,6 +1200,16 @@ static int mkv_read_header(AVFormatContext *s)
/* Can only build the index after tracks are loaded */
mkv_build_index(s);
/* close segments which were not needed for the virtual timeline */
for (i = 0; i < ctx->num_segments; i++) {
if (!ctx->segments[i]->matroska) {
if (ctx->segments[i]->free_avio)
avio_closep(&ctx->segments[i]->iostream->pb);
ctx->segments[i]->free_avio = 0;
av_freep(&ctx->segments[i]->iostream);
}
}
return 0;
}