diff --git a/Makefile b/Makefile index f7b94f44e9..e91564703e 100644 --- a/Makefile +++ b/Makefile @@ -230,6 +230,7 @@ SOURCES = talloc.c \ sub/find_subfiles.c \ sub/img_convert.c \ sub/sd_lavc.c \ + sub/sd_spu.c \ sub/spudec.c \ sub/sub.c \ sub/subassconvert.c \ diff --git a/core/command.c b/core/command.c index 7eb36be5f3..4e82ceccd8 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" @@ -1307,8 +1306,7 @@ static int mp_property_sub_visibility(m_option_t *prop, int action, switch (action) { case M_PROPERTY_SET: opts->sub_visibility = *(int *)arg; - if (vo_spudec) - vo_osd_changed(OSDTYPE_SPU); + osd_changed_all(mpctx->osd); return M_PROPERTY_OK; case M_PROPERTY_GET: *(int *)arg = opts->sub_visibility; @@ -1317,23 +1315,6 @@ static int mp_property_sub_visibility(m_option_t *prop, int action, 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 @@ -1514,7 +1495,7 @@ static const m_option_t mp_properties[] = { 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-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), diff --git a/core/mp_core.h b/core/mp_core.h index 665588c5c0..db6bc570e1 100644 --- a/core/mp_core.h +++ b/core/mp_core.h @@ -31,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 @@ -293,7 +292,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 bb6a0872cc..c596aa8504 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -95,8 +95,6 @@ #include "core/codecs.h" -#include "sub/spudec.h" - #include "osdep/getch2.h" #include "osdep/timer.h" @@ -554,12 +552,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) { @@ -1104,38 +1096,6 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, 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); - } -#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); - } - - if (vo_spudec != NULL) { - mpctx->initialized_flags |= INITIALIZED_SPUDEC; - mp_property_do("sub-forced-only", M_PROPERTY_SET, - &mpctx->opts.forced_subs_only, mpctx); - } -} - int mp_get_cache_percent(struct MPContext *mpctx) { if (mpctx->stream) { @@ -1867,10 +1827,6 @@ static void reset_subtitles(struct MPContext *mpctx) if (mpctx->sh_sub) sub_reset(mpctx->sh_sub, mpctx->osd); set_osd_subtitle(mpctx, NULL); - if (vo_spudec) { - spudec_reset(vo_spudec); - vo_osd_changed(OSDTYPE_SPU); - } } static void update_subtitles(struct MPContext *mpctx, double refpts_tl) @@ -1892,46 +1848,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) double curpts_s = refpts_tl - mpctx->osd->sub_offset; double refpts_s = refpts_tl - video_offset; - // DVD sub: - if (is_dvd_sub(type) && !(sh_sub && sh_sub->active)) { - int timestamp; - // Get a sub packet from the demuxer (or the vobsub.c thing, which - // should be a demuxer, but isn't). - while (1) { - // Vobsub - len = 0; - { - // DVD sub - assert(d_sub->sh == sh_sub); - len = ds_get_packet_sub(d_sub, (unsigned char **)&packet); - if (len > 0) { - // XXX This is wrong, sh_video->pts can be arbitrarily - // much behind demuxing position. Unfortunately using - // d_video->pts which would have been the simplest - // improvement doesn't work because mpeg specific hacks - // in video.c set d_video->pts to 0. - float x = d_sub->pts - refpts_s; - if (x > -20 && x < 20) // prevent missing subs on pts reset - timestamp = 90000 * d_sub->pts; - else - timestamp = 90000 * curpts_s; - mp_dbg(MSGT_CPLAYER, MSGL_V, "\rDVD sub: len=%d " - "v_pts=%5.3f s_pts=%5.3f ts=%d \n", len, - refpts_s, d_sub->pts, timestamp); - } - } - if (len <= 0 || !packet) - break; - // create it only here, since with some broken demuxers we might - // type = v but no DVD sub and we currently do not change the - // "original frame size" ever after init, leading to wrong-sized - // PGS subtitles. - if (!vo_spudec) - vo_spudec = spudec_new(NULL); - if (timestamp >= 0) - spudec_assemble(vo_spudec, packet, len, timestamp); - } - } else if (d_sub && sh_sub && sh_sub->active) { + if (d_sub && sh_sub && sh_sub->active) { bool non_interleaved = is_non_interleaved(mpctx, track); if (non_interleaved) ds_get_next_pts(d_sub); @@ -1979,11 +1896,6 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) ds_get_next_pts(d_sub); } } - if (vo_spudec) { - spudec_heartbeat(vo_spudec, 90000 * curpts_s); - if (spudec_changed(vo_spudec)) - vo_osd_changed(OSDTYPE_SPU); - } if (!mpctx->osd->render_bitmap_subs) set_osd_subtitle(mpctx, sub_get_text(mpctx->osd, curpts_s)); @@ -2030,10 +1942,10 @@ static double timing_sleep(struct MPContext *mpctx, double time_frame) } static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st, - struct sh_video *sh_video) + int width, int height) { #ifdef CONFIG_DVDREAD - if (st->type != STREAMTYPE_DVD || !sh_video) + if (st->type != STREAMTYPE_DVD) return; struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS; @@ -2042,10 +1954,13 @@ static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st, float cmatrix[3][4]; mp_get_yuv2rgb_coeffs(&csp, cmatrix); - int width = sh_video->disp_w; - int height = sh_video->disp_h; int *palette = ((dvd_priv_t *)st->priv)->cur_pgc->palette; + if (width == 0 || height == 0) { + width = 720; + height = 480; + } + char *s = NULL; s = talloc_asprintf_append(s, "size: %dx%d\n", width, height); s = talloc_asprintf_append(s, "palette: "); @@ -2072,6 +1987,7 @@ static void reinit_subs(struct MPContext *mpctx) { struct MPOpts *opts = &mpctx->opts; struct track *track = mpctx->current_track[STREAM_SUB]; + struct osd_state *osd = mpctx->osd; assert(!(mpctx->initialized_flags & INITIALIZED_SUB)); @@ -2098,24 +2014,17 @@ static void reinit_subs(struct MPContext *mpctx) mpctx->initialized_flags |= INITIALIZED_SUB; + osd->sub_video_w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0; + osd->sub_video_h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0; + if (track->sh_sub) { - sub_init(track->sh_sub, mpctx->osd); + sub_init(track->sh_sub, osd); } else if (track->stream) { - struct stream *s = track->demuxer ? track->demuxer->stream : NULL; - if (s && s->type == STREAMTYPE_DVD) - set_dvdsub_fake_extradata(mpctx->sh_sub, s, mpctx->sh_video); - // lavc dvdsubdec doesn't read color/resolution on Libav 9.1 and below - // Don't use it for new ffmpeg; spudec can't handle ffmpeg .idx demuxing - // (ffmpeg added .idx demuxing during lavc 54.79.100) - bool broken_lavc = false; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 40, 0) - broken_lavc = true; -#endif - if (is_dvd_sub(mpctx->sh_sub->gsh->codec) && track->demuxer - && (track->demuxer->type == DEMUXER_TYPE_MPEG_PS || broken_lavc)) - init_vo_spudec(mpctx); - else - sub_init(mpctx->sh_sub, mpctx->osd); + if (track->demuxer && track->demuxer->stream) { + set_dvdsub_fake_extradata(mpctx->sh_sub, track->demuxer->stream, + osd->sub_video_w, osd->sub_video_h); + } + sub_init(mpctx->sh_sub, osd); } // Decides whether to use OSD path or normal subtitle rendering path. diff --git a/demux/demux_mpg.c b/demux/demux_mpg.c index b30fb8e6e1..d47b3afd86 100644 --- a/demux/demux_mpg.c +++ b/demux/demux_mpg.c @@ -523,7 +523,7 @@ static int demux_mpg_read_packet(demuxer_t *demux,int id){ if(!demux->s_streams[aid]){ sh_sub_t *sh = new_sh_sub(demux, aid); - if (sh) sh->gsh->codec = "dvd_subtitle"; + if (sh) sh->gsh->codec = "dvd_subtitle_mpg"; mp_msg(MSGT_DEMUX,MSGL_V,"==> Found subtitle: %d\n",aid); } diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 3de7d1223d..36ba25edd2 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -29,6 +29,16 @@ extern const struct sd_functions sd_ass; extern const struct sd_functions sd_lavc; +extern const struct sd_functions sd_spu; + +static const struct sd_functions *sd_list[] = { +#ifdef CONFIG_ASS + &sd_ass, +#endif + &sd_lavc, + &sd_spu, + NULL +}; bool is_text_sub(const char *t) { @@ -46,20 +56,20 @@ bool is_ass_sub(const char *t) bool is_dvd_sub(const char *t) { - return t && strcmp(t, "dvd_subtitle") == 0; + return t && (strcmp(t, "dvd_subtitle") == 0 || + strcmp(t, "dvd_subtitle_mpg") == 0); } void sub_init(struct sh_sub *sh, struct osd_state *osd) { - const char *format = sh->gsh->codec; + sh->sd_driver = NULL; + for (int n = 0; sd_list[n]; n++) { + if (sd_list[n]->supports_format(sh->gsh->codec)) { + sh->sd_driver = sd_list[n]; + break; + } + } - assert(!osd->sh_sub); - if (sd_lavc.supports_format(format)) - sh->sd_driver = &sd_lavc; -#ifdef CONFIG_ASS - if (sd_ass.supports_format(format)) - sh->sd_driver = &sd_ass; -#endif if (sh->sd_driver) { if (sh->sd_driver->init(sh, osd) < 0) return; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 1501f5da54..3dfbbd05fb 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -295,7 +295,6 @@ struct sh_sub *sd_ass_create_from_track(struct ass_track *track, .gsh = talloc_struct(sh, struct sh_stream, { .codec = codec, }), - .sd_driver = &sd_ass, .context = talloc_struct(sh, struct sd_ass_priv, { .ass_track = track, .vsfilter_aspect = is_ass_sub(codec), diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index e067da6a43..4e1d80d8ce 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -43,6 +43,14 @@ struct sd_lavc_priv { static bool supports_format(const char *format) { + // lavc dvdsubdec doesn't read color/resolution on Libav 9.1 and below, + // so fall back to sd_spu in this case. Never use sd_spu with new ffmpeg; + // spudec can't handle ffmpeg .idx demuxing (added to lavc in 54.79.100). +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 40, 0) + if (is_dvd_sub(format)) + return false; +#endif + enum AVCodecID cid = mp_codec_to_av_codec_id(format); // Supported codecs must be known to decode to paletted bitmaps switch (cid) { diff --git a/sub/sd_spu.c b/sub/sd_spu.c new file mode 100644 index 0000000000..fc4e4701dc --- /dev/null +++ b/sub/sd_spu.c @@ -0,0 +1,99 @@ +/* + * 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 . + */ + +#include +#include + +#include "talloc.h" +#include "core/options.h" +#include "demux/stheader.h" +#include "sd.h" +#include "sub.h" +#include "spudec.h" + +struct sd_spu_priv { + void *spudec; +}; + +static bool supports_format(const char *format) +{ + return is_dvd_sub(format); +} + +static int init(struct sh_sub *sh, struct osd_state *osd) +{ + if (sh->initialized) + return 0; + void *spudec = spudec_new_scaled(osd->sub_video_w, osd->sub_video_h, + sh->extradata, sh->extradata_len); + if (!spudec) + return -1; + struct sd_spu_priv *priv = talloc_zero(NULL, struct sd_spu_priv); + priv->spudec = spudec; + sh->context = priv; + return 0; +} + +static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, + int data_len, double pts, double duration) +{ + struct sd_spu_priv *priv = sh->context; + + if (pts < 0 || data_len == 0) + return; + + spudec_assemble(priv->spudec, data, data_len, pts * 90000); +} + +static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, + struct mp_osd_res d, double pts, + struct sub_bitmaps *res) +{ + struct MPOpts *opts = sh->opts; + struct sd_spu_priv *priv = sh->context; + + spudec_set_forced_subs_only(priv->spudec, opts->forced_subs_only); + spudec_heartbeat(priv->spudec, pts * 90000); + + if (spudec_visible(priv->spudec)) + spudec_get_indexed(priv->spudec, &d, res); +} + +static void reset(struct sh_sub *sh, struct osd_state *osd) +{ + struct sd_spu_priv *priv = sh->context; + + spudec_reset(priv->spudec); +} + +static void uninit(struct sh_sub *sh) +{ + struct sd_spu_priv *priv = sh->context; + + spudec_free(priv->spudec); + talloc_free(priv); +} + +const struct sd_functions sd_spu = { + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_bitmaps = get_bitmaps, + .reset = reset, + .switch_off = reset, + .uninit = uninit, +}; diff --git a/sub/spudec.c b/sub/spudec.c index 59c3058251..87cd46bdd7 100644 --- a/sub/spudec.c +++ b/sub/spudec.c @@ -575,6 +575,13 @@ void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pt #endif } +void spudec_set_changed(void *this) +{ + spudec_handle_t *spu = this; + + spu->spu_changed = 1; +} + void spudec_reset(void *this) // called after seek { spudec_handle_t *spu = this; @@ -583,6 +590,7 @@ void spudec_reset(void *this) // called after seek spu->now_pts = 0; spu->end_pts = 0; spu->packet_size = spu->packet_offset = 0; + spudec_set_changed(spu); } void spudec_heartbeat(void *this, unsigned int pts100) @@ -611,7 +619,7 @@ void spudec_heartbeat(void *this, unsigned int pts100) spudec_process_data(spu, packet); } spudec_free_packet(packet); - spu->spu_changed = 1; + spudec_set_changed(spu); } } @@ -626,10 +634,11 @@ int spudec_visible(void *this){ void spudec_set_forced_subs_only(void * const this, const unsigned int flag) { - if(this){ - ((spudec_handle_t *)this)->forced_subs_only=flag; - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled"); - } + spudec_handle_t *spu = this; + if (!!flag != !!spu->forced_subs_only) { + spu->forced_subs_only = !!flag; + spudec_set_changed(spu); + } } void spudec_get_indexed(void *this, struct mp_osd_res *dim, @@ -738,17 +747,14 @@ static void spudec_parse_extradata(spudec_handle_t *this, free(buffer); } -void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len) +void *spudec_new_scaled(unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len) { spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t)); if (this){ this->orig_frame_height = frame_height; this->orig_frame_width = frame_width; // set up palette: - if (palette) - memcpy(this->global_palette, palette, sizeof(this->global_palette)); - else - this->auto_palette = 1; + this->auto_palette = 1; if (extradata) spudec_parse_extradata(this, extradata, extradata_len); /* XXX Although the video frame is some size, the SPU frame is @@ -768,11 +774,6 @@ void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigne return this; } -void *spudec_new(unsigned int *palette) -{ - return spudec_new_scaled(palette, 0, 0, NULL, 0); -} - void spudec_free(void *this) { spudec_handle_t *spu = this; diff --git a/sub/spudec.h b/sub/spudec.h index fa395798ac..3a44bd1e2e 100644 --- a/sub/spudec.h +++ b/sub/spudec.h @@ -27,12 +27,12 @@ struct mp_osd_res; void spudec_heartbeat(void *this, unsigned int pts100); void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100); void spudec_get_indexed(void *this, struct mp_osd_res *dim, struct sub_bitmaps *res); -void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len); -void *spudec_new(unsigned int *palette); +void *spudec_new_scaled(unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len); void spudec_free(void *this); void spudec_reset(void *this); // called after seek int spudec_visible(void *this); // check if spu is visible int spudec_changed(void *this); +void spudec_set_changed(void *this); void spudec_set_forced_subs_only(void * const this, const unsigned int flag); #endif /* MPLAYER_SPUDEC_H */ diff --git a/sub/sub.c b/sub/sub.c index 4e5420627c..d26ce17551 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -37,7 +37,6 @@ #include "dec_sub.h" #include "img_convert.h" #include "draw_bmp.h" -#include "spudec.h" #include "subreader.h" #include "video/mp_image.h" #include "video/mp_image_pool.h" @@ -48,9 +47,6 @@ int sub_visibility=1; float sub_delay = 0; float sub_fps = 0; -void *vo_spudec=NULL; -void *vo_vobsub=NULL; - static const struct osd_style_opts osd_style_opts_def = { .font = "Sans", .font_size = 45, @@ -114,7 +110,6 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib) osd->objs[n] = obj; } - osd->objs[OSDTYPE_SPU]->is_sub = true; // spudec.c osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c osd->objs[OSDTYPE_SUBTEXT]->is_sub = true; // osd_libass.c @@ -155,12 +150,6 @@ void osd_set_sub(struct osd_state *osd, const char *text) vo_osd_changed(OSDTYPE_SUBTEXT); } -static bool spu_visible(struct osd_state *osd, struct osd_object *obj) -{ - struct MPOpts *opts = osd->opts; - return opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec); -} - static void render_object(struct osd_state *osd, struct osd_object *obj, struct mp_osd_res res, double video_pts, const bool sub_formats[SUBBITMAP_COUNT], @@ -179,10 +168,7 @@ static void render_object(struct osd_state *osd, struct osd_object *obj, obj->force_redraw = true; obj->vo_res = res; - if (obj->type == OSDTYPE_SPU) { - if (spu_visible(osd, obj)) - spudec_get_indexed(vo_spudec, &obj->vo_res, out_imgs); - } else if (obj->type == OSDTYPE_SUB) { + if (obj->type == OSDTYPE_SUB) { if (osd->render_bitmap_subs) { double sub_pts = video_pts; if (sub_pts != MP_NOPTS_VALUE) diff --git a/sub/sub.h b/sub/sub.h index a922d2b162..6b15cf90c0 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -86,7 +86,6 @@ struct mp_osd_res { enum mp_osdtype { OSDTYPE_SUB, OSDTYPE_SUBTEXT, - OSDTYPE_SPU, OSDTYPE_PROGBAR, OSDTYPE_OSD, @@ -144,6 +143,10 @@ struct osd_state { struct MPOpts *opts; + // Video resolution used for subtitle decoding. Doesn't necessarily match + // the resolution of the VO, nor does it have to be the OSD resolution. + int sub_video_w, sub_video_h; + // Internal to sub.c struct mp_draw_sub_cache *draw_cache; @@ -152,9 +155,6 @@ struct osd_state { struct ass_library *osd_ass_library; }; -extern void* vo_spudec; -extern void* vo_vobsub; - // Start of OSD symbols in osd_font.pfb #define OSD_CODEPOINTS 0xE000