diff options
42 files changed, 1770 insertions, 1525 deletions
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index f1aea822e7..8239375079 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -93,13 +93,11 @@ - ``--aspect=16:9`` or ``--aspect=1.7777`` --ass, --no-ass - Render ASS subtitles natively, and convert text subtitles in other formats - to ASS internally (enabled by default). + Render ASS subtitles natively (enabled by default). - If ``--no-ass`` is specified, all subtitles are converted to plain text - internally. All tags and style declarations are stripped and ignored. The - subtitle renderer uses the font style as specified by the ``--sub-text-`` - options instead. + If ``--no-ass`` is specified, all tags and style declarations are stripped + and ignored on display. The subtitle renderer uses the font style as + specified by the ``--sub-text-`` options instead. *NOTE*: Using ``--no-ass`` may lead to incorrect or completely broken rendering of ASS/SSA subtitles. It can sometimes be useful to forcibly @@ -1287,7 +1285,10 @@ See ``quit_watch_later`` input command. --no-sub - Disables display of internal and external subtitles. + Don't select any subtitle when the file is loaded. + +--no-sub-visibility + Disable display of subtitles, but still select and decode them. --no-video Do not play video. With some demuxers this may not work. In those cases @@ -206,6 +206,7 @@ SOURCES = talloc.c \ demux/demux_mf.c \ demux/demux_mkv.c \ demux/demux_mpg.c \ + demux/demux_sub.c \ demux/demux_ts.c \ demux/mp3_hdr.c \ demux/parse_es.c \ @@ -228,13 +229,16 @@ SOURCES = talloc.c \ stream/url.c \ sub/dec_sub.c \ sub/draw_bmp.c \ - sub/find_sub.c \ sub/find_subfiles.c \ sub/img_convert.c \ sub/sd_lavc.c \ + sub/sd_lavc_conv.c \ + sub/sd_microdvd.c \ + sub/sd_movtext.c \ + sub/sd_spu.c \ + sub/sd_srt.c \ sub/spudec.c \ sub/sub.c \ - sub/subassconvert.c \ sub/subreader.c \ video/csputils.c \ video/fmt-conversion.c \ diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index 8abc0a6035..b5a4ee1ef8 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -402,13 +402,10 @@ static int decode_new_packet(struct sh_audio *sh) } AVPacket pkt; - av_init_packet(&pkt); + mp_set_av_packet(&pkt, mpkt); pkt.data = start; pkt.size = insize; - if (mpkt && mpkt->avpacket) { - pkt.side_data = mpkt->avpacket->side_data; - pkt.side_data_elems = mpkt->avpacket->side_data_elems; - } + if (pts != MP_NOPTS_VALUE && !packet_already_used) { sh->pts = pts; sh->pts_bytes = 0; @@ -2684,6 +2684,17 @@ else fi +echocheck "libavcodec AV_CODEC_PROP_TEXT_SUB API" +_avcodec_has_text_flag_api=no +statement_check libavcodec/avcodec.h 'int x = AV_CODEC_PROP_TEXT_SUB' && _avcodec_has_text_flag_api=yes +if test "$_avcodec_has_text_flag_api" = yes ; then + def_avcodec_has_text_flag_api='#define HAVE_AV_CODEC_PROP_TEXT_SUB 1' +else + def_avcodec_has_text_flag_api='#define HAVE_AV_CODEC_PROP_TEXT_SUB 0' +fi +echores "$_avcodec_has_text_flag_api" + + echocheck "libavutil QP API" _avutil_has_qp_api=no statement_check libavutil/frame.h 'av_frame_get_qp_table(NULL, NULL, NULL)' && _avutil_has_qp_api=yes @@ -3268,6 +3279,7 @@ $def_zlib $def_avutil_has_refcounting $def_avutil_has_qp_api +$def_avcodec_has_text_flag_api $def_libpostproc $def_libavdevice $def_libavfilter diff --git a/core/av_common.c b/core/av_common.c index 5e6c8a4352..a4dc525aa9 100644 --- a/core/av_common.c +++ b/core/av_common.c @@ -18,8 +18,10 @@ #include <assert.h> #include <libavutil/common.h> +#include <libavcodec/avcodec.h> #include "core/mp_talloc.h" +#include "demux/demux_packet.h" #include "av_common.h" #include "codecs.h" @@ -58,6 +60,24 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st) avctx->bits_per_coded_sample = st->bits_per_coded_sample; } +// Set dst from mpkt. Note that dst is not refcountable. +// mpkt can be NULL to generate empty packets (used to flush delayed data). +// Does not set pts or duration fields. +void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt) +{ + av_init_packet(dst); + dst->data = mpkt ? mpkt->buffer : NULL; + dst->size = mpkt ? mpkt->len : 0; + /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info + * from demuxer. */ + if (mpkt && mpkt->keyframe) + dst->flags |= AV_PKT_FLAG_KEY; + if (mpkt && mpkt->avpacket) { + dst->side_data = mpkt->avpacket->side_data; + dst->side_data_elems = mpkt->avpacket->side_data_elems; + } +} + void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type) { AVCodec *cur = NULL; diff --git a/core/av_common.h b/core/av_common.h index 25593ed3d0..2fa8f127b0 100644 --- a/core/av_common.h +++ b/core/av_common.h @@ -22,8 +22,10 @@ #include <libavcodec/avcodec.h> struct mp_decoder_list; +struct demux_packet; void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st); +void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt); void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type); int mp_codec_to_av_codec_id(const char *codec); const char *mp_codec_from_av_codec_id(int codec_id); diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h index 5a2c32e0d0..c29d4c4060 100644 --- a/core/cfg-mplayer.h +++ b/core/cfg-mplayer.h @@ -472,17 +472,18 @@ const m_option_t common_opts[] = { OPT_STRINGLIST("sub", sub_name, 0), OPT_PATHLIST("sub-paths", sub_paths, 0), - {"subcp", &sub_cp, CONF_TYPE_STRING, 0, 0, 0, NULL}, - {"sub-delay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL}, - {"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL}, + OPT_STRING("subcp", sub_cp, 0), + OPT_FLOAT("sub-delay", sub_delay, 0), + OPT_FLOAT("subfps", sub_fps, 0), OPT_FLAG("autosub", sub_auto, 0), + OPT_FLAG("sub-visibility", sub_visibility, 0), OPT_FLAG("sub-forced-only", forced_subs_only, 0), // enable Closed Captioning display - {"overlapsub", &suboverlap_enabled, CONF_TYPE_FLAG, 0, 0, 2, NULL}, - {"sub-no-text-pp", &sub_no_text_pp, CONF_TYPE_FLAG, 0, 0, 1, NULL}, - {"autosub-match", &sub_match_fuzziness, CONF_TYPE_CHOICE, 0, - M_CHOICES(({"exact", 0}, {"fuzzy", 1}, {"all", 2}))}, - {"sub-pos", &sub_pos, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL}, + OPT_FLAG_CONSTANTS("overlapsub", suboverlap_enabled, 0, 0, 2), + OPT_FLAG_STORE("sub-no-text-pp", sub_no_text_pp, 0, 1), + OPT_CHOICE("autosub-match", sub_match_fuzziness, 0, + ({"exact", 0}, {"fuzzy", 1}, {"all", 2})), + OPT_INTRANGE("sub-pos", sub_pos, 0, 0, 100), OPT_FLOATRANGE("sub-gauss", sub_gauss, 0, 0.0, 3.0), OPT_FLAG("sub-gray", sub_gray, 0), OPT_FLAG("ass", ass_enabled, 0), diff --git a/core/command.c b/core/command.c index 3ee8f39c3d..416fbcd06a 100644 --- a/core/command.c +++ b/core/command.c @@ -50,7 +50,6 @@ #include "audio/filter/af.h" #include "video/decode/dec_video.h" #include "audio/decode/dec_audio.h" -#include "sub/spudec.h" #include "core/path.h" #include "sub/ass_mp.h" #include "stream/tv.h" @@ -1273,11 +1272,12 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg, static int mp_property_sub_delay(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_video) return M_PROPERTY_UNAVAILABLE; switch (action) { case M_PROPERTY_PRINT: - *(char **)arg = format_delay(sub_delay); + *(char **)arg = format_delay(opts->sub_delay); return M_PROPERTY_OK; } return mp_property_generic_option(prop, action, arg, mpctx); @@ -1286,56 +1286,16 @@ static int mp_property_sub_delay(m_option_t *prop, int action, void *arg, static int mp_property_sub_pos(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_video) return M_PROPERTY_UNAVAILABLE; if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%d/100", sub_pos); + *(char **)arg = talloc_asprintf(NULL, "%d/100", opts->sub_pos); return M_PROPERTY_OK; } return property_osd_helper(prop, action, arg, mpctx); } -/// Subtitle visibility (RW) -static int mp_property_sub_visibility(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct MPOpts *opts = &mpctx->opts; - - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_SET: - opts->sub_visibility = *(int *)arg; - vo_osd_changed(OSDTYPE_SUBTITLE); - if (vo_spudec) - vo_osd_changed(OSDTYPE_SPU); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(int *)arg = opts->sub_visibility; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Show only forced subtitles (RW) -static int mp_property_sub_forced_only(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct MPOpts *opts = &mpctx->opts; - - if (!vo_spudec) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_SET) { - opts->forced_subs_only = *(int *)arg; - spudec_set_forced_subs_only(vo_spudec, opts->forced_subs_only); - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - - #ifdef CONFIG_TV static tvi_handle_t *get_tvh(struct MPContext *mpctx) @@ -1513,9 +1473,8 @@ static const m_option_t mp_properties[] = { M_OPTION_PROPERTY_CUSTOM("sid", mp_property_sub), M_OPTION_PROPERTY_CUSTOM("sub-delay", mp_property_sub_delay), M_OPTION_PROPERTY_CUSTOM("sub-pos", mp_property_sub_pos), - { "sub-visibility", mp_property_sub_visibility, CONF_TYPE_FLAG, - M_OPT_RANGE, 0, 1, NULL }, - M_OPTION_PROPERTY_CUSTOM("sub-forced-only", mp_property_sub_forced_only), + M_OPTION_PROPERTY_CUSTOM("sub-visibility", property_osd_helper), + M_OPTION_PROPERTY_CUSTOM("sub-forced-only", property_osd_helper), M_OPTION_PROPERTY_CUSTOM("sub-scale", property_osd_helper), #ifdef CONFIG_ASS M_OPTION_PROPERTY_CUSTOM("ass-use-margins", property_osd_helper), @@ -1991,26 +1950,18 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) } case MP_CMD_SUB_STEP: - if (sh_video) { - int movement = cmd->args[0].v.i; - struct track *track = mpctx->current_track[STREAM_SUB]; - bool available = false; - if (track && track->subdata) { - available = true; - step_sub(track->subdata, mpctx->video_pts, movement); - } #ifdef CONFIG_ASS - struct ass_track *ass_track = sub_get_ass_track(mpctx->osd); + if (mpctx->osd->dec_sub) { + int movement = cmd->args[0].v.i; + struct ass_track *ass_track = sub_get_ass_track(mpctx->osd->dec_sub); if (ass_track) { - available = true; - sub_delay += ass_step_sub(ass_track, - (mpctx->video_pts + sub_delay) * 1000 + .5, movement) / 1000.; - } -#endif - if (available) set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration, - "Sub delay: %d ms", ROUND(sub_delay * 1000)); + "Sub delay: %d ms", ROUND(opts->sub_delay * 1000)); + double cur = (mpctx->video_pts + opts->sub_delay) * 1000 + .5; + opts->sub_delay += ass_step_sub(ass_track, cur, movement) / 1000.; + } } +#endif break; case MP_CMD_OSD: { @@ -2194,7 +2145,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (tv_channel_list) { set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "Channel: %s", tv_channel_current->name); - //vo_osd_changed(OSDTYPE_SUBTITLE); } } #ifdef CONFIG_PVR @@ -2232,7 +2182,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (tv_channel_list) { set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "Channel: %s", tv_channel_current->name); - //vo_osd_changed(OSDTYPE_SUBTITLE); } } #ifdef CONFIG_PVR @@ -2265,7 +2214,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (tv_channel_list) { set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "Channel: %s", tv_channel_current->name); - //vo_osd_changed(OSDTYPE_SUBTITLE); } } #ifdef CONFIG_PVR diff --git a/core/defaultopts.c b/core/defaultopts.c index 5f6521ebfb..a8a6b26930 100644 --- a/core/defaultopts.c +++ b/core/defaultopts.c @@ -74,6 +74,7 @@ void set_default_mplayer_options(struct MPOpts *opts) .sub_id = -1, .audio_display = 1, .sub_visibility = 1, + .sub_pos = 100, .extension_parsing = 1, .audio_output_channels = MP_CHMAP_INIT_STEREO, .audio_output_format = -1, // AF_FORMAT_UNKNOWN @@ -89,6 +90,7 @@ void set_default_mplayer_options(struct MPOpts *opts) .ass_vsfilter_aspect_compat = 1, .ass_style_override = 1, .use_embedded_fonts = 1, + .suboverlap_enabled = 1, .hwdec_codecs = "all", diff --git a/core/mp_core.h b/core/mp_core.h index 9e61c8ffa3..d7aa42e38d 100644 --- a/core/mp_core.h +++ b/core/mp_core.h @@ -22,8 +22,6 @@ #include <stdbool.h> #include "core/options.h" -#include "sub/subreader.h" -#include "sub/find_subfiles.h" #include "audio/mixer.h" #include "demux/demux.h" @@ -33,7 +31,6 @@ #define INITIALIZED_AO 2 #define INITIALIZED_VOL 4 #define INITIALIZED_GETCH2 8 -#define INITIALIZED_SPUDEC 32 #define INITIALIZED_STREAM 64 #define INITIALIZED_DEMUXER 512 #define INITIALIZED_ACODEC 1024 @@ -106,15 +103,9 @@ struct track { // Invariant: (!demuxer && !stream) || stream->demuxer == demuxer struct sh_stream *stream; - // NOTE: demuxer subtitles, i.e. if stream!=NULL, do not use the following - // fields. The data is stored in stream->sub this case. - - // External text subtitle using libass subtitle renderer. - // The sh_sub is a dummy and doesn't belong to a demuxer. - struct sh_sub *sh_sub; - - // External text subtitle using non-libass subtitle renderer. - struct sub_data *subdata; + // For external subtitles, which are read fully on init. Do not attempt + // to read packets from them. + bool preloaded; }; enum { @@ -129,7 +120,6 @@ typedef struct MPContext { struct osd_state *osd; struct mp_osd_msg *osd_msg_stack; char *terminal_osd_text; - subtitle subs; // subtitle list used when reading subtitles from demuxer int add_osd_seek_info; // bitfield of enum mp_osd_seek_info double osd_visible; // for the osd bar only @@ -299,7 +289,6 @@ extern int forced_subs_only; void uninit_player(struct MPContext *mpctx, unsigned int mask); void reinit_audio_chain(struct MPContext *mpctx); -void init_vo_spudec(struct MPContext *mpctx); double playing_audio_pts(struct MPContext *mpctx); struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, float fps, int noerr); diff --git a/core/mplayer.c b/core/mplayer.c index b5123a3d41..baae55b581 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -74,6 +74,7 @@ #include "sub/subreader.h" #include "sub/find_subfiles.h" #include "sub/dec_sub.h" +#include "sub/sd.h" #include "core/mp_osd.h" #include "video/out/vo.h" @@ -94,8 +95,6 @@ #include "core/codecs.h" -#include "sub/spudec.h" - #include "osdep/getch2.h" #include "osdep/timer.h" @@ -282,10 +281,6 @@ static void print_stream(struct MPContext *mpctx, struct track *t) if (t->title) mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", t->title); const char *codec = s ? s->codec : NULL; - if (!codec && t->sh_sub) // external subs hack - codec = t->sh_sub->gsh->codec; - if (!codec && t->subdata) - codec = t->subdata->codec; mp_msg(MSGT_CPLAYER, MSGL_INFO, " (%s)", codec ? codec : "<unknown>"); if (t->is_external) mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)"); @@ -462,8 +457,10 @@ static void uninit_subs(struct demuxer *demuxer) { for (int i = 0; i < MAX_S_STREAMS; i++) { struct sh_sub *sh = demuxer->s_streams[i]; - if (sh && sh->initialized) - sub_uninit(sh); + if (sh) { + sub_destroy(sh->dec_sub); + sh->dec_sub = NULL; + } } } @@ -485,14 +482,10 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mask & INITIALIZED_SUB) { mpctx->initialized_flags &= ~INITIALIZED_SUB; - struct track *track = mpctx->current_track[STREAM_SUB]; - // One of these was active; they can't be both active. - assert(!(mpctx->sh_sub && track && track->sh_sub)); if (mpctx->sh_sub) - sub_switchoff(mpctx->sh_sub, mpctx->osd); - if (track && track->sh_sub) - sub_switchoff(track->sh_sub, mpctx->osd); + sub_reset(mpctx->sh_sub->dec_sub); cleanup_demux_stream(mpctx, STREAM_SUB); + mpctx->osd->dec_sub = NULL; reset_subtitles(mpctx); } @@ -555,12 +548,6 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) getch2_disable(); } - if (mask & INITIALIZED_SPUDEC) { - mpctx->initialized_flags &= ~INITIALIZED_SPUDEC; - spudec_free(vo_spudec); - vo_spudec = NULL; - } - if (mask & INITIALIZED_VOL) { mpctx->initialized_flags &= ~INITIALIZED_VOL; if (mpctx->mixer.ao) { @@ -1048,96 +1035,67 @@ 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; sub_data *subd = NULL; - struct sh_sub *sh = NULL; if (filename == NULL) return NULL; - if (opts->ass_enabled) { + // 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 - struct ass_track *asst = mp_ass_read_stream(mpctx->ass_library, - filename, sub_cp); - bool is_native_ass = asst; - const char *codec = NULL; - if (!asst) { - subd = sub_read_file(filename, fps, &mpctx->opts); - if (subd) { - codec = subd->codec; - asst = mp_ass_read_subdata(mpctx->ass_library, opts, subd, fps); - talloc_free(subd); - subd = NULL; - } - } - if (asst) { - sh = sd_ass_create_from_track(asst, is_native_ass, opts); - if (codec) - sh->gsh->codec = codec; - } -#endif - } else + asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->sub_cp); + if (!asst) subd = sub_read_file(filename, fps, &mpctx->opts); - - - if (!sh && !subd) { - 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; - } - - struct track *track = talloc_ptrtype(NULL, track); - *track = (struct track) { - .type = STREAM_SUB, - .title = talloc_strdup(track, filename), - .user_tid = find_new_tid(mpctx, STREAM_SUB), - .demuxer_id = -1, - .is_external = true, - .sh_sub = talloc_steal(track, sh), - .subdata = talloc_steal(track, subd), - .external_filename = talloc_strdup(track, filename), - }; - MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track); - return track; -} - -void init_vo_spudec(struct MPContext *mpctx) -{ - uninit_player(mpctx, INITIALIZED_SPUDEC); - unsigned width, height; - - // we currently can't work without video stream - if (!mpctx->sh_video) - return; - - width = mpctx->sh_video->disp_w; - height = mpctx->sh_video->disp_h; - -#ifdef CONFIG_DVDREAD - if (vo_spudec == NULL && mpctx->stream->type == STREAMTYPE_DVD) { - vo_spudec = spudec_new_scaled(((dvd_priv_t *)(mpctx->stream->priv))-> - cur_pgc->palette, width, height, NULL, 0); + if (asst || subd) { + 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 = asst ? "ass" : subd->codec; + s->sub->track = asst; + s->sub->sub_data = subd; + + 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 - if (vo_spudec == NULL && mpctx->sh_sub) { - sh_sub_t *sh = mpctx->sh_sub; - vo_spudec = spudec_new_scaled(NULL, width, height, sh->extradata, - sh->extradata_len); - } + // Used with libavformat subtitles. + struct track *ext = open_external_file(mpctx, filename, NULL, 0, STREAM_SUB); + if (ext) + return ext; - if (vo_spudec != NULL) { - mpctx->initialized_flags |= INITIALIZED_SPUDEC; - mp_property_do("sub-forced-only", M_PROPERTY_SET, - &mpctx->opts.forced_subs_only, mpctx); - } + 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) @@ -1433,7 +1391,7 @@ static mp_osd_msg_t *get_osd_msg(struct MPContext |