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.c350
1 files changed, 233 insertions, 117 deletions
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index f4bbfc5739..75a47ed3ad 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -29,12 +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/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"
@@ -72,47 +77,48 @@ 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;
- int propagate_opts;
+ 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})),
- OPT_CHOICE("demuxer-lavf-linearize-timestamps", linearize_ts, 0,
- ({"no", 0}, {"auto", -1}, {"yes", 1})),
- OPT_FLAG("demuxer-lavf-propagate-opts", propagate_opts, 0),
+ {"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.
@@ -120,7 +126,7 @@ const struct m_sub_options demux_lavf_conf = {
.sub_cp = "auto",
.rtsp_transport = 2,
.linearize_ts = -1,
- .propagate_opts = 1,
+ .propagate_opts = true,
},
};
@@ -137,15 +143,15 @@ struct format_hack {
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 linearize_audio_ts : 1;// compensate timestamp resets (audio only)
- bool fix_editlists : 1;
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}
@@ -169,11 +175,11 @@ static const struct format_hack format_hacks[] = {
{"mxf", .use_stream_ids = true},
{"avi", .use_stream_ids = true},
{"asf", .use_stream_ids = true},
- {"mp4", .skipinfo = true, .fix_editlists = true, .no_pcm_seek = true,
- .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 },
@@ -183,6 +189,10 @@ static const struct format_hack format_hacks[] = {
// reset timestamps, which causes all sorts of problems.
{"ogg", .linearize_audio_ts = true, .use_stream_ids = 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"),
TEXTSUB("sami"), TEXTSUB("srt"), TEXTSUB("stl"), TEXTSUB("subviewer"),
@@ -197,8 +207,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}
};
@@ -214,12 +222,18 @@ struct stream_info {
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;
AVIOContext *pb;
@@ -229,7 +243,6 @@ typedef struct lavf_priv {
double seek_delay;
struct demux_lavf_opts *opts;
- double mf_fps;
bool pcm_seek_hack_disabled;
AVStream *pcm_seek_hack;
@@ -238,6 +251,8 @@ typedef struct lavf_priv {
int linearize_ts;
bool any_ts_fixed;
+ int retry_counter;
+
AVDictionary *av_opts;
// Proxying nested streams.
@@ -245,7 +260,11 @@ typedef struct lavf_priv {
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)
@@ -255,16 +274,10 @@ static void update_read_stats(struct demuxer *demuxer)
for (int n = 0; n < priv->num_nested; n++) {
struct nested_stream *nest = &priv->nested[n];
-#if !HAVE_FFMPEG_STRICT_ABI
- // Note: accessing the bytes_read field is not allowed by FFmpeg's API.
- // This is fully intentional - there is no other way to get this
- // information (not even by custom I/O, because the connection reuse
- // mechanism by the HLS demuxer would get disabled).
int64_t cur = nest->id->bytes_read;
int64_t new = cur - nest->last_bytes;
nest->last_bytes = cur;
demux_report_unbuffered_read_bytes(demuxer, new);
-#endif
}
}
@@ -433,7 +446,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;
@@ -451,11 +464,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;
@@ -493,6 +517,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;
@@ -518,11 +546,6 @@ 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;
@@ -590,16 +613,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
@@ -643,6 +675,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;
@@ -656,12 +715,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)
@@ -700,20 +772,31 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
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;
}
+ 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.
sh->codec->avi_dts = matches_avinputformat_name(priv, "avi");
@@ -796,10 +879,11 @@ 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
@@ -837,7 +921,7 @@ 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);
}
@@ -892,7 +976,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;
@@ -904,36 +992,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;
+ 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) {
@@ -953,8 +1040,6 @@ 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, priv->avif->name,
demuxer->global, demuxer->log);
@@ -963,12 +1048,12 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
} 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;
@@ -984,6 +1069,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);
@@ -991,9 +1077,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,
@@ -1002,9 +1085,14 @@ 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;
}
@@ -1012,14 +1100,27 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
mp_set_avdict(&dopts, lavfdopts->avopts);
if (av_dict_copy(&priv->av_opts, dopts, 0) < 0) {
- av_dict_free(&dopts);
- return -1;
+ 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);
@@ -1037,7 +1138,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
@@ -1049,12 +1150,12 @@ 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);
@@ -1072,25 +1173,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.
@@ -1099,7 +1203,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
"This format is marked by FFmpeg as having no timestamps!\n"
"FFmpeg will likely make up its own broken timestamps. For\n"
"video streams you can correct this with:\n"
- " --no-correct-pts --fps=VALUE\n"
+ " --no-correct-pts --container-fps-override=VALUE\n"
"with VALUE being the real framerate of the stream. You can\n"
"expect seeking and buffering estimation to be generally\n"
"broken as well.\n");
@@ -1114,6 +1218,13 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
}
return 0;
+
+fail:
+ if (!priv->avfc)
+ avformat_free_context(avfc);
+ av_dict_free(&dopts);
+
+ return -1;
}
static bool demux_lavf_read_packet(struct demuxer *demux,
@@ -1126,13 +1237,17 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
update_read_stats(demux);
if (r < 0) {
av_packet_unref(pkt);
- if (r == AVERROR(EAGAIN))
- return true;
if (r == AVERROR_EOF)
return false;
MP_WARN(demux, "error reading packet: %s.\n", av_err2str(r));
- return false;
+ if (priv->retry_counter >= 10) {
+ MP_ERR(demux, "...treating it as fatal error.\n");
+ return false;
+ }
+ priv->retry_counter += 1;
+ return true;
}
+ priv->retry_counter = 0;
add_new_streams(demux);
update_metadata(demux);
@@ -1161,8 +1276,6 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
dp->duration = pkt->duration * av_q2d(st->time_base);
dp->pos = pkt->pos;
dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
- if (pkt->flags & AV_PKT_FLAG_DISCARD)
- MP_ERR(demux, "Edit lists are not correctly supported (FFmpeg issue).\n");
av_packet_unref(pkt);
if (priv->format_hack.clear_filepos)
@@ -1197,7 +1310,7 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
if (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);
+ mp_tags_move_from_av_dictionary(tags, &st->metadata);
double pts = MP_PTS_OR_DEF(dp->pts, dp->dts);
demux_stream_tags_changed(demux, stream, tags, pts);
}
@@ -1251,10 +1364,11 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
if (priv->pcm_seek_hack && !priv->pcm_seek_hack_packet_size) {
// This might for example be the initial seek. Fuck it up like the
// bullshit it is.
- AVPacket pkt = {0};
- if (av_read_frame(priv->avfc, &pkt) >= 0)
- priv->pcm_seek_hack_packet_size = pkt.size;
- av_packet_unref(&pkt);
+ AVPacket *pkt = av_packet_alloc();
+ MP_HANDLE_OOM(pkt);
+ if (av_read_frame(priv->avfc, pkt) >= 0)
+ priv->pcm_seek_hack_packet_size = pkt->size;
+ av_packet_free(&pkt);
add_new_streams(demuxer);
}
if (priv->pcm_seek_hack && priv->pcm_seek_hack_packet_size &&
@@ -1322,6 +1436,8 @@ static void demux_close_lavf(demuxer_t *demuxer)
}
if (priv->own_stream)
free_stream(priv->stream);
+ if (priv->av_opts)
+ av_dict_free(&priv->av_opts);
talloc_free(priv);
demuxer->priv = NULL;
}