stream: auto-detect .iso DVD-Video, DVD-Audio and Blu-ray images
Passing a disc image directly (e.g. `mpv disc.iso`) previously fell through to FFmpeg. Route it to the matching disc backend instead, like the VIDEO_TS.IFO/AUDIO_TS.IFO/BDMV redirectors already do.
This commit is contained in:
1 parent
fee9b14d46
commit
02ec593037
6 files changed
+99
-21
No files matched your search
@@ -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/<number>`` (selects <number>.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.)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+21
-2
@@ -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)) {
|
||||
|
||||
+34
-10
@@ -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)) {
|
||||
|
||||
+31
-9
@@ -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)) {
|
||||
|
||||
Reference in new issue
Block a user