diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst index 9f7fd0b0eb..d60fb1dd01 100644 --- a/DOCS/man/mpv.rst +++ b/DOCS/man/mpv.rst @@ -1336,6 +1336,9 @@ PROTOCOLS Play a Blu-ray disc. Since libbluray 1.0.1, you can read from ISO files by passing them to ``--bluray-device``. + A Blu-ray ``.iso`` image passed directly (e.g. ``mpv disc.iso``) is also + detected and opened. + ``title`` can be: ``longest`` or ``first`` (selects the default playlist); ``menu`` (starts in the disc menu, see ``--disc-menu`` and the ``discnav`` command); ``mpls/`` (selects .mpls playlist); @@ -1352,6 +1355,9 @@ PROTOCOLS it will probably try to open an actual optical drive, if available and implemented for the OS. + A DVD-Video ``.iso`` image passed directly (e.g. ``mpv disc.iso``) is also + detected and opened. + ``dvdnav://`` is an old alias for ``dvd://`` and does exactly the same thing. @@ -1365,6 +1371,10 @@ PROTOCOLS with its tracks, are exposed as a video track and shown by default. Can be disabled with ``--vid=no``. + A DVD-Audio ``.iso`` image passed directly (e.g. ``mpv disc.iso``) is also + detected and opened. For hybrid discs, the DVD-Audio zone is preferred over + the DVD-Video zone. + ``dvb://[cardnumber@]channel`` ``--dvbin-...`` Digital TV via DVB. (Linux only.) diff --git a/stream/stream.c b/stream/stream.c index 2cc9ae3e1d..1b82238f83 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -368,6 +368,7 @@ static int stream_create_instance(const stream_info_t *sinfo, s->log = mp_log_new(s, s->global->log, sinfo->name); } s->info = sinfo; + s->autoprobed = !args->sinfo; s->cancel = args->cancel; s->url = talloc_strdup(s, url); s->path = talloc_strdup(s, path); diff --git a/stream/stream.h b/stream/stream.h index 2d42ce4d5f..9fda0a8dff 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -255,6 +255,8 @@ typedef struct stream { bool is_regular : 1; // regular file bool access_references : 1; // open other streams bool allow_partial_read : 1; // allows partial read with stream_read_file() + bool autoprobed : 1; // opened by the autoprobe loop, not explicitly + // requested, failures should stay quiet struct mp_log *log; struct mpv_global *global; diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c index 8972d7e71a..de83df3f31 100644 --- a/stream/stream_bluray.c +++ b/stream/stream_bluray.c @@ -119,6 +119,7 @@ struct bd_overlay_plane { struct bluray_priv_s { BLURAY *bd; struct mp_log *bluray_log; + bool probing; // open is an .iso auto-detection probe BLURAY_TITLE_INFO *title_info; int num_titles; int current_angle; @@ -1047,9 +1048,11 @@ static bool check_disc_info(stream_t *s) // check Blu-ray if (!info->bluray_detected) { - MP_ERR(s, "Given stream is not a Blu-ray.\n"); + if (!b->probing) + MP_ERR(s, "Given stream is not a Blu-ray.\n"); return false; } + b->probing = false; // check AACS if (info->aacs_detected) { @@ -1159,7 +1162,8 @@ static int bluray_stream_open_internal(stream_t *s) BLURAY *bd = bd_open(device_tmp, NULL); talloc_free(device_tmp); if (!bd) { - MP_ERR(s, "Couldn't open Blu-ray device: %s\n", device); + if (!b->probing) + MP_ERR(s, "Couldn't open Blu-ray device: %s\n", device); ret = STREAM_UNSUPPORTED; goto err; } @@ -1385,6 +1389,21 @@ static int bdmv_dir_stream_open(stream_t *stream) if (!path) goto unsupported; + // Hand the .iso to libbluray as the device. Opening validates the disc + // info, so it doubles as the probe (see priv->probing). + if (bstr_case_endswith(bstr0(path), bstr0(".iso"))) { + priv->cfg_device = talloc_strdup(priv, path); + priv->probing = stream->autoprobed; + int r = bluray_stream_open_internal(stream); + if (r != STREAM_OK) { + if (priv->probing) + goto unsupported; + return r; + } + MP_INFO(stream, "Blu-ray ISO image detected. Redirecting to bluray://\n"); + return r; + } + // We allow the path to point to a directory containing BDMV/, a // directory containing MovieObject.bdmv, or that file itself. if (!check_bdmv(path)) { diff --git a/stream/stream_dvda.c b/stream/stream_dvda.c index db9dfa9266..cd6c1bd536 100644 --- a/stream/stream_dvda.c +++ b/stream/stream_dvda.c @@ -101,6 +101,8 @@ struct priv { mp_mutex still_lock; // guards the still-picture state below int page; // forced still page (--dvda-page), -1 = time-based char *device; + struct mp_log *lib_log; // used by libdvdread's log callback + bool probing; // open is an .iso auto-detection probe struct dvda_opts *opts; }; @@ -225,11 +227,13 @@ static bool read_disc_structure(stream_t *stream) ifo_handle_t *amg = ifoOpenVMGI(priv->dvd); if (!amg || amg->ifo_format != IFO_AUDIO || !amg->amgi_mat) { - MP_ERR(stream, "Could not read AUDIO_TS.IFO.\n"); + if (!priv->probing) + MP_ERR(stream, "Could not read AUDIO_TS.IFO.\n"); if (amg) ifoClose(amg); return false; } + priv->probing = false; int num_ats = amg->amgi_mat->amg_nr_of_title_sets; ifoClose(amg); @@ -583,9 +587,10 @@ static void stream_dvda_close(stream_t *stream) DVDClose(priv->dvd); } -static void dvda_log(void *priv, dvd_logger_level_t level, +static void dvda_log(void *p, dvd_logger_level_t level, const char *fmt, va_list va) { + struct priv *priv = p; int lvl; switch (level) { case DVD_LOGGER_LEVEL_ERROR: lvl = MSGL_ERR; break; @@ -594,10 +599,12 @@ static void dvda_log(void *priv, dvd_logger_level_t level, case DVD_LOGGER_LEVEL_INFO: default: lvl = MSGL_V; break; } - if (!mp_msg_test(priv, lvl)) + if (priv->probing) + lvl = MPMAX(lvl, MSGL_V); + if (!mp_msg_test(priv->lib_log, lvl)) return; - mp_msg_va(priv, lvl, fmt, va); - mp_msg(priv, lvl, "\n"); + mp_msg_va(priv->lib_log, lvl, fmt, va); + mp_msg(priv->lib_log, lvl, "\n"); } static int open_s_internal(stream_t *stream) @@ -622,16 +629,18 @@ static int open_s_internal(stream_t *stream) if (!path) goto err; - struct mp_log *log = mp_log_new(stream, stream->log, "/libdvdread"); + priv->lib_log = mp_log_new(stream, stream->log, "/libdvdread"); const dvd_logger_cb logger_cb = { .pf_log = dvda_log }; - priv->dvd = DVDOpenAudio(log, &logger_cb, path); + priv->dvd = DVDOpenAudio(priv, &logger_cb, path); if (!priv->dvd) { - MP_ERR(stream, "Couldn't open DVD-Audio device: %s\n", path); + if (!priv->probing) + MP_ERR(stream, "Couldn't open DVD-Audio device: %s\n", path); goto err; } if (!read_disc_structure(stream)) { - MP_ERR(stream, "No DVD-Audio titles found: %s\n", path); + if (!priv->probing) + MP_ERR(stream, "No DVD-Audio titles found: %s\n", path); goto err; } @@ -666,7 +675,7 @@ static int open_s_internal(stream_t *stream) err: stream_dvda_close(stream); - return STREAM_ERROR; + return priv->probing ? STREAM_UNSUPPORTED : STREAM_ERROR; } static int open_s(stream_t *stream) @@ -742,6 +751,21 @@ static int ifo_dvda_stream_open(stream_t *stream) if (!path) goto unsupported; + // Hand the .iso to libdvdread as the device. Opening validates the AMG + // IFO, so it doubles as the probe (see priv->probing). + if (bstr_case_endswith(bstr0(path), bstr0(".iso"))) { + priv->device = talloc_strdup(priv, path); + priv->probing = stream->autoprobed; + int r = open_s_internal(stream); + if (r != STREAM_OK) { + if (priv->probing) + goto unsupported; + return r; + } + MP_INFO(stream, "DVD-Audio ISO image detected. Redirecting to dvda://\n"); + return r; + } + // We allow the path to point to a directory containing AUDIO_TS/, a // directory containing AUDIO_TS.IFO, or that file itself. if (!check_ifo(path)) { diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c index 07aeac4ef0..c068f82b34 100644 --- a/stream/stream_dvdnav.c +++ b/stream/stream_dvdnav.c @@ -66,6 +66,8 @@ struct priv { dvdnav_t *dvdnav; // handle to libdvdnav stuff struct mp_log *log; // borrowed from the stream + struct mp_log *lib_log; // used by libdvdnav's log callback + bool probing; // open is an .iso auto-detection probe char *filename; // path int64_t duration; // in 90 kHz PTS ticks int title; @@ -1023,9 +1025,10 @@ static void stream_dvdnav_close(stream_t *s) mp_mutex_destroy(&priv->lock); } -static void dvdnav_log(void *priv, dvdnav_logger_level_t level, +static void dvdnav_log(void *p, dvdnav_logger_level_t level, const char *fmt, va_list va) { + struct priv *priv = p; int lvl; switch (level) { case DVDNAV_LOGGER_LEVEL_ERROR: lvl = MSGL_ERR; break; @@ -1034,17 +1037,20 @@ static void dvdnav_log(void *priv, dvdnav_logger_level_t level, case DVDNAV_LOGGER_LEVEL_INFO: default: lvl = MSGL_V; break; } - if (!mp_msg_test(priv, lvl)) + if (priv->probing) + lvl = MPMAX(lvl, MSGL_V); + if (!mp_msg_test(priv->lib_log, lvl)) return; - mp_msg_va(priv, lvl, fmt, va); - mp_msg(priv, lvl, "\n"); + mp_msg_va(priv->lib_log, lvl, fmt, va); + mp_msg(priv->lib_log, lvl, "\n"); } static dvdnav_status_t nav_open(stream_t *stream, dvdnav_t **dest, const char *path) { - struct mp_log *log = mp_log_new(stream, stream->log, "/libdvdnav"); + struct priv *priv = stream->priv; + priv->lib_log = mp_log_new(stream, stream->log, "/libdvdnav"); const dvdnav_logger_cb logger_cb = { .pf_log = dvdnav_log }; - return dvdnav_open2(dest, log, &logger_cb, path); + return dvdnav_open2(dest, priv, &logger_cb, path); } static struct priv *new_dvdnav_stream(stream_t *stream, char *filename) @@ -1099,11 +1105,12 @@ static int open_s_internal(stream_t *stream) else filename = DEFAULT_OPTICAL_DEVICE; if (!new_dvdnav_stream(stream, filename)) { - MP_ERR(stream, "Couldn't open DVD device: %s\n", - filename); - ret = STREAM_ERROR; + if (!priv->probing) + MP_ERR(stream, "Couldn't open DVD device: %s\n", filename); + ret = priv->probing ? STREAM_UNSUPPORTED : STREAM_ERROR; goto err; } + priv->probing = false; int32_t num_titles = 0; dvdnav_get_number_of_titles(priv->dvdnav, &num_titles); @@ -1233,6 +1240,21 @@ static int ifo_dvdnav_stream_open(stream_t *stream) if (!path) goto unsupported; + // Hand the .iso to libdvdnav as the device. Opening validates the VMG + // IFO, so it doubles as the probe (see priv->probing). + if (bstr_case_endswith(bstr0(path), bstr0(".iso"))) { + priv->device = talloc_strdup(priv, path); + priv->probing = stream->autoprobed; + int r = open_s_internal(stream); + if (r != STREAM_OK) { + if (priv->probing) + goto unsupported; + return r; + } + MP_INFO(stream, "DVD ISO image detected. Redirecting to dvd://\n"); + return r; + } + // We allow the path to point to a directory containing VIDEO_TS/, a // directory containing VIDEO_TS.IFO, or that file itself. if (!check_ifo(path)) {