diff options
Diffstat (limited to 'sub/dec_sub.c')
-rw-r--r-- | sub/dec_sub.c | 127 |
1 files changed, 93 insertions, 34 deletions
diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 18d826e..94ff3ba 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -20,6 +20,7 @@ #include <string.h> #include <math.h> #include <assert.h> +#include <limits.h> #include "demux/demux.h" #include "sd.h" @@ -47,7 +48,9 @@ struct dec_sub { struct mp_log *log; struct mpv_global *global; struct mp_subtitle_opts *opts; + struct mp_subtitle_shared_opts *shared_opts; struct m_config_cache *opts_cache; + struct m_config_cache *shared_opts_cache; struct mp_recorder_sink *recorder_sink; @@ -60,6 +63,7 @@ struct dec_sub { bool preload_attempted; double video_fps; double sub_speed; + bool sub_visible; struct mp_codec_params *codec; double start, end; @@ -68,7 +72,9 @@ struct dec_sub { struct sd *sd; struct demux_packet *new_segment; - struct demux_packet *cached_pkts[2]; + struct demux_packet **cached_pkts; + int cached_pkt_pos; + int num_cached_pkts; }; static void update_subtitle_speed(struct dec_sub *sub) @@ -91,20 +97,22 @@ static void update_subtitle_speed(struct dec_sub *sub) // Return the subtitle PTS used for a given video PTS. static double pts_to_subtitle(struct dec_sub *sub, double pts) { - struct mp_subtitle_opts *opts = sub->opts; + struct mp_subtitle_shared_opts *opts = sub->shared_opts; + float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order]; if (pts != MP_NOPTS_VALUE) - pts = (pts * sub->play_dir - opts->sub_delay) / sub->sub_speed; + pts = (pts * sub->play_dir - delay) / sub->sub_speed; return pts; } static double pts_from_subtitle(struct dec_sub *sub, double pts) { - struct mp_subtitle_opts *opts = sub->opts; + struct mp_subtitle_shared_opts *opts = sub->shared_opts; + float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order]; if (pts != MP_NOPTS_VALUE) - pts = (pts * sub->sub_speed + opts->sub_delay) * sub->play_dir; + pts = (pts * sub->sub_speed + delay) * sub->play_dir; return pts; } @@ -115,6 +123,17 @@ static void wakeup_demux(void *ctx) mp_dispatch_interrupt(q); } +static void sub_destroy_cached_pkts(struct dec_sub *sub) +{ + int index = 0; + while (index < sub->num_cached_pkts) { + TA_FREEP(&sub->cached_pkts[index]); + ++index; + } + sub->cached_pkt_pos = 0; + sub->num_cached_pkts = 0; +} + void sub_destroy(struct dec_sub *sub) { if (!sub) @@ -138,7 +157,9 @@ static struct sd *init_decoder(struct dec_sub *sub) .global = sub->global, .log = mp_log_new(sd, sub->log, driver->name), .opts = sub->opts, + .shared_opts = sub->shared_opts, .driver = driver, + .order = sub->order, .attachments = sub->attachments, .codec = sub->codec, .preload_ok = true, @@ -170,6 +191,7 @@ struct dec_sub *sub_create(struct mpv_global *global, struct track *track, .log = mp_log_new(sub, global->log, "sub"), .global = global, .opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_sub_opts), + .shared_opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_shared_sub_opts), .sh = track->stream, .codec = track->stream->codec, .attachments = talloc_steal(sub, attachments), @@ -181,6 +203,7 @@ struct dec_sub *sub_create(struct mpv_global *global, struct track *track, .end = MP_NOPTS_VALUE, }; sub->opts = sub->opts_cache->opts; + sub->shared_opts = sub->shared_opts_cache->opts; mp_mutex_init_type(&sub->lock, MP_MUTEX_RECURSIVE); sub->sd = init_decoder(sub); @@ -211,7 +234,6 @@ static void update_segment(struct dec_sub *sub) talloc_free(sub->sd); sub->sd = new; update_subtitle_speed(sub); - sub_control(sub, SD_CTRL_SET_TOP, &sub->order); } else { // We'll just keep the current decoder, and feed it possibly // invalid data (not our fault if it crashes or something). @@ -251,7 +273,7 @@ void sub_preload(struct dec_sub *sub) if (!pkt) break; sub->sd->driver->decode(sub->sd, pkt); - talloc_free(pkt); + MP_TARRAY_APPEND(sub, sub->cached_pkts, sub->num_cached_pkts, pkt); } demux_set_stream_wakeup_cb(sub->sh, NULL, NULL); @@ -266,12 +288,51 @@ static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p) (p->start != sub->start || p->end != sub->end || p->codec != sub->codec); } -// Read packets from the demuxer stream passed to sub_create(). Return true if -// enough packets were read, false if the player should wait until the demuxer -// signals new packets available (and then should retry). -bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force) +static bool is_packet_visible(struct demux_packet *p, double video_pts) +{ + return p && p->pts <= video_pts && (video_pts <= p->pts + p->sub_duration || + p->sub_duration < 0); +} + +static bool update_pkt_cache(struct dec_sub *sub, double video_pts) +{ + if (!sub->cached_pkts[sub->cached_pkt_pos]) + return false; + + struct demux_packet *pkt = sub->cached_pkts[sub->cached_pkt_pos]; + struct demux_packet *next_pkt = sub->cached_pkt_pos + 1 < sub->num_cached_pkts ? + sub->cached_pkts[sub->cached_pkt_pos + 1] : NULL; + if (!pkt) + return false; + + double pts = video_pts + sub->shared_opts->sub_delay[sub->order]; + double next_pts = next_pkt ? next_pkt->pts : INT_MAX; + double end_pts = pkt->sub_duration >= 0 ? pkt->pts + pkt->sub_duration : INT_MAX; + + if (next_pts < pts || end_pts < pts) { + if (sub->cached_pkt_pos + 1 < sub->num_cached_pkts) { + TA_FREEP(&sub->cached_pkts[sub->cached_pkt_pos]); + pkt = NULL; + sub->cached_pkt_pos++; + } + if (next_pts < pts) + return true; + } + + if (pkt && pkt->animated) + return true; + + return false; +} + +// Read packets from the demuxer stream passed to sub_create(). Signals if +// enough packets were read and if the subtitle state updated in anyway. If +// packets_read is false, the player should wait until the demuxer signals new +// packets and retry. +void sub_read_packets(struct dec_sub *sub, double video_pts, bool force, + bool *packets_read, bool *sub_updated) { - bool r = true; + *packets_read = true; mp_mutex_lock(&sub->lock); video_pts = pts_to_subtitle(sub, video_pts); while (1) { @@ -291,7 +352,8 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force) break; // (Use this mechanism only if sub_delay matters to avoid corner cases.) - double min_pts = sub->opts->sub_delay < 0 || force ? video_pts : MP_NOPTS_VALUE; + float delay = sub->order < 0 ? 0.0f : sub->shared_opts->sub_delay[sub->order]; + double min_pts = delay < 0 || force ? video_pts : MP_NOPTS_VALUE; struct demux_packet *pkt; int st = demux_read_packet_async_until(sub->sh, min_pts, &pkt); @@ -301,24 +363,16 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force) // happen for interleaved subtitle streams, which never return "wait" // when reading, unless min_pts is set. if (st <= 0) { - r = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE && - sub->last_pkt_pts > video_pts); + *packets_read = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE && + sub->last_pkt_pts > video_pts); break; } if (sub->recorder_sink) mp_recorder_feed_packet(sub->recorder_sink, pkt); - - // Update cached packets - if (sub->cached_pkts[0]) { - if (sub->cached_pkts[1]) - talloc_free(sub->cached_pkts[1]); - sub->cached_pkts[1] = sub->cached_pkts[0]; - } - sub->cached_pkts[0] = pkt; - sub->last_pkt_pts = pkt->pts; + MP_TARRAY_APPEND(sub, sub->cached_pkts, sub->num_cached_pkts, pkt); if (is_new_segment(sub, pkt)) { sub->new_segment = demux_copy_packet(pkt); @@ -330,19 +384,24 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force) if (!(sub->preload_attempted && sub->sd->preload_ok)) sub->sd->driver->decode(sub->sd, pkt); } + if (sub->cached_pkts && sub->num_cached_pkts) { + bool visible = is_packet_visible(sub->cached_pkts[sub->cached_pkt_pos], video_pts); + *sub_updated = update_pkt_cache(sub, video_pts) || sub->sub_visible != visible; + sub->sub_visible = visible; + } mp_mutex_unlock(&sub->lock); - return r; } -// Redecode both cached packets if needed. +// Redecode all cached packets if needed. // Used with UPDATE_SUB_HARD and UPDATE_SUB_FILT. void sub_redecode_cached_packets(struct dec_sub *sub) { mp_mutex_lock(&sub->lock); - if (sub->cached_pkts[0]) - sub->sd->driver->decode(sub->sd, sub->cached_pkts[0]); - if (sub->cached_pkts[1]) - sub->sd->driver->decode(sub->sd, sub->cached_pkts[1]); + int index = sub->cached_pkt_pos; + while (index < sub->num_cached_pkts) { + sub->sd->driver->decode(sub->sd, sub->cached_pkts[index]); + ++index; + } mp_mutex_unlock(&sub->lock); } @@ -417,8 +476,7 @@ void sub_reset(struct dec_sub *sub) sub->sd->driver->reset(sub->sd); sub->last_pkt_pts = MP_NOPTS_VALUE; sub->last_vo_pts = MP_NOPTS_VALUE; - TA_FREEP(&sub->cached_pkts[0]); - TA_FREEP(&sub->cached_pkts[1]); + sub_destroy_cached_pkts(sub); TA_FREEP(&sub->new_segment); mp_mutex_unlock(&sub->lock); } @@ -455,6 +513,7 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg) int flags = (uintptr_t)arg; if (m_config_cache_update(sub->opts_cache)) update_subtitle_speed(sub); + m_config_cache_update(sub->shared_opts_cache); propagate = true; if (flags & UPDATE_SUB_HARD) { // forget about the previous preload because @@ -489,10 +548,10 @@ void sub_set_play_dir(struct dec_sub *sub, int dir) bool sub_is_primary_visible(struct dec_sub *sub) { - return !!sub->opts->sub_visibility; + return sub->shared_opts->sub_visibility[0]; } bool sub_is_secondary_visible(struct dec_sub *sub) { - return !!sub->opts->sec_sub_visibility; + return sub->shared_opts->sub_visibility[1]; } |