mirror of
https://github.com/mpv-player/mpv
synced 2024-12-28 18:12:22 +00:00
Remove optical disc fancification layers
This removes anything related to DVD/BD/CD that negatively affected the core code. It includes trying to rewrite timestamps (since DVDs and Blurays do not set packet stream timestamps to playback time, and can even have resets mid-stream), export of chapters, stream languages, export of title/track lists, and all that. Only basic seeking is supported. It is very much possible that seeking completely fails on some discs (on some parts of the timeline), because timestamp rewriting was removed. Note that I don't give a shit about optical media. If you want to watch them, rip them. Keeping some bare support for DVD/BD is the most I'm going to do to appease the type of lazy, obnoxious users who will care. There are other players which are better at optical discs.
This commit is contained in:
parent
cf36e3d15b
commit
a9d83eac40
@ -80,6 +80,8 @@ Interface changes
|
||||
and `--macos-title-bar-appearance`.
|
||||
- The default for `--vulkan-async-compute` has changed to `yes` from `no`
|
||||
with the move to libplacebo as the back-end for vulkan rendering.
|
||||
- Remove "disc-titles", "disc-title", "disc-title-list", and "angle"
|
||||
properties. dvd:// does not support title ranges anymore.
|
||||
--- mpv 0.29.0 ---
|
||||
- drop --opensles-sample-rate, as --audio-samplerate should be used if desired
|
||||
- drop deprecated --videotoolbox-format, --ff-aid, --ff-vid, --ff-sid,
|
||||
|
@ -1182,8 +1182,6 @@ Property list
|
||||
``media-title``
|
||||
If the currently played file has a ``title`` tag, use that.
|
||||
|
||||
Otherwise, if the media type is DVD, return the volume ID of DVD.
|
||||
|
||||
Otherwise, return the ``filename`` property.
|
||||
|
||||
``file-format``
|
||||
@ -1296,40 +1294,6 @@ Property list
|
||||
Current MKV edition number. Setting this property to a different value will
|
||||
restart playback. The number of the first edition is 0.
|
||||
|
||||
``disc-titles``
|
||||
Number of BD/DVD titles.
|
||||
|
||||
This has a number of sub-properties. Replace ``N`` with the 0-based edition
|
||||
index.
|
||||
|
||||
``disc-titles/count``
|
||||
Number of titles.
|
||||
|
||||
``disc-titles/id``
|
||||
Title ID as integer. Currently, this is the same as the title index.
|
||||
|
||||
``disc-titles/length``
|
||||
Length in seconds. Can be unavailable in a number of cases (currently
|
||||
it works for libdvdnav only).
|
||||
|
||||
When querying the property with the client API using ``MPV_FORMAT_NODE``,
|
||||
or with Lua ``mp.get_property_native``, this will return a mpv_node with
|
||||
the following contents:
|
||||
|
||||
::
|
||||
|
||||
MPV_FORMAT_NODE_ARRAY
|
||||
MPV_FORMAT_NODE_MAP (for each edition)
|
||||
"id" MPV_FORMAT_INT64
|
||||
"length" MPV_FORMAT_DOUBLE
|
||||
|
||||
``disc-title-list``
|
||||
List of BD/DVD titles.
|
||||
|
||||
``disc-title`` (RW)
|
||||
Current BD/DVD title number. Writing works only for ``dvdnav://`` and
|
||||
``bd://`` (and aliases for these).
|
||||
|
||||
``chapters``
|
||||
Number of chapters.
|
||||
|
||||
@ -1369,9 +1333,6 @@ Property list
|
||||
"title" MPV_FORMAT_STRING
|
||||
"default" MPV_FORMAT_FLAG
|
||||
|
||||
``angle`` (RW)
|
||||
Current DVD angle.
|
||||
|
||||
``metadata``
|
||||
Metadata key/value pairs.
|
||||
|
||||
|
@ -57,7 +57,6 @@ 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;
|
||||
@ -68,7 +67,6 @@ extern const demuxer_desc_t demuxer_desc_timeline;
|
||||
* libraries and demuxers requiring binary support. */
|
||||
|
||||
const demuxer_desc_t *const demuxer_list[] = {
|
||||
&demuxer_desc_disc,
|
||||
&demuxer_desc_edl,
|
||||
&demuxer_desc_cue,
|
||||
&demuxer_desc_rawaudio,
|
||||
@ -2357,7 +2355,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
.access_references = opts->access_references,
|
||||
.events = DEMUX_EVENT_ALL,
|
||||
.duration = -1,
|
||||
.extended_ctrls = stream->extended_ctrls,
|
||||
};
|
||||
demuxer->seekable = stream->seekable;
|
||||
|
||||
|
@ -32,7 +32,6 @@
|
||||
|
||||
enum demux_ctrl {
|
||||
DEMUXER_CTRL_SWITCHED_TRACKS = 1,
|
||||
DEMUXER_CTRL_RESYNC,
|
||||
DEMUXER_CTRL_IDENTIFY_PROGRAM,
|
||||
DEMUXER_CTRL_STREAM_CTRL,
|
||||
DEMUXER_CTRL_GET_READER_STATE,
|
||||
|
@ -1,379 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
double seek_arg[] = {seek_pts, flags};
|
||||
stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, seek_arg);
|
||||
demux_control(p->slave, DEMUXER_CTRL_RESYNC, NULL);
|
||||
|
||||
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 int d_fill_buffer(demuxer_t *demuxer)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
|
||||
struct demux_packet *pkt = demux_read_any_packet(p->slave);
|
||||
if (!pkt)
|
||||
return 0;
|
||||
|
||||
demux_update(p->slave);
|
||||
|
||||
if (p->seek_reinit)
|
||||
reset_pts(demuxer);
|
||||
|
||||
add_streams(demuxer);
|
||||
if (pkt->stream >= p->num_streams) { // out of memory?
|
||||
talloc_free(pkt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sh_stream *sh = p->streams[pkt->stream];
|
||||
if (!demux_stream_is_selected(sh)) {
|
||||
talloc_free(pkt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (p->is_cdda) {
|
||||
demux_add_packet(sh, pkt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
demux_add_packet(sh, 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",
|
||||
.does_not_own_stream = true,
|
||||
};
|
||||
|
||||
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(demuxer->stream, ¶ms, demuxer->global);
|
||||
if (!p->slave)
|
||||
return -1;
|
||||
|
||||
// So that we don't miss initial packets of delayed subtitle streams.
|
||||
demux_set_stream_autoselect(p->slave, true);
|
||||
|
||||
// With cache enabled, the stream can be seekable. This causes demux_lavf.c
|
||||
// (actually libavformat/mpegts.c) to seek sometimes when reading a packet.
|
||||
// It does this to seek back a bit in case the current file position points
|
||||
// into the middle of a packet.
|
||||
if (!p->is_cdda) {
|
||||
demuxer->stream->seekable = false;
|
||||
|
||||
// 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;
|
||||
|
||||
demuxer->extended_ctrls = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void d_close(demuxer_t *demuxer)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
demux_free(p->slave);
|
||||
}
|
||||
|
||||
static int d_control(demuxer_t *demuxer, int cmd, void *arg)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
|
||||
switch (cmd) {
|
||||
case DEMUXER_CTRL_RESYNC:
|
||||
demux_flush(p->slave);
|
||||
break; // relay to slave demuxer
|
||||
case DEMUXER_CTRL_SWITCHED_TRACKS:
|
||||
reselect_streams(demuxer);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
return demux_control(p->slave, cmd, arg);
|
||||
}
|
||||
|
||||
const demuxer_desc_t demuxer_desc_disc = {
|
||||
.name = "disc",
|
||||
.desc = "CD/DVD/BD wrapper",
|
||||
.fill_buffer = d_fill_buffer,
|
||||
.open = d_open,
|
||||
.close = d_close,
|
||||
.seek = d_seek,
|
||||
.control = d_control,
|
||||
};
|
@ -213,6 +213,7 @@ typedef struct lavf_priv {
|
||||
int cur_program;
|
||||
char *mime_type;
|
||||
double seek_delay;
|
||||
bool optical_crap_hack;
|
||||
|
||||
struct demux_lavf_opts *opts;
|
||||
double mf_fps;
|
||||
@ -1056,6 +1057,13 @@ 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1120,6 +1128,13 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
|
||||
int avsflags = 0;
|
||||
int64_t seek_pts_av = 0;
|
||||
|
||||
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 (!(flags & SEEK_FORWARD))
|
||||
avsflags = AVSEEK_FLAG_BACKWARD;
|
||||
|
||||
@ -1234,11 +1249,6 @@ redo:
|
||||
|
||||
return CONTROL_OK;
|
||||
}
|
||||
case DEMUXER_CTRL_RESYNC:
|
||||
stream_drop_buffers(priv->stream);
|
||||
avio_flush(priv->avfc->pb);
|
||||
avformat_flush(priv->avfc);
|
||||
return CONTROL_OK;
|
||||
case DEMUXER_CTRL_REPLACE_STREAM:
|
||||
if (priv->own_stream)
|
||||
free_stream(priv->stream);
|
||||
|
@ -218,43 +218,8 @@ int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
|
||||
} else {
|
||||
// filename
|
||||
void *tmp = talloc_new(NULL);
|
||||
bstr file = p.arg;
|
||||
char *file0 = bstrdup0(tmp, p.arg);
|
||||
#if HAVE_GPL
|
||||
// expand DVD filename entries like dvd://1-3 into component titles
|
||||
if (bstr_startswith0(file, "dvd://")) {
|
||||
int offset = 6;
|
||||
char *splitpos = strstr(file0 + offset, "-");
|
||||
if (splitpos != NULL) {
|
||||
char *endpos;
|
||||
int start_title = strtol(file0 + offset, &endpos, 10);
|
||||
int end_title;
|
||||
//entries like dvd://-2 imply start at title 1
|
||||
if (start_title < 0) {
|
||||
end_title = abs(start_title);
|
||||
start_title = 1;
|
||||
} else
|
||||
end_title = strtol(splitpos + 1, &endpos, 10);
|
||||
|
||||
#define dvd_range(a) (a >= 0 && a < 255)
|
||||
if (dvd_range(start_title) && dvd_range(end_title)
|
||||
&& (start_title < end_title)) {
|
||||
for (int j = start_title; j <= end_title; j++) {
|
||||
char *f = talloc_asprintf(tmp, "dvd://%d%s", j,
|
||||
endpos);
|
||||
playlist_add_file(files, f);
|
||||
}
|
||||
} else
|
||||
MP_ERR(config, "Invalid play entry %s\n", file0);
|
||||
|
||||
} else // dvd:// or dvd://x entry
|
||||
playlist_add_file(files, file0);
|
||||
} else {
|
||||
process_non_option(files, file0);
|
||||
}
|
||||
#else
|
||||
process_non_option(files, file0);
|
||||
#endif
|
||||
talloc_free(tmp);
|
||||
}
|
||||
}
|
||||
|
145
player/command.c
145
player/command.c
@ -901,39 +901,6 @@ static int mp_property_playback_time(void *ctx, struct m_property *prop,
|
||||
return property_time(action, arg, get_playback_time(mpctx));
|
||||
}
|
||||
|
||||
/// Current BD/DVD title (RW)
|
||||
static int mp_property_disc_title(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct demuxer *d = mpctx->demuxer;
|
||||
if (!d || !d->extended_ctrls)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
unsigned int title = -1;
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
if (demux_stream_control(d, STREAM_CTRL_GET_CURRENT_TITLE, &title) < 0)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
*(int*)arg = title;
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_GET_TYPE:
|
||||
*(struct m_option *)arg = (struct m_option){
|
||||
.type = CONF_TYPE_INT,
|
||||
.flags = M_OPT_MIN,
|
||||
.min = -1,
|
||||
};
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_SET:
|
||||
title = *(int*)arg;
|
||||
if (demux_stream_control(d, STREAM_CTRL_SET_CURRENT_TITLE, &title) < 0)
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
if (!mpctx->stop_play)
|
||||
mpctx->stop_play = PT_CURRENT_ENTRY;
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/// Current chapter (RW)
|
||||
static int mp_property_chapter(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
@ -1216,53 +1183,6 @@ static int property_list_editions(void *ctx, struct m_property *prop,
|
||||
get_edition_entry, mpctx);
|
||||
}
|
||||
|
||||
/// Number of titles in BD/DVD
|
||||
static int mp_property_disc_titles(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
unsigned int num_titles;
|
||||
if (!demuxer || !demuxer->extended_ctrls ||
|
||||
demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES,
|
||||
&num_titles) < 1)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
return m_property_int_ro(action, arg, num_titles);
|
||||
}
|
||||
|
||||
static int get_disc_title_entry(int item, int action, void *arg, void *ctx)
|
||||
{
|
||||
struct MPContext *mpctx = ctx;
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
|
||||
double len = item;
|
||||
if (demux_stream_control(demuxer, STREAM_CTRL_GET_TITLE_LENGTH, &len) < 1)
|
||||
len = -1;
|
||||
|
||||
struct m_sub_property props[] = {
|
||||
{"id", SUB_PROP_INT(item)},
|
||||
{"length", {.type = CONF_TYPE_TIME}, {.time = len},
|
||||
.unavailable = len < 0},
|
||||
{0}
|
||||
};
|
||||
|
||||
return m_property_read_sub(props, action, arg);
|
||||
}
|
||||
|
||||
static int mp_property_list_disc_titles(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
unsigned int num_titles;
|
||||
if (!demuxer || !demuxer->extended_ctrls ||
|
||||
demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES,
|
||||
&num_titles) < 1)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
return m_property_read_list(action, arg, num_titles,
|
||||
get_disc_title_entry, mpctx);
|
||||
}
|
||||
|
||||
/// Number of chapters in file
|
||||
static int mp_property_chapters(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
@ -1286,67 +1206,6 @@ static int mp_property_editions(void *ctx, struct m_property *prop,
|
||||
return m_property_int_ro(action, arg, demuxer->num_editions);
|
||||
}
|
||||
|
||||
/// Current dvd angle (RW)
|
||||
static int mp_property_angle(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
if (!demuxer || !demuxer->extended_ctrls)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
int ris, angles = -1, angle = 1;
|
||||
|
||||
ris = demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_ANGLES, &angles);
|
||||
if (ris == STREAM_UNSUPPORTED)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
ris = demux_stream_control(demuxer, STREAM_CTRL_GET_ANGLE, &angle);
|
||||
if (ris == STREAM_UNSUPPORTED)
|
||||
return -1;
|
||||
|
||||
if (angle < 0 || angles <= 1)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
*(int *) arg = angle;
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_PRINT: {
|
||||
*(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
case M_PROPERTY_SET:
|
||||
angle = *(int *)arg;
|
||||
if (angle < 0 || angle > angles)
|
||||
return M_PROPERTY_ERROR;
|
||||
|
||||
demux_flush(demuxer);
|
||||
ris = demux_stream_control(demuxer, STREAM_CTRL_SET_ANGLE, &angle);
|
||||
if (ris == STREAM_OK) {
|
||||
demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL);
|
||||
demux_flush(demuxer);
|
||||
}
|
||||
|
||||
reset_audio_state(mpctx);
|
||||
reset_video_state(mpctx);
|
||||
mp_wakeup_core(mpctx);
|
||||
|
||||
return ris == STREAM_OK ? M_PROPERTY_OK : M_PROPERTY_ERROR;
|
||||
case M_PROPERTY_GET_TYPE: {
|
||||
struct m_option opt = {
|
||||
.type = CONF_TYPE_INT,
|
||||
.flags = CONF_RANGE,
|
||||
.min = 1,
|
||||
.max = angles,
|
||||
};
|
||||
*(struct m_option *)arg = opt;
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int get_tag_entry(int item, int action, void *arg, void *ctx)
|
||||
{
|
||||
struct mp_tags *tags = ctx;
|
||||
@ -3774,13 +3633,10 @@ static const struct m_property mp_properties_base[] = {
|
||||
{"audio-pts", mp_property_audio_pts},
|
||||
{"playtime-remaining", mp_property_playtime_remaining},
|
||||
{"playback-time", mp_property_playback_time},
|
||||
{"disc-title", mp_property_disc_title},
|
||||
{"chapter", mp_property_chapter},
|
||||
{"edition", mp_property_edition},
|
||||
{"disc-titles", mp_property_disc_titles},
|
||||
{"chapters", mp_property_chapters},
|
||||
{"editions", mp_property_editions},
|
||||
{"angle", mp_property_angle},
|
||||
{"metadata", mp_property_metadata},
|
||||
{"filtered-metadata", mp_property_filtered_metadata},
|
||||
{"chapter-metadata", mp_property_chapter_metadata},
|
||||
@ -3808,7 +3664,6 @@ static const struct m_property mp_properties_base[] = {
|
||||
{"chapter-list", mp_property_list_chapters},
|
||||
{"track-list", property_list_tracks},
|
||||
{"edition-list", property_list_editions},
|
||||
{"disc-title-list", mp_property_list_disc_titles},
|
||||
|
||||
{"playlist", mp_property_playlist},
|
||||
{"playlist-pos", mp_property_playlist_pos},
|
||||
|
@ -174,11 +174,6 @@ static char *mp_get_playback_resume_config_filename(struct MPContext *mpctx,
|
||||
realpath = mp_path_join(tmp, cwd, fname);
|
||||
}
|
||||
}
|
||||
if (bstr_startswith0(bfname, "dvd://") && opts->dvd_opts && opts->dvd_opts->device)
|
||||
realpath = talloc_asprintf(tmp, "%s - %s", realpath, opts->dvd_opts->device);
|
||||
if ((bstr_startswith0(bfname, "br://") || bstr_startswith0(bfname, "bd://") ||
|
||||
bstr_startswith0(bfname, "bluray://")) && opts->bluray_device)
|
||||
realpath = talloc_asprintf(tmp, "%s - %s", realpath, opts->bluray_device);
|
||||
uint8_t md5[16];
|
||||
av_md5_sum(md5, realpath, strlen(realpath));
|
||||
char *conf = talloc_strdup(tmp, "");
|
||||
|
@ -63,6 +63,10 @@ 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,
|
||||
|
||||
// TV
|
||||
STREAM_CTRL_TV_SET_SCAN,
|
||||
STREAM_CTRL_SET_TV_FREQ,
|
||||
@ -80,23 +84,6 @@ enum stream_ctrl {
|
||||
STREAM_CTRL_DVB_GET_CHANNEL_NAME,
|
||||
STREAM_CTRL_DVB_STEP_CHANNEL,
|
||||
|
||||
// Optical discs
|
||||
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 {
|
||||
|
@ -175,119 +175,19 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
|
||||
struct bluray_priv_s *b = s->priv;
|
||||
|
||||
switch (cmd) {
|
||||
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: {
|
||||
case STREAM_CTRL_OPTICAL_CRAP_HACK1: {
|
||||
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_GET_CURRENT_TIME: {
|
||||
*((double *) arg) = BD_TIME_TO_MP(bd_tell_time(b->bd));
|
||||
return STREAM_OK;
|
||||
}
|
||||
case STREAM_CTRL_SEEK_TO_TIME: {
|
||||
case STREAM_CTRL_OPTICAL_CRAP_HACK2: {
|
||||
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;
|
||||
@ -456,7 +356,6 @@ static int bluray_stream_open_internal(stream_t *s)
|
||||
s->control = bluray_stream_control;
|
||||
s->sector_size = BLURAY_SECTOR_SIZE;
|
||||
s->priv = b;
|
||||
s->demuxer = "+disc";
|
||||
|
||||
MP_VERBOSE(s, "Blu-ray successfully opened.\n");
|
||||
|
||||
|
@ -226,42 +226,10 @@ 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;
|
||||
|
@ -190,73 +190,6 @@ 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;
|
||||
@ -340,92 +273,16 @@ 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_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: {
|
||||
case STREAM_CTRL_OPTICAL_CRAP_HACK1: {
|
||||
if (priv->duration) {
|
||||
*(double *)arg = (double)priv->duration / 1000.0;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
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: {
|
||||
case STREAM_CTRL_OPTICAL_CRAP_HACK2: {
|
||||
double *args = arg;
|
||||
double d = args[0]; // absolute target timestamp
|
||||
int flags = args[1]; // from SEEK_* flags (demux.h)
|
||||
@ -449,63 +306,6 @@ 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;
|
||||
@ -621,7 +421,6 @@ 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;
|
||||
|
@ -275,7 +275,6 @@ def build(ctx):
|
||||
( "demux/cue.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" ),
|
||||
|
Loading…
Reference in New Issue
Block a user