1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-18 12:55:16 +00:00

core: fix DVD subtitle selection

Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.

demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.

Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.

(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)

Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
This commit is contained in:
wm4 2012-08-30 16:43:31 +02:00
parent fbc424ef35
commit d21b109bf7
7 changed files with 112 additions and 31 deletions

View File

@ -24,6 +24,7 @@
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
@ -70,6 +71,7 @@ typedef struct lavf_priv {
int audio_streams;
int video_streams;
int sub_streams;
int autoselect_sub;
int64_t last_pts;
int astreams[MAX_A_STREAMS];
int vstreams[MAX_V_STREAMS];
@ -164,6 +166,7 @@ static int lavf_check_file(demuxer_t *demuxer)
if (!demuxer->priv)
demuxer->priv = calloc(sizeof(lavf_priv_t), 1);
priv = demuxer->priv;
priv->autoselect_sub = -1;
char *format = lavfdopts->format;
if (!format)
@ -302,6 +305,8 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
AVCodec *avc = avcodec_find_decoder(codec->codec_id);
const char *codec_name = avc ? avc->name : "unknown";
bool set_demuxer_id = matches_avinputformat_name(priv, "mpeg");
switch (codec->codec_type) {
case AVMEDIA_TYPE_AUDIO: {
WAVEFORMATEX *wf;
@ -310,6 +315,8 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
if (!sh_audio)
break;
sh_audio->demuxer_codecname = codec_name;
if (set_demuxer_id)
sh_audio->gsh->demuxer_id = st->id;
stream_type = "audio";
priv->astreams[priv->audio_streams] = i;
sh_audio->libav_codec_id = codec->codec_id;
@ -391,6 +398,8 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
if (!sh_video)
break;
sh_video->demuxer_codecname = codec_name;
if (set_demuxer_id)
sh_video->gsh->demuxer_id = st->id;
stream_type = "video";
priv->vstreams[priv->video_streams] = i;
sh_video->libav_codec_id = codec->codec_id;
@ -503,6 +512,8 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
if (!sh_sub)
break;
sh_sub->demuxer_codecname = codec_name;
if (set_demuxer_id)
sh_sub->gsh->demuxer_id = st->id;
stream_type = "subtitle";
priv->sstreams[priv->sub_streams] = i;
sh_sub->libav_codec_id = codec->codec_id;
@ -774,6 +785,14 @@ static int demux_lavf_fill_buffer(demuxer_t *demux, demux_stream_t *dsds)
id = pkt->stream_index;
assert(id >= 0 && id < MAX_S_STREAMS);
if (demux->s_streams[id] && demux->sub->id == -1 &&
demux->s_streams[id]->gsh->demuxer_id == priv->autoselect_sub)
{
priv->autoselect_sub = -1;
demux->sub->id = id;
}
if (id == demux->audio->id || priv->internet_radio_hack) {
// audio
ds = demux->audio;
@ -943,6 +962,12 @@ static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg)
return DEMUXER_CTRL_OK;
}
}
case DEMUXER_CTRL_AUTOSELECT_SUBTITLE:
{
demuxer->sub->id = -1;
priv->autoselect_sub = *((int *)arg);
return DEMUXER_CTRL_OK;
}
case DEMUXER_CTRL_IDENTIFY_PROGRAM:
{
demux_program_t *prog = arg;

View File

@ -1369,16 +1369,3 @@ int demuxer_set_angle(demuxer_t *demuxer, int angle)
return angle;
}
char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *sh)
{
struct stream_lang_req req = { .id = sh->tid }; // assume 1:1 mapping
switch (sh->type) {
case STREAM_AUDIO: req.type = stream_ctrl_audio; break;
case STREAM_SUB: req.type = stream_ctrl_sub; break;
default: return NULL;
}
if (stream_control(d->stream, STREAM_CTRL_GET_LANG, &req) == STREAM_OK)
return req.name;
return NULL;
}

View File

@ -93,6 +93,7 @@ enum timestamp_type {
#define DEMUXER_CTRL_SWITCH_VIDEO 14
#define DEMUXER_CTRL_IDENTIFY_PROGRAM 15
#define DEMUXER_CTRL_CORRECT_PTS 16
#define DEMUXER_CTRL_AUTOSELECT_SUBTITLE 17
#define SEEK_ABSOLUTE (1 << 0)
#define SEEK_FACTOR (1 << 1)
@ -406,6 +407,4 @@ int demuxer_angles_count(struct demuxer *demuxer);
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id);
char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *s);
#endif /* MPLAYER_DEMUXER_H */

View File

@ -252,6 +252,7 @@ int use_filedir_conf;
#include "metadata.h"
static void reset_subtitles(struct MPContext *mpctx);
static void reinit_subs(struct MPContext *mpctx);
static float get_relative_time(struct MPContext *mpctx)
{
@ -941,6 +942,20 @@ static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
return new_id + 1;
}
// Map stream number (as used by libdvdread) to MPEG IDs (as used by demuxer).
static int map_id_from_demuxer(struct demuxer *d, enum stream_type type, int id)
{
if (d->stream->type == STREAMTYPE_DVD && type == STREAM_SUB)
id = id & 0x1F;
return id;
}
static int map_id_to_demuxer(struct demuxer *d, enum stream_type type, int id)
{
if (d->stream->type == STREAMTYPE_DVD && type == STREAM_SUB)
id = id | 0x20;
return id;
}
static struct track *add_stream_track(struct MPContext *mpctx,
struct sh_stream *stream,
bool under_timeline)
@ -949,7 +964,21 @@ static struct track *add_stream_track(struct MPContext *mpctx,
struct track *track = mpctx->tracks[i];
if (track->stream == stream)
return track;
// DVD subtitle track that was added later
if (stream->type == STREAM_SUB && track->type == STREAM_SUB &&
map_id_from_demuxer(stream->demuxer, stream->type,
stream->demuxer_id) == track->demuxer_id
&& !track->stream)
{
track->stream = stream;
track->demuxer_id = stream->demuxer_id;
// Initialize lazily selected track
if (track == mpctx->current_track[STREAM_SUB])
reinit_subs(mpctx);
return track;
}
}
struct track *track = talloc_ptrtype(NULL, track);
*track = (struct track) {
.type = stream->type,
@ -964,12 +993,16 @@ static struct track *add_stream_track(struct MPContext *mpctx,
};
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
// Needed for DVD and Blu-Ray. (Note that at least with DVDs and demux_lavf,
// this code is broken: unlike demux_mpg, the demuxer streams are not
// directly mapped to MPEG stream IDs.)
if (!track->lang)
track->lang = talloc_steal(track, demuxer_stream_lang(track->demuxer,
track->stream));
// Needed for DVD and Blu-ray.
if (!track->lang) {
struct stream_lang_req req = {
.type = track->type,
.id = map_id_from_demuxer(track->demuxer, track->type,
track->demuxer_id)
};
stream_control(track->demuxer->stream, STREAM_CTRL_GET_LANG, &req);
track->lang = talloc_steal(track, req.name);
}
return track;
}
@ -980,6 +1013,31 @@ static void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer)
add_stream_track(mpctx, demuxer->streams[n], !!mpctx->timeline);
}
static void add_dvd_tracks(struct MPContext *mpctx)
{
#ifdef CONFIG_DVDREAD
struct demuxer *demuxer = mpctx->demuxer;
struct stream *stream = demuxer->stream;
if (stream->type == STREAMTYPE_DVD) {
int n_subs = dvd_number_of_subs(stream);
for (int n = 0; n < n_subs; n++) {
struct track *track = talloc_ptrtype(NULL, track);
*track = (struct track) {
.type = STREAM_SUB,
.user_tid = find_new_tid(mpctx, STREAM_SUB),
.demuxer_id = n,
.demuxer = mpctx->demuxer,
};
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
struct stream_lang_req req = {.type = STREAM_SUB, .id = n};
stream_control(stream, STREAM_CTRL_GET_LANG, &req);
track->lang = talloc_steal(track, req.name);
}
}
#endif
}
void add_subtitles(struct MPContext *mpctx, char *filename, float fps,
int noerr)
{
@ -1901,6 +1959,22 @@ static void reinit_subs(struct MPContext *mpctx)
if (!track)
return;
if (track->demuxer && !track->stream) {
// Lazily added DVD track - we must not miss the first subtitle packet,
// which makes the demuxer create the sh_stream, and contains the first
// subtitle event.
// demux_mpg - maps IDs directly to the logical stream number
track->demuxer->sub->id = track->demuxer_id;
// demux_lavf - IDs are essentially random, have to use MPEG IDs
int id = map_id_to_demuxer(track->demuxer, track->type,
track->demuxer_id);
demux_control(track->demuxer, DEMUXER_CTRL_AUTOSELECT_SUBTITLE, &id);
return;
}
mpctx->initialized_flags |= INITIALIZED_SUB;
if (track->vobsub_id_plus_one) {
@ -3795,6 +3869,7 @@ goto_enable_cache:
// On the contrary, the EDL and CUE demuxers are empty wrappers.
mpctx->demuxer = mpctx->timeline[0].source;
}
add_dvd_tracks(mpctx);
add_demuxer_tracks(mpctx, mpctx->demuxer);
mpctx->timeline_part = 0;

View File

@ -96,13 +96,8 @@
#define STREAM_CTRL_GET_NUM_TITLES 12
#define STREAM_CTRL_GET_LANG 13
enum stream_ctrl_type {
stream_ctrl_audio,
stream_ctrl_sub,
};
struct stream_lang_req {
enum stream_ctrl_type type;
int type; // STREAM_AUDIO, STREAM_SUB
int id;
char *name;
};

View File

@ -199,11 +199,11 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
BLURAY_STREAM_INFO *si = NULL;
int count = 0;
switch (req->type) {
case stream_ctrl_audio:
case STREAM_AUDIO:
count = ti->clips[0].audio_stream_count;
si = ti->clips[0].audio_streams;
break;
case stream_ctrl_sub:
case STREAM_SUB:
count = ti->clips[0].pg_stream_count;
si = ti->clips[0].pg_streams;
break;

View File

@ -693,10 +693,10 @@ static int control(stream_t *stream,int cmd,void* arg)
struct stream_lang_req *req = arg;
int lang = 0;
switch(req->type) {
case stream_ctrl_audio:
case STREAM_AUDIO:
lang = dvd_lang_from_aid(stream, req->id);
break;
case stream_ctrl_sub:
case STREAM_SUB:
lang = dvd_lang_from_sid(stream, req->id);
break;
}