From cfa45c40dc0cfe44b699029168b62d4d3e16c288 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 22 Jun 2013 02:09:52 +0200 Subject: 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. --- DOCS/man/en/changes.rst | 1 + DOCS/man/en/options.rst | 11 +---- Makefile | 4 +- core/command.c | 5 +-- core/mp_core.h | 3 +- core/mplayer.c | 83 ++++++------------------------------- core/options.c | 1 - core/options.h | 1 - demux/demux.c | 17 +------- demux/demux.h | 6 +-- demux/demux_libass.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ demux/demux_sub.c | 38 ----------------- sub/ass_mp.c | 28 ------------- sub/ass_mp.h | 2 - 14 files changed, 131 insertions(+), 177 deletions(-) create mode 100644 demux/demux_libass.c delete mode 100644 demux/demux_sub.c diff --git a/DOCS/man/en/changes.rst b/DOCS/man/en/changes.rst index de3b24b710..ed83c402f3 100644 --- a/DOCS/man/en/changes.rst +++ b/DOCS/man/en/changes.rst @@ -128,6 +128,7 @@ Command line switches -dumpstream --stream-dump= -capture --stream-capture= -stop-xscreensaver --stop-screensaver + -subfile --sub =================================== =================================== *NOTE*: ``-opt val`` becomes ``--opt=val``. diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index b9068b1562..83ceb15c49 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -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= @@ -2046,14 +2045,6 @@ --sub-delay= Delays subtitles by seconds. Can be negative. ---subfile= - 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= Specify the framerate of the subtitle file (default: movie fps). diff --git a/Makefile b/Makefile index 4e244d8489..1fa78d676b 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/core/command.c b/core/command.c index 4da0653425..c39bb3c16d 100644 --- a/core/command.c +++ b/core/command.c @@ -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); diff --git a/core/mp_core.h b/core/mp_core.h index a327e43822..c958132700 100644 --- a/core/mp_core.h +++ b/core/mp_core.h @@ -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); diff --git a/core/mplayer.c b/core/mplayer.c index 67d82fdb35..6e453278f0 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -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, ¶ms); 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); diff --git a/core/options.c b/core/options.c index 2e0d32bb60..3ce4315572 100644 --- a/core/options.c +++ b/core/options.c @@ -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), diff --git a/core/options.h b/core/options.h index c31d2063c9..6ec051ddca 100644 --- a/core/options.h +++ b/core/options.h @@ -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; diff --git a/demux/demux.c b/demux/demux.c index 369e7f3226..b66f5e3e61 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -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, diff --git a/demux/demux.h b/demux/demux.h index 9c10ca0201..f49a236b80 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -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, diff --git a/demux/demux_libass.c b/demux/demux_libass.c new file mode 100644 index 0000000000..1b7dc24452 --- /dev/null +++ b/demux/demux_libass.c @@ -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 . + */ + +// 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 +#include + +#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, +}; diff --git a/demux/demux_sub.c b/demux/demux_sub.c deleted file mode 100644 index ab99091215..0000000000 --- a/demux/demux_sub.c +++ /dev/null @@ -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 . - */ - -// 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, -}; diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 48a713f6ee..5e87223041 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -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) { diff --git a/sub/ass_mp.h b/sub/ass_mp.h index dbdc95f031..c0fba934eb 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -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, -- cgit v1.2.3