diff options
Diffstat (limited to 'demux/demux_lavf.c')
-rw-r--r-- | demux/demux_lavf.c | 217 |
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 && |