diff options
Diffstat (limited to 'player/sub.c')
-rw-r--r-- | player/sub.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/player/sub.c b/player/sub.c new file mode 100644 index 0000000..f3e42fe --- /dev/null +++ b/player/sub.c @@ -0,0 +1,214 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <stdbool.h> +#include <inttypes.h> +#include <math.h> +#include <assert.h> + +#include "mpv_talloc.h" + +#include "common/msg.h" +#include "options/options.h" +#include "common/common.h" +#include "common/global.h" + +#include "stream/stream.h" +#include "sub/dec_sub.h" +#include "demux/demux.h" +#include "video/mp_image.h" + +#include "core.h" + +// 0: primary sub, 1: secondary sub, -1: not selected +static int get_order(struct MPContext *mpctx, struct track *track) +{ + for (int n = 0; n < num_ptracks[STREAM_SUB]; n++) { + if (mpctx->current_track[n][STREAM_SUB] == track) + return n; + } + return -1; +} + +static void reset_subtitles(struct MPContext *mpctx, struct track *track) +{ + if (track->d_sub) { + sub_reset(track->d_sub); + sub_set_play_dir(track->d_sub, mpctx->play_dir); + } + term_osd_set_subs(mpctx, NULL); +} + +void reset_subtitle_state(struct MPContext *mpctx) +{ + for (int n = 0; n < mpctx->num_tracks; n++) + reset_subtitles(mpctx, mpctx->tracks[n]); + term_osd_set_subs(mpctx, NULL); +} + +void uninit_sub(struct MPContext *mpctx, struct track *track) +{ + if (track && track->d_sub) { + reset_subtitles(mpctx, track); + sub_select(track->d_sub, false); + int order = get_order(mpctx, track); + osd_set_sub(mpctx->osd, order, NULL); + sub_destroy(track->d_sub); + track->d_sub = NULL; + } +} + +void uninit_sub_all(struct MPContext *mpctx) +{ + for (int n = 0; n < mpctx->num_tracks; n++) + uninit_sub(mpctx, mpctx->tracks[n]); +} + +static bool update_subtitle(struct MPContext *mpctx, double video_pts, + struct track *track) +{ + struct dec_sub *dec_sub = track ? track->d_sub : NULL; + + if (!dec_sub || video_pts == MP_NOPTS_VALUE) + return true; + + if (mpctx->vo_chain) { + struct mp_image_params params = mpctx->vo_chain->filter->input_params; + if (params.imgfmt) + sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, ¶ms); + } + + if (track->demuxer->fully_read && sub_can_preload(dec_sub)) { + // Assume fully_read implies no interleaved audio/video streams. + // (Reading packets will change the demuxer position.) + demux_seek(track->demuxer, 0, 0); + sub_preload(dec_sub); + } + + if (!sub_read_packets(dec_sub, video_pts, mpctx->paused)) + return false; + + // Handle displaying subtitles on terminal; never done for secondary subs + if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out) { + char *text = sub_get_text(dec_sub, video_pts, SD_TEXT_TYPE_PLAIN); + term_osd_set_subs(mpctx, text); + talloc_free(text); + } + + // Handle displaying subtitles on VO with no video being played. This is + // quite different, because normally subtitles are redrawn on new video + // frames, using the video frames' timestamps. + if (mpctx->video_out && mpctx->video_status == STATUS_EOF && + (mpctx->opts->subs_rend->sub_past_video_end || + !mpctx->current_track[0][STREAM_VIDEO] || + mpctx->current_track[0][STREAM_VIDEO]->image)) { + if (osd_get_force_video_pts(mpctx->osd) != video_pts) { + osd_set_force_video_pts(mpctx->osd, video_pts); + osd_query_and_reset_want_redraw(mpctx->osd); + vo_redraw(mpctx->video_out); + // Force an arbitrary minimum FPS + mp_set_timeout(mpctx, 0.1); + } + } + + return true; +} + +// 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) +{ + bool ok = true; + for (int n = 0; n < num_ptracks[STREAM_SUB]; n++) + ok &= update_subtitle(mpctx, video_pts, mpctx->current_track[n][STREAM_SUB]); + return ok; +} + +static struct attachment_list *get_all_attachments(struct MPContext *mpctx) +{ + struct attachment_list *list = talloc_zero(NULL, struct attachment_list); + struct demuxer *prev_demuxer = NULL; + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *t = mpctx->tracks[n]; + if (!t->demuxer || prev_demuxer == t->demuxer) + continue; + prev_demuxer = t->demuxer; + for (int i = 0; i < t->demuxer->num_attachments; i++) { + struct demux_attachment *att = &t->demuxer->attachments[i]; + struct demux_attachment copy = { + .name = talloc_strdup(list, att->name), + .type = talloc_strdup(list, att->type), + .data = talloc_memdup(list, att->data, att->data_size), + .data_size = att->data_size, + }; + MP_TARRAY_APPEND(list, list->entries, list->num_entries, copy); + } + } + return list; +} + +static bool init_subdec(struct MPContext *mpctx, struct track *track) +{ + assert(!track->d_sub); + + if (!track->demuxer || !track->stream) + return false; + + track->d_sub = sub_create(mpctx->global, track, + get_all_attachments(mpctx), + get_order(mpctx, track)); + if (!track->d_sub) + return false; + + 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); + + return true; +} + +void reinit_sub(struct MPContext *mpctx, struct track *track) +{ + if (!track || !track->stream || track->stream->type != STREAM_SUB) + return; + + assert(!track->d_sub); + + if (!init_subdec(mpctx, track)) { + error_on_track(mpctx, track); + return; + } + + sub_select(track->d_sub, true); + int order = get_order(mpctx, track); + osd_set_sub(mpctx->osd, order, track->d_sub); + sub_control(track->d_sub, SD_CTRL_SET_TOP, &order); + + // When paused we have to wait for packets to be available. + // So just retry until we get a packet in this case. + if (mpctx->playback_initialized) + while (!update_subtitles(mpctx, mpctx->playback_pts) && mpctx->paused); +} + +void reinit_sub_all(struct MPContext *mpctx) +{ + for (int n = 0; n < num_ptracks[STREAM_SUB]; n++) + reinit_sub(mpctx, mpctx->current_track[n][STREAM_SUB]); +} |