sub: add demux_libass wrapper, drop old hacks

demux_libass.c allows us to make subtitle format detection part of the
normal file loading process. libass has no probe function, but trying to
load the start of a file (the first 4 KB) is good enough. Hope that
libass can even handle random binary input gracefully without printing
stupid log messages, and that the libass parser doesn't accept too many
non-ASS files as input.

This doesn't handle the -subcp option correctly yet. This will be fixed
later.
This commit is contained in:
wm4 2013-06-22 02:09:52 +02:00
parent 1bfae45a88
commit cfa45c40dc
14 changed files with 131 additions and 177 deletions

View File

@ -128,6 +128,7 @@ Command line switches
-dumpstream --stream-dump=<filename>
-capture --stream-capture=<filename>
-stop-xscreensaver --stop-screensaver
-subfile --sub
=================================== ===================================
*NOTE*: ``-opt val`` becomes ``--opt=val``.

View File

@ -1998,8 +1998,7 @@
subtitles muxed with audio/video, or subtitles in the ASS format.
--sub-demuxer=<[+]name>
Force subtitle demuxer type for ``--subfile``. Using a '+' before the name
will force it, this will skip some checks! Give the demuxer name as
Force subtitle demuxer type for ``--sub``. Give the demuxer name as
printed by ``--sub-demuxer=help``.
--sub-paths=<path1:path2:...>
@ -2046,14 +2045,6 @@
--sub-delay=<sec>
Delays subtitles by <sec> seconds. Can be negative.
--subfile=<filename>
Open the given file with a demuxer, and use its subtitle streams. Same as
``--audiofile``, but for subtitle streams.
*NOTE*: use ``--sub`` for subtitle files. This option is useless, unless
you want to force libavformat subtitle parsers instead of libass or
internal subtitle parsers.
--subfps=<rate>
Specify the framerate of the subtitle file (default: movie fps).

View File

@ -37,7 +37,8 @@ SOURCES-$(DVDREAD) += stream/stream_dvd.c \
SOURCES-$(FTP) += stream/stream_ftp.c
SOURCES-$(HAVE_SYS_MMAN_H) += audio/filter/af_export.c osdep/mmap_anon.c
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c \
demux/demux_libass.c
SOURCES-$(LIBBLURAY) += stream/stream_bluray.c
SOURCES-$(LIBBS2B) += audio/filter/af_bs2b.c
@ -207,7 +208,6 @@ SOURCES = talloc.c \
demux/demux_mf.c \
demux/demux_mkv.c \
demux/demux_mpg.c \
demux/demux_sub.c \
demux/demux_subreader.c \
demux/demux_ts.c \
demux/mp3_hdr.c \

View File

@ -2281,7 +2281,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_SUB_ADD:
if (sh_video) {
mp_add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
mp_add_subtitles(mpctx, cmd->args[0].v.s, 0);
}
break;
@ -2296,8 +2296,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
struct track *sub = mp_track_by_tid(mpctx, STREAM_SUB, cmd->args[0].v.i);
if (sh_video && sub && sub->is_external && sub->external_filename)
{
struct track *nsub = mp_add_subtitles(mpctx, sub->external_filename,
sh_video->fps, 0);
struct track *nsub = mp_add_subtitles(mpctx, sub->external_filename, 0);
if (nsub) {
mp_remove_track(mpctx, sub);
mp_switch_track(mpctx, nsub->type, nsub);

View File

@ -290,8 +290,7 @@ extern int forced_subs_only;
void uninit_player(struct MPContext *mpctx, unsigned int mask);
void reinit_audio_chain(struct MPContext *mpctx);
double playing_audio_pts(struct MPContext *mpctx);
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
float fps, int noerr);
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, int noerr);
int reinit_video_chain(struct MPContext *mpctx);
int reinit_video_filters(struct MPContext *mpctx);
void pause_player(struct MPContext *mpctx);

View File

@ -196,9 +196,6 @@ static const char av_desync_help_text[] = _(
static void reset_subtitles(struct MPContext *mpctx);
static void reinit_subs(struct MPContext *mpctx);
static struct track *open_external_file(struct MPContext *mpctx, char *filename,
char *demuxer_name, int stream_cache,
enum stream_type filter);
static double get_relative_time(struct MPContext *mpctx)
{
@ -981,6 +978,9 @@ static struct track *add_stream_track(struct MPContext *mpctx,
};
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
if (stream->type == STREAM_SUB)
track->preloaded = !!stream->sub->track;
// Needed for DVD and Blu-ray.
if (!track->lang) {
struct stream_lang_req req = {
@ -1027,65 +1027,6 @@ static void add_dvd_tracks(struct MPContext *mpctx)
#endif
}
#ifdef CONFIG_ASS
static int free_sub_data(void *ptr)
{
struct sh_sub *sh_sub = *(struct sh_sub **)ptr;
if (sh_sub->track)
ass_free_track(sh_sub->track);
talloc_free(sh_sub->sub_data);
return 1;
}
#endif
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
float fps, int noerr)
{
struct MPOpts *opts = &mpctx->opts;
struct ass_track *asst = NULL;
if (filename == NULL)
return NULL;
// Note: no text subtitles without libass. This is mainly because sd_ass is
// used for rendering. Even when showing subtitles with term-osd, going
// through sd_ass makes the code much simpler, as sd_ass can handle all
// the weird special-cases.
#ifdef CONFIG_ASS
asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->sub_cp);
if (asst) {
struct demuxer *d = new_sub_pseudo_demuxer(opts);
assert(d->num_streams == 1);
struct sh_stream *s = d->streams[0];
assert(s->type == STREAM_SUB);
s->codec = "ass";
s->sub->track = asst;
struct sh_sub **pptr = talloc(d, struct sh_sub*);
*pptr = s->sub;
talloc_set_destructor(pptr, free_sub_data);
struct track *t = add_stream_track(mpctx, s, false);
t->is_external = true;
t->preloaded = true;
t->title = talloc_strdup(t, filename);
t->external_filename = talloc_strdup(t, filename);
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, d);
return t;
}
#endif
// Used with libavformat subtitles.
struct track *ext = open_external_file(mpctx, filename, NULL, 0, STREAM_SUB);
if (ext)
return ext;
mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR,
"Cannot load subtitles: %s\n", filename);
return NULL;
}
int mp_get_cache_percent(struct MPContext *mpctx)
{
if (mpctx->stream) {
@ -3916,16 +3857,15 @@ static void open_subtitles_from_options(struct MPContext *mpctx)
// after reading video params we should load subtitles because
// we know fps so now we can adjust subtitle time to ~6 seconds AST
// check .sub
double sub_fps = mpctx->sh_video ? mpctx->sh_video->fps : 25;
if (mpctx->opts.sub_name) {
for (int i = 0; mpctx->opts.sub_name[i] != NULL; ++i)
mp_add_subtitles(mpctx, mpctx->opts.sub_name[i], sub_fps, 0);
mp_add_subtitles(mpctx, mpctx->opts.sub_name[i], 0);
}
if (mpctx->opts.sub_auto) { // auto load sub file ...
char **tmp = find_text_subtitles(&mpctx->opts, mpctx->filename);
int nsub = MP_TALLOC_ELEMS(tmp);
for (int i = 0; i < nsub; i++) {
struct track *track = mp_add_subtitles(mpctx, tmp[i], sub_fps, 1);
struct track *track = mp_add_subtitles(mpctx, tmp[i], 1);
if (track)
track->auto_loaded = true;
}
@ -3955,9 +3895,12 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
case STREAM_SUB: ss = -1; break;
}
vs = -1; // avi can't go without video
struct demuxer_params params = {
.ass_library = mpctx->ass_library, // demux_libass requires it
};
struct demuxer *demuxer =
demux_open_withparams(&mpctx->opts, stream, format, demuxer_name,
as, vs, ss, filename, NULL);
as, vs, ss, filename, &params);
if (!demuxer) {
free_stream(stream);
goto err_out;
@ -3995,12 +3938,11 @@ static void open_audiofiles_from_options(struct MPContext *mpctx)
opts->audio_stream_cache, STREAM_AUDIO);
}
// Just for -subfile. open_subtitles_from_options handles -sub text sub files.
static void open_subfiles_from_options(struct MPContext *mpctx)
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, int noerr)
{
struct MPOpts *opts = &mpctx->opts;
open_external_file(mpctx, opts->sub_stream, opts->sub_demuxer_name,
0, STREAM_SUB);
return open_external_file(mpctx, filename, opts->sub_demuxer_name, 0,
STREAM_SUB);
}
static void print_timeline(struct MPContext *mpctx)
@ -4303,7 +4245,6 @@ goto_reopen_demuxer: ;
open_subtitles_from_options(mpctx);
open_audiofiles_from_options(mpctx);
open_subfiles_from_options(mpctx);
check_previous_track_selection(mpctx);

View File

@ -406,7 +406,6 @@ const m_option_t mp_opts[] = {
// demuxer.c - select audio/sub file/demuxer
OPT_STRING("audiofile", audio_stream, 0),
OPT_INTRANGE("audiofile-cache", audio_stream_cache, 0, 50, 65536),
OPT_STRING("subfile", sub_stream, 0),
OPT_STRING("demuxer", demuxer_name, 0),
OPT_STRING("audio-demuxer", audio_demuxer_name, 0),
OPT_STRING("sub-demuxer", sub_demuxer_name, 0),

View File

@ -150,7 +150,6 @@ typedef struct MPOpts {
char *audio_stream;
int audio_stream_cache;
char *sub_stream;
char *demuxer_name;
char *audio_demuxer_name;
char *sub_demuxer_name;

View File

@ -67,7 +67,7 @@ extern const demuxer_desc_t demuxer_desc_mpeg_es;
extern const demuxer_desc_t demuxer_desc_mpeg4_es;
extern const demuxer_desc_t demuxer_desc_h264_es;
extern const demuxer_desc_t demuxer_desc_mpeg_ts;
extern const demuxer_desc_t demuxer_desc_sub;
extern const demuxer_desc_t demuxer_desc_libass;
extern const demuxer_desc_t demuxer_desc_subreader;
/* Please do not add any new demuxers here. If you want to implement a new
@ -82,6 +82,7 @@ const demuxer_desc_t *const demuxer_list[] = {
#ifdef CONFIG_TV
&demuxer_desc_tv,
#endif
&demuxer_desc_libass,
&demuxer_desc_matroska,
&demuxer_desc_lavf,
&demuxer_desc_subreader,
@ -99,8 +100,6 @@ const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_mpeg_ts,
// auto-probe last, because it checks file-extensions only
&demuxer_desc_mf,
// no auto-probe
&demuxer_desc_sub,
/* 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. */
@ -289,18 +288,6 @@ static demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
return d;
}
// for demux_sub.c
demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts)
{
struct stream *s = open_stream("null://", NULL, NULL);
assert(s);
struct demuxer *d = new_demuxer(opts, s, DEMUXER_TYPE_SUB,
-1, -1, -1, NULL);
new_sh_stream(d, STREAM_SUB);
talloc_steal(d, s);
return d;
}
static struct sh_stream *new_sh_stream_id(demuxer_t *demuxer,
enum stream_type type,
int stream_index,

View File

@ -72,13 +72,13 @@ enum demuxer_type {
DEMUXER_TYPE_EDL,
DEMUXER_TYPE_CUE,
DEMUXER_TYPE_SUBREADER,
DEMUXER_TYPE_LIBASS,
/* Values after this are for internal use and can not be selected
* as demuxer type by the user (-demuxer option). */
DEMUXER_TYPE_END,
DEMUXER_TYPE_PLAYLIST,
DEMUXER_TYPE_SUB,
};
enum timestamp_type {
@ -217,6 +217,7 @@ struct demuxer_params {
unsigned char (*matroska_wanted_uids)[16];
int matroska_wanted_segment;
bool *matroska_was_valid;
struct ass_library *ass_library;
};
typedef struct demuxer {
@ -307,9 +308,6 @@ static inline void *realloc_struct(void *ptr, size_t nmemb, size_t size)
return realloc(ptr, nmemb * size);
}
demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts);
void free_demuxer(struct demuxer *demuxer);
int demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,

108
demux/demux_libass.c Normal file
View File

@ -0,0 +1,108 @@
/*
* 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/>.
*/
// Note: just wraps libass, and makes the subtitle track available though
// sh_sub->track. It doesn't produce packets and doesn't support seeking.
#include <ass/ass.h>
#include <ass/ass_types.h>
#include "core/options.h"
#include "core/mp_msg.h"
#include "stream/stream.h"
#include "demux.h"
#define PROBE_SIZE (4 * 1024)
struct priv {
ASS_Track *track;
};
static int d_check_file(struct demuxer *demuxer)
{
struct stream *s = demuxer->stream;
// Older versions of libass will behave strange if renderer and track
// library handles mismatch, so make sure everything uses a global handle.
ASS_Library *lib = demuxer->params ? demuxer->params->ass_library : NULL;
if (!lib)
return 0;
// Probe by loading a part of the beginning of the file with libass.
// Incomplete scripts are usually ok, and we hope libass is not verbose
// when dealing with (from its perspective) completely broken binary
// garbage.
bstr buf = stream_peek(s, PROBE_SIZE);
// Older versions of libass will overwrite the input buffer, and despite
// passing length, expect a 0 termination.
void *tmp = talloc_size(NULL, buf.len + 1);
memcpy(tmp, buf.start, buf.len);
buf.start = tmp;
buf.start[buf.len] = '\0';
ASS_Track *track = ass_read_memory(lib, buf.start, buf.len, NULL);
talloc_free(buf.start);
if (!track)
return 0;
ass_free_track(track);
// Actually load the full thing.
buf = stream_read_complete(s, NULL, 100000000);
if (!buf.start) {
mp_tmsg(MSGT_ASS, MSGL_ERR, "Refusing to load subtitle file "
"larger than 100 MB: %s\n", demuxer->filename);
return 0;
}
track = ass_read_memory(lib, buf.start, buf.len, NULL);
talloc_free(buf.start);
if (!track)
return 0;
track->name = strdup(demuxer->filename);
struct priv *p = talloc_ptrtype(demuxer, p);
*p = (struct priv) {
.track = track,
};
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
sh->sub->track = track;
sh->codec = "ass";
return DEMUXER_TYPE_LIBASS;
}
static void d_close(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
if (p) {
if (p->track)
ass_free_track(p->track);
}
}
const struct demuxer_desc demuxer_desc_libass = {
.info = "Read subtitles with libass",
.name = "libass",
.shortdesc = "ASS/SSA subtitles (libass)",
.author = "",
.comment = "",
.safe_check = 1,
.type = DEMUXER_TYPE_LIBASS,
.check_file = d_check_file,
.close = d_close,
};

View File

@ -1,38 +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 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/>.
*/
// Note: not a real demuxer. The frontend has its own code to open subtitle
// code, and then creates a new dummy demuxer with new_sub_demuxer().
// But eventually, all subtitles should be opened this way, and this
// file can be removed.
#include "demux.h"
static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds)
{
return 0;
}
const struct demuxer_desc demuxer_desc_sub = {
.info = "External subtitles pseudo demuxer",
.name = "sub",
.shortdesc = "sub",
.author = "",
.comment = "",
.type = DEMUXER_TYPE_SUB,
.fill_buffer = dummy_fill_buffer,
};

View File

@ -112,34 +112,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
return track;
}
ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
char *charset)
{
ASS_Track *track;
struct stream *s = open_stream(fname, NULL, NULL);
if (!s)
// Stream code should have printed an error already
return NULL;
struct bstr content = stream_read_complete(s, NULL, 100000000);
if (content.start == NULL)
mp_tmsg(MSGT_ASS, MSGL_ERR, "Refusing to load subtitle file "
"larger than 100 MB: %s\n", fname);
free_stream(s);
if (content.len == 0) {
talloc_free(content.start);
return NULL;
}
content.start[content.len] = 0;
track = ass_read_memory(library, content.start, content.len, charset);
if (track) {
free(track->name);
track->name = strdup(fname);
}
talloc_free(content.start);
return track;
}
void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
struct mp_osd_res *dim)
{

View File

@ -49,8 +49,6 @@ void mp_ass_set_style(ASS_Style *style, int res_y, struct osd_style_opts *opts);
void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts);
ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts);
ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
char *charset);
struct MPOpts;
void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,