1
0
Fork 0

matroskadec_haali: support for linked segments in ordered chapters

This commit is contained in:
Hendrik Leppkes 2013-03-24 19:05:03 +01:00
parent 15a545e85a
commit 4fd0350c9d
Signed by: hendrik
GPG Key ID: 846079A4B0A7C1B5
1 changed files with 189 additions and 25 deletions

View File

@ -39,6 +39,9 @@
#include <zlib.h>
#endif
#include <io.h>
#include <windows.h>
#define IO_BUFFER_SIZE 32768
static const char *matroska_doctypes[] = { "matroska", "webm" };
@ -55,9 +58,18 @@ typedef struct MatroskaTrack {
int ms_compat;
} MatroskaTrack;
typedef struct MatroskaSegment {
int index;
AVIOStream *iostream;
MatroskaFile *matroska;
SegmentInfo *info;
int free_avio;
} MatroskaSegment;
typedef struct VirtualTimelineEntry {
int index;
Chapter *chapter;
MatroskaSegment *segment;
ulonglong start;
ulonglong stop;
longlong offset;
@ -65,7 +77,6 @@ typedef struct VirtualTimelineEntry {
} VirtualTimelineEntry;
typedef struct MatroskaDemuxContext {
AVIOStream *iostream;
MatroskaFile *matroska;
int num_tracks;
@ -83,6 +94,10 @@ typedef struct MatroskaDemuxContext {
VirtualTimelineEntry *timeline;
int num_timeline;
int timeline_position;
MatroskaSegment **segments;
int num_segments;
int segments_scanned;
} MatroskaDemuxContext;
static int aviostream_read(struct AVIOStream *cc,ulonglong pos,void *buffer,int count)
@ -254,6 +269,150 @@ static int matroska_aac_sri(int samplerate)
return sri;
}
static int mkv_uid_compare(char first[16], char second[16])
{
return !memcmp(first, second, 16);
}
static int mkv_uid_zero(char uid[16])
{
char zero[16] = {0};
return mkv_uid_compare(uid, zero);
}
static MatroskaSegment* mkv_open_segment(AVFormatContext *s, AVIOContext *pb, ulonglong base)
{
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
char ErrorMessage[256];
MatroskaSegment *segment = av_mallocz(sizeof(*segment));
segment->index = ctx->num_segments;
segment->iostream = aviostream_create(pb);
segment->matroska = mkv_OpenEx(&segment->iostream->base, base, 0, ErrorMessage, sizeof(ErrorMessage));
if (!segment->matroska) {
av_log(s, AV_LOG_ERROR, "mkv_OpenEx returned error: %s\n", ErrorMessage);
av_freep(&segment);
return NULL;
}
segment->info = mkv_GetFileInfo(segment->matroska);
av_dynarray_add(&ctx->segments, &ctx->num_segments, segment);
return segment;
}
static int mkv_find_segments_avio(AVFormatContext *s, AVIOContext *pb, ulonglong base)
{
MatroskaSegment *segment;
int found = 0;
int64_t size = avio_size(pb);
if (base >= size)
return 0;
while (base < size) {
av_log(s, AV_LOG_INFO, "Scanning for Segment at %I64d\n", base);
segment = mkv_open_segment(s, pb, base);
if (!segment)
break;
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]);
if (!found && base == 0) {
segment->free_avio = 1;
found = 1;
}
base = mkv_GetSegmentTop(segment->matroska);
}
return found;
}
static void mkv_find_segments_file(AVFormatContext *s, const char *path, const char *file, ulonglong base)
{
AVIOContext *pb = NULL;
int num_segments;
char *filename = av_asprintf("%s/%s", path, file);
if (avio_open(&pb, filename, AVIO_FLAG_READ) < 0) {
av_log(s, AV_LOG_ERROR, "Error opening file %s\n", filename);
goto done;
}
av_log(s, AV_LOG_INFO, "Opening %s, size %I64d...\n", filename, avio_size(pb));
num_segments = mkv_find_segments_avio(s, pb, base);
if (num_segments == 0) {
av_log(s, AV_LOG_WARNING, "File %s could not be opened as MKV\n", filename);
avio_closep(&pb);
}
done:
av_freep(&filename);
}
static void mkv_find_segments(AVFormatContext *s)
{
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
char *filename, *filespec;
const char *path, *file;
int ret = 0;
intptr_t handle;
struct _wfinddata_t finddata;
filename = av_strdup(s->filename);
file = av_basename(filename);
path = av_dirname(filename);
filespec = av_asprintf("%s/*.mk?", path);
wchar_t wfilespec[4096];
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filespec, -1, wfilespec, 4096)) {
handle = _wfindfirst(wfilespec, &finddata);
if (handle != -1) {
while (ret == 0) {
char mkvFileName[4096];
WideCharToMultiByte(CP_UTF8, 0, finddata.name, -1, mkvFileName, 4096, NULL, NULL);
// Skip the main file, it was processed elsewhere
if (av_strcasecmp(mkvFileName, file) != 0) {
mkv_find_segments_file(s, path, mkvFileName, 0);
}
ret = _wfindnext(handle, &finddata);
}
_findclose(handle);
}
}
av_freep(&filename);
av_freep(&filespec);
}
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))
return ctx->segments[0];
if (!ctx->segments_scanned) {
/* scan for segments within this file */
ulonglong base = mkv_GetSegmentTop(ctx->segments[0]->matroska);
mkv_find_segments_avio(s, ctx->segments[0]->iostream->pb, base);
/* and for segments in other files */
mkv_find_segments(s);
ctx->segments_scanned = 1;
}
for (i = 1; i < ctx->num_segments; i++) {
if (mkv_uid_compare(ctx->segments[i]->info->UID, uid)) {
return ctx->segments[i];
}
}
return NULL;
}
static void mkv_build_timeline(AVFormatContext *s, Chapter *edition)
{
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
@ -266,6 +425,7 @@ static void mkv_build_timeline(AVFormatContext *s, Chapter *edition)
ctx->num_timeline = 0;
for (i = 0; i < edition->nChildren; i++) {
Chapter *chapter = &edition->Children[i];
MatroskaSegment *segment = mkv_get_segment(s, chapter->SegmentUID);
/* check that the chapter timeline is valid */
if (chapter->End < chapter->Start) {
edition->Ordered = 0;
@ -273,14 +433,15 @@ static void mkv_build_timeline(AVFormatContext *s, Chapter *edition)
av_freep(&ctx->timeline);
return;
}
if (chapter->Enabled && chapter->End > chapter->Start) {
if (segment && chapter->Enabled && chapter->End > chapter->Start) {
VirtualTimelineEntry *vt = &ctx->timeline[ctx->num_timeline];
vt->index = ctx->num_timeline;
vt->chapter = chapter;
vt->segment = segment;
vt->start = duration;
vt->stop = vt->start + (chapter->End - chapter->Start);
vt->offset = (longlong)chapter->Start - (longlong)vt->start;
vt->need_seek = (vt->index == 0 && chapter->Start != 0) || (vt->index > 0 && chapter->Start != ctx->timeline[vt->index-1].chapter->End);
vt->need_seek = (vt->index == 0 && (chapter->Start != 0 || segment->index != 0)) || (vt->index > 0 && (chapter->Start != ctx->timeline[vt->index-1].chapter->End || segment != ctx->timeline[vt->index-1].segment));
duration = vt->stop;
ctx->num_timeline++;
@ -406,7 +567,7 @@ static void mkv_process_virtual_cues(AVFormatContext *s, VirtualTimelineEntry *v
int i;
Cue *cues = NULL;
mkv_GetCues(ctx->matroska, &cues, &count);
mkv_GetCues(vt->segment->matroska, &cues, &count);
for (u = 0; u < count; u++) {
Cue *cue = &cues[u];
if (cue->Time >= vt->chapter->Start && cue->Time < vt->chapter->End) {
@ -513,36 +674,32 @@ static void mkv_Seek_CueAware(MatroskaFile *mf, ulonglong time, int flags)
static int mkv_read_header(AVFormatContext *s)
{
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
char ErrorMessage[256];
int i, j, num_tracks;
SegmentInfo *segment;
char ErrorMessage[256];
MatroskaSegment *segment;
Chapter *chapters = NULL;
Attachment *attachments = NULL;
unsigned int count, u;
ctx->iostream = aviostream_create(s->pb);
av_log(s, AV_LOG_DEBUG, "Opening MKV file");
ctx->matroska = mkv_OpenEx(&ctx->iostream->base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (!ctx->matroska) {
av_log(s, AV_LOG_ERROR, "mkv_OpenEx returned error: %s", ErrorMessage);
segment = mkv_open_segment(s, s->pb, 0);
if (!segment)
return -1;
}
/* Gather information */
segment = mkv_GetFileInfo(ctx->matroska);
ctx->matroska = segment->matroska;
if (segment->Duration)
s->duration = segment->Duration / (1000000000 / AV_TIME_BASE);
av_dict_set(&s->metadata, "title", segment->Title, 0);
if (segment->info->Duration)
s->duration = segment->info->Duration / (1000000000 / AV_TIME_BASE);
av_dict_set(&s->metadata, "title", segment->info->Title, 0);
/* Process Editions/Chapters */
mkv_GetChapters(ctx->matroska, &chapters, &count);
if (count > 0) {
mkv_process_editions(s, chapters, count);
if (ctx->virtual_timeline && ctx->timeline[0].need_seek) {
mkv_Seek(ctx->matroska, ctx->timeline[0].chapter->Start, MKVF_SEEK_TO_PREV_KEYFRAME);
}
}
if (ctx->virtual_timeline && ctx->timeline[0].need_seek) {
ctx->matroska = ctx->timeline[ctx->timeline_position].segment->matroska;
mkv_Seek_CueAware(ctx->matroska, ctx->timeline[0].chapter->Start, MKVF_SEEK_TO_PREV_KEYFRAME);
}
/* Tracks */
@ -766,7 +923,7 @@ static int mkv_read_header(AVFormatContext *s)
if(st->codec->extradata == NULL)
break;
st->codec->extradata_size = (int)attach->Length;
aviostream_read(ctx->iostream, attach->Position, st->codec->extradata, st->codec->extradata_size);
aviostream_read(segment->iostream, attach->Position, st->codec->extradata, st->codec->extradata_size);
for (i=0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) {
if (!strncmp(ff_mkv_mime_tags[i].str, attach->MimeType, strlen(ff_mkv_mime_tags[i].str))) {
@ -808,7 +965,8 @@ static int mkv_packet_timeline_update(AVFormatContext *s, ulonglong *start_time,
}
if (ctx->timeline[ctx->timeline_position].need_seek) {
av_log(s, AV_LOG_INFO, "Seeking to timeline %d (position %I64d)\n", ctx->timeline_position, ctx->timeline[ctx->timeline_position].chapter->Start);
mkv_Seek(ctx->matroska, ctx->timeline[ctx->timeline_position].chapter->Start, MKVF_SEEK_TO_PREV_KEYFRAME);
ctx->matroska = ctx->timeline[ctx->timeline_position].segment->matroska;
mkv_Seek_CueAware(ctx->matroska, ctx->timeline[ctx->timeline_position].chapter->Start, MKVF_SEEK_TO_PREV_KEYFRAME);
// Need to discard the current frame, and re-read after the seek
return AVERROR(EAGAIN);
}
@ -933,8 +1091,13 @@ static int mkv_read_close(AVFormatContext *s)
MatroskaDemuxContext *ctx = (MatroskaDemuxContext *)s->priv_data;
int i;
mkv_Close(ctx->matroska);
av_freep(&ctx->iostream);
for (i = 0; i < ctx->num_segments; i++) {
mkv_Close(ctx->segments[i]->matroska);
if (ctx->segments[i]->free_avio)
avio_closep(&ctx->segments[i]->iostream->pb);
av_freep(&ctx->segments[i]->iostream);
}
av_freep(&ctx->segments);
for (i = 0; i < ctx->num_tracks; i++) {
av_freep(&ctx->tracks[i].cs);
@ -955,6 +1118,7 @@ static int mkv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp
/* Update timeline and segment for ordered chapters */
if (ctx->virtual_timeline) {
VirtualTimelineEntry *vt = mkv_get_timeline_entry(s, timestamp);
ctx->matroska = vt->segment->matroska;
ctx->timeline_position = vt->index;
timestamp += vt->offset;
}