summaryrefslogtreecommitdiffstats
path: root/core/mplayer.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/mplayer.c')
-rw-r--r--core/mplayer.c415
1 files changed, 150 insertions, 265 deletions
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 *mpctx)
if (mpctx->osd_visible && now >= mpctx->osd_visible) {
mpctx->osd_visible = 0;
mpctx->osd->progbar_type = -1; // disable
- vo_osd_changed(OSDTYPE_PROGBAR);
+ osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
}
if (mpctx->osd_function_visible && now >= mpctx->osd_function_visible) {
mpctx->osd_function_visible = 0;
@@ -1492,7 +1450,7 @@ void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
mpctx->osd->progbar_type = type;
mpctx->osd->progbar_value = (val - min) / (max - min);
mpctx->osd->progbar_num_stops = 0;
- vo_osd_changed(OSDTYPE_PROGBAR);
+ osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
return;
}
@@ -1509,7 +1467,7 @@ static void update_osd_bar(struct MPContext *mpctx, int type,
float new_value = (val - min) / (max - min);
if (new_value != mpctx->osd->progbar_value) {
mpctx->osd->progbar_value = new_value;
- vo_osd_changed(OSDTYPE_PROGBAR);
+ osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
}
}
}
@@ -1545,25 +1503,20 @@ void set_osd_function(struct MPContext *mpctx, int osd_function)
/**
* \brief Display text subtitles on the OSD
*/
-void set_osd_subtitle(struct MPContext *mpctx, subtitle *subs)
-{
- int i;
- vo_sub = subs;
- vo_osd_changed(OSDTYPE_SUBTITLE);
- if (!mpctx->sh_video) {
- // reverse order, since newest set_osd_msg is displayed first
- for (i = SUB_MAX_TEXT - 1; i >= 0; i--) {
- if (!subs || i >= subs->lines || !subs->text[i])
- rm_osd_msg(mpctx, OSD_MSG_SUB_BASE + i);
- else {
- // HACK: currently display time for each sub line
- // except the last is set to 2 seconds.
- int display_time = i == subs->lines - 1 ? 180000 : 2000;
- set_osd_msg(mpctx, OSD_MSG_SUB_BASE + i, 1, display_time,
- "%s", subs->text[i]);
- }
+static void set_osd_subtitle(struct MPContext *mpctx, const char *text)
+{
+ if (!text)
+ text = "";
+ if (strcmp(mpctx->osd->sub_text, text) != 0) {
+ osd_set_sub(mpctx->osd, text);
+ if (!mpctx->sh_video) {
+ rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
+ if (text && text[0])
+ set_osd_msg(mpctx, OSD_MSG_SUB_BASE, 1, INT_MAX, "%s", text);
}
}
+ if (!text[0])
+ rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
}
// sym == mpctx->osd_function
@@ -1876,149 +1829,79 @@ static bool is_non_interleaved(struct MPContext *mpctx, struct track *track)
static void reset_subtitles(struct MPContext *mpctx)
{
if (mpctx->sh_sub)
- sub_reset(mpctx->sh_sub, mpctx->osd);
- sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
- if (vo_sub)
- set_osd_subtitle(mpctx, NULL);
- if (vo_spudec) {
- spudec_reset(vo_spudec);
- vo_osd_changed(OSDTYPE_SPU);
- }
+ sub_reset(mpctx->sh_sub->dec_sub);
+ set_osd_subtitle(mpctx, NULL);
+ osd_changed(mpctx->osd, OSDTYPE_SUB);
}
static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
{
struct MPOpts *opts = &mpctx->opts;
- struct sh_video *sh_video = mpctx->sh_video;
- struct sh_sub *sh_sub = mpctx->sh_sub;
- struct demux_stream *d_sub = sh_sub ? sh_sub->ds : NULL;
- unsigned char *packet = NULL;
- int len;
- const char *type = sh_sub ? sh_sub->gsh->codec : NULL;
-
- mpctx->osd->sub_offset = mpctx->video_offset;
+ if (!(mpctx->initialized_flags & INITIALIZED_SUB))
+ return;
struct track *track = mpctx->current_track[STREAM_SUB];
- if (!track)
- return;
+ struct sh_sub *sh_sub = mpctx->sh_sub;
+ assert(track && sh_sub);
+ struct dec_sub *dec_sub = sh_sub->dec_sub;
- if (!track->under_timeline)
- mpctx->osd->sub_offset = 0;
+ double video_offset = track->under_timeline ? mpctx->video_offset : 0;
- double refpts_s = refpts_tl - mpctx->osd->sub_offset;
- double curpts_s = refpts_s + sub_delay;
+ mpctx->osd->sub_offset = video_offset - opts->sub_delay;
- // find sub
- if (track->subdata) {
- if (sub_fps == 0)
- sub_fps = sh_video ? sh_video->fps : 25;
- find_sub(mpctx, track->subdata, curpts_s *
- (track->subdata->sub_uses_time ? 100. : sub_fps));
- }
+ 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 && (is_text_sub(type) || (sh_sub && sh_sub->active))) {
+ if (!track->preloaded) {
+ struct demux_stream *d_sub = sh_sub->ds;
bool non_interleaved = is_non_interleaved(mpctx, track);
- if (non_interleaved)
- ds_get_next_pts(d_sub);
- while (d_sub->first) {
+ while (1) {
+ if (non_interleaved)
+ ds_get_next_pts(d_sub);
+ if (!d_sub->first)
+ break;
double subpts_s = ds_get_next_pts(d_sub);
+ if (subpts_s == MP_NOPTS_VALUE) {
+ // Try old method of getting PTS. This is only needed in the
+ // DVD playback case with demux_mpg.
+ // 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
+ subpts_s = d_sub->pts;
+ else
+ subpts_s = curpts_s;
+ }
if (subpts_s > curpts_s) {
mp_dbg(MSGT_CPLAYER, MSGL_DBG2,
"Sub early: c_pts=%5.3f s_pts=%5.3f\n",
curpts_s, subpts_s);
// Libass handled subs can be fed to it in advance
- if (!opts->ass_enabled || !is_text_sub(type))
+ if (!sub_accept_packets_in_advance(dec_sub))
break;
// Try to avoid demuxing whole file at once
if (non_interleaved && subpts_s > curpts_s + 1)
break;
}
- double duration = d_sub->first->duration;
- len = ds_get_packet_sub(d_sub, &packet);
+ struct demux_packet pkt;
+ struct demux_packet *orig = ds_get_packet_sub(d_sub);
+ if (!orig)
+ break;
+ pkt = *orig;
+ pkt.pts = subpts_s;
mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f "
- "duration=%5.3f len=%d\n", curpts_s, subpts_s, duration,
- len);
- if (type && strcmp(type, "mov_text") == 0) {
- if (len < 2)
- continue;
- len = FFMIN(len - 2, AV_RB16(packet));
- packet += 2;
- }
- if (sh_sub && sh_sub->active) {
- sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration);
- } else if (subpts_s != MP_NOPTS_VALUE) {
- // text sub
- if (duration < 0)
- sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
- if (is_ass_sub(type)) { // ssa/ass subs without libass => convert to plaintext
- int i;
- unsigned char *p = packet;
- for (i = 0; i < 8 && *p != '\0'; p++)
- if (*p == ',')
- i++;
- if (*p == '\0') /* Broken line? */
- continue;
- len -= p - packet;
- packet = p;
- }
- double endpts_s = MP_NOPTS_VALUE;
- if (subpts_s != MP_NOPTS_VALUE && duration >= 0)
- endpts_s = subpts_s + duration;
- sub_add_text(&mpctx->subs, packet, len, endpts_s);
- set_osd_subtitle(mpctx, &mpctx->subs);
- }
- if (non_interleaved)
- ds_get_next_pts(d_sub);
+ "duration=%5.3f len=%d\n", curpts_s, pkt.pts, pkt.duration,
+ pkt.len);
+ sub_decode(dec_sub, &pkt);
}
- if (!opts->ass_enabled)
- if (sub_clear_text(&mpctx->subs, curpts_s))
- set_osd_subtitle(mpctx, &mpctx->subs);
- }
- 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 || !mpctx->sh_video)
+ set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s));
}
static int check_framedrop(struct MPContext *mpctx, double frame_time)
@@ -2061,11 +1944,11 @@ static double timing_sleep(struct MPContext *mpctx, double time_frame)
return time_frame;
}
-static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
- struct sh_video *sh_video)
+static void set_dvdsub_fake_extradata(struct dec_sub *dec_sub, struct stream *st,
+ 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;
@@ -2074,10 +1957,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: ");
@@ -2093,9 +1979,7 @@ static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
}
s = talloc_asprintf_append(s, "\n");
- free(sh_sub->extradata);
- sh_sub->extradata = strdup(s);
- sh_sub->extradata_len = strlen(s);
+ sub_set_extradata(dec_sub, s, strlen(s));
talloc_free(s);
#endif
}
@@ -2108,10 +1992,12 @@ static void reinit_subs(struct MPContext *mpctx)
assert(!(mpctx->initialized_flags & INITIALIZED_SUB));
init_demux_stream(mpctx, STREAM_SUB);
-
- if (!track)
+ if (!mpctx->sh_sub)
return;
+ if (!mpctx->sh_sub->dec_sub)
+ mpctx->sh_sub->dec_sub = sub_create(opts);
+
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
@@ -2127,32 +2013,32 @@ static void reinit_subs(struct MPContext *mpctx)
return;
}
+ assert(track->demuxer && track->stream);
mpctx->initialized_flags |= INITIALIZED_SUB;
- if (track->subdata || track->sh_sub) {
-#ifdef CONFIG_ASS
- if (opts->ass_enabled && track->sh_sub)
- sub_init(track->sh_sub, mpctx->osd);
-#endif
- vo_osd_changed(OSDTYPE_SUBTITLE);
- } 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);
+ struct sh_sub *sh_sub = mpctx->sh_sub;
+ struct dec_sub *dec_sub = sh_sub->dec_sub;
+ assert(dec_sub);
+
+ if (!sub_is_initialized(dec_sub)) {
+ int w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0;
+ int h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0;
+
+ set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h);
+ sub_set_video_res(dec_sub, w, h);
+ sub_set_ass_renderer(dec_sub, mpctx->osd->ass_library,
+ mpctx->osd->ass_renderer);
+ sub_init_from_sh(dec_sub, sh_sub);
}
+
+ mpctx->osd->dec_sub = dec_sub;
+
+ // Decides whether to use OSD path or normal subtitle rendering path.
+ mpctx->osd->render_bitmap_subs =
+ opts->ass_enabled || !sub_has_get_text(dec_sub);
+
+ reset_subtitles(mpctx);
}
static char *track_layout_hash(struct MPContext *mpctx)
@@ -2597,7 +2483,7 @@ int reinit_video_chain(struct MPContext *mpctx)
mpctx->delay = 0;
mpctx->vo_pts_history_seek_ts++;
- // ========== Init display (sh_video->disp_w*sh_video->disp_h/out_fmt) ============
+ reset_subtitles(mpctx);
return 1;
@@ -4552,7 +4438,6 @@ terminate_playback: // don't jump here after ao/vo/getch initialization!
talloc_free(mpctx->resolve_result);
mpctx->resolve_result = NULL;
- vo_sub = NULL;
#ifdef CONFIG_ASS
if (mpctx->osd->ass_renderer)
ass_renderer_done(mpctx->osd->ass_renderer);