diff options
Diffstat (limited to 'player/sub.c')
-rw-r--r-- | player/sub.c | 281 |
1 files changed, 78 insertions, 203 deletions
diff --git a/player/sub.c b/player/sub.c index c2a3e80cb3..6e707b3d95 100644 --- a/player/sub.c +++ b/player/sub.c @@ -22,7 +22,7 @@ #include <assert.h> #include "config.h" -#include "talloc.h" +#include "mpv_talloc.h" #include "common/msg.h" #include "options/options.h" @@ -30,7 +30,6 @@ #include "common/global.h" #include "stream/stream.h" -#include "sub/ass_mp.h" #include "sub/dec_sub.h" #include "demux/demux.h" #include "video/mp_image.h" @@ -39,265 +38,141 @@ #include "core.h" -#if HAVE_LIBASS - -static const char *const font_mimetypes[] = { - "application/x-truetype-font", - "application/vnd.ms-opentype", - "application/x-font-ttf", - "application/x-font", // probably incorrect - NULL -}; - -static const char *const font_exts[] = {".ttf", ".ttc", ".otf", NULL}; - -static bool attachment_is_font(struct mp_log *log, struct demux_attachment *att) +// 0: primary sub, 1: secondary sub, -1: not selected +static int get_order(struct MPContext *mpctx, struct track *track) { - if (!att->name || !att->type || !att->data || !att->data_size) - return false; - for (int n = 0; font_mimetypes[n]; n++) { - if (strcmp(font_mimetypes[n], att->type) == 0) - return true; + for (int n = 0; n < NUM_PTRACKS; n++) { + if (mpctx->current_track[n][STREAM_SUB] == track) + return n; } - // fallback: match against file extension - char *ext = strlen(att->name) > 4 ? att->name + strlen(att->name) - 4 : ""; - for (int n = 0; font_exts[n]; n++) { - if (strcasecmp(ext, font_exts[n]) == 0) { - mp_warn(log, "Loading font attachment '%s' with MIME type %s. " - "Assuming this is a broken Matroska file, which was " - "muxed without setting a correct font MIME type.\n", - att->name, att->type); - return true; - } - } - return false; + return -1; } -static void add_subtitle_fonts_from_sources(struct MPContext *mpctx) -{ - if (mpctx->opts->ass_enabled) { - for (int j = 0; j < mpctx->num_sources; j++) { - struct demuxer *d = mpctx->sources[j]; - for (int i = 0; i < d->num_attachments; i++) { - struct demux_attachment *att = d->attachments + i; - if (mpctx->opts->use_embedded_fonts && - attachment_is_font(mpctx->log, att)) - { - ass_add_font(mpctx->ass_library, att->name, att->data, - att->data_size); - } - } - } - } -} - -static void init_sub_renderer(struct MPContext *mpctx) -{ - struct MPOpts *opts = mpctx->opts; - - if (mpctx->ass_renderer) - return; - - if (!mpctx->ass_log) - mpctx->ass_log = mp_log_new(mpctx, mpctx->global->log, "!libass"); - - mpctx->ass_library = mp_ass_init(mpctx->global, mpctx->ass_log); - - add_subtitle_fonts_from_sources(mpctx); - - if (opts->ass_style_override) - ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list); - - mpctx->ass_renderer = ass_renderer_init(mpctx->ass_library); - - mp_ass_configure_fonts(mpctx->ass_renderer, opts->sub_text_style, - mpctx->global, mpctx->ass_log); -} - -void uninit_sub_renderer(struct MPContext *mpctx) -{ - if (mpctx->ass_renderer) - ass_renderer_done(mpctx->ass_renderer); - mpctx->ass_renderer = NULL; - if (mpctx->ass_library) - ass_library_done(mpctx->ass_library); - mpctx->ass_library = NULL; -} - -#else /* HAVE_LIBASS */ - -static void init_sub_renderer(struct MPContext *mpctx) {} -void uninit_sub_renderer(struct MPContext *mpctx) {} - -#endif - -static void reset_subtitles(struct MPContext *mpctx, int order) +static void reset_subtitles(struct MPContext *mpctx, struct track *track) { - if (mpctx->d_sub[order]) - sub_reset(mpctx->d_sub[order]); + if (track->d_sub) + sub_reset(track->d_sub); term_osd_set_subs(mpctx, NULL); } void reset_subtitle_state(struct MPContext *mpctx) { - reset_subtitles(mpctx, 0); - reset_subtitles(mpctx, 1); -} - -void uninit_stream_sub_decoders(struct demuxer *demuxer) -{ - for (int i = 0; i < demuxer->num_streams; i++) { - struct sh_stream *sh = demuxer->streams[i]; - if (sh->sub) { - sub_destroy(sh->sub->dec_sub); - sh->sub->dec_sub = NULL; - } + for (int n = 0; n < mpctx->num_tracks; n++) { + struct dec_sub *d_sub = mpctx->tracks[n]->d_sub; + if (d_sub) + sub_reset(d_sub); } + term_osd_set_subs(mpctx, NULL); } -void uninit_sub(struct MPContext *mpctx, int order) +void uninit_sub(struct MPContext *mpctx, struct track *track) { - if (mpctx->d_sub[order]) { - reset_subtitles(mpctx, order); - mpctx->d_sub[order] = NULL; // Note: not free'd. - osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, NULL); + if (track && track->d_sub) { + reset_subtitles(mpctx, track); + sub_select(track->d_sub, false); + int order = get_order(mpctx, track); + if (order >= 0 && order <= 1) + osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, NULL); reselect_demux_streams(mpctx); } } void uninit_sub_all(struct MPContext *mpctx) { - uninit_sub(mpctx, 0); - uninit_sub(mpctx, 1); + for (int n = 0; n < mpctx->num_tracks; n++) + uninit_sub(mpctx, mpctx->tracks[n]); } -// When reading subtitles from a demuxer, and we read video or audio from the -// demuxer, we should not explicitly read subtitle packets. (With external -// subs, we have to.) -static bool is_interleaved(struct MPContext *mpctx, struct track *track) -{ - if (track->is_external || !track->demuxer) - return false; - - struct demuxer *demuxer = track->demuxer; - for (int t = 0; t < mpctx->num_tracks; t++) { - struct track *other = mpctx->tracks[t]; - if (other != track && other->selected && other->demuxer == demuxer && - (other->type == STREAM_VIDEO || other->type == STREAM_AUDIO)) - return true; - } - return track->demuxer == mpctx->demuxer; -} - -static void update_subtitle(struct MPContext *mpctx, int order) +static bool update_subtitle(struct MPContext *mpctx, double video_pts, + struct track *track) { struct MPOpts *opts = mpctx->opts; - struct track *track = mpctx->current_track[order][STREAM_SUB]; - struct dec_sub *dec_sub = mpctx->d_sub[order]; + struct dec_sub *dec_sub = track ? track->d_sub : NULL; - if (!track || !dec_sub) - return; + if (!dec_sub || video_pts == MP_NOPTS_VALUE) + return true; - if (mpctx->d_video) { - struct mp_image_params params = mpctx->d_video->vfilter->override_params; + if (mpctx->vo_chain) { + struct mp_image_params params = mpctx->vo_chain->vf->input_params; if (params.imgfmt) sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, ¶ms); } - double refpts_s = mpctx->playback_pts; - double curpts_s = refpts_s - opts->sub_delay; + video_pts -= opts->sub_delay; - if (!track->preloaded && track->stream) { - struct sh_stream *sh_stream = track->stream; - bool interleaved = is_interleaved(mpctx, track); - - assert(sh_stream->sub->dec_sub == dec_sub); - - while (1) { - if (interleaved && !demux_has_packet(sh_stream)) - break; - double subpts_s = demux_get_next_pts(sh_stream); - if (!demux_has_packet(sh_stream)) - break; - if (subpts_s > curpts_s) { - MP_DBG(mpctx, "Sub early: c_pts=%5.3f s_pts=%5.3f\n", - curpts_s, subpts_s); - // Often subs can be handled in advance - if (!sub_accepts_packet_in_advance(dec_sub)) - break; - // Try to avoid demuxing whole file at once - if (subpts_s > curpts_s + 1 && !interleaved) - break; - } - struct demux_packet *pkt = demux_read_packet(sh_stream); - MP_DBG(mpctx, "Sub: c_pts=%5.3f s_pts=%5.3f duration=%5.3f len=%d\n", - curpts_s, pkt->pts, pkt->duration, pkt->len); - sub_decode(dec_sub, pkt); - talloc_free(pkt); - } + if (!track->preloaded) { + if (!sub_read_packets(dec_sub, video_pts)) + return false; } // Handle displaying subtitles on terminal; never done for secondary subs - if (order == 0 && !mpctx->video_out) - term_osd_set_subs(mpctx, sub_get_text(dec_sub, curpts_s)); + if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out) + term_osd_set_subs(mpctx, sub_get_text(dec_sub, video_pts)); + + return true; } -void update_subtitles(struct MPContext *mpctx) +// Return true if the subtitles for the given PTS are ready; false if the player +// should wait for new demuxer data, and then should retry. +bool update_subtitles(struct MPContext *mpctx, double video_pts) { - update_subtitle(mpctx, 0); - update_subtitle(mpctx, 1); + bool ok = true; + for (int n = 0; n < NUM_PTRACKS; n++) + ok &= update_subtitle(mpctx, video_pts, mpctx->current_track[n][STREAM_SUB]); + return ok; } -static void reinit_subdec(struct MPContext *mpctx, struct track *track, - struct dec_sub *dec_sub) +static bool init_subdec(struct MPContext *mpctx, struct track *track) { struct MPOpts *opts = mpctx->opts; - if (sub_is_initialized(dec_sub)) - return; + assert(!track->d_sub); - struct sh_video *sh_video = - mpctx->d_video ? mpctx->d_video->header->video : NULL; - int w = sh_video ? sh_video->disp_w : 0; - int h = sh_video ? sh_video->disp_h : 0; - float fps = sh_video ? sh_video->fps : 25; + if (!track->demuxer || !track->stream) + return false; - init_sub_renderer(mpctx); + track->d_sub = sub_create(mpctx->global, track->demuxer, track->stream); + if (!track->d_sub) + return false; - sub_set_video_res(dec_sub, w, h); - sub_set_video_fps(dec_sub, fps); - sub_set_ass_renderer(dec_sub, mpctx->ass_library, mpctx->ass_renderer, - &mpctx->ass_lock); - sub_init_from_sh(dec_sub, track->stream); + struct track *vtrack = mpctx->current_track[0][STREAM_VIDEO]; + struct mp_codec_params *v_c = + vtrack && vtrack->stream ? vtrack->stream->codec : NULL; + double fps = v_c ? v_c->fps : 25; + sub_control(track->d_sub, SD_CTRL_SET_VIDEO_DEF_FPS, &fps); // Don't do this if the file has video/audio streams. Don't do it even // if it has only sub streams, because reading packets will change the // demuxer position. - if (!track->preloaded && track->is_external && !opts->sub_clear_on_seek) { + if (track->is_external && !opts->sub_clear_on_seek) { demux_seek(track->demuxer, 0, SEEK_ABSOLUTE); - track->preloaded = sub_read_all_packets(dec_sub, track->stream); + track->preloaded = sub_read_all_packets(track->d_sub); if (track->preloaded) demux_stop_thread(track->demuxer); } + + return true; } -void reinit_subs(struct MPContext *mpctx, int order) +void reinit_sub(struct MPContext *mpctx, struct track *track) { - struct track *track = mpctx->current_track[order][STREAM_SUB]; - - assert(!mpctx->d_sub[order]); + if (!track || !track->stream || track->stream->type != STREAM_SUB) + return; - struct sh_stream *sh = track ? track->stream : NULL; - if (!sh) + if (!track->d_sub && !init_subdec(mpctx, track)) { + error_on_track(mpctx, track); return; + } - // The decoder is cached in the stream header in order to make ordered - // chapters work better. - if (!sh->sub->dec_sub) - sh->sub->dec_sub = sub_create(mpctx->global); - mpctx->d_sub[order] = sh->sub->dec_sub; + sub_select(track->d_sub, true); + int order = get_order(mpctx, track); + if (order >= 0 && order <= 1) + osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, track->d_sub); + sub_control(track->d_sub, SD_CTRL_SET_TOP, &(bool){!!order}); +} - reinit_subdec(mpctx, track, sh->sub->dec_sub); - osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, sh->sub->dec_sub); - sub_control(sh->sub->dec_sub, SD_CTRL_SET_TOP, &(bool){!!order}); +void reinit_sub_all(struct MPContext *mpctx) +{ + for (int n = 0; n < NUM_PTRACKS; n++) + reinit_sub(mpctx, mpctx->current_track[n][STREAM_SUB]); } |