summaryrefslogtreecommitdiffstats
path: root/player/sub.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/sub.c')
-rw-r--r--player/sub.c281
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, &params);
}
- 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]);
}