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.c217
1 files changed, 138 insertions, 79 deletions
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 2b65471b02..182642494e 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -29,14 +29,15 @@
#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/opt.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"
@@ -76,14 +77,14 @@ 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 = {
@@ -96,10 +97,10 @@ const struct m_sub_options demux_lavf_conf = {
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_FLAG(allow_mimetype)},
+ {"demuxer-lavf-allow-mimetype", OPT_BOOL(allow_mimetype)},
{"demuxer-lavf-probescore", OPT_INT(probescore),
M_RANGE(1, AVPROBE_SCORE_MAX)},
- {"demuxer-lavf-hacks", OPT_FLAG(hacks)},
+ {"demuxer-lavf-hacks", OPT_BOOL(hacks)},
{"demuxer-lavf-o", OPT_KEYVALUELIST(avopts)},
{"sub-codepage", OPT_STRING(sub_cp)},
{"rtsp-transport", OPT_CHOICE(rtsp_transport,
@@ -110,14 +111,14 @@ const struct m_sub_options demux_lavf_conf = {
{"udp_multicast", 4})},
{"demuxer-lavf-linearize-timestamps", OPT_CHOICE(linearize_ts,
{"no", 0}, {"auto", -1}, {"yes", 1})},
- {"demuxer-lavf-propagate-opts", OPT_FLAG(propagate_opts)},
+ {"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.
@@ -125,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,
},
};
@@ -146,7 +147,6 @@ struct format_hack {
// 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;
@@ -175,8 +175,7 @@ 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},
@@ -185,6 +184,7 @@ static const struct format_hack format_hacks[] = {
// 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.
@@ -223,6 +223,12 @@ 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;
@@ -238,7 +244,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;
@@ -256,7 +261,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)
@@ -456,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 = check <= DEMUX_CHECK_REQUEST ? 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;
@@ -498,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;
@@ -590,10 +614,17 @@ 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;
@@ -652,11 +683,26 @@ static bool is_image(AVStream *st, bool attached_picture, const AVInputFormat *a
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;
@@ -688,6 +734,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
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)
@@ -726,24 +773,25 @@ 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 (is_image(st, sh->attached_picture, priv->avif)) {
MP_VERBOSE(demuxer, "Assuming this is an image format.\n");
sh->image = true;
- sh->codec->fps = priv->mf_fps;
+ 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 = av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL))) {
+ 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);
@@ -832,8 +880,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);
+ 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
@@ -871,7 +922,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);
}
@@ -926,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;
@@ -938,36 +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;
+ 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) {
@@ -987,8 +1041,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);
@@ -997,12 +1049,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;
@@ -1026,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,
@@ -1037,9 +1086,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;
}
@@ -1047,8 +1101,8 @@ 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 &&
@@ -1067,8 +1121,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
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);
@@ -1086,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
@@ -1098,12 +1151,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);
@@ -1121,25 +1174,25 @@ 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;
@@ -1151,7 +1204,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");
@@ -1166,6 +1219,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,
@@ -1217,8 +1277,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)
@@ -1253,7 +1311,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);
}
@@ -1307,10 +1365,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 &&