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