summaryrefslogtreecommitdiffstats
path: root/demux
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-07-05 16:57:56 +0200
committerwm4 <wm4@nowhere>2014-07-05 17:07:15 +0200
commit338004bcfc224727d1477d303aa1521bf3ac21be (patch)
tree5066a689bb13f4c00a360d9b33a485d81eb17d72 /demux
parent4d2a4afdef24c343b98e71dddcb3382e85c09c96 (diff)
downloadmpv-338004bcfc224727d1477d303aa1521bf3ac21be.tar.bz2
mpv-338004bcfc224727d1477d303aa1521bf3ac21be.tar.xz
dvd, bluray, cdda: add demux_disc containing all related hacks
DVD and Bluray (and to some extent cdda) require awful hacks all over the codebase to make them work. The main reason is that they act like container, but are entirely implemented on the stream layer. The raw mpeg data resulting from these streams must be "extended" with the container-like metadata transported via STREAM_CTRLs. The result were hacks all over demux.c and some higher-level parts. Add a "disc" pseudo-demuxer, and move all these hacks and special-cases to it.
Diffstat (limited to 'demux')
-rw-r--r--demux/demux.c86
-rw-r--r--demux/demux.h5
-rw-r--r--demux/demux_disc.c276
-rw-r--r--demux/demux_lavf.c16
4 files changed, 295 insertions, 88 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 7b780c4411..d6aff6d858 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -52,12 +52,14 @@ extern const demuxer_desc_t demuxer_desc_lavf;
extern const demuxer_desc_t demuxer_desc_libass;
extern const demuxer_desc_t demuxer_desc_subreader;
extern const demuxer_desc_t demuxer_desc_playlist;
+extern const demuxer_desc_t demuxer_desc_disc;
/* Please do not add any new demuxers here. If you want to implement a new
* demuxer, add it to libavformat, except for wrappers around external
* 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,
@@ -86,7 +88,6 @@ struct demux_stream {
struct demux_packet *tail;
};
-static void add_stream_chapters(struct demuxer *demuxer);
void demuxer_sort_chapters(demuxer_t *demuxer);
static void ds_free_packs(struct demux_stream *ds)
@@ -131,8 +132,6 @@ struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
case STREAM_SUB: sh->sub = talloc_zero(demuxer, struct sh_sub); break;
}
- sh->ds->selected = demuxer->stream_autoselect;
-
return sh;
}
@@ -332,6 +331,18 @@ bool demux_stream_eof(struct sh_stream *sh)
return !sh || sh->ds->eof;
}
+// Read and return any packet we find.
+struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
+{
+ demux_fill_buffer(demuxer);
+ for (int n = 0; n < demuxer->num_streams; n++) {
+ struct sh_stream *sh = demuxer->streams[n];
+ if (sh->ds->head)
+ return demux_read_packet(sh);
+ }
+ return NULL;
+}
+
// ====================================================================
void demuxer_help(struct mp_log *log)
@@ -472,15 +483,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
demuxer->filetype, desc->desc);
else
mp_verbose(log, "Detected file format: %s\n", desc->desc);
- if (stream_manages_timeline(demuxer->stream)) {
- // Incorrect, but fixes some behavior with DVD/BD
- demuxer->ts_resets_possible = false;
- // Doesn't work, because stream_pts is a "guess".
- demuxer->accurate_seek = false;
- // Can be seekable even if the stream isn't.
- demuxer->seekable = true;
- }
- add_stream_chapters(demuxer);
demuxer_sort_chapters(demuxer);
demux_info_update(demuxer);
demux_export_replaygain(demuxer);
@@ -572,42 +574,9 @@ int demux_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
if (rel_seek_secs == MP_NOPTS_VALUE && (flags & SEEK_ABSOLUTE))
return 0;
- // clear demux buffers:
+ // clear the packet queues
demux_flush(demuxer);
- /* Note: this is for DVD and BD playback. The stream layer has to do these
- * seeks, and the demuxer has to react to DEMUXER_CTRL_RESYNC in order to
- * deal with the suddenly changing stream position.
- */
- struct stream *stream = demuxer->stream;
- if (stream_manages_timeline(stream)) {
- double pts;
-
- if (flags & SEEK_ABSOLUTE)
- pts = 0.0f;
- else {
- if (demuxer->stream_pts == MP_NOPTS_VALUE)
- goto dmx_seek;
- pts = demuxer->stream_pts;
- }
-
- if (flags & SEEK_FACTOR) {
- double tmp = 0;
- if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH,
- &tmp) == STREAM_UNSUPPORTED)
- goto dmx_seek;
- pts += tmp * rel_seek_secs;
- } else
- pts += rel_seek_secs;
-
- if (stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, &pts)
- != STREAM_UNSUPPORTED) {
- demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL);
- return 1;
- }
- }
-
- dmx_seek:
if (demuxer->desc->seek)
demuxer->desc->seek(demuxer, rel_seek_secs, flags);
@@ -698,11 +667,6 @@ void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
}
}
-void demuxer_enable_autoselect(struct demuxer *demuxer)
-{
- demuxer->stream_autoselect = true;
-}
-
bool demuxer_stream_is_selected(struct demuxer *d, struct sh_stream *stream)
{
return stream && stream->ds->selected;
@@ -761,29 +725,9 @@ int demuxer_add_chapter(demuxer_t *demuxer, struct bstr name,
return demuxer->num_chapters - 1;
}
-static void add_stream_chapters(struct demuxer *demuxer)
-{
- if (demuxer->num_chapters)
- return;
- int num_chapters = 0;
- if (stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS,
- &num_chapters) != STREAM_OK)
- return;
- for (int n = 0; n < num_chapters; n++) {
- double p = n;
- if (stream_control(demuxer->stream, STREAM_CTRL_GET_CHAPTER_TIME, &p)
- != STREAM_OK)
- return;
- demuxer_add_chapter(demuxer, bstr0(""), p * 1e9, 0, 0);
- }
-}
-
double demuxer_get_time_length(struct demuxer *demuxer)
{
double len;
- if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) > 0)
- return len;
- // <= 0 means DEMUXER_CTRL_NOTIMPL or DEMUXER_CTRL_DONTKNOW
if (demux_control(demuxer, DEMUXER_CTRL_GET_TIME_LENGTH, &len) > 0)
return len;
return -1;
diff --git a/demux/demux.h b/demux/demux.h
index 10c4984564..56ac02ae88 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -38,10 +38,10 @@ struct MPOpts;
enum demuxer_type {
DEMUXER_TYPE_GENERIC = 0,
- DEMUXER_TYPE_TV,
DEMUXER_TYPE_MATROSKA,
DEMUXER_TYPE_EDL,
DEMUXER_TYPE_CUE,
+ DEMUXER_TYPE_DISC,
};
// DEMUXER control commands/answers
@@ -190,7 +190,6 @@ typedef struct demuxer {
struct sh_stream **streams;
int num_streams;
- bool stream_autoselect;
struct demux_edition *editions;
int num_editions;
@@ -234,6 +233,7 @@ struct demux_packet *demux_read_packet(struct sh_stream *sh);
double demux_get_next_pts(struct sh_stream *sh);
bool demux_has_packet(struct sh_stream *sh);
bool demux_stream_eof(struct sh_stream *sh);
+struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
struct sh_stream *new_sh_stream(struct demuxer *demuxer, enum stream_type type);
@@ -253,7 +253,6 @@ void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
struct sh_stream *stream);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected);
-void demuxer_enable_autoselect(struct demuxer *demuxer);
void demuxer_help(struct mp_log *log);
diff --git a/demux/demux_disc.c b/demux/demux_disc.c
new file mode 100644
index 0000000000..2901bb596b
--- /dev/null
+++ b/demux/demux_disc.c
@@ -0,0 +1,276 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <assert.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+
+#include "stream/stream.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
+ struct sh_stream *dvd_subs[32];
+};
+
+static void reselect_streams(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ for (int n = 0; n < MPMIN(p->slave->num_streams, p->num_streams); n++) {
+ if (p->streams[n]) {
+ demuxer_select_track(p->slave, p->slave->streams[n],
+ demuxer_stream_is_selected(demuxer, p->streams[n]));
+ }
+ }
+}
+
+static void get_disc_lang(struct stream *stream, struct sh_stream *sh)
+{
+ struct stream_lang_req req = {.type = sh->type, .id = sh->demuxer_id};
+ if (stream->uncached_type == STREAMTYPE_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 (stream->uncached_type != STREAMTYPE_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 = new_sh_stream(demuxer, STREAM_SUB);
+ if (!sh)
+ break;
+ sh->demuxer_id = n + 0x20;
+ sh->codec = "dvd_subtitle";
+ get_disc_lang(stream, sh);
+ // 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;
+ csp.int_bits_in = 8;
+ csp.int_bits_out = 8;
+ float cmatrix[3][4];
+ mp_get_yuv2rgb_coeffs(&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 c[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
+ mp_map_int_color(cmatrix, 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->sub->extradata = s;
+ sh->sub->extradata_len = strlen(s);
+ }
+ }
+}
+
+static void add_streams(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ for (int n = p->num_streams; n < p->slave->num_streams; n++) {
+ struct sh_stream *src = p->slave->streams[n];
+ if (src->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 = new_sh_stream(demuxer, 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 = talloc_strdup(sh, src->codec);
+ sh->format = src->format;
+ sh->lav_headers = src->lav_headers;
+ if (sh && src->video) {
+ double ar;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
+ == STREAM_OK)
+ src->video->aspect = ar;
+ }
+ if (sh && src->audio)
+ sh->audio = src->audio;
+ }
+ reselect_streams(demuxer);
+}
+
+static void d_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
+{
+ struct priv *p = demuxer->priv;
+
+ if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) {
+ demux_seek(p->slave, rel_seek_secs, flags);
+ return;
+ }
+
+ double pts;
+ if (flags & SEEK_ABSOLUTE)
+ pts = 0.0f;
+ else {
+ if (demuxer->stream_pts == MP_NOPTS_VALUE)
+ return;
+ pts = demuxer->stream_pts;
+ }
+
+ if (flags & SEEK_FACTOR) {
+ double tmp = 0;
+ stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp);
+ pts += tmp * rel_seek_secs;
+ } else {
+ pts += rel_seek_secs;
+ }
+
+ stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, &pts);
+ demux_control(p->slave, DEMUXER_CTRL_RESYNC, NULL);
+}
+
+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;
+
+ add_streams(demuxer);
+ if (pkt->stream >= p->num_streams) { // out of memory?
+ abort();
+ talloc_free(pkt);
+ return 0;
+ }
+
+ struct sh_stream *sh = p->streams[pkt->stream];
+
+ // Use only one stream for stream_pts, otherwise PTS might be jumpy.
+ if (sh->type == STREAM_VIDEO) {
+ double pts;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_CURRENT_TIME, &pts) > 0)
+ pkt->stream_pts = pts;
+ }
+
+ demuxer_add_packet(demuxer, 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, bstr0(""), p * 1e9, 0, 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;
+
+ char *demux = "+lavf";
+ if (demuxer->stream->uncached_type == STREAMTYPE_CDDA)
+ demux = "+rawaudio";
+
+ p->slave = demux_open(demuxer->stream, demux, NULL, demuxer->global);
+ if (!p->slave)
+ return -1;
+
+ // Incorrect, but fixes some behavior
+ demuxer->ts_resets_possible = false;
+ // Doesn't work, because stream_pts is a "guess".
+ demuxer->accurate_seek = false;
+ // Can be seekable even if the stream isn't.
+ demuxer->seekable = true;
+
+ add_dvd_streams(demuxer);
+ add_streams(demuxer);
+ add_stream_chapters(demuxer);
+
+ return 0;
+}
+
+static void d_close(demuxer_t *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ free_demuxer(p->slave);
+}
+
+static int d_control(demuxer_t *demuxer, int cmd, void *arg)
+{
+ struct priv *p = demuxer->priv;
+
+ switch (cmd) {
+ case DEMUXER_CTRL_GET_TIME_LENGTH: {
+ double len;
+ if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) < 1)
+ break;
+ *(double *)arg = len;
+ return DEMUXER_CTRL_OK;
+ }
+ case DEMUXER_CTRL_SWITCHED_TRACKS:
+ reselect_streams(demuxer);
+ return DEMUXER_CTRL_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,
+ .type = DEMUXER_TYPE_DISC,
+};
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 619f06b32b..3c18866312 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -54,6 +54,7 @@
#include "stheader.h"
#include "options/m_option.h"
+
#define INITIAL_PROBE_SIZE STREAM_BUFFER_SIZE
#define PROBE_BUF_SIZE FFMIN(STREAM_MAX_BUFFER_SIZE, 2 * 1024 * 1024)
@@ -153,8 +154,6 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
struct demuxer *demuxer = opaque;
struct stream *stream = demuxer->stream;
int64_t current_pos;
- if (stream_manages_timeline(stream))
- return -1;
MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %d)\n", stream, pos, whence);
if (whence == SEEK_END || whence == AVSEEK_SIZE) {
int64_t end;
@@ -264,8 +263,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
MP_FATAL(demuxer, "Unknown lavf format %s\n", format);
return -1;
}
- MP_INFO(demuxer, "Forced lavf %s demuxer\n",
- priv->avif->long_name);
+ MP_VERBOSE(demuxer, "Forced lavf %s demuxer\n", priv->avif->long_name);
goto success;
}
@@ -762,10 +760,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
- double time;
- if (stream_control(demuxer->stream, STREAM_CTRL_GET_START_TIME, &time) > 0)
- demuxer->start_time = time;
-
return 0;
}
@@ -816,12 +810,6 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
dp->duration = pkt->convergence_duration * av_q2d(st->time_base);
dp->pos = pkt->pos;
dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
- // Use only one stream for stream_pts, otherwise PTS might be jumpy.
- if (stream->type == STREAM_VIDEO) {
- double pts;
- if (stream_control(demux->stream, STREAM_CTRL_GET_CURRENT_TIME, &pts) > 0)
- dp->stream_pts = pts;
- }
if (dp->pts != MP_NOPTS_VALUE) {
priv->last_pts = dp->pts * AV_TIME_BASE;
} else if (dp->dts != MP_NOPTS_VALUE) {