#include "config.h"
#include "config_components.h"
#include <inttypes.h>
#include <stdio.h>
#include "libavutil/avstring.h"
#include "libavutil/base64.h"
#include "libavutil/bprint.h"
#include "libavutil/dict.h"
#include "libavutil/dict_internal.h"
#include "libavutil/display.h"
#include "libavutil/hdr_dynamic_metadata.h"
#include "libavutil/intfloat.h"
#include "libavutil/intreadwrite.h"
#if CONFIG_LZO
#include "libavutil/lzo.h"
#endif
#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mathematics.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time_internal.h"
#include "libavutil/spherical.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/defs.h"
#include "libavcodec/flac.h"
#include "libavcodec/itut35.h"
#include "libavcodec/mpeg4audio.h"
#include "libavcodec/packet_internal.h"
#include "avformat.h"
#include "avio_internal.h"
#include "demux.h"
#include "dovi_isom.h"
#include "internal.h"
#include "isom.h"
#include "matroska.h"
#include "oggdec.h"
#include "riff.h"
#if CONFIG_SIPR_DECODER
#include "rmsipr.h"
#endif
#if CONFIG_BZLIB
#include <bzlib.h>
#endif
#if CONFIG_ZLIB
#include <zlib.h>
#endif
#include "qtpalette.h"
#define EBML_UNKNOWN_LENGTH …
#define NEEDS_CHECKING …
#define LEVEL_ENDED …
#define SKIP_THRESHOLD …
#define UNKNOWN_EQUIV …
EbmlType;
CountedElement;
EbmlSyntax;
EbmlList;
EbmlBin;
Ebml;
MatroskaTrackCompression;
MatroskaTrackEncryption;
MatroskaTrackEncoding;
MatroskaMasteringMeta;
MatroskaTrackVideoColor;
MatroskaTrackVideoProjection;
MatroskaTrackVideo;
MatroskaTrackAudio;
MatroskaTrackPlane;
MatroskaTrackOperation;
MatroskaBlockAdditionMapping;
MatroskaTrack;
MatroskaAttachment;
MatroskaChapter;
MatroskaIndexPos;
MatroskaIndex;
MatroskaTag;
MatroskaTagTarget;
MatroskaTags;
MatroskaSeekhead;
MatroskaLevel;
MatroskaBlockMore;
MatroskaBlock;
MatroskaCluster;
MatroskaLevel1Element;
MatroskaDemuxContext;
#define CHILD_OF …
static EbmlSyntax ebml_syntax[3], matroska_segment[9], matroska_track_video_color[15], matroska_track_video[19],
matroska_track[33], matroska_track_encoding[6], matroska_track_encodings[2],
matroska_track_combine_planes[2], matroska_track_operation[2], matroska_block_addition_mapping[5], matroska_tracks[2],
matroska_attachments[2], matroska_chapter_entry[9], matroska_chapter[6], matroska_chapters[2],
matroska_index_entry[3], matroska_index[2], matroska_tag[3], matroska_tags[2], matroska_seekhead[2],
matroska_blockadditions[2], matroska_blockgroup[8], matroska_cluster_parsing[8];
static EbmlSyntax ebml_header[] = …;
static EbmlSyntax ebml_syntax[] = …;
static EbmlSyntax matroska_info[] = …;
static EbmlSyntax matroska_mastering_meta[] = …;
static EbmlSyntax matroska_track_video_color[] = …;
static EbmlSyntax matroska_track_video_projection[] = …;
static EbmlSyntax matroska_track_video[] = …;
static EbmlSyntax matroska_track_audio[] = …;
static EbmlSyntax matroska_track_encoding_compression[] = …;
static EbmlSyntax matroska_track_encoding_encryption[] = …;
static EbmlSyntax matroska_track_encoding[] = …;
static EbmlSyntax matroska_track_encodings[] = …;
static EbmlSyntax matroska_track_plane[] = …;
static EbmlSyntax matroska_track_combine_planes[] = …;
static EbmlSyntax matroska_track_operation[] = …;
static EbmlSyntax matroska_block_addition_mapping[] = …;
static EbmlSyntax matroska_track[] = …;
static EbmlSyntax matroska_tracks[] = …;
static EbmlSyntax matroska_attachment[] = …;
static EbmlSyntax matroska_attachments[] = …;
static EbmlSyntax matroska_chapter_display[] = …;
static EbmlSyntax matroska_chapter_entry[] = …;
static EbmlSyntax matroska_chapter[] = …;
static EbmlSyntax matroska_chapters[] = …;
static EbmlSyntax matroska_index_pos[] = …;
static EbmlSyntax matroska_index_entry[] = …;
static EbmlSyntax matroska_index[] = …;
static EbmlSyntax matroska_simpletag[] = …;
static EbmlSyntax matroska_tagtargets[] = …;
static EbmlSyntax matroska_tag[] = …;
static EbmlSyntax matroska_tags[] = …;
static EbmlSyntax matroska_seekhead_entry[] = …;
static EbmlSyntax matroska_seekhead[] = …;
static EbmlSyntax matroska_segment[] = …;
static EbmlSyntax matroska_segments[] = …;
static EbmlSyntax matroska_blockmore[] = …;
static EbmlSyntax matroska_blockadditions[] = …;
static EbmlSyntax matroska_blockgroup[] = …;
static EbmlSyntax matroska_cluster_parsing[] = …;
static EbmlSyntax matroska_cluster_enter[] = …;
#undef CHILD_OF
static const CodecMime mkv_image_mime_tags[] = …;
static const CodecMime mkv_mime_tags[] = …;
static const char * const matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = …;
static const char *const matroska_doctypes[] = …;
static int matroska_reset_status(MatroskaDemuxContext *matroska,
uint32_t id, int64_t position)
{ … }
static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
{ … }
static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb,
int max_size, uint64_t *number, int eof_forbidden)
{ … }
static int ebml_read_length(MatroskaDemuxContext *matroska, AVIOContext *pb,
uint64_t *number)
{ … }
static int ebml_read_uint(AVIOContext *pb, int size,
uint64_t default_value, uint64_t *num)
{ … }
static int ebml_read_sint(AVIOContext *pb, int size,
int64_t default_value, int64_t *num)
{ … }
static int ebml_read_float(AVIOContext *pb, int size,
double default_value, double *num)
{ … }
static int ebml_read_ascii(AVIOContext *pb, int size,
const char *default_value, char **str)
{ … }
static int ebml_read_binary(AVIOContext *pb, int length,
int64_t pos, EbmlBin *bin)
{ … }
static int ebml_read_master(MatroskaDemuxContext *matroska,
uint64_t length, int64_t pos)
{ … }
static int matroska_ebmlnum_sint(MatroskaDemuxContext *matroska,
AVIOContext *pb, int64_t *num)
{ … }
static int ebml_parse(MatroskaDemuxContext *matroska,
EbmlSyntax *syntax, void *data);
static EbmlSyntax *ebml_parse_id(EbmlSyntax *syntax, uint32_t id)
{ … }
static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
void *data)
{ … }
static int is_ebml_id_valid(uint32_t id)
{ … }
static MatroskaLevel1Element *matroska_find_level1_elem(MatroskaDemuxContext *matroska,
uint32_t id, int64_t pos)
{ … }
static int ebml_parse(MatroskaDemuxContext *matroska,
EbmlSyntax *syntax, void *data)
{ … }
static void ebml_free(EbmlSyntax *syntax, void *data)
{ … }
static int matroska_probe(const AVProbeData *p)
{ … }
static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska,
uint64_t num)
{ … }
static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
MatroskaTrack *track)
{ … }
static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
AVDictionary **metadata, char *prefix)
{ … }
static void matroska_convert_tags(AVFormatContext *s)
{ … }
static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska,
int64_t pos)
{ … }
static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
{ … }
static void matroska_add_index_entries(MatroskaDemuxContext *matroska)
{ … }
static void matroska_parse_cues(MatroskaDemuxContext *matroska) { … }
static int matroska_parse_content_encodings(MatroskaTrackEncoding *encodings,
unsigned nb_encodings,
MatroskaTrack *track,
char **key_id_base64, void *logctx)
{ … }
static int matroska_aac_profile(char *codec_id)
{ … }
static int matroska_aac_sri(int samplerate)
{ … }
static void matroska_metadata_creation_time(AVDictionary **metadata, int64_t date_utc)
{ … }
static int matroska_parse_flac(AVFormatContext *s,
MatroskaTrack *track,
int *offset)
{ … }
static int mkv_field_order(const MatroskaDemuxContext *matroska, uint64_t field_order)
{ … }
static void mkv_stereo_mode_display_mul(int stereo_mode,
int *h_width, int *h_height)
{ … }
static int mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode)
{ … }
static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) { … }
static int mkv_create_display_matrix(AVStream *st,
const MatroskaTrackVideoProjection *proj,
void *logctx)
{ … }
static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track,
void *logctx)
{ … }
static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const MatroskaTrack *track,
EbmlBin *bin)
{ … }
static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, MatroskaTrack *track)
{ … }
static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID *codec_id)
{ … }
enum { … };
#define AAC_MAX_EXTRADATA_SIZE …
#define TTA_EXTRADATA_SIZE …
#define WAVPACK_EXTRADATA_SIZE …
static int mka_parse_audio_codec(MatroskaTrack *track, AVCodecParameters *par,
const MatroskaDemuxContext *matroska,
AVFormatContext *s, int *extradata_offset)
{ … }
static int mka_parse_audio(MatroskaTrack *track, AVStream *st,
AVCodecParameters *par,
const MatroskaDemuxContext *matroska,
AVFormatContext *s, int *extradata_offset)
{ … }
static int mkv_parse_video_codec(MatroskaTrack *track, AVCodecParameters *par,
const MatroskaDemuxContext *matroska,
int *extradata_offset)
{ … }
static int mkv_parse_video(MatroskaTrack *track, AVStream *st,
AVCodecParameters *par,
const MatroskaDemuxContext *matroska,
int *extradata_offset)
{ … }
static int mkv_parse_subtitle_codec(MatroskaTrack *track, AVStream *st,
AVCodecParameters *par,
const MatroskaDemuxContext *matroska)
{ … }
static int matroska_parse_tracks(AVFormatContext *s)
{ … }
static int matroska_read_header(AVFormatContext *s)
{ … }
static int matroska_deliver_packet(MatroskaDemuxContext *matroska,
AVPacket *pkt)
{ … }
static void matroska_clear_queue(MatroskaDemuxContext *matroska)
{ … }
static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
int size, int type, AVIOContext *pb,
uint32_t lace_size[256], int *laces)
{ … }
static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
MatroskaTrack *track, AVStream *st,
uint8_t *data, int size, uint64_t timecode,
int64_t pos)
{ … }
static int matroska_parse_wavpack(MatroskaTrack *track,
uint8_t **data, int *size)
{ … }
static int matroska_parse_prores(MatroskaTrack *track,
uint8_t **data, int *size)
{ … }
static int matroska_parse_webvtt(MatroskaDemuxContext *matroska,
MatroskaTrack *track,
AVStream *st,
uint8_t *data, int data_len,
uint64_t timecode,
uint64_t duration,
int64_t pos)
{ … }
static int matroska_parse_block_additional(MatroskaDemuxContext *matroska,
MatroskaTrack *track, AVPacket *pkt,
const uint8_t *data, int size, uint64_t id)
{ … }
static int matroska_parse_frame(MatroskaDemuxContext *matroska,
MatroskaTrack *track, AVStream *st,
AVBufferRef *buf, uint8_t *data, int pkt_size,
uint64_t timecode, uint64_t lace_duration,
int64_t pos, int is_keyframe,
MatroskaBlockMore *blockmore, int nb_blockmore,
int64_t discard_padding)
{ … }
static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf, uint8_t *data,
int size, int64_t pos, uint64_t cluster_time,
uint64_t block_duration, int is_keyframe,
MatroskaBlockMore *blockmore, int nb_blockmore,
int64_t cluster_pos, int64_t discard_padding)
{ … }
static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
{ … }
static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt)
{ … }
static int matroska_read_seek(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{ … }
static int matroska_read_close(AVFormatContext *s)
{ … }
#if CONFIG_WEBM_DASH_MANIFEST_DEMUXER
typedef struct {
int64_t start_time_ns;
int64_t end_time_ns;
int64_t start_offset;
int64_t end_offset;
} CueDesc;
static CueDesc get_cue_desc(AVFormatContext *s, int64_t ts, int64_t cues_start) {
MatroskaDemuxContext *matroska = s->priv_data;
FFStream *const sti = ffstream(s->streams[0]);
AVIndexEntry *const index_entries = sti->index_entries;
int nb_index_entries = sti->nb_index_entries;
CueDesc cue_desc;
int i;
if (ts >= (int64_t)(matroska->duration * matroska->time_scale))
return (CueDesc) {-1, -1, -1, -1};
for (i = 1; i < nb_index_entries; i++) {
if (index_entries[i - 1].timestamp * matroska->time_scale <= ts &&
index_entries[i].timestamp * matroska->time_scale > ts) {
break;
}
}
--i;
if (index_entries[i].timestamp > matroska->duration)
return (CueDesc) {-1, -1, -1, -1};
cue_desc.start_time_ns = index_entries[i].timestamp * matroska->time_scale;
cue_desc.start_offset = index_entries[i].pos - matroska->segment_start;
if (i != nb_index_entries - 1) {
cue_desc.end_time_ns = index_entries[i + 1].timestamp * matroska->time_scale;
cue_desc.end_offset = index_entries[i + 1].pos - matroska->segment_start;
} else {
cue_desc.end_time_ns = matroska->duration * matroska->time_scale;
cue_desc.end_offset = cues_start - matroska->segment_start;
}
return cue_desc;
}
static int webm_clusters_start_with_keyframe(AVFormatContext *s)
{
MatroskaDemuxContext *matroska = s->priv_data;
AVStream *const st = s->streams[0];
FFStream *const sti = ffstream(st);
uint32_t id = matroska->current_id;
int64_t cluster_pos, before_pos;
int index, rv = 1;
if (sti->nb_index_entries <= 0)
return 0;
index = av_index_search_timestamp(st, 0, 0);
if (index < 0)
return 0;
cluster_pos = sti->index_entries[index].pos;
before_pos = avio_tell(s->pb);
while (1) {
uint64_t cluster_id, cluster_length;
int read;
AVPacket *pkt;
avio_seek(s->pb, cluster_pos, SEEK_SET);
read = ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id, 1);
if (read < 0 || cluster_id != 0xF43B675)
break;
read = ebml_read_length(matroska, matroska->ctx->pb, &cluster_length);
if (read < 0)
break;
matroska_reset_status(matroska, 0, cluster_pos);
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0 ||
!matroska->queue.head) {
break;
}
pkt = &matroska->queue.head->pkt;
cluster_pos += 4 + read + cluster_length;
if (!(pkt->flags & AV_PKT_FLAG_KEY)) {
rv = 0;
break;
}
}
matroska_reset_status(matroska, id, before_pos);
return rv;
}
static int buffer_size_after_time_downloaded(int64_t time_ns, double search_sec, int64_t bps,
double min_buffer, double* buffer,
double* sec_to_download, AVFormatContext *s,
int64_t cues_start)
{
double nano_seconds_per_second = 1000000000.0;
double time_sec = time_ns / nano_seconds_per_second;
int rv = 0;
int64_t time_to_search_ns = (int64_t)(search_sec * nano_seconds_per_second);
int64_t end_time_ns = time_ns + time_to_search_ns;
double sec_downloaded = 0.0;
CueDesc desc_curr = get_cue_desc(s, time_ns, cues_start);
if (desc_curr.start_time_ns == -1)
return -1;
*sec_to_download = 0.0;
if (time_ns > desc_curr.start_time_ns) {
int64_t cue_nano = desc_curr.end_time_ns - time_ns;
double percent = (double)(cue_nano) / (desc_curr.end_time_ns - desc_curr.start_time_ns);
double cueBytes = (desc_curr.end_offset - desc_curr.start_offset) * percent;
double timeToDownload = (cueBytes * 8.0) / bps;
sec_downloaded += (cue_nano / nano_seconds_per_second) - timeToDownload;
*sec_to_download += timeToDownload;
if (desc_curr.end_time_ns >= end_time_ns) {
double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second;
double percent_to_sub = search_sec / (desc_end_time_sec - time_sec);
sec_downloaded = percent_to_sub * sec_downloaded;
*sec_to_download = percent_to_sub * *sec_to_download;
}
if ((sec_downloaded + *buffer) <= min_buffer) {
return 1;
}
desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start);
}
while (desc_curr.start_time_ns != -1) {
int64_t desc_bytes = desc_curr.end_offset - desc_curr.start_offset;
int64_t desc_ns = desc_curr.end_time_ns - desc_curr.start_time_ns;
double desc_sec = desc_ns / nano_seconds_per_second;
double bits = (desc_bytes * 8.0);
double time_to_download = bits / bps;
sec_downloaded += desc_sec - time_to_download;
*sec_to_download += time_to_download;
if (desc_curr.end_time_ns >= end_time_ns) {
double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second;
double percent_to_sub = search_sec / (desc_end_time_sec - time_sec);
sec_downloaded = percent_to_sub * sec_downloaded;
*sec_to_download = percent_to_sub * *sec_to_download;
if ((sec_downloaded + *buffer) <= min_buffer)
rv = 1;
break;
}
if ((sec_downloaded + *buffer) <= min_buffer) {
rv = 1;
break;
}
desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start);
}
*buffer = *buffer + sec_downloaded;
return rv;
}
static int64_t webm_dash_manifest_compute_bandwidth(AVFormatContext *s, int64_t cues_start)
{
MatroskaDemuxContext *matroska = s->priv_data;
AVStream *st = s->streams[0];
FFStream *const sti = ffstream(st);
double bandwidth = 0.0;
for (int i = 0; i < sti->nb_index_entries; i++) {
int64_t prebuffer_ns = 1000000000;
int64_t time_ns = sti->index_entries[i].timestamp * matroska->time_scale;
double nano_seconds_per_second = 1000000000.0;
int64_t prebuffered_ns;
double prebuffer_bytes = 0.0;
int64_t temp_prebuffer_ns = prebuffer_ns;
int64_t pre_bytes, pre_ns;
double pre_sec, prebuffer, bits_per_second;
CueDesc desc_beg = get_cue_desc(s, time_ns, cues_start);
CueDesc desc_end = desc_beg;
if (time_ns > INT64_MAX - prebuffer_ns)
return -1;
prebuffered_ns = time_ns + prebuffer_ns;
while (desc_end.start_time_ns != -1 && desc_end.end_time_ns < prebuffered_ns) {
prebuffer_bytes += desc_end.end_offset - desc_end.start_offset;
temp_prebuffer_ns -= desc_end.end_time_ns - desc_end.start_time_ns;
desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start);
}
if (desc_end.start_time_ns == -1) {
if (matroska->duration * matroska->time_scale >= prebuffered_ns)
return -1;
bits_per_second = 0.0;
} else {
pre_bytes = desc_end.end_offset - desc_end.start_offset;
pre_ns = desc_end.end_time_ns - desc_end.start_time_ns;
if (pre_ns <= 0)
return -1;
pre_sec = pre_ns / nano_seconds_per_second;
prebuffer_bytes +=
pre_bytes * ((temp_prebuffer_ns / nano_seconds_per_second) / pre_sec);
prebuffer = prebuffer_ns / nano_seconds_per_second;
bits_per_second = 0.0;
do {
int64_t desc_bytes = desc_end.end_offset - desc_beg.start_offset;
int64_t desc_ns = desc_end.end_time_ns - desc_beg.start_time_ns;
double desc_sec, calc_bits_per_second, percent, mod_bits_per_second;
if (desc_bytes <= 0)
return -1;
desc_sec = desc_ns / nano_seconds_per_second;
calc_bits_per_second = (desc_bytes * 8) / desc_sec;
percent = (desc_bytes - prebuffer_bytes) / desc_bytes;
mod_bits_per_second = calc_bits_per_second * percent;
if (prebuffer < desc_sec) {
double search_sec =
(double)(matroska->duration * matroska->time_scale) / nano_seconds_per_second;
int64_t bps = (int64_t)(mod_bits_per_second) + 1;
const double min_buffer = 0.0;
double buffer = prebuffer;
double sec_to_download = 0.0;
int rv = buffer_size_after_time_downloaded(prebuffered_ns, search_sec, bps,
min_buffer, &buffer, &sec_to_download,
s, cues_start);
if (rv < 0) {
return -1;
} else if (rv == 0) {
bits_per_second = (double)(bps);
break;
}
}
desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start);
} while (desc_end.start_time_ns != -1);
}
if (bandwidth < bits_per_second) bandwidth = bits_per_second;
}
return (int64_t)bandwidth;
}
static int webm_dash_manifest_cues(AVFormatContext *s, int64_t init_range)
{
MatroskaDemuxContext *matroska = s->priv_data;
EbmlList *seekhead_list = &matroska->seekhead;
MatroskaSeekhead *seekhead = seekhead_list->elem;
AVStream *const st = s->streams[0];
FFStream *const sti = ffstream(st);
AVBPrint bprint;
char *buf;
int64_t cues_start = -1, cues_end = -1, before_pos, bandwidth;
int i;
int ret;
for (i = 0; i < seekhead_list->nb_elem; i++)
if (seekhead[i].id == MATROSKA_ID_CUES)
break;
if (i >= seekhead_list->nb_elem) return -1;
before_pos = avio_tell(matroska->ctx->pb);
cues_start = seekhead[i].pos + matroska->segment_start;
if (avio_seek(matroska->ctx->pb, cues_start, SEEK_SET) == cues_start) {
uint64_t cues_length, cues_id;
int bytes_read;
bytes_read = ebml_read_num (matroska, matroska->ctx->pb, 4, &cues_id, 1);
if (bytes_read < 0 || cues_id != (MATROSKA_ID_CUES & 0xfffffff))
return bytes_read < 0 ? bytes_read : AVERROR_INVALIDDATA;
bytes_read = ebml_read_length(matroska, matroska->ctx->pb, &cues_length);
if (bytes_read < 0)
return bytes_read;
cues_end = cues_start + 4 + bytes_read + cues_length - 1;
}
avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
if (cues_start == -1 || cues_end == -1) return -1;
matroska_parse_cues(matroska);
if (!sti->nb_index_entries)
return AVERROR_INVALIDDATA;
av_dict_set_int(&s->streams[0]->metadata, CUES_START, cues_start, 0);
av_dict_set_int(&s->streams[0]->metadata, CUES_END, cues_end, 0);
if (cues_start <= init_range)
av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, cues_start - 1, 0);
bandwidth = webm_dash_manifest_compute_bandwidth(s, cues_start);
if (bandwidth < 0) return -1;
av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH, bandwidth, 0);
av_dict_set_int(&s->streams[0]->metadata, CLUSTER_KEYFRAME, webm_clusters_start_with_keyframe(s), 0);
av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED);
for (int i = 0; i < sti->nb_index_entries; i++)
av_bprintf(&bprint, "%" PRId64",", sti->index_entries[i].timestamp);
if (!av_bprint_is_complete(&bprint)) {
av_bprint_finalize(&bprint, NULL);
return AVERROR(ENOMEM);
}
bprint.str[--bprint.len] = '\0';
if ((ret = av_bprint_finalize(&bprint, &buf)) < 0)
return ret;
av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS,
buf, AV_DICT_DONT_STRDUP_VAL);
return 0;
}
static int webm_dash_manifest_read_header(AVFormatContext *s)
{
char *buf;
int ret = matroska_read_header(s);
int64_t init_range;
MatroskaTrack *tracks;
MatroskaDemuxContext *matroska = s->priv_data;
if (ret) {
av_log(s, AV_LOG_ERROR, "Failed to read file headers\n");
return -1;
}
if (!matroska->tracks.nb_elem || !s->nb_streams) {
av_log(s, AV_LOG_ERROR, "No track found\n");
return AVERROR_INVALIDDATA;
}
if (!matroska->is_live) {
buf = av_asprintf("%g", matroska->duration);
if (!buf)
return AVERROR(ENOMEM);
av_dict_set(&s->streams[0]->metadata, DURATION,
buf, AV_DICT_DONT_STRDUP_VAL);
init_range = avio_tell(s->pb) - 5;
av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, init_range, 0);
}
buf = strrchr(s->url, '/');
av_dict_set(&s->streams[0]->metadata, FILENAME, buf ? ++buf : s->url, 0);
tracks = matroska->tracks.elem;
av_dict_set_int(&s->streams[0]->metadata, TRACK_NUMBER, tracks[0].num, 0);
if (!matroska->is_live) {
ret = webm_dash_manifest_cues(s, init_range);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Error parsing Cues\n");
return ret;
}
}
if (matroska->bandwidth > 0) {
av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH,
matroska->bandwidth, 0);
}
return 0;
}
static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt)
{
return AVERROR_EOF;
}
#define OFFSET …
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_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
{ "bandwidth", "bandwidth of this stream to be specified in the DASH manifest.", OFFSET(bandwidth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, 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,
};
const FFInputFormat ff_webm_dash_manifest_demuxer = {
.p.name = "webm_dash_manifest",
.p.long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
.p.priv_class = &webm_dash_class,
.priv_data_size = sizeof(MatroskaDemuxContext),
.flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
.read_header = webm_dash_manifest_read_header,
.read_packet = webm_dash_manifest_read_packet,
.read_close = matroska_read_close,
};
#endif
const FFInputFormat ff_matroska_demuxer = …;