|
|
@@ -40,6 +40,7 @@ |
|
|
#include "libavutil/intreadwrite.h" |
|
|
#include "libavutil/intreadwrite.h" |
|
|
#include "libavutil/lzo.h" |
|
|
#include "libavutil/lzo.h" |
|
|
#include "libavutil/mathematics.h" |
|
|
#include "libavutil/mathematics.h" |
|
|
|
|
|
#include "libavutil/opt.h" |
|
|
#include "libavutil/time_internal.h" |
|
|
#include "libavutil/time_internal.h" |
|
|
|
|
|
|
|
|
#include "libavcodec/bytestream.h" |
|
|
#include "libavcodec/bytestream.h" |
|
|
@@ -260,6 +261,7 @@ typedef struct MatroskaLevel1Element { |
|
|
} MatroskaLevel1Element; |
|
|
} MatroskaLevel1Element; |
|
|
|
|
|
|
|
|
typedef struct MatroskaDemuxContext { |
|
|
typedef struct MatroskaDemuxContext { |
|
|
|
|
|
const AVClass *class; |
|
|
AVFormatContext *ctx; |
|
|
AVFormatContext *ctx; |
|
|
|
|
|
|
|
|
/* EBML stuff */ |
|
|
/* EBML stuff */ |
|
|
@@ -307,6 +309,9 @@ typedef struct MatroskaDemuxContext { |
|
|
|
|
|
|
|
|
/* File has SSA subtitles which prevent incremental cluster parsing. */ |
|
|
/* File has SSA subtitles which prevent incremental cluster parsing. */ |
|
|
int contains_ssa; |
|
|
int contains_ssa; |
|
|
|
|
|
|
|
|
|
|
|
/* WebM DASH Manifest live flag/ */ |
|
|
|
|
|
int is_live; |
|
|
} MatroskaDemuxContext; |
|
|
} MatroskaDemuxContext; |
|
|
|
|
|
|
|
|
typedef struct MatroskaBlock { |
|
|
typedef struct MatroskaBlock { |
|
|
@@ -698,7 +703,7 @@ static int ebml_level_end(MatroskaDemuxContext *matroska) |
|
|
return 1; |
|
|
return 1; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return 0; |
|
|
|
|
|
|
|
|
return (matroska->is_live && matroska->ctx->pb->eof_reached) ? 1 : 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
@@ -949,8 +954,11 @@ static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, |
|
|
if (!matroska->current_id) { |
|
|
if (!matroska->current_id) { |
|
|
uint64_t id; |
|
|
uint64_t id; |
|
|
int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id); |
|
|
int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id); |
|
|
if (res < 0) |
|
|
|
|
|
return res; |
|
|
|
|
|
|
|
|
if (res < 0) { |
|
|
|
|
|
// in live mode, finish parsing if EOF is reached. |
|
|
|
|
|
return (matroska->is_live && matroska->ctx->pb->eof_reached && |
|
|
|
|
|
res == AVERROR_EOF) ? 1 : res; |
|
|
|
|
|
} |
|
|
matroska->current_id = id | 1 << 7 * res; |
|
|
matroska->current_id = id | 1 << 7 * res; |
|
|
} |
|
|
} |
|
|
return ebml_parse_id(matroska, syntax, matroska->current_id, data); |
|
|
return ebml_parse_id(matroska, syntax, matroska->current_id, data); |
|
|
@@ -3417,26 +3425,27 @@ static int webm_dash_manifest_read_header(AVFormatContext *s) |
|
|
return -1; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// initialization range |
|
|
|
|
|
// 5 is the offset of Cluster ID. |
|
|
|
|
|
av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, avio_tell(s->pb) - 5, 0); |
|
|
|
|
|
|
|
|
if (!matroska->is_live) { |
|
|
|
|
|
buf = av_asprintf("%g", matroska->duration); |
|
|
|
|
|
if (!buf) return AVERROR(ENOMEM); |
|
|
|
|
|
av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0); |
|
|
|
|
|
av_free(buf); |
|
|
|
|
|
|
|
|
|
|
|
// initialization range |
|
|
|
|
|
// 5 is the offset of Cluster ID. |
|
|
|
|
|
av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, avio_tell(s->pb) - 5, 0); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// basename of the file |
|
|
// basename of the file |
|
|
buf = strrchr(s->filename, '/'); |
|
|
buf = strrchr(s->filename, '/'); |
|
|
av_dict_set(&s->streams[0]->metadata, FILENAME, buf ? ++buf : s->filename, 0); |
|
|
av_dict_set(&s->streams[0]->metadata, FILENAME, buf ? ++buf : s->filename, 0); |
|
|
|
|
|
|
|
|
// duration |
|
|
|
|
|
buf = av_asprintf("%g", matroska->duration); |
|
|
|
|
|
if (!buf) return AVERROR(ENOMEM); |
|
|
|
|
|
av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0); |
|
|
|
|
|
av_free(buf); |
|
|
|
|
|
|
|
|
|
|
|
// track number |
|
|
// track number |
|
|
tracks = matroska->tracks.elem; |
|
|
tracks = matroska->tracks.elem; |
|
|
av_dict_set_int(&s->streams[0]->metadata, TRACK_NUMBER, tracks[0].num, 0); |
|
|
av_dict_set_int(&s->streams[0]->metadata, TRACK_NUMBER, tracks[0].num, 0); |
|
|
|
|
|
|
|
|
// parse the cues and populate Cue related fields |
|
|
// parse the cues and populate Cue related fields |
|
|
return webm_dash_manifest_cues(s); |
|
|
|
|
|
|
|
|
return matroska->is_live ? 0 : webm_dash_manifest_cues(s); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
@@ -3444,6 +3453,19 @@ static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
return AVERROR_EOF; |
|
|
return AVERROR_EOF; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#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 }, |
|
|
|
|
|
{ NULL }, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static const AVClass webm_dash_class = { |
|
|
|
|
|
.class_name = "WebM DASH Manifest demuxer", |
|
|
|
|
|
.item_name = av_default_item_name, |
|
|
|
|
|
.option = options, |
|
|
|
|
|
.version = LIBAVUTIL_VERSION_INT, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
AVInputFormat ff_matroska_demuxer = { |
|
|
AVInputFormat ff_matroska_demuxer = { |
|
|
.name = "matroska,webm", |
|
|
.name = "matroska,webm", |
|
|
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), |
|
|
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), |
|
|
@@ -3464,4 +3486,5 @@ AVInputFormat ff_webm_dash_manifest_demuxer = { |
|
|
.read_header = webm_dash_manifest_read_header, |
|
|
.read_header = webm_dash_manifest_read_header, |
|
|
.read_packet = webm_dash_manifest_read_packet, |
|
|
.read_packet = webm_dash_manifest_read_packet, |
|
|
.read_close = matroska_read_close, |
|
|
.read_close = matroska_read_close, |
|
|
|
|
|
.priv_class = &webm_dash_class, |
|
|
}; |
|
|
}; |