-
-
Save rcombs/16e91a2c04c541b9014f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit 1df23b88d0a7d5e40ae9bac281127e299598683f | |
Author: Rodger Combs <rodger.combs@gmail.com> | |
Date: Sun Oct 18 17:49:52 2015 -0500 | |
WIP: Matroska ordered edition support | |
diff --git a/libavformat/matroska.h b/libavformat/matroska.h | |
index a654e0c..dd91fb8 100644 | |
--- a/libavformat/matroska.h | |
+++ b/libavformat/matroska.h | |
@@ -226,6 +226,8 @@ | |
#define MATROSKA_ID_CHAPTERUID 0x73C4 | |
#define MATROSKA_ID_CHAPTERFLAGHIDDEN 0x98 | |
#define MATROSKA_ID_CHAPTERFLAGENABLED 0x4598 | |
+#define MATROSKA_ID_CHAPTERSEGMENTUID 0x6E67 | |
+#define MATROSKA_ID_CHAPTERSEGMENTEDITIONUID 0x6EBC | |
#define MATROSKA_ID_CHAPTERPHYSEQUIV 0x63C3 | |
typedef enum { | |
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c | |
index 02cc6a5..a6fcc18 100644 | |
--- a/libavformat/matroskadec.c | |
+++ b/libavformat/matroskadec.c | |
@@ -36,6 +36,7 @@ | |
#include "libavutil/avstring.h" | |
#include "libavutil/base64.h" | |
#include "libavutil/dict.h" | |
+#include "libavutil/dynarray.h" | |
#include "libavutil/intfloat.h" | |
#include "libavutil/intreadwrite.h" | |
#include "libavutil/lzo.h" | |
@@ -111,6 +112,8 @@ typedef struct Ebml { | |
uint64_t doctype_version; | |
} Ebml; | |
+AVInputFormat ff_matroska_demuxer; | |
+ | |
typedef struct MatroskaTrackCompression { | |
uint64_t algo; | |
EbmlBin settings; | |
@@ -203,11 +206,21 @@ typedef struct MatroskaChapter { | |
uint64_t start; | |
uint64_t end; | |
uint64_t uid; | |
+ EbmlBin segment_uid; | |
char *title; | |
AVChapter *chapter; | |
+ int valid; | |
+ AVFormatContext *avf; | |
} MatroskaChapter; | |
+typedef struct MatroskaEdition { | |
+ uint64_t uid; | |
+ uint64_t flag_default; | |
+ uint64_t flag_ordered; | |
+ EbmlList chapters; | |
+} MatroskaEdition; | |
+ | |
typedef struct MatroskaIndexPos { | |
uint64_t track; | |
uint64_t pos; | |
@@ -260,6 +273,13 @@ typedef struct MatroskaLevel1Element { | |
int parsed; | |
} MatroskaLevel1Element; | |
+typedef struct MatroskaSubSegment { | |
+ AVFormatContext *avf; | |
+ EbmlBin uid; | |
+ int used; | |
+ char *filename; | |
+} MatroskaSubSegment; | |
+ | |
typedef struct MatroskaDemuxContext { | |
const AVClass *class; | |
AVFormatContext *ctx; | |
@@ -270,14 +290,15 @@ typedef struct MatroskaDemuxContext { | |
int level_up; | |
uint32_t current_id; | |
+ EbmlBin segment_uid; | |
uint64_t time_scale; | |
double duration; | |
char *title; | |
char *muxingapp; | |
- EbmlBin date_utc; | |
+ EbmlBin date_utc; | |
EbmlList tracks; | |
EbmlList attachments; | |
- EbmlList chapters; | |
+ EbmlList editions; | |
EbmlList index; | |
EbmlList tags; | |
EbmlList seekhead; | |
@@ -312,6 +333,20 @@ typedef struct MatroskaDemuxContext { | |
/* WebM DASH Manifest live flag/ */ | |
int is_live; | |
+ | |
+ int external_linking; | |
+ | |
+ char *segment_list_file; | |
+ MatroskaSubSegment *segments; | |
+ int nb_segments; | |
+ | |
+ int edition_idx; | |
+ MatroskaEdition *cur_edition; | |
+ int chapter_idx; | |
+ MatroskaChapter *cur_chapter; | |
+ int ordered; | |
+ int64_t chapter_ts; | |
+ int64_t ts_offset; | |
} MatroskaDemuxContext; | |
typedef struct MatroskaBlock { | |
@@ -341,13 +376,13 @@ static const EbmlSyntax ebml_syntax[] = { | |
}; | |
static const EbmlSyntax matroska_info[] = { | |
+ { MATROSKA_ID_SEGMENTUID, EBML_BIN, 0, offsetof(MatroskaDemuxContext, segment_uid) }, | |
{ MATROSKA_ID_TIMECODESCALE, EBML_UINT, 0, offsetof(MatroskaDemuxContext, time_scale), { .u = 1000000 } }, | |
{ MATROSKA_ID_DURATION, EBML_FLOAT, 0, offsetof(MatroskaDemuxContext, duration) }, | |
{ MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext, title) }, | |
{ MATROSKA_ID_WRITINGAPP, EBML_NONE }, | |
{ MATROSKA_ID_MUXINGAPP, EBML_UTF8, 0, offsetof(MatroskaDemuxContext, muxingapp) }, | |
{ MATROSKA_ID_DATEUTC, EBML_BIN, 0, offsetof(MatroskaDemuxContext, date_utc) }, | |
- { MATROSKA_ID_SEGMENTUID, EBML_NONE }, | |
{ 0 } | |
}; | |
@@ -487,22 +522,24 @@ static const EbmlSyntax matroska_chapter_entry[] = { | |
{ MATROSKA_ID_CHAPTERDISPLAY, EBML_NEST, 0, 0, { .n = matroska_chapter_display } }, | |
{ MATROSKA_ID_CHAPTERFLAGHIDDEN, EBML_NONE }, | |
{ MATROSKA_ID_CHAPTERFLAGENABLED, EBML_NONE }, | |
+ { MATROSKA_ID_CHAPTERSEGMENTUID, EBML_BIN, 0, offsetof(MatroskaChapter, segment_uid) }, | |
+ { MATROSKA_ID_CHAPTERSEGMENTEDITIONUID, EBML_UINT }, | |
{ MATROSKA_ID_CHAPTERPHYSEQUIV, EBML_NONE }, | |
{ MATROSKA_ID_CHAPTERATOM, EBML_NONE }, | |
{ 0 } | |
}; | |
-static const EbmlSyntax matroska_chapter[] = { | |
- { MATROSKA_ID_CHAPTERATOM, EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaDemuxContext, chapters), { .n = matroska_chapter_entry } }, | |
- { MATROSKA_ID_EDITIONUID, EBML_NONE }, | |
+static const EbmlSyntax matroska_edition[] = { | |
+ { MATROSKA_ID_EDITIONUID, EBML_UINT, 0, offsetof(MatroskaEdition, uid) }, | |
{ MATROSKA_ID_EDITIONFLAGHIDDEN, EBML_NONE }, | |
- { MATROSKA_ID_EDITIONFLAGDEFAULT, EBML_NONE }, | |
- { MATROSKA_ID_EDITIONFLAGORDERED, EBML_NONE }, | |
+ { MATROSKA_ID_EDITIONFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaEdition, flag_default) }, | |
+ { MATROSKA_ID_EDITIONFLAGORDERED, EBML_UINT, 0, offsetof(MatroskaEdition, flag_ordered) }, | |
+ { MATROSKA_ID_CHAPTERATOM, EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaEdition, chapters), { .n = matroska_chapter_entry } }, | |
{ 0 } | |
}; | |
static const EbmlSyntax matroska_chapters[] = { | |
- { MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, { .n = matroska_chapter } }, | |
+ { MATROSKA_ID_EDITIONENTRY, EBML_NEST, sizeof(MatroskaEdition), offsetof(MatroskaDemuxContext, editions), { .n = matroska_edition } }, | |
{ 0 } | |
}; | |
@@ -1400,7 +1437,7 @@ static void matroska_convert_tags(AVFormatContext *s) | |
{ | |
MatroskaDemuxContext *matroska = s->priv_data; | |
MatroskaTags *tags = matroska->tags.elem; | |
- int i, j; | |
+ int i, j, k; | |
for (i = 0; i < matroska->tags.nb_elem; i++) { | |
if (tags[i].target.attachuid) { | |
@@ -1411,12 +1448,15 @@ static void matroska_convert_tags(AVFormatContext *s) | |
matroska_convert_tag(s, &tags[i].tag, | |
&attachment[j].stream->metadata, NULL); | |
} else if (tags[i].target.chapteruid) { | |
- MatroskaChapter *chapter = matroska->chapters.elem; | |
- for (j = 0; j < matroska->chapters.nb_elem; j++) | |
- if (chapter[j].uid == tags[i].target.chapteruid && | |
- chapter[j].chapter) | |
- matroska_convert_tag(s, &tags[i].tag, | |
- &chapter[j].chapter->metadata, NULL); | |
+ MatroskaEdition *edition = matroska->editions.elem; | |
+ for (j = 0; j < matroska->editions.nb_elem; j++) { | |
+ MatroskaChapter *chapter = edition[j].chapters.elem; | |
+ for (k = 0; k < edition[j].chapters.nb_elem; k++) | |
+ if (chapter[k].uid == tags[i].target.chapteruid && | |
+ chapter[k].chapter) | |
+ matroska_convert_tag(s, &tags[i].tag, | |
+ &chapter[k].chapter->metadata, NULL); | |
+ } | |
} else if (tags[i].target.trackuid) { | |
MatroskaTrack *track = matroska->tracks.elem; | |
for (j = 0; j < matroska->tracks.nb_elem; j++) | |
@@ -2102,14 +2142,167 @@ static int matroska_parse_tracks(AVFormatContext *s) | |
return 0; | |
} | |
+static int match_uid(EbmlBin *a, EbmlBin *b) | |
+{ | |
+ return a->size == 16 && b->size == 16 && !memcmp(a->data, b->data, 16); | |
+} | |
+ | |
+static void free_segment(MatroskaSubSegment *segment) | |
+{ | |
+ av_free(segment->uid.data); | |
+ av_free(segment->filename); | |
+ avformat_close_input(&segment->avf); | |
+ memset(segment, 0, sizeof(*segment)); | |
+} | |
+ | |
+static int open_potential_segment(AVFormatContext *s, MatroskaSubSegment *segment) | |
+{ | |
+ MatroskaDemuxContext *submat, *matroska = s->priv_data; | |
+ int ret = 0; | |
+ AVFormatContext *avf = avformat_alloc_context(); | |
+ AVDictionary *opts = NULL; | |
+ if (!avf) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ avf->interrupt_callback = s->interrupt_callback; | |
+ | |
+ if ((ret = ff_copy_whitelists(avf, s)) < 0) | |
+ goto fail; | |
+ | |
+ if ((ret = av_dict_set(&opts, "external_linking", "0", 0)) < 0) | |
+ goto fail; | |
+ | |
+ if ((ret = avformat_open_input(&avf, segment->filename, &ff_matroska_demuxer, &opts)) < 0) { | |
+ av_log(s, AV_LOG_VERBOSE, "Impossible to open '%s': %s\n", segment->filename, av_err2str(ret)); | |
+ ret = 1; | |
+ goto fail; | |
+ } | |
+ | |
+ submat = avf->priv_data; | |
+ | |
+ if (submat->segment_uid.size != 16) | |
+ goto fail; | |
+ | |
+ segment->avf = avf; | |
+ segment->used = 0; | |
+ segment->uid = submat->segment_uid; | |
+ if(!(segment->uid.data = av_memdup(segment->uid.data, segment->uid.size))) { | |
+ ret = AVERROR(ENOMEM); | |
+ goto fail; | |
+ } | |
+ | |
+ return 0; | |
+ | |
+fail: | |
+ av_free(segment->filename); | |
+ avformat_close_input(&avf); | |
+ memset(segment, 0, sizeof(*segment)); | |
+ return ret; | |
+} | |
+ | |
+static int matroska_ext(const char* name) | |
+{ | |
+ return 1; | |
+} | |
+ | |
+static int find_segments(AVFormatContext *s) | |
+{ | |
+ MatroskaDemuxContext *matroska = s->priv_data; | |
+ int ret = 0; | |
+ | |
+ if (0) { | |
+ | |
+ } else { | |
+ AVIODirContext *dir = NULL; | |
+ AVIODirEntry *entry = NULL; | |
+ const char *dirname; | |
+ char *path = av_strdup(s->filename); | |
+ if (!path) | |
+ return AVERROR(ENOMEM); | |
+ dirname = av_dirname(path); | |
+ if ((ret = avio_open_dir(&dir, dirname, NULL)) < 0) | |
+ goto dir_fail; | |
+ | |
+ while ((ret = avio_read_dir(dir, &entry)) >= 0 && entry) { | |
+ MatroskaSubSegment *segment = NULL; | |
+ char *filename = NULL; | |
+ if (entry->type != AVIO_ENTRY_FILE || !matroska_ext(entry->name)) | |
+ goto dirent_fail; | |
+ filename = av_asprintf("%s/%s", dirname, entry->name); | |
+ if (!filename) { | |
+ ret = AVERROR(ENOMEM); | |
+ goto dirent_fail; | |
+ } | |
+ if (!strcmp(filename, s->filename)) | |
+ goto dirent_fail; | |
+ | |
+ AV_DYNARRAY_ADD(INT_MAX, sizeof(*matroska->segments), matroska->segments, | |
+ matroska->nb_segments, { | |
+ segment = &matroska->segments[matroska->nb_segments]; | |
+ }, { | |
+ ret = AVERROR(ENOMEM); | |
+ goto dirent_fail; | |
+ }); | |
+ | |
+ if(!(segment->filename = av_strdup(filename))) { | |
+ ret = AVERROR(ENOMEM); | |
+ goto dirent_fail; | |
+ } | |
+ | |
+ if ((ret = open_potential_segment(s, segment)) != 0) | |
+ matroska->nb_segments--; | |
+dirent_fail: | |
+ av_free(filename); | |
+ avio_free_directory_entry(&entry); | |
+ if (ret < 0) | |
+ break; | |
+ } | |
+dir_fail: | |
+ av_free(path); | |
+ } | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int find_segment(AVFormatContext *s, MatroskaChapter *chapter) | |
+{ | |
+ MatroskaDemuxContext *matroska = s->priv_data; | |
+ int ret = 0; | |
+ | |
+ if (match_uid(&chapter->segment_uid, &matroska->segment_uid)) { | |
+ av_freep(&chapter->segment_uid.data); | |
+ chapter->segment_uid.size = 0; | |
+ return 1; | |
+ } | |
+ | |
+ if (!matroska->nb_segments) | |
+ if ((ret = find_segments(s)) < 0) | |
+ return ret; | |
+ | |
+ for (int i = 0; i < matroska->nb_segments; i++) { | |
+ if (match_uid(&chapter->segment_uid, &matroska->segments[i].uid)) { | |
+ MatroskaSubSegment *segment = &matroska->segments[i]; | |
+ if (!segment->avf) { | |
+ if ((ret = open_potential_segment(s, segment)) < 0) | |
+ return ret; | |
+ else if (ret) | |
+ continue; | |
+ } | |
+ segment->used = 1; | |
+ chapter->avf = segment->avf; | |
+ return 1; | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
static int matroska_read_header(AVFormatContext *s) | |
{ | |
MatroskaDemuxContext *matroska = s->priv_data; | |
EbmlList *attachments_list = &matroska->attachments; | |
- EbmlList *chapters_list = &matroska->chapters; | |
MatroskaAttachment *attachments; | |
- MatroskaChapter *chapters; | |
- uint64_t max_start = 0; | |
+ MatroskaEdition *editions; | |
int64_t pos; | |
Ebml ebml = { 0 }; | |
int i, j, res; | |
@@ -2230,21 +2423,58 @@ static int matroska_read_header(AVFormatContext *s) | |
} | |
} | |
- chapters = chapters_list->elem; | |
- for (i = 0; i < chapters_list->nb_elem; i++) | |
- if (chapters[i].start != AV_NOPTS_VALUE && chapters[i].uid && | |
- (max_start == 0 || chapters[i].start > max_start)) { | |
- chapters[i].chapter = | |
- avpriv_new_chapter(s, chapters[i].uid, | |
- (AVRational) { 1, 1000000000 }, | |
- chapters[i].start, chapters[i].end, | |
- chapters[i].title); | |
- if (chapters[i].chapter) { | |
- av_dict_set(&chapters[i].chapter->metadata, | |
- "title", chapters[i].title, 0); | |
+ editions = matroska->editions.elem; | |
+ for (i = 0; i < matroska->editions.nb_elem; i++) { | |
+ MatroskaEdition *edition = &editions[i]; | |
+ int ordered = edition->flag_ordered; | |
+ uint64_t offset = 0; | |
+ uint64_t max_start = 0; | |
+ MatroskaChapter *chapters = edition->chapters.elem; | |
+ for (j = 0; j < edition->chapters.nb_elem; j++) { | |
+ MatroskaChapter *chapter = &chapters[j]; | |
+ int64_t end; | |
+ if (chapter->start == AV_NOPTS_VALUE || chapter->end <= chapter->start || | |
+ !chapter->uid || | |
+ (!ordered && max_start != 0 && chapter->start > max_start)) | |
+ continue; | |
+ if (ordered && chapter->segment_uid.size) { | |
+ if (!matroska->external_linking) | |
+ continue; | |
+ res = find_segment(s, chapter); | |
+ if (res < 0) | |
+ return res; | |
+ else if (!res) | |
+ continue; | |
} | |
- max_start = chapters[i].start; | |
+ end = (ordered && chapter->end != AV_NOPTS_VALUE) ? | |
+ offset + chapter->end - chapter->start : | |
+ chapter->end; | |
+ chapter->chapter = | |
+ avpriv_new_chapter(s, chapter->uid, | |
+ (AVRational) { 1, 1000000000 }, | |
+ ordered ? offset : chapter->start, | |
+ end, | |
+ chapter->title); | |
+ chapter->valid = 1; | |
+ if (ordered) | |
+ offset = end; | |
+ else | |
+ max_start = chapter->start; | |
} | |
+ if (ordered) | |
+ matroska->ctx->duration = av_rescale(offset, AV_TIME_BASE, 1000000000); | |
+ matroska->ordered = ordered; | |
+ matroska->cur_edition = edition; | |
+ if (edition->chapters.nb_elem) | |
+ matroska->cur_chapter = &chapters[0]; | |
+ break; // FIXME: Multiple editions | |
+ } | |
+ | |
+ for (i = 0; i < matroska->nb_segments; i++) { | |
+ MatroskaSubSegment *segment = &matroska->segments[i]; | |
+ if (!segment->used) | |
+ free_segment(segment); | |
+ } | |
matroska_add_index_entries(matroska); | |
@@ -2253,14 +2483,84 @@ static int matroska_read_header(AVFormatContext *s) | |
return 0; | |
} | |
+static int matroska_chapter_finished(MatroskaDemuxContext *matroska, AVPacket *pkt) | |
+{ | |
+ if (matroska->ordered) { | |
+ AVStream *st = matroska->ctx->streams[pkt->stream_index]; | |
+ if (!matroska->cur_chapter) | |
+ return 1; | |
+ return av_compare_ts(pkt->pts, st->time_base, matroska->cur_chapter->end, | |
+ (AVRational) { 1, 1000000000 }) >= 0; | |
+ } else { | |
+ return 0; | |
+ } | |
+} | |
+ | |
+static int advance_chapter(MatroskaDemuxContext *matroska) | |
+{ | |
+ MatroskaChapter *chapters = matroska->cur_edition->chapters.elem; | |
+ matroska->chapter_ts += matroska->cur_chapter->end - matroska->cur_chapter->start; | |
+ do { | |
+ matroska->chapter_idx++; | |
+ } while (matroska->chapter_idx < matroska->cur_edition->chapters.nb_elem && | |
+ !chapters[matroska->chapter_idx].valid); | |
+ if (matroska->chapter_idx == matroska->cur_edition->chapters.nb_elem) { | |
+ matroska->done = 1; | |
+ matroska->cur_chapter = NULL; | |
+ } else { | |
+ matroska->cur_chapter = &chapters[matroska->chapter_idx]; | |
+ matroska->ts_offset = matroska->chapter_ts - matroska->cur_chapter->start; | |
+ } | |
+ return matroska->done; | |
+} | |
+ | |
+static int execute_chapter_seek(MatroskaDemuxContext *matroska, int stream_index, | |
+ int64_t timestamp, int flags) | |
+{ | |
+ AVFormatContext *avf; | |
+ if (!matroska->ordered || !matroska->cur_chapter) | |
+ return 0; | |
+ avf = matroska->cur_chapter->avf ? matroska->cur_chapter->avf : matroska->ctx; | |
+ if (timestamp == AV_NOPTS_VALUE) | |
+ timestamp = matroska->chapter_ts; | |
+ if (stream_index >= 0) { | |
+ AVStream *st = avf->streams[stream_index]; | |
+ timestamp -= av_rescale_q(matroska->ts_offset, (AVRational) { 1, 1000000000 }, | |
+ st->time_base); | |
+ } else { | |
+ timestamp = av_rescale_q(timestamp - matroska->ts_offset, (AVRational) { 1, 1000000000 }, | |
+ AV_TIME_BASE_Q); | |
+ } | |
+ return av_seek_frame(avf, stream_index, timestamp, flags); | |
+} | |
+ | |
+static int is_external_chapter(MatroskaDemuxContext *matroska) | |
+{ | |
+ if (!matroska->ordered || !matroska->cur_chapter) | |
+ return 0; | |
+ return matroska->cur_chapter->avf != NULL; | |
+} | |
+ | |
/* | |
* Put one packet in an application-supplied AVPacket struct. | |
- * Returns 0 on success or -1 on failure. | |
+ * Returns 0 on success, 1 on recoverable failure, and <0 on error. | |
*/ | |
static int matroska_deliver_packet(MatroskaDemuxContext *matroska, | |
AVPacket *pkt) | |
{ | |
if (matroska->num_packets > 0) { | |
+ if (matroska_chapter_finished(matroska, matroska->packets[0])) { | |
+ int64_t end; | |
+ if (!matroska->cur_chapter) | |
+ return AVERROR_EOF; | |
+ end = matroska->cur_chapter->end; | |
+ if (advance_chapter(matroska)) | |
+ return AVERROR_EOF; | |
+ if (is_external_chapter(matroska) || matroska->cur_chapter->start != end) { | |
+ execute_chapter_seek(matroska, -1, AV_NOPTS_VALUE, 0); | |
+ return 2; | |
+ } | |
+ } | |
memcpy(pkt, matroska->packets[0], sizeof(AVPacket)); | |
av_freep(&matroska->packets[0]); | |
if (matroska->num_packets > 1) { | |
@@ -2280,7 +2580,7 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska, | |
return 0; | |
} | |
- return -1; | |
+ return 1; | |
} | |
/* | |
@@ -3023,16 +3323,63 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) | |
static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt) | |
{ | |
MatroskaDemuxContext *matroska = s->priv_data; | |
+ int ret = 0; | |
- while (matroska_deliver_packet(matroska, pkt)) { | |
- int64_t pos = avio_tell(matroska->ctx->pb); | |
- if (matroska->done) | |
- return AVERROR_EOF; | |
- if (matroska_parse_cluster(matroska) < 0) | |
- matroska_resync(matroska, pos); | |
+ do { | |
+ if (is_external_chapter(matroska)) { | |
+ if ((ret = av_read_frame(matroska->cur_chapter->avf, pkt)) < 0) { | |
+ if (ret != AVERROR_EOF) | |
+ return ret; | |
+ } | |
+ if (matroska_chapter_finished(matroska, pkt) || ret == AVERROR_EOF) { | |
+ av_free_packet(pkt); | |
+ if (advance_chapter(matroska)) | |
+ return AVERROR_EOF; | |
+ execute_chapter_seek(matroska, -1, AV_NOPTS_VALUE, 0); | |
+ ret = 2; | |
+ } | |
+ } else { | |
+ while ((ret = matroska_deliver_packet(matroska, pkt)) == 1) { | |
+ int64_t pos = avio_tell(matroska->ctx->pb); | |
+ if (matroska->done) | |
+ return AVERROR_EOF; | |
+ if (matroska_parse_cluster(matroska) < 0) | |
+ matroska_resync(matroska, pos); | |
+ } | |
+ } | |
+ } while (ret == 2); | |
+ | |
+ if (!ret && matroska->ts_offset) { | |
+ AVStream *st = s->streams[pkt->stream_index]; | |
+ int64_t delta = av_rescale_q(matroska->ts_offset, (AVRational) { 1, 1000000000 }, | |
+ st->time_base); | |
+ if (pkt->pts != AV_NOPTS_VALUE) | |
+ pkt->pts += delta; | |
+ if (pkt->dts != AV_NOPTS_VALUE) | |
+ pkt->dts += delta; | |
} | |
- return 0; | |
+ return ret; | |
+} | |
+ | |
+static int seek_chapter_timestamp(MatroskaDemuxContext *matroska, AVStream *st, | |
+ int64_t timestamp) | |
+{ | |
+ MatroskaChapter *chapters = matroska->cur_edition->chapters.elem; | |
+ int64_t end = 0; | |
+ matroska->chapter_idx = 0; | |
+ matroska->chapter_ts = 0; | |
+ matroska->ts_offset = 0; | |
+ matroska->cur_chapter = &chapters[matroska->chapter_idx]; | |
+ while (!matroska->done) { | |
+ int64_t duration = matroska->cur_chapter->end - matroska->cur_chapter->start; | |
+ end += duration; | |
+ if (av_compare_ts(timestamp, st->time_base, end, | |
+ (AVRational) { 1, 1000000000 }) < 0) | |
+ break; | |
+ advance_chapter(matroska); | |
+ } | |
+ return matroska->done; | |
} | |
static int matroska_read_seek(AVFormatContext *s, int stream_index, | |
@@ -3043,6 +3390,16 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, | |
AVStream *st = s->streams[stream_index]; | |
int i, index, index_sub, index_min; | |
+ if (matroska->ordered) { | |
+ MatroskaEdition *edition = matroska->cur_edition; | |
+ if (!seek_chapter_timestamp(matroska, st, timestamp) && | |
+ is_external_chapter(matroska)) { | |
+ return execute_chapter_seek(matroska, stream_index, timestamp, flags); | |
+ } | |
+ timestamp -= av_rescale_q(matroska->ts_offset, (AVRational) { 1, 1000000000 }, | |
+ st->time_base); | |
+ } | |
+ | |
/* Parse the CUES now since we need the index data to seek. */ | |
if (matroska->cues_parsing_deferred > 0) { | |
matroska->cues_parsing_deferred = 0; | |
@@ -3484,9 +3841,17 @@ static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt) | |
#define OFFSET(x) offsetof(MatroskaDemuxContext, x) | |
static const AVOption options[] = { | |
{ "live", "flag indicating that the input is a live file that only has the headers.", OFFSET(is_live), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, | |
+ { "external_linking", "whether or not to enable external segment linking", OFFSET(external_linking), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, | |
{ NULL }, | |
}; | |
+static const AVClass matroska_class = { | |
+ .class_name = "Matroska demuxer", | |
+ .item_name = av_default_item_name, | |
+ .option = options, | |
+ .version = LIBAVUTIL_VERSION_INT, | |
+}; | |
+ | |
static const AVClass webm_dash_class = { | |
.class_name = "WebM DASH Manifest demuxer", | |
.item_name = av_default_item_name, | |
@@ -3504,7 +3869,8 @@ AVInputFormat ff_matroska_demuxer = { | |
.read_packet = matroska_read_packet, | |
.read_close = matroska_read_close, | |
.read_seek = matroska_read_seek, | |
- .mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska" | |
+ .mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska", | |
+ .priv_class = &matroska_class, | |
}; | |
AVInputFormat ff_webm_dash_manifest_demuxer = { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment