summaryrefslogtreecommitdiffstats
path: root/demux/demux_lavf.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux_lavf.c')
-rw-r--r--demux/demux_lavf.c660
1 files changed, 440 insertions, 220 deletions
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index bccc0c53a1..182642494e 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2004 Michael Niedermayer <michaelni@gmx.at>
- * Copyright (C) 2018 Google LLC
*
* This file is part of mpv.
*
@@ -30,13 +29,17 @@
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
+
#include <libavutil/avutil.h>
#include <libavutil/avstring.h>
-#include <libavutil/mathematics.h>
-#include <libavutil/replaygain.h>
-#include <libavutil/spherical.h>
#include <libavutil/display.h>
+#include <libavutil/dovi_meta.h>
+#include <libavutil/mathematics.h>
#include <libavutil/opt.h>
+#include <libavutil/pixdesc.h>
+#include <libavutil/replaygain.h>
+
+#include "audio/chmap_avchannel.h"
#include "common/msg.h"
#include "common/tags.h"
@@ -60,7 +63,7 @@
#endif
#define INITIAL_PROBE_SIZE STREAM_BUFFER_SIZE
-#define PROBE_BUF_SIZE FFMIN(STREAM_MAX_BUFFER_SIZE, 2 * 1024 * 1024)
+#define PROBE_BUF_SIZE (10 * 1024 * 1024)
// Should correspond to IO_BUFFER_SIZE in libavformat/aviobuf.c (not public)
@@ -74,48 +77,56 @@ struct demux_lavf_opts {
int probescore;
float analyzeduration;
int buffersize;
- int allow_mimetype;
+ bool allow_mimetype;
char *format;
char **avopts;
- int hacks;
+ bool hacks;
char *sub_cp;
int rtsp_transport;
+ int linearize_ts;
+ bool propagate_opts;
};
const struct m_sub_options demux_lavf_conf = {
.opts = (const m_option_t[]) {
- OPT_INTRANGE("demuxer-lavf-probesize", probesize, 0, 32, INT_MAX),
- OPT_CHOICE("demuxer-lavf-probe-info", probeinfo, 0,
- ({"no", 0}, {"yes", 1}, {"auto", -1}, {"nostreams", -2})),
- OPT_STRING("demuxer-lavf-format", format, 0),
- OPT_FLOATRANGE("demuxer-lavf-analyzeduration", analyzeduration, 0,
- 0, 3600),
- OPT_INTRANGE("demuxer-lavf-buffersize", buffersize, 0, 1,
- 10 * 1024 * 1024, OPTDEF_INT(BIO_BUFFER_SIZE)),
- OPT_FLAG("demuxer-lavf-allow-mimetype", allow_mimetype, 0),
- OPT_INTRANGE("demuxer-lavf-probescore", probescore, 0,
- 1, AVPROBE_SCORE_MAX),
- OPT_FLAG("demuxer-lavf-hacks", hacks, 0),
- OPT_KEYVALUELIST("demuxer-lavf-o", avopts, 0),
- OPT_STRING("sub-codepage", sub_cp, 0),
- OPT_CHOICE("rtsp-transport", rtsp_transport, 0,
- ({"lavf", 0},
- {"udp", 1},
- {"tcp", 2},
- {"http", 3})),
+ {"demuxer-lavf-probesize", OPT_INT(probesize), M_RANGE(32, INT_MAX)},
+ {"demuxer-lavf-probe-info", OPT_CHOICE(probeinfo,
+ {"no", 0}, {"yes", 1}, {"auto", -1}, {"nostreams", -2})},
+ {"demuxer-lavf-format", OPT_STRING(format)},
+ {"demuxer-lavf-analyzeduration", OPT_FLOAT(analyzeduration),
+ M_RANGE(0, 3600)},
+ {"demuxer-lavf-buffersize", OPT_INT(buffersize),
+ M_RANGE(1, 10 * 1024 * 1024), OPTDEF_INT(BIO_BUFFER_SIZE)},
+ {"demuxer-lavf-allow-mimetype", OPT_BOOL(allow_mimetype)},
+ {"demuxer-lavf-probescore", OPT_INT(probescore),
+ M_RANGE(1, AVPROBE_SCORE_MAX)},
+ {"demuxer-lavf-hacks", OPT_BOOL(hacks)},
+ {"demuxer-lavf-o", OPT_KEYVALUELIST(avopts)},
+ {"sub-codepage", OPT_STRING(sub_cp)},
+ {"rtsp-transport", OPT_CHOICE(rtsp_transport,
+ {"lavf", 0},
+ {"udp", 1},
+ {"tcp", 2},
+ {"http", 3},
+ {"udp_multicast", 4})},
+ {"demuxer-lavf-linearize-timestamps", OPT_CHOICE(linearize_ts,
+ {"no", 0}, {"auto", -1}, {"yes", 1})},
+ {"demuxer-lavf-propagate-opts", OPT_BOOL(propagate_opts)},
{0}
},
.size = sizeof(struct demux_lavf_opts),
.defaults = &(const struct demux_lavf_opts){
.probeinfo = -1,
- .allow_mimetype = 1,
- .hacks = 1,
+ .allow_mimetype = true,
+ .hacks = true,
// AVPROBE_SCORE_MAX/4 + 1 is the "recommended" limit. Below that, the
// user is supposed to retry with larger probe sizes until a higher
// value is reached.
.probescore = AVPROBE_SCORE_MAX/4 + 1,
.sub_cp = "auto",
.rtsp_transport = 2,
+ .linearize_ts = -1,
+ .propagate_opts = true,
},
};
@@ -129,17 +140,18 @@ struct format_hack {
bool max_probe : 1; // use probescore only if max. probe size reached
bool ignore : 1; // blacklisted
bool no_stream : 1; // do not wrap struct stream as AVIOContext
- bool use_stream_ids : 1; // export the native stream IDs
+ bool use_stream_ids : 1; // has a meaningful native stream IDs (export it)
bool fully_read : 1; // set demuxer.fully_read flag
bool detect_charset : 1; // format is a small text file, possibly not UTF8
- bool image_format : 1; // expected to contain exactly 1 frame
// Do not confuse player's position estimation (position is into external
// segment, with e.g. HLS, player knows about the playlist main file only).
bool clear_filepos : 1;
- bool ignore_start : 1;
- bool fix_editlists : 1;
+ bool linearize_audio_ts : 1;// compensate timestamp resets (audio only)
bool is_network : 1;
bool no_seek : 1;
+ bool no_pcm_seek : 1;
+ bool no_seek_on_no_duration : 1;
+ bool readall_on_no_streamseek : 1;
};
#define BLACKLIST(fmt) {fmt, .ignore = true}
@@ -160,18 +172,27 @@ static const struct format_hack format_hacks[] = {
{"sdp", .clear_filepos = true, .is_network = true, .no_seek = true},
{"mpeg", .use_stream_ids = true},
{"mpegts", .use_stream_ids = true},
-
- {"mp4", .skipinfo = true, .fix_editlists = true},
- {"matroska", .skipinfo = true},
+ {"mxf", .use_stream_ids = true},
+ {"avi", .use_stream_ids = true},
+ {"asf", .use_stream_ids = true},
+ {"mp4", .skipinfo = true, .no_pcm_seek = true, .use_stream_ids = true},
+ {"matroska", .skipinfo = true, .no_pcm_seek = true, .use_stream_ids = true},
{"v4l2", .no_seek = true},
+ {"rtsp", .no_seek_on_no_duration = true},
// In theory, such streams might contain timestamps, but virtually none do.
{"h264", .if_flags = AVFMT_NOTIMESTAMPS },
{"hevc", .if_flags = AVFMT_NOTIMESTAMPS },
+ {"vvc", .if_flags = AVFMT_NOTIMESTAMPS },
+
+ // Some Ogg shoutcast streams are essentially concatenated OGG files. They
+ // reset timestamps, which causes all sorts of problems.
+ {"ogg", .linearize_audio_ts = true, .use_stream_ids = true},
- // Rebasing start time to 0 is very weird with ogg shoutcast streams.
- {"ogg", .ignore_start = true},
+ // At some point, FFmpeg lost the ability to read gif from unseekable
+ // streams.
+ {"gif", .readall_on_no_streamseek = true},
TEXTSUB("aqtitle"), TEXTSUB("jacosub"), TEXTSUB("microdvd"),
TEXTSUB("mpl2"), TEXTSUB("mpsub"), TEXTSUB("pjs"), TEXTSUB("realtext"),
@@ -187,8 +208,6 @@ static const struct format_hack format_hacks[] = {
BLACKLIST("bin"),
// Useless, does not work with custom streams.
BLACKLIST("image2"),
- // Image demuxers ("<name>_pipe" is detected explicitly)
- {"image2pipe", .image_format = true},
{0}
};
@@ -197,32 +216,56 @@ struct nested_stream {
int64_t last_bytes;
};
+struct stream_info {
+ struct sh_stream *sh;
+ double last_key_pts;
+ double highest_pts;
+ double ts_offset;
+};
+
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(59, 10, 100)
+ #define HAVE_IO_CLOSE2 1
+#else
+ #define HAVE_IO_CLOSE2 0
+#endif
+
typedef struct lavf_priv {
struct stream *stream;
bool own_stream;
char *filename;
struct format_hack format_hack;
- AVInputFormat *avif;
+ const AVInputFormat *avif;
int avif_flags;
AVFormatContext *avfc;
- bstr init_fragment;
- int64_t stream_pos;
AVIOContext *pb;
- struct sh_stream **streams; // NULL for unknown streams
+ struct stream_info **streams; // NULL for unknown streams
int num_streams;
char *mime_type;
double seek_delay;
- bool optical_crap_hack;
struct demux_lavf_opts *opts;
- double mf_fps;
+
+ bool pcm_seek_hack_disabled;
+ AVStream *pcm_seek_hack;
+ int pcm_seek_hack_packet_size;
+
+ int linearize_ts;
+ bool any_ts_fixed;
+
+ int retry_counter;
+
+ AVDictionary *av_opts;
// Proxying nested streams.
struct nested_stream *nested;
int num_nested;
int (*default_io_open)(struct AVFormatContext *s, AVIOContext **pb,
const char *url, int flags, AVDictionary **options);
+#if HAVE_IO_CLOSE2
+ int (*default_io_close2)(struct AVFormatContext *s, AVIOContext *pb);
+#else
void (*default_io_close)(struct AVFormatContext *s, AVIOContext *pb);
+#endif
} lavf_priv_t;
static void update_read_stats(struct demuxer *demuxer)
@@ -235,7 +278,7 @@ static void update_read_stats(struct demuxer *demuxer)
int64_t cur = nest->id->bytes_read;
int64_t new = cur - nest->last_bytes;
nest->last_bytes = cur;
- demuxer->total_unbuffered_read_bytes += new;
+ demux_report_unbuffered_read_bytes(demuxer, new);
}
}
@@ -261,16 +304,10 @@ static int mp_read(void *opaque, uint8_t *buf, int size)
struct demuxer *demuxer = opaque;
lavf_priv_t *priv = demuxer->priv;
struct stream *stream = priv->stream;
- int ret;
+ if (!stream)
+ return 0;
- if (priv->stream_pos < priv->init_fragment.len) {
- ret = MPMIN(size, priv->init_fragment.len - priv->stream_pos);
- memcpy(buf, priv->init_fragment.start + priv->stream_pos, ret);
- priv->stream_pos += ret;
- } else {
- ret = stream_read_partial(stream, buf, size);
- priv->stream_pos = priv->init_fragment.len + stream_tell(stream);
- }
+ int ret = stream_read_partial(stream, buf, size);
MP_TRACE(demuxer, "%d=mp_read(%p, %p, %d), pos: %"PRId64", eof:%d\n",
ret, stream, buf, size, stream_tell(stream), stream->eof);
@@ -282,6 +319,8 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
struct demuxer *demuxer = opaque;
lavf_priv_t *priv = demuxer->priv;
struct stream *stream = priv->stream;
+ if (!stream)
+ return -1;
MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %s)\n", stream, pos,
whence == SEEK_END ? "end" :
@@ -291,12 +330,11 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
int64_t end = stream_get_size(stream);
if (end < 0)
return -1;
- end += priv->init_fragment.len;
if (whence == AVSEEK_SIZE)
return end;
pos += end;
} else if (whence == SEEK_CUR) {
- pos += priv->stream_pos;
+ pos += stream_tell(stream);
} else if (whence != SEEK_SET) {
return -1;
}
@@ -304,23 +342,12 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
if (pos < 0)
return -1;
- int64_t stream_target = pos - priv->init_fragment.len;
- bool seek_before = stream_target < 0;
- if (seek_before)
- stream_target = 0; // within init segment - seek real stream to 0
-
int64_t current_pos = stream_tell(stream);
- if (stream_seek(stream, stream_target) == 0) {
+ if (stream_seek(stream, pos) == 0) {
stream_seek(stream, current_pos);
return -1;
}
- if (seek_before) {
- priv->stream_pos = pos;
- } else {
- priv->stream_pos = priv->init_fragment.len + stream_tell(stream);
- }
-
return pos;
}
@@ -336,7 +363,7 @@ static int64_t mp_read_seek(void *opaque, int stream_idx, int64_t ts, int flags)
.flags = flags,
};
- if (stream_control(stream, STREAM_CTRL_AVSEEK, &cmd) == STREAM_OK) {
+ if (stream && stream_control(stream, STREAM_CTRL_AVSEEK, &cmd) == STREAM_OK) {
stream_drop_buffers(stream);
return 0;
}
@@ -356,7 +383,7 @@ static void convert_charset(struct demuxer *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
char *cp = priv->opts->sub_cp;
- if (!cp || mp_charset_is_utf8(cp))
+ if (!cp || !cp[0] || mp_charset_is_utf8(cp))
return;
bstr data = stream_read_complete(priv->stream, NULL, 128 * 1024 * 1024);
if (!data.start) {
@@ -376,7 +403,7 @@ static void convert_charset(struct demuxer *demuxer)
data = conv;
}
if (data.start) {
- priv->stream = open_memory_stream(data.start, data.len);
+ priv->stream = stream_memory_open(demuxer->global, data.start, data.len);
priv->own_stream = true;
}
talloc_free(alloc);
@@ -420,7 +447,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
if (!lavfdopts->allow_mimetype || !mime_type)
mime_type = "";
- AVInputFormat *forced_format = NULL;
+ const AVInputFormat *forced_format = NULL;
const char *format = lavfdopts->format;
if (!format)
format = s->lavf_type;
@@ -438,11 +465,22 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
}
}
+ // HLS streams do not seem to be well tagged, so matching by MIME type is
+ // not enough - we need to strip the query string and match by their
+ // extensions. We also pass jpg filenames to fix issues like #13192 (jpgs
+ // being treated as videos due to a bogus second frame) and #13431 (jpgs
+ // misdetected as mpegts). We don't pass filenames otherwise to not
+ // misdetect files with a wrong extension, as described in 74e62ed2d1.
+ bstr ext = bstr_get_ext(mp_is_url(bstr0(priv->filename))
+ ? bstr_split(bstr0(priv->filename), "?#", NULL)
+ : bstr0(priv->filename));
AVProbeData avpd = {
- // Disable file-extension matching with normal checks
- .filename = priv->filename,
+ .filename = !bstrcasecmp0(ext, "m3u8") || !bstrcasecmp0(ext, "m3u") ||
+ !bstrcasecmp0(ext, "jpg") || !bstrcasecmp0(ext, "jpeg") ||
+ check <= DEMUX_CHECK_REQUEST ? priv->filename : "",
.buf_size = 0,
.buf = av_mallocz(PROBE_BUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE),
+ .mime_type = lavfdopts->allow_mimetype ? mime_type : NULL,
};
if (!avpd.buf)
return -1;
@@ -457,15 +495,10 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
} else {
int nsize = av_clip(avpd.buf_size * 2, INITIAL_PROBE_SIZE,
PROBE_BUF_SIZE);
- bstr buf = stream_peek(s, nsize);
- if (demuxer->params && demuxer->params->init_fragment.len) {
- buf = demuxer->params->init_fragment;
- buf.len = MPMIN(buf.len, nsize);
- }
- if (buf.len <= avpd.buf_size)
+ nsize = stream_read_peek(s, avpd.buf, nsize);
+ if (nsize <= avpd.buf_size)
final_probe = true;
- memcpy(avpd.buf, buf.start, buf.len);
- avpd.buf_size = buf.len;
+ avpd.buf_size = nsize;
priv->avif = av_probe_input_format2(&avpd, avpd.buf_size > 0, &score);
}
@@ -485,6 +518,10 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
break;
}
+ // AVIF always needs to find stream info
+ if (bstrcasecmp0(ext, "avif") == 0)
+ priv->format_hack.skipinfo = false;
+
if (score >= lavfdopts->probescore)
break;
@@ -510,14 +547,13 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
return -1;
}
- if (bstr_endswith0(bstr0(priv->avif->name), "_pipe")) {
- MP_VERBOSE(demuxer, "Assuming this is an image format.\n");
- priv->format_hack.image_format = true;
- }
-
if (lavfdopts->hacks)
priv->avif_flags = priv->avif->flags | priv->format_hack.if_flags;
+ priv->linearize_ts = lavfdopts->linearize_ts;
+ if (priv->linearize_ts < 0 && !priv->format_hack.linearize_audio_ts)
+ priv->linearize_ts = 0;
+
demuxer->filetype = priv->avif->name;
if (priv->format_hack.detect_charset)
@@ -567,7 +603,7 @@ static void select_tracks(struct demuxer *demuxer, int start)
{
lavf_priv_t *priv = demuxer->priv;
for (int n = start; n < priv->num_streams; n++) {
- struct sh_stream *stream = priv->streams[n];
+ struct sh_stream *stream = priv->streams[n]->sh;
AVStream *st = priv->avfc->streams[n];
bool selected = stream && demux_stream_is_selected(stream) &&
!stream->attached_picture;
@@ -578,16 +614,25 @@ static void select_tracks(struct demuxer *demuxer, int start)
static void export_replaygain(demuxer_t *demuxer, struct sh_stream *sh,
AVStream *st)
{
- for (int i = 0; i < st->nb_side_data; i++) {
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 15, 100)
+ AVPacketSideData *side_data = st->codecpar->coded_side_data;
+ int nb_side_data = st->codecpar->nb_coded_side_data;
+#else
+ AVPacketSideData *side_data = st->side_data;
+ int nb_side_data = st->nb_side_data;
+#endif
+ for (int i = 0; i < nb_side_data; i++) {
AVReplayGain *av_rgain;
struct replaygain_data *rgain;
- AVPacketSideData *src_sd = &st->side_data[i];
+ AVPacketSideData *src_sd = &side_data[i];
if (src_sd->type != AV_PKT_DATA_REPLAYGAIN)
continue;
av_rgain = (AVReplayGain*)src_sd->data;
rgain = talloc_ptrtype(demuxer, rgain);
+ rgain->track_gain = rgain->album_gain = 0;
+ rgain->track_peak = rgain->album_peak = 1;
// Set values in *rgain, using track gain as a fallback for album gain
// if the latter is not present. This behavior matches that in
@@ -631,6 +676,33 @@ static int dict_get_decimal(AVDictionary *dict, const char *entry, int def)
return def;
}
+static bool is_image(AVStream *st, bool attached_picture, const AVInputFormat *avif)
+{
+ return st->nb_frames <= 1 && (
+ attached_picture ||
+ bstr_endswith0(bstr0(avif->name), "_pipe") ||
+ strcmp(avif->name, "alias_pix") == 0 ||
+ strcmp(avif->name, "gif") == 0 ||
+ strcmp(avif->name, "ico") == 0 ||
+ strcmp(avif->name, "image2pipe") == 0 ||
+ (st->codecpar->codec_id == AV_CODEC_ID_AV1 && st->nb_frames == 1)
+ );
+}
+
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 15, 100)
+static inline const uint8_t *mp_av_stream_get_side_data(const AVStream *st,
+ enum AVPacketSideDataType type)
+{
+ const AVPacketSideData *sd;
+ sd = av_packet_side_data_get(st->codecpar->coded_side_data,
+ st->codecpar->nb_coded_side_data,
+ type);
+ return sd ? sd->data : NULL;
+}
+#else
+#define mp_av_stream_get_side_data(st, type) av_stream_get_side_data(st, type, NULL)
+#endif
+
static void handle_new_stream(demuxer_t *demuxer, int i)
{
lavf_priv_t *priv = demuxer->priv;
@@ -644,12 +716,25 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
case AVMEDIA_TYPE_AUDIO: {
sh = demux_alloc_sh_stream(STREAM_AUDIO);
+#if !HAVE_AV_CHANNEL_LAYOUT
// probably unneeded
mp_chmap_set_unknown(&sh->codec->channels, codec->channels);
if (codec->channel_layout)
mp_chmap_from_lavc(&sh->codec->channels, codec->channel_layout);
+#else
+ if (!mp_chmap_from_av_layout(&sh->codec->channels, &codec->ch_layout)) {
+ char layout[128] = {0};
+ MP_WARN(demuxer,
+ "Failed to convert channel layout %s to mpv one!\n",
+ av_channel_layout_describe(&codec->ch_layout,
+ layout, 128) < 0 ?
+ "undefined" : layout);
+ }
+#endif
+
sh->codec->samplerate = codec->sample_rate;
sh->codec->bitrate = codec->bit_rate;
+ sh->codec->format_name = talloc_strdup(sh, av_get_sample_fmt_name(codec->format));
double delay = 0;
if (codec->sample_rate > 0)
@@ -658,6 +743,8 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
export_replaygain(demuxer, sh, st);
+ sh->seek_preroll = delay;
+
break;
}
case AVMEDIA_TYPE_VIDEO: {
@@ -675,31 +762,40 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
}
}
+ if (!sh->attached_picture) {
+ // A real video stream probably means it's a packet based format.
+ priv->pcm_seek_hack_disabled = true;
+ priv->pcm_seek_hack = NULL;
+ // Also, we don't want to do this shit for ogv videos.
+ if (priv->linearize_ts < 0)
+ priv->linearize_ts = 0;
+ }
+
sh->codec->disp_w = codec->width;
sh->codec->disp_h = codec->height;
+ sh->codec->format_name = talloc_strdup(sh, av_get_pix_fmt_name(codec->format));
if (st->avg_frame_rate.num)
sh->codec->fps = av_q2d(st->avg_frame_rate);
- if (priv->format_hack.image_format)
- sh->codec->fps = priv->mf_fps;
+ if (is_image(st, sh->attached_picture, priv->avif)) {
+ MP_VERBOSE(demuxer, "Assuming this is an image format.\n");
+ sh->image = true;
+ sh->codec->fps = demuxer->opts->mf_fps;
+ }
sh->codec->par_w = st->sample_aspect_ratio.num;
sh->codec->par_h = st->sample_aspect_ratio.den;
- uint8_t *sd = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
+ const uint8_t *sd = mp_av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX);
if (sd) {
- double r = av_display_rotation_get((uint32_t *)sd);
+ double r = av_display_rotation_get((int32_t *)sd);
if (!isnan(r))
sh->codec->rotate = (((int)(-r) % 360) + 360) % 360;
}
- sd = av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, NULL);
- if (sd) {
- AVSphericalMapping *sp = (void *)sd;
- struct mp_spherical_params *mpsp = &sh->codec->spherical;
- mpsp->type = sp->projection == AV_SPHERICAL_EQUIRECTANGULAR ?
- MP_SPHERICAL_EQUIRECTANGULAR : MP_SPHERICAL_UNKNOWN;
- mpsp->ref_angles[0] = sp->yaw / (float)(1 << 16);
- mpsp->ref_angles[1] = sp->pitch / (float)(1 << 16);
- mpsp->ref_angles[2] = sp->roll / (float)(1 << 16);
+ if ((sd = mp_av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF))) {
+ const AVDOVIDecoderConfigurationRecord *cfg = (void *) sd;
+ MP_VERBOSE(demuxer, "Found Dolby Vision config record: profile "
+ "%d level %d\n", cfg->dv_profile, cfg->dv_level);
+ av_format_inject_global_side_data(avfc);
}
// This also applies to vfw-muxed mkv, but we can't detect these easily.
@@ -740,8 +836,14 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
default: ;
}
+ struct stream_info *info = talloc_zero(priv, struct stream_info);
+ *info = (struct stream_info){
+ .sh = sh,
+ .last_key_pts = MP_NOPTS_VALUE,
+ .highest_pts = MP_NOPTS_VALUE,
+ };
assert(priv->num_streams == i); // directly mapped
- MP_TARRAY_APPEND(priv, priv->streams, priv->num_streams, sh);
+ MP_TARRAY_APPEND(priv, priv->streams, priv->num_streams, info);
if (sh) {
sh->ff_index = st->index;
@@ -778,11 +880,31 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
if (lang && lang->value && strcmp(lang->value, "und") != 0)
sh->lang = talloc_strdup(sh, lang->value);
sh->hls_bitrate = dict_get_decimal(st->metadata, "variant_bitrate", 0);
- if (!sh->title && sh->hls_bitrate > 0)
- sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
+ AVProgram *prog = av_find_program_from_stream(avfc, NULL, i);
+ if (prog)
+ sh->program_id = prog->id;
sh->missing_timestamps = !!(priv->avif_flags & AVFMT_NOTIMESTAMPS);
- mp_tags_copy_from_av_dictionary(sh->tags, st->metadata);
+ mp_tags_move_from_av_dictionary(sh->tags, &st->metadata);
demux_add_sh_stream(demuxer, sh);
+
+ // Unfortunately, there is no better way to detect PCM codecs, other
+ // than listing them all manually. (Or other "frameless" codecs. Or
+ // rather, codecs with frames so small libavformat will put multiple of
+ // them into a single packet, but not preserve these artificial packet
+ // boundaries on seeking.)
+ if (sh->codec->codec && strncmp(sh->codec->codec, "pcm_", 4) == 0 &&
+ codec->block_align && !priv->pcm_seek_hack_disabled &&
+ priv->opts->hacks && !priv->format_hack.no_pcm_seek &&
+ st->time_base.num == 1 && st->time_base.den == codec->sample_rate)
+ {
+ if (priv->pcm_seek_hack) {
+ // More than 1 audio stream => usually doesn't apply.
+ priv->pcm_seek_hack_disabled = true;
+ priv->pcm_seek_hack = NULL;
+ } else {
+ priv->pcm_seek_hack = st;
+ }
+ }
}
select_tracks(demuxer, i);
@@ -800,20 +922,10 @@ static void update_metadata(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
if (priv->avfc->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
- mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->metadata);
+ mp_tags_move_from_av_dictionary(demuxer->metadata, &priv->avfc->metadata);
priv->avfc->event_flags = 0;
demux_metadata_changed(demuxer);
}
-
- for (int n = 0; n < priv->num_streams; n++) {
- AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
- if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
- st->event_flags = 0;
- struct mp_tags *tags = talloc_zero(NULL, struct mp_tags);
- mp_tags_copy_from_av_dictionary(tags, st->metadata);
- demux_set_stream_tags(demuxer, priv->streams[n], tags);
- }
- }
}
static int interrupt_cb(void *ctx)
@@ -836,8 +948,27 @@ static int nested_io_open(struct AVFormatContext *s, AVIOContext **pb,
struct demuxer *demuxer = s->opaque;
lavf_priv_t *priv = demuxer->priv;
+ if (priv->opts->propagate_opts) {
+ // Copy av_opts to options, but only entries that are not present in
+ // options. (Hope this will break less by not overwriting important
+ // settings.)
+ AVDictionaryEntry *cur = NULL;
+ while ((cur = av_dict_get(priv->av_opts, "", cur, AV_DICT_IGNORE_SUFFIX)))
+ {
+ if (!*options || !av_dict_get(*options, cur->key, NULL, 0)) {
+ MP_TRACE(demuxer, "Nested option: '%s'='%s'\n",
+ cur->key, cur->value);
+ av_dict_set(options, cur->key, cur->value, 0);
+ } else {
+ MP_TRACE(demuxer, "Skipping nested option: '%s'\n", cur->key);
+ }
+ }
+ }
+
int r = priv->default_io_open(s, pb, url, flags, options);
if (r >= 0) {
+ if (options)
+ mp_avdict_print_unset(demuxer->log, MSGL_TRACE, *options);
struct nested_stream nest = {
.id = *pb,
};
@@ -846,7 +977,11 @@ static int nested_io_open(struct AVFormatContext *s, AVIOContext **pb,
return r;
}
+#if HAVE_IO_CLOSE2
+static int nested_io_close2(struct AVFormatContext *s, AVIOContext *pb)
+#else
static void nested_io_close(struct AVFormatContext *s, AVIOContext *pb)
+#endif
{
struct demuxer *demuxer = s->opaque;
lavf_priv_t *priv = demuxer->priv;
@@ -858,39 +993,35 @@ static void nested_io_close(struct AVFormatContext *s, AVIOContext *pb)
}
}
-
+#if HAVE_IO_CLOSE2
+ return priv->default_io_close2(s, pb);
+#else
priv->default_io_close(s, pb);
+#endif
}
static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
{
- AVFormatContext *avfc;
+ AVFormatContext *avfc = NULL;
AVDictionaryEntry *t = NULL;
float analyze_duration = 0;
lavf_priv_t *priv = talloc_zero(NULL, lavf_priv_t);
+ AVDictionary *dopts = NULL;
+
demuxer->priv = priv;
priv->stream = demuxer->stream;
priv->opts = mp_get_config_group(priv, demuxer->global, &demux_lavf_conf);
struct demux_lavf_opts *lavfdopts = priv->opts;
- int index_mode;
- mp_read_option_raw(demuxer->global, "index", &m_option_type_choice,
- &index_mode);
- mp_read_option_raw(demuxer->global, "mf-fps", &m_option_type_double,
- &priv->mf_fps);
-
if (lavf_check_file(demuxer, check) < 0)
- return -1;
-
- if (demuxer->params)
- priv->init_fragment = bstrdup(priv, demuxer->params->init_fragment);
+ goto fail;
avfc = avformat_alloc_context();
if (!avfc)
- return -1;
+ goto fail;
- if (index_mode != 1)
+ if (demuxer->opts->index_mode != 1)
avfc->flags |= AVFMT_FLAG_IGNIDX;
if (lavfdopts->probesize) {
@@ -910,21 +1041,20 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
"analyzeduration to %f\n", analyze_duration);
}
- AVDictionary *dopts = NULL;
-
if ((priv->avif_flags & AVFMT_NOFILE) || priv->format_hack.no_stream) {
- mp_setup_av_network_options(&dopts, demuxer->global, demuxer->log);
+ mp_setup_av_network_options(&dopts, priv->avif->name,
+ demuxer->global, demuxer->log);
// This might be incorrect.
demuxer->seekable = true;
} else {
void *buffer = av_malloc(lavfdopts->buffersize);
if (!buffer)
- return -1;
+ goto fail;
priv->pb = avio_alloc_context(buffer, lavfdopts->buffersize, 0,
demuxer, mp_read, NULL, mp_seek);
if (!priv->pb) {
av_free(buffer);
- return -1;
+ goto fail;
}
priv->pb->read_seek = mp_read_seek;
priv->pb->seekable = demuxer->seekable ? AVIO_SEEKABLE_NORMAL : 0;
@@ -940,6 +1070,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
case 1: transport = "udp"; break;
case 2: transport = "tcp"; break;
case 3: transport = "http"; break;
+ case 4: transport = "udp_multicast"; break;
}
if (transport)
av_dict_set(&dopts, "rtsp_transport", transport, 0);
@@ -947,9 +1078,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
guess_and_set_vobsub_name(demuxer, &dopts);
- if (priv->format_hack.fix_editlists)
- av_dict_set(&dopts, "advanced_editlist", "0", 0);
-
avfc->interrupt_callback = (AVIOInterruptCB){
.callback = interrupt_cb,
.opaque = demuxer,
@@ -958,19 +1086,42 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
avfc->opaque = demuxer;
if (demuxer->access_references) {
priv->default_io_open = avfc->io_open;
- priv->default_io_close = avfc->io_close;
avfc->io_open = nested_io_open;
+#if HAVE_IO_CLOSE2
+ priv->default_io_close2 = avfc->io_close2;
+ avfc->io_close2 = nested_io_close2;
+#else
+ priv->default_io_close = avfc->io_close;
avfc->io_close = nested_io_close;
+#endif
} else {
avfc->io_open = block_io_open;
}
mp_set_avdict(&dopts, lavfdopts->avopts);
+ if (av_dict_copy(&priv->av_opts, dopts, 0) < 0) {
+ MP_ERR(demuxer, "av_dict_copy() failed\n");
+ goto fail;
+ }
+
+ if (priv->format_hack.readall_on_no_streamseek && priv->pb &&
+ !priv->pb->seekable)
+ {
+ MP_VERBOSE(demuxer, "Non-seekable demuxer pre-read hack...\n");
+ // Read incremental to avoid unnecessary large buffer sizes.
+ int r = 0;
+ for (int n = 16; n < 29; n++) {
+ r = stream_peek(priv->stream, 1 << n);
+ if (r < (1 << n))
+ break;
+ }
+ MP_VERBOSE(demuxer, "...did read %d bytes.\n", r);
+ }
+
if (avformat_open_input(&avfc, priv->filename, priv->avif, &dopts) < 0) {
MP_ERR(demuxer, "avformat_open_input() failed\n");
- av_dict_free(&dopts);
- return -1;
+ goto fail;
}
mp_avdict_print_unset(demuxer->log, MSGL_V, dopts);
@@ -988,7 +1139,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
if (probeinfo) {
if (avformat_find_stream_info(avfc, NULL) < 0) {
MP_ERR(demuxer, "av_find_stream_info() failed\n");
- return -1;
+ goto fail;
}
MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
@@ -1000,17 +1151,17 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
t = av_dict_get(c->metadata, "title", NULL, 0);
int index = demuxer_add_chapter(demuxer, t ? t->value : "",
c->start * av_q2d(c->time_base), i);
- mp_tags_copy_from_av_dictionary(demuxer->chapters[index].metadata, c->metadata);
+ mp_tags_move_from_av_dictionary(demuxer->chapters[index].metadata, &c->metadata);
}
add_new_streams(demuxer);
- mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
+ mp_tags_move_from_av_dictionary(demuxer->metadata, &avfc->metadata);
demuxer->ts_resets_possible =
priv->avif_flags & (AVFMT_TS_DISCONT | AVFMT_NOTIMESTAMPS);
- if (avfc->start_time != AV_NOPTS_VALUE && !priv->format_hack.ignore_start)
+ if (avfc->start_time != AV_NOPTS_VALUE)
demuxer->start_time = avfc->start_time / (double)AV_TIME_BASE;
demuxer->fully_read = priv->format_hack.fully_read;
@@ -1023,25 +1174,28 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->is_network |= priv->format_hack.is_network;
demuxer->seekable &= !priv->format_hack.no_seek;
- if (priv->avfc->duration > 0) {
- demuxer->duration = (double)priv->avfc->duration / AV_TIME_BASE;
- } else {
- double total_duration = 0;
- double av_duration = 0;
- for (int n = 0; n < priv->avfc->nb_streams; n++) {
- AVStream *st = priv->avfc->streams[n];
- if (st->duration <= 0)
- continue;
- double f_duration = st->duration * av_q2d(st->time_base);
- total_duration = MPMAX(total_duration, f_duration);
- if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
- st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
- av_duration = MPMAX(av_duration, f_duration);
- }
- double duration = av_duration > 0 ? av_duration : total_duration;
- if (duration > 0)
- demuxer->duration = duration;
+ // We initially prefer track durations over container durations because they
+ // have a higher degree of precision over the container duration which are
+ // only accurate to the 6th decimal place. This is probably a lavf bug.
+ double total_duration = -1;
+ double av_duration = -1;
+ for (int n = 0; n < priv->avfc->nb_streams; n++) {
+ AVStream *st = priv->avfc->streams[n];
+ if (st->duration <= 0)
+ continue;
+ double f_duration = st->duration * av_q2d(st->time_base);
+ total_duration = MPMAX(total_duration, f_duration);
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
+ st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ av_duration = MPMAX(av_duration, f_duration);
}
+ double duration = av_duration > 0 ? av_duration : total_duration;
+ if (duration <= 0 && priv->avfc->duration > 0)
+ duration = (double)priv->avfc->duration / AV_TIME_BASE;
+ demuxer->duration = duration;
+
+ if (demuxer->duration < 0 && priv->format_hack.no_seek_on_no_duration)
+ demuxer->seekable = false;
// In some cases, libavformat will export bogus bullshit timestamps anyway,
// such as with mjpeg.
@@ -1050,23 +1204,32 @@