Skip to content

Instantly share code, notes, and snippets.

@rcombs

rcombs/stdin Secret

Last active October 18, 2015 22:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rcombs/16e91a2c04c541b9014f to your computer and use it in GitHub Desktop.
Save rcombs/16e91a2c04c541b9014f to your computer and use it in GitHub Desktop.
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