diff options
Diffstat (limited to 'core/mplayer.c')
-rw-r--r-- | core/mplayer.c | 415 |
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); |