summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-10-03 00:22:18 +0200
committerwm4 <wm4@nowhere>2019-10-03 00:22:18 +0200
commit1c63869d0ae01fd8e720353719e4c8b8da12e0b5 (patch)
tree942771789b91389636d2d65baa1238ab9b992a86
parent8c58375dbd968fff22c9ef5a473c456657c5fc78 (diff)
downloadmpv-1c63869d0ae01fd8e720353719e4c8b8da12e0b5.tar.bz2
mpv-1c63869d0ae01fd8e720353719e4c8b8da12e0b5.tar.xz
demux: restore some of the DVD/BD/CDDA interaction layers
This partially reverts commit a9d83eac40c94f44d19fab7b6955331f10efe301 ("Remove optical disc fancification layers"). Mostly due to the timestamp crap, this was never really going to work. The playback layer is sensitive to timestamps, and derives the playback time directly from the low level packet timestamps. DVD/BD works differently, and libdvdnav/libbluray do not make it easy at all to compensate for this. Which is why it never worked well, but not doing it at all is even more awful. demux_disc.c tried this and rewrote packet timestamps from low level TS to playback time. So restore demux_disc.c, which should bring behavior back to the old often non-working but slightly better state. I did not revert anything that affects components above the demuxer layer. For example, the properties for switching DVD angles or listing disc titles are still gone. (Disc titles could be reimplemented as editions. But not by me.) This commit modifies the reverted code a bit; this can't be avoided, because the internal API changed quite a bit. The old seek resync in demux_lavf.c (which was a hack) is replaced with a hack. SEEK_FORCE and demux_params.external_stream are new additions. Some of this could/should be further cleaned up. If you don't want "proper" DVD/BD support to disappear, you should probably volunteer. Now why am I wasting my time for this? Just because some idiot users are too lazy to rip their ever-wearing out shitty physical discs? Then why should I not be lazy and drop support completely? They won't even be thankful for me maintaining this horrible garbage for no compensation.
-rw-r--r--demux/demux.c31
-rw-r--r--demux/demux.h2
-rw-r--r--demux/demux_disc.c359
-rw-r--r--demux/demux_lavf.c15
-rw-r--r--stream/stream.h31
-rw-r--r--stream/stream_bluray.c105
-rw-r--r--stream/stream_cdda.c32
-rw-r--r--stream/stream_dvdnav.c207
-rw-r--r--wscript_build.py1
9 files changed, 751 insertions, 32 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 3d3c1d0f19..01f15de496 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -58,12 +58,14 @@ extern const demuxer_desc_t demuxer_desc_mf;
extern const demuxer_desc_t demuxer_desc_matroska;
extern const demuxer_desc_t demuxer_desc_lavf;
extern const demuxer_desc_t demuxer_desc_playlist;
+extern const demuxer_desc_t demuxer_desc_disc;
extern const demuxer_desc_t demuxer_desc_rar;
extern const demuxer_desc_t demuxer_desc_libarchive;
extern const demuxer_desc_t demuxer_desc_null;
extern const demuxer_desc_t demuxer_desc_timeline;
static const demuxer_desc_t *const demuxer_list[] = {
+ &demuxer_desc_disc,
&demuxer_desc_edl,
&demuxer_desc_cue,
&demuxer_desc_rawaudio,
@@ -263,6 +265,8 @@ struct demux_internal {
struct mp_recorder *dumper;
int dumper_status;
+ bool owns_stream;
+
// -- Access from demuxer thread only
bool enable_recording;
struct mp_recorder *recorder;
@@ -1084,7 +1088,8 @@ static void demux_shutdown(struct demux_internal *in)
talloc_free(in->cache);
in->cache = NULL;
- free_stream(demuxer->stream);
+ if (in->owns_stream)
+ free_stream(demuxer->stream);
demuxer->stream = NULL;
}
@@ -3105,7 +3110,7 @@ void demux_close_stream(struct demuxer *demuxer)
struct demux_internal *in = demuxer->in;
assert(!in->threading && demuxer == in->d_thread);
- if (!demuxer->stream)
+ if (!demuxer->stream || !in->owns_stream)
return;
MP_VERBOSE(demuxer, "demuxer read all data; closing stream\n");
@@ -3197,6 +3202,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.highest_av_pts = MP_NOPTS_VALUE,
.seeking_in_progress = MP_NOPTS_VALUE,
.demux_ts = MP_NOPTS_VALUE,
+ .owns_stream = !params->external_stream,
};
pthread_mutex_init(&in->lock, NULL);
pthread_cond_init(&in->wakeup, NULL);
@@ -3369,11 +3375,14 @@ struct demuxer *demux_open_url(const char *url,
struct mp_cancel *priv_cancel = mp_cancel_new(NULL);
if (cancel)
mp_cancel_set_parent(priv_cancel, cancel);
- struct stream *s = stream_create(url, STREAM_READ | params->stream_flags,
- priv_cancel, global);
- if (s && params->init_fragment.len) {
- s = create_webshit_concat_stream(global, priv_cancel,
- params->init_fragment, s);
+ struct stream *s = params->external_stream;
+ if (!s) {
+ s = stream_create(url, STREAM_READ | params->stream_flags,
+ priv_cancel, global);
+ if (s && params->init_fragment.len) {
+ s = create_webshit_concat_stream(global, priv_cancel,
+ params->init_fragment, s);
+ }
}
if (!s) {
talloc_free(priv_cancel);
@@ -3385,7 +3394,8 @@ struct demuxer *demux_open_url(const char *url,
assert(d->cancel);
} else {
params->demuxer_failed = true;
- free_stream(s);
+ if (!params->external_stream)
+ free_stream(s);
talloc_free(priv_cancel);
}
return d;
@@ -3694,6 +3704,9 @@ static bool queue_seek(struct demux_internal *in, double seek_pts, int flags,
bool set_backwards = flags & SEEK_SATAN;
flags &= ~(unsigned)SEEK_SATAN;
+ bool force_seek = flags & SEEK_FORCE;
+ flags &= ~(unsigned)SEEK_FORCE;
+
struct demux_cached_range *cache_target =
find_cache_seek_range(in, seek_pts, flags);
@@ -3702,7 +3715,7 @@ static bool queue_seek(struct demux_internal *in, double seek_pts, int flags,
MP_VERBOSE(in, "Cached seek not possible.\n");
return false;
}
- if (!in->d_thread->seekable) {
+ if (!in->d_thread->seekable && !force_seek) {
MP_WARN(in, "Cannot seek in this file.\n");
return false;
}
diff --git a/demux/demux.h b/demux/demux.h
index 6ffd5587ec..3e3abbc84f 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -61,6 +61,7 @@ struct demux_reader_state {
#define SEEK_CACHED (1 << 3) // allow packet cache seeks only
#define SEEK_SATAN (1 << 4) // enable backward demuxing
#define SEEK_HR (1 << 5) // hr-seek (this is a weak hint only)
+#define SEEK_FORCE (1 << 6) // ignore unseekable flag
// Strictness of the demuxer open format check.
// demux.c will try by default: NORMAL, UNSAFE (in this order)
@@ -171,6 +172,7 @@ struct demuxer_params {
bool skip_lavf_probing;
bool stream_record; // if true, enable stream recording if option is set
int stream_flags;
+ struct stream *external_stream; // if set, use this, don't open or close streams
// result
bool demuxer_failed;
};
diff --git a/demux/demux_disc.c b/demux/demux_disc.c
new file mode 100644
index 0000000000..13e73caa39
--- /dev/null
+++ b/demux/demux_disc.c
@@ -0,0 +1,359 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+
+#include "stream/stream.h"
+#include "video/mp_image.h"
+#include "demux.h"
+#include "stheader.h"
+
+#include "video/csputils.h"
+
+struct priv {
+ struct demuxer *slave;
+ // streams[slave_stream_index] == our_stream
+ struct sh_stream **streams;
+ int num_streams;
+ // This contains each DVD sub stream, or NULL. Needed because DVD packets
+ // can come arbitrarily late in the MPEG stream, so the slave demuxer
+ // might add the streams only later.
+ struct sh_stream *dvd_subs[32];
+ // Used to rewrite the raw MPEG timestamps to playback time.
+ double base_time; // playback display start time of current segment
+ double base_dts; // packet DTS that maps to base_time
+ double last_dts; // DTS of previously demuxed packet
+ bool seek_reinit; // needs reinit after seek
+
+ bool is_dvd, is_cdda;
+};
+
+// If the timestamp difference between subsequent packets is this big, assume
+// a reset. It should be big enough to account for 1. low video framerates and
+// large audio frames, and 2. bad interleaving.
+#define DTS_RESET_THRESHOLD 5.0
+
+static void reselect_streams(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ int num_slave = demux_get_num_stream(p->slave);
+ for (int n = 0; n < MPMIN(num_slave, p->num_streams); n++) {
+ if (p->streams[n]) {
+ demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
+ MP_NOPTS_VALUE, demux_stream_is_selected(p->streams[n]));
+ }
+ }
+}
+
+static void get_disc_lang(struct stream *stream, struct sh_stream *sh, bool dvd)
+{
+ struct stream_lang_req req = {.type = sh->type, .id = sh->demuxer_id};
+ if (dvd && sh->type == STREAM_SUB)
+ req.id = req.id & 0x1F; // mpeg ID to index
+ stream_control(stream, STREAM_CTRL_GET_LANG, &req);
+ if (req.name[0])
+ sh->lang = talloc_strdup(sh, req.name);
+}
+
+static void add_dvd_streams(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ struct stream *stream = demuxer->stream;
+ if (!p->is_dvd)
+ return;
+ struct stream_dvd_info_req info;
+ if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
+ for (int n = 0; n < MPMIN(32, info.num_subs); n++) {
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_SUB);
+ sh->demuxer_id = n + 0x20;
+ sh->codec->codec = "dvd_subtitle";
+ get_disc_lang(stream, sh, true);
+ // p->streams _must_ match with p->slave->streams, so we can't add
+ // it yet - it has to be done when the real stream appears, which
+ // could be right on start, or any time later.
+ p->dvd_subs[n] = sh;
+
+ // emulate the extradata
+ struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
+ struct mp_cmat cmatrix;
+ mp_get_csp_matrix(&csp, &cmatrix);
+
+ char *s = talloc_strdup(sh, "");
+ s = talloc_asprintf_append(s, "palette: ");
+ for (int i = 0; i < 16; i++) {
+ int color = info.palette[i];
+ int y[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
+ int c[3];
+ mp_map_fixp_color(&cmatrix, 8, y, 8, c);
+ color = (c[2] << 16) | (c[1] << 8) | c[0];
+
+ if (i != 0)
+ s = talloc_asprintf_append(s, ", ");
+ s = talloc_asprintf_append(s, "%06x", color);
+ }
+ s = talloc_asprintf_append(s, "\n");
+
+ sh->codec->extradata = s;
+ sh->codec->extradata_size = strlen(s);
+
+ demux_add_sh_stream(demuxer, sh);
+ }
+ }
+}
+
+static void add_streams(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ for (int n = p->num_streams; n < demux_get_num_stream(p->slave); n++) {
+ struct sh_stream *src = demux_get_stream(p->slave, n);
+ if (src->type == STREAM_SUB) {
+ struct sh_stream *sub = NULL;
+ if (src->demuxer_id >= 0x20 && src->demuxer_id <= 0x3F)
+ sub = p->dvd_subs[src->demuxer_id - 0x20];
+ if (sub) {
+ assert(p->num_streams == n); // directly mapped
+ MP_TARRAY_APPEND(p, p->streams, p->num_streams, sub);
+ continue;
+ }
+ }
+ struct sh_stream *sh = demux_alloc_sh_stream(src->type);
+ assert(p->num_streams == n); // directly mapped
+ MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
+ // Copy all stream fields that might be relevant
+ *sh->codec = *src->codec;
+ sh->demuxer_id = src->demuxer_id;
+ if (src->type == STREAM_VIDEO) {
+ double ar;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
+ == STREAM_OK)
+ {
+ struct mp_image_params f = {.w = src->codec->disp_w,
+ .h = src->codec->disp_h};
+ mp_image_params_set_dsize(&f, 1728 * ar, 1728);
+ sh->codec->par_w = f.p_w;
+ sh->codec->par_h = f.p_h;
+ }
+ }
+ get_disc_lang(demuxer->stream, sh, p->is_dvd);
+ demux_add_sh_stream(demuxer, sh);
+ }
+ reselect_streams(demuxer);
+}
+
+static void d_seek(demuxer_t *demuxer, double seek_pts, int flags)
+{
+ struct priv *p = demuxer->priv;
+
+ if (p->is_cdda) {
+ demux_seek(p->slave, seek_pts, flags);
+ return;
+ }
+
+ if (flags & SEEK_FACTOR) {
+ double tmp = 0;
+ stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp);
+ seek_pts *= tmp;
+ }
+
+ MP_VERBOSE(demuxer, "seek to: %f\n", seek_pts);
+
+ // Supposed to induce a seek reset. Does it even work? I don't know.
+ // It will log some bogus error messages, since the demuxer will try a
+ // low level seek, which will obviously not work. But it will probably
+ // clear its internal buffers.
+ demux_seek(p->slave, 0, SEEK_FACTOR | SEEK_FORCE);
+ stream_drop_buffers(demuxer->stream);
+
+ double seek_arg[] = {seek_pts, flags};
+ stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, seek_arg);
+
+ p->seek_reinit = true;
+}
+
+static void reset_pts(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ double base;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_CURRENT_TIME, &base) < 1)
+ base = 0;
+
+ MP_VERBOSE(demuxer, "reset to time: %f\n", base);
+
+ p->base_dts = p->last_dts = MP_NOPTS_VALUE;
+ p->base_time = base;
+ p->seek_reinit = false;
+}
+
+static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt)
+{
+ struct priv *p = demuxer->priv;
+
+ struct demux_packet *pkt = demux_read_any_packet(p->slave);
+ if (!pkt)
+ return false;
+
+ demux_update(p->slave, MP_NOPTS_VALUE);
+
+ if (p->seek_reinit)
+ reset_pts(demuxer);
+
+ add_streams(demuxer);
+ if (pkt->stream >= p->num_streams) { // out of memory?
+ talloc_free(pkt);
+ return true;
+ }
+
+ struct sh_stream *sh = p->streams[pkt->stream];
+ if (!demux_stream_is_selected(sh)) {
+ talloc_free(pkt);
+ return true;
+ }
+
+ pkt->stream = sh->index;
+
+ if (p->is_cdda) {
+ *out_pkt = pkt;
+ return true;
+ }
+
+ MP_TRACE(demuxer, "ipts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
+
+ if (sh->type == STREAM_SUB) {
+ if (p->base_dts == MP_NOPTS_VALUE)
+ MP_WARN(demuxer, "subtitle packet along PTS reset\n");
+ } else if (pkt->dts != MP_NOPTS_VALUE) {
+ // Use the very first DTS to rebase the start time of the MPEG stream
+ // to the playback time.
+ if (p->base_dts == MP_NOPTS_VALUE)
+ p->base_dts = pkt->dts;
+
+ if (p->last_dts == MP_NOPTS_VALUE)
+ p->last_dts = pkt->dts;
+
+ if (fabs(p->last_dts - pkt->dts) >= DTS_RESET_THRESHOLD) {
+ MP_WARN(demuxer, "PTS discontinuity: %f->%f\n", p->last_dts, pkt->dts);
+ p->base_time += p->last_dts - p->base_dts;
+ p->base_dts = pkt->dts - pkt->duration;
+ }
+ p->last_dts = pkt->dts;
+ }
+
+ if (p->base_dts != MP_NOPTS_VALUE) {
+ double delta = -p->base_dts + p->base_time;
+ if (pkt->pts != MP_NOPTS_VALUE)
+ pkt->pts += delta;
+ if (pkt->dts != MP_NOPTS_VALUE)
+ pkt->dts += delta;
+ }
+
+ MP_TRACE(demuxer, "opts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
+
+ *out_pkt = pkt;
+ return 1;
+}
+
+static void add_stream_chapters(struct demuxer *demuxer)
+{
+ int num = 0;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, &num) < 1)
+ return;
+ for (int n = 0; n < num; n++) {
+ double p = n;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_CHAPTER_TIME, &p) < 1)
+ continue;
+ demuxer_add_chapter(demuxer, "", p, 0);
+ }
+}
+
+static int d_open(demuxer_t *demuxer, enum demux_check check)
+{
+ struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
+
+ if (check != DEMUX_CHECK_FORCE)
+ return -1;
+
+ struct demuxer_params params = {
+ .force_format = "+lavf",
+ .external_stream = demuxer->stream,
+ };
+
+ struct stream *cur = demuxer->stream;
+ const char *sname = "";
+ if (cur->info)
+ sname = cur->info->name;
+
+ p->is_cdda = strcmp(sname, "cdda") == 0;
+ p->is_dvd = strcmp(sname, "dvd") == 0 ||
+ strcmp(sname, "ifo") == 0 ||
+ strcmp(sname, "dvdnav") == 0 ||
+ strcmp(sname, "ifo_dvdnav") == 0;
+
+ if (p->is_cdda)
+ params.force_format = "+rawaudio";
+
+ char *t = NULL;
+ stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t);
+ if (t) {
+ mp_tags_set_str(demuxer->metadata, "TITLE", t);
+ talloc_free(t);
+ }
+
+ // Initialize the playback time. We need to read _some_ data to get the
+ // correct stream-layer time (at least with libdvdnav).
+ stream_peek(demuxer->stream, 1);
+ reset_pts(demuxer);
+
+ p->slave = demux_open_url("-", &params, demuxer->cancel, demuxer->global);
+ if (!p->slave)
+ return -1;
+
+ // Can be seekable even if the stream isn't.
+ demuxer->seekable = true;
+
+ add_dvd_streams(demuxer);
+ add_streams(demuxer);
+ add_stream_chapters(demuxer);
+
+ double len;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) >= 1)
+ demuxer->duration = len;
+
+ return 0;
+}
+
+static void d_close(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ demux_free(p->slave);
+}
+
+const demuxer_desc_t demuxer_desc_disc = {
+ .name = "disc",
+ .desc = "CD/DVD/BD wrapper",
+ .read_packet = d_read_packet,
+ .open = d_open,
+ .close = d_close,
+ .seek = d_seek,
+ .switched_tracks = reselect_streams,
+};
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 4696927ef9..281259a15e 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -222,7 +222,6 @@ typedef struct lavf_priv {
int num_streams;
char *mime_type;
double seek_delay;
- bool optical_crap_hack;
struct demux_lavf_opts *opts;
double mf_fps;
@@ -1080,13 +1079,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
"broken as well.\n");
}
- double len = -1;
- if (stream_control(priv->stream, STREAM_CTRL_OPTICAL_CRAP_HACK1, &len) > 0) {
- priv->optical_crap_hack = true;
- demuxer->duration = len;
- demuxer->seekable = true;
- }
-
if (demuxer->fully_read) {
demux_close_stream(demuxer);
if (priv->own_stream)
@@ -1197,13 +1189,6 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
int64_t seek_pts_av = 0;
int seek_stream = -1;
- if (priv->optical_crap_hack) {
- if (flags & SEEK_FACTOR)
- seek_pts = seek_pts * demuxer->duration;
- stream_control(priv->stream, STREAM_CTRL_OPTICAL_CRAP_HACK2, &seek_pts);
- return;
- }
-
if (priv->any_ts_fixed) {
// helpful message to piss of users
MP_WARN(demuxer, "Some timestamps returned by the demuxer were linearized. "
diff --git a/stream/stream.h b/stream/stream.h
index 87862f1acc..dcea82dace 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -53,9 +53,34 @@ enum stream_ctrl {
STREAM_CTRL_HAS_AVSEEK,
STREAM_CTRL_GET_METADATA,
- // Garbage compatibility for obnoxious users
- STREAM_CTRL_OPTICAL_CRAP_HACK1,
- STREAM_CTRL_OPTICAL_CRAP_HACK2,
+ // Optical discs (internal interface between streams and demux_disc)
+ STREAM_CTRL_GET_TIME_LENGTH,
+ STREAM_CTRL_GET_DVD_INFO,
+ STREAM_CTRL_GET_DISC_NAME,
+ STREAM_CTRL_GET_NUM_CHAPTERS,
+ STREAM_CTRL_GET_CURRENT_TIME,
+ STREAM_CTRL_GET_CHAPTER_TIME,
+ STREAM_CTRL_SEEK_TO_TIME,
+ STREAM_CTRL_GET_ASPECT_RATIO,
+ STREAM_CTRL_GET_NUM_ANGLES,
+ STREAM_CTRL_GET_ANGLE,
+ STREAM_CTRL_SET_ANGLE,
+ STREAM_CTRL_GET_NUM_TITLES,
+ STREAM_CTRL_GET_TITLE_LENGTH, // double* (in: title number, out: len)
+ STREAM_CTRL_GET_LANG,
+ STREAM_CTRL_GET_CURRENT_TITLE,
+ STREAM_CTRL_SET_CURRENT_TITLE,
+};
+
+struct stream_lang_req {
+ int type; // STREAM_AUDIO, STREAM_SUB
+ int id;
+ char name[50];
+};
+
+struct stream_dvd_info_req {
+ unsigned int palette[16];
+ int num_subs;
};
// for STREAM_CTRL_AVSEEK
diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c
index 41b96fe5db..8614a26a5c 100644
--- a/stream/stream_bluray.c
+++ b/stream/stream_bluray.c
@@ -175,19 +175,119 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
struct bluray_priv_s *b = s->priv;
switch (cmd) {
- case STREAM_CTRL_OPTICAL_CRAP_HACK1: {
+ case STREAM_CTRL_GET_NUM_CHAPTERS: {
+ const BLURAY_TITLE_INFO *ti = b->title_info;
+ if (!ti)
+ return STREAM_UNSUPPORTED;
+ *((unsigned int *) arg) = ti->chapter_count;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_CHAPTER_TIME: {
+ const BLURAY_TITLE_INFO *ti = b->title_info;
+ if (!ti)
+ return STREAM_UNSUPPORTED;
+ int chapter = *(double *)arg;
+ double time = MP_NOPTS_VALUE;
+ if (chapter >= 0 || chapter < ti->chapter_count)
+ time = BD_TIME_TO_MP(ti->chapters[chapter].start);
+ if (time == MP_NOPTS_VALUE)
+ return STREAM_ERROR;
+ *(double *)arg = time;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_SET_CURRENT_TITLE: {
+ const uint32_t title = *((unsigned int*)arg);
+ if (title >= b->num_titles || !play_title(b, title))
+ return STREAM_UNSUPPORTED;
+ b->current_title = title;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_CURRENT_TITLE: {
+ *((unsigned int *) arg) = b->current_title;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_NUM_TITLES: {
+ *((unsigned int *)arg) = b->num_titles;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_TIME_LENGTH: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (!ti)
return STREAM_UNSUPPORTED;
*((double *) arg) = BD_TIME_TO_MP(ti->duration);
return STREAM_OK;
}
- case STREAM_CTRL_OPTICAL_CRAP_HACK2: {
+ case STREAM_CTRL_GET_CURRENT_TIME: {
+ *((double *) arg) = BD_TIME_TO_MP(bd_tell_time(b->bd));
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_SEEK_TO_TIME: {
double pts = *((double *) arg);
bd_seek_time(b->bd, BD_TIME_FROM_MP(pts));
stream_drop_buffers(s);
+ // API makes it hard to determine seeking success
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_NUM_ANGLES: {
+ const BLURAY_TITLE_INFO *ti = b->title_info;
+ if (!ti)
+ return STREAM_UNSUPPORTED;
+ *((int *) arg) = ti->angle_count;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_ANGLE: {
+ *((int *) arg) = b->current_angle;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_SET_ANGLE: {
+ const BLURAY_TITLE_INFO *ti = b->title_info;
+ if (!ti)
+ return STREAM_UNSUPPORTED;
+ int angle = *((int *) arg);
+ if (angle < 0 || angle > ti->angle_count)
+ return STREAM_UNSUPPORTED;
+ b->current_angle = angle;
+ bd_seamless_angle_change(b->bd, angle);
return STREAM_OK;
}
+ case STREAM_CTRL_GET_LANG: {
+ const BLURAY_TITLE_INFO *ti = b->title_info;
+ if (ti && ti->clip_count) {
+ struct stream_lang_req *req = arg;
+ BLURAY_STREAM_INFO *si = NULL;
+ int count = 0;
+ switch (req->type) {
+ case STREAM_AUDIO:
+ count = ti->clips[0].audio_stream_count;
+ si = ti->clips[0].audio_streams;
+ break;
+ case STREAM_SUB:
+ count = ti->clips[0].pg_stream_count;
+ si = ti->clips[0].pg_streams;
+ break;
+ }
+ for (int n = 0; n < count; n++) {
+ BLURAY_STREAM_INFO *i = &si[n];
+ if (i->pid == req->id) {
+ snprintf(req->name, sizeof(req->name), "%.4s", i->lang);
+ return STREAM_OK;
+ }
+ }
+ }
+ return STREAM_ERROR;
+ }
+ case STREAM_CTRL_GET_DISC_NAME: {
+ const struct meta_dl *meta = bd_get_meta(b->bd);
+ if (!meta || !meta->di_name || !meta->di_name[0])
+ break;
+ *(char**)arg = talloc_strdup(NULL, meta->di_name);
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_SIZE:
+ *(int64_t *)arg = bd_get_title_size(b->bd);
+ return STREAM_OK;
+ default:
+ break;
}
return STREAM_UNSUPPORTED;
@@ -355,6 +455,7 @@ static int bluray_stream_open_internal(stream_t *s)
s->close = bluray_stream_close;
s->control = bluray_stream_control;
s->priv = b;
+ s->demuxer = "+disc";
MP_VERBOSE(s, "Blu-ray successfully opened.\n");
diff --git a/stream/stream_cdda.c b/stream/stream_cdda.c
index dd8405a4a1..ed7725107b 100644
--- a/stream/stream_cdda.c
+++ b/stream/stream_cdda.c
@@ -226,10 +226,42 @@ static void close_cdda(stream_t *s)
cdda_close(p->cd);
}
+static int get_track_by_sector(cdda_priv *p, unsigned int sector)
+{
+ int i;
+ for (i = p->cd->tracks; i >= 0; --i)
+ if (p->cd->disc_toc[i].dwStartSector <= sector)
+ break;
+ return i;
+}
+
static int control(stream_t *stream, int cmd, void *arg)
{
cdda_priv *p = stream->priv;
switch (cmd) {
+ case STREAM_CTRL_GET_NUM_CHAPTERS:
+ {
+ int start_track = get_track_by_sector(p, p->start_sector);
+ int end_track = get_track_by_sector(p, p->end_sector);
+ if (start_track == -1 || end_track == -1)
+ return STREAM_ERROR;
+ *(unsigned int *)arg = end_track + 1 - start_track;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_CHAPTER_TIME:
+ {
+ int track = *(double *)arg;
+ int start_track = get_track_by_sector(p, p->start_sector);
+ int end_track = get_track_by_sector(p, p->end_sector);
+ track += start_track;
+ if (track > end_track)
+ return STREAM_ERROR;
+ int64_t sector = p->cd->disc_toc[track].dwStartSector;
+ int64_t pos = sector * CDIO_CD_FRAMESIZE_RAW;
+ // Assume standard audio CD: 44.1khz, 2 channels, s16 samples
+ *(double *)arg = pos / (44100.0 * 2 * 2);
+ return STREAM_OK;
+ }
case STREAM_CTRL_GET_SIZE:
*(int64_t *)arg =
(p->end_sector + 1 - p->start_sector) * CDIO_CD_FRAMESIZE_RAW;
diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c
index 9a95446455..b4b0dc21b2 100644
--- a/stream/stream_dvdnav.c
+++ b/stream/stream_dvdnav.c
@@ -190,6 +190,73 @@ static int dvd_probe(const char *path, const char *ext, const char *sig)
return r;
}
+/**
+ * \brief mp_dvdnav_lang_from_aid() returns the language corresponding to audio id 'aid'
+ * \param stream: - stream pointer
+ * \param sid: physical subtitle id
+ * \return 0 on error, otherwise language id
+ */
+static int mp_dvdnav_lang_from_aid(stream_t *stream, int aid)
+{
+ uint8_t lg;
+ uint16_t lang;
+ struct priv *priv = stream->priv;
+
+ if (aid < 0)
+ return 0;
+ lg = dvdnav_get_audio_logical_stream(priv->dvdnav, aid & 0x7);
+ if (lg == 0xff)
+ return 0;
+ lang = dvdnav_audio_stream_to_lang(priv->dvdnav, lg);
+ if (lang == 0xffff)
+ return 0;
+ return lang;
+}
+
+/**
+ * \brief mp_dvdnav_lang_from_sid() returns the language corresponding to subtitle id 'sid'
+ * \param stream: - stream pointer
+ * \param sid: physical subtitle id
+ * \return 0 on error, otherwise language id
+ */
+static int mp_dvdnav_lang_from_sid(stream_t *stream, int sid)
+{
+ uint8_t k;
+ uint16_t lang;
+ struct priv *priv = stream->priv;
+ if (sid < 0)
+ return 0;
+ for (k = 0; k < 32; k++)
+ if (dvdnav_get_spu_logical_stream(priv->dvdnav, k) == sid)
+ break;
+ if (k == 32)
+ return 0;
+ lang = dvdnav_spu_stream_to_lang(priv->dvdnav, k);
+ if (lang == 0xffff)
+ return 0;
+ return lang;
+}
+
+/**
+ * \brief mp_dvdnav_number_of_subs() returns the count of available subtitles
+ * \param stream: - stream pointer
+ * \return 0 on error, something meaningful otherwise
+ */
+static int mp_dvdnav_number_of_subs(stream_t *stream)
+{
+ struct priv *priv = stream->priv;
+ uint8_t lg, k, n = 0;
+
+ for (k = 0; k < 32; k++) {
+ lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k);
+ if (lg == 0xff)
+ continue;
+ if (lg >= n)
+ n = lg + 1;
+ }
+ return n;
+}
+
static int fill_buffer(stream_t *s, char *buf, int max_len)
{
struct priv *priv = s->priv;
@@ -209,7 +276,7 @@ static int fill_buffer(stream_t *s, char *buf, int max_len)
}
if (event != DVDNAV_BLOCK_OK) {
const char *name = LOOKUP_NAME(mp_dvdnav_events, event);
- MP_VERBOSE(s, "DVDNAV: event %s (%d).\n", name, event);
+ MP_TRACE(s, "DVDNAV: event %s (%d).\n", name, event);
}
switch (event) {
case DVDNAV_BLOCK_OK:
@@ -273,16 +340,92 @@ static int control(stream_t *stream, int cmd, void *arg)
{
struct priv *priv = stream->priv;
dvdnav_t *dvdnav = priv->dvdnav;
+ int tit, part;
switch (cmd) {
- case STREAM_CTRL_OPTICAL_CRAP_HACK1: {
+ case STREAM_CTRL_GET_NUM_CHAPTERS: {
+ if (dvdnav_current_title_info(dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
+ break;
+ if (dvdnav_get_number_of_parts(dvdnav, tit, &part) != DVDNAV_STATUS_OK)
+ break;
+ if (!part)
+ break;
+ *(unsigned int *)arg = part;
+ return 1;
+ }
+ case STREAM_CTRL_GET_CHAPTER_TIME: {
+ double *ch = arg;
+ int chapter = *ch;
+ if (dvdnav_current_title_info(dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
+ break;
+ uint64_t *parts = NULL, duration = 0;
+ int n = dvdnav_describe_title_chapters(dvdnav, tit, &parts, &duration);
+ if (!parts)
+ break;
+ if (chapter < 0 || chapter + 1 > n)
+ break;
+ *ch = chapter > 0 ? parts[chapter - 1] / 90000.0 : 0;
+ free(parts);
+ return 1;
+ }
+ case STREAM_CTRL_GET_TIME_LENGTH: {
if (priv->duration) {
*(double *)arg = (double)priv->duration / 1000.0;
return 1;
}
break;
}
- case STREAM_CTRL_OPTICAL_CRAP_HACK2: {
+ case STREAM_CTRL_GET_ASPECT_RATIO: {
+ uint8_t ar = dvdnav_get_video_aspect(dvdnav);
+ *(double *)arg = !ar ? 4.0 / 3.0 : 16.0 / 9.0;
+ return 1;
+ }
+ case STREAM_CTRL_GET_CURRENT_TIME: {
+ double tm;
+ tm = dvdnav_get_current_time(dvdnav) / 90000.0f;
+ if (tm != -1) {
+ *(double *)arg = tm;
+ return 1;
+ }
+ break;
+ }
+ case STREAM_CTRL_GET_NUM_TITLES: {
+ int32_t num_titles = 0;
+ if (dvdnav_get_number_of_titles(dvdnav, &num_titles) != DVDNAV_STATUS_OK)
+ break;
+ *((unsigned int*)arg)= num_titles;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_TITLE_LENGTH: {
+ int t = *(double *)arg;
+ int32_t num_titles = 0;
+ if (dvdnav_get_number_of_titles(dvdnav, &num_titles) != DVDNAV_STATUS_OK)
+ break;
+ if (t < 0 || t >= num_titles)
+ break;
+ uint64_t duration = 0;
+ uint64_t *parts = NULL;
+ dvdnav_describe_title_chapters(dvdnav, t + 1, &parts, &duration);
+ if (!parts)
+ break;
+ free(parts);
+ *(double *)arg = duration / 90000.0;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_CURRENT_TITLE: {
+ if (dvdnav_current_title_info(dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
+ break;
+ *((unsigned int *) arg) = tit - 1;
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_SET_CURRENT_TITLE: {
+ int title = *((unsigned int *) arg);
+ if (dvdnav_title_play(priv->dvdnav, title + 1) != DVDNAV_STATUS_OK)
+ break;
+ stream_drop_buffers(stream);
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_SEEK_TO_TIME: {
double *args = arg;
double d = args[0]; // absolute target timestamp
int flags = args[1]; // from SEEK_* flags (demux.h)
@@ -306,6 +449,63 @@ static int control(stream_t *stream, int cmd, void *arg)
MP_VERBOSE(stream, "block: %lu\n", (unsigned long)pos);
return STREAM_OK;
}
+ case STREAM_CTRL_GET_NUM_ANGLES: {
+ uint32_t curr, angles;
+ if (dvdnav_get_angle_info(dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
+ break;
+ *(int *)arg = angles;
+ return 1;
+ }
+ case STREAM_CTRL_GET_ANGLE: {
+ uint32_t curr, angles;
+ if (dvdnav_get_angle_info(dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
+ break;
+ *(int *)arg = curr;
+ return 1;
+ }
+ case STREAM_CTRL_SET_ANGLE: {
+ uint32_t curr, angles;
+ int new_angle = *(int *)arg;
+ if (dvdnav_get_angle_info(dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
+ break;
+ if (new_angle > angles || new_angle < 1)
+ break;
+ if (dvdnav_angle_change(dvdnav, new_angle) != DVDNAV_STATUS_OK)
+ return 1;
+ }
+ case STREAM_CTRL_GET_LANG: {
+ struct stream_lang_req *req = arg;
+ int lang = 0;
+ switch (req->type) {
+ case STREAM_AUDIO:
+ lang = mp_dvdnav_lang_from_aid(stream, req->id);
+ break;
+ case STREAM_SUB:
+ lang = mp_dvdnav_lang_from_sid(stream, req->id);
+ break;
+ }
+ if (!lang)
+ break;
+ snprintf(req->name, sizeof(req->name), "%c%c", lang >> 8, lang);
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_DVD_INFO: {
+ struct stream_dvd_info_req *req = arg;
+ memset(req, 0, sizeof(*req));
+ req->num_subs = mp_dvdnav_number_of_subs(stream);
+ assert(sizeof(uint32_t) == sizeof(unsigned int));
+ memcpy(req->palette, priv->spu_clut, sizeof(req->palette));
+ return STREAM_OK;
+ }
+ case STREAM_CTRL_GET_DISC_NAME: {
+ const char *volume = NULL;
+ if (dvdnav_get_title_string(dvdnav, &volume) != DVDNAV_STATUS_OK)
+ break;
+ if (!volume || !volume[0])
+ break;
+ *(char**)arg = talloc_strdup(NULL, volume);
+ return STREAM_OK;
+ }
}
return STREAM_UNSUPPORTED;
@@ -420,6 +620,7 @@ static int open_s_internal(stream_t *stream)
stream->fill_buffer = fill_buffer;
stream->control = control;
stream->close = stream_dvdnav_close;
+ stream->demuxer = "+disc";
stream->lavf_type = "mpeg";
return STREAM_OK;
diff --git a/wscript_build.py b/wscript_build.py
index 2ad713efa5..95cba8391e 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -275,6 +275,7 @@ def build(ctx):
( "demux/cache.c" ),
( "demux/demux.c" ),
( "demux/demux_cue.c" ),
+ ( "demux/demux_disc.c" ),
( "demux/demux_edl.c" ),
( "demux/demux_lavf.c" ),
( "demux/demux_libarchive.c", "libarchive" ),