summaryrefslogtreecommitdiffstats
path: root/sub/dec_sub.c
diff options
context:
space:
mode:
Diffstat (limited to 'sub/dec_sub.c')
-rw-r--r--sub/dec_sub.c282
1 files changed, 196 insertions, 86 deletions
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index f98a478be4..c03393a007 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.c
@@ -20,9 +20,8 @@
#include <string.h>
#include <math.h>
#include <assert.h>
-#include <pthread.h>
+#include <limits.h>
-#include "config.h"
#include "demux/demux.h"
#include "sd.h"
#include "dec_sub.h"
@@ -39,19 +38,19 @@ extern const struct sd_functions sd_lavc;
static const struct sd_functions *const sd_list[] = {
&sd_lavc,
-#if HAVE_LIBASS
&sd_ass,
-#endif
NULL
};
struct dec_sub {
- pthread_mutex_t lock;
+ mp_mutex lock;
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;
@@ -59,10 +58,12 @@ struct dec_sub {
struct sh_stream *sh;
int play_dir;
+ int order;
double last_pkt_pts;
bool preload_attempted;
double video_fps;
double sub_speed;
+ bool sub_visible;
struct mp_codec_params *codec;
double start, end;
@@ -71,8 +72,9 @@ struct dec_sub {
struct sd *sd;
struct demux_packet *new_segment;
-
- struct mp_dispatch_queue *demux_waiter;
+ struct demux_packet **cached_pkts;
+ int cached_pkt_pos;
+ int num_cached_pkts;
};
static void update_subtitle_speed(struct dec_sub *sub)
@@ -95,40 +97,43 @@ 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;
}
-void sub_lock(struct dec_sub *sub)
-{
- pthread_mutex_lock(&sub->lock);
-}
-
-void sub_unlock(struct dec_sub *sub)
-{
- pthread_mutex_unlock(&sub->lock);
-}
-
static void wakeup_demux(void *ctx)
{
struct mp_dispatch_queue *q = ctx;
mp_dispatch_interrupt(q);
}
+static void 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)
@@ -139,7 +144,7 @@ void sub_destroy(struct dec_sub *sub)
sub->sd->driver->uninit(sub->sd);
}
talloc_free(sub->sd);
- pthread_mutex_destroy(&sub->lock);
+ mp_mutex_destroy(&sub->lock);
talloc_free(sub);
}
@@ -152,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,
@@ -172,32 +179,32 @@ static struct sd *init_decoder(struct dec_sub *sub)
// Thread-safety of the returned object: all functions are thread-safe,
// except sub_get_bitmaps() and sub_get_text(). Decoder backends (sd_*)
// do not need to acquire locks.
-// Ownership of attachments goes to the caller, and is released with
+// Ownership of attachments goes to the callee, and is released with
// talloc_free() (even on failure).
-struct dec_sub *sub_create(struct mpv_global *global, struct sh_stream *sh,
- struct attachment_list *attachments)
+struct dec_sub *sub_create(struct mpv_global *global, struct track *track,
+ struct attachment_list *attachments, int order)
{
- assert(sh && sh->type == STREAM_SUB);
+ assert(track->stream && track->stream->type == STREAM_SUB);
struct dec_sub *sub = talloc(NULL, struct dec_sub);
*sub = (struct dec_sub){
.log = mp_log_new(sub, global->log, "sub"),
.global = global,
.opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_sub_opts),
- .sh = sh,
- .codec = sh->codec,
+ .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),
.play_dir = 1,
+ .order = order,
.last_pkt_pts = MP_NOPTS_VALUE,
.last_vo_pts = MP_NOPTS_VALUE,
.start = MP_NOPTS_VALUE,
.end = MP_NOPTS_VALUE,
- .demux_waiter = mp_dispatch_create(sub),
};
sub->opts = sub->opts_cache->opts;
- mpthread_mutex_init_recursive(&sub->lock);
-
- demux_set_stream_wakeup_cb(sub->sh, wakeup_demux, sub->demux_waiter);
+ sub->shared_opts = sub->shared_opts_cache->opts;
+ mp_mutex_init(&sub->lock);
sub->sd = init_decoder(sub);
if (sub->sd) {
@@ -241,15 +248,18 @@ static void update_segment(struct dec_sub *sub)
bool sub_can_preload(struct dec_sub *sub)
{
bool r;
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
r = sub->sd->driver->accept_packets_in_advance && !sub->preload_attempted;
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
return r;
}
void sub_preload(struct dec_sub *sub)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
+
+ struct mp_dispatch_queue *demux_waiter = mp_dispatch_create(NULL);
+ demux_set_stream_wakeup_cb(sub->sh, wakeup_demux, demux_waiter);
sub->preload_attempted = true;
@@ -257,16 +267,19 @@ void sub_preload(struct dec_sub *sub)
struct demux_packet *pkt = NULL;
int r = demux_read_packet_async(sub->sh, &pkt);
if (r == 0) {
- mp_dispatch_queue_process(sub->demux_waiter, INFINITY);
+ mp_dispatch_queue_process(demux_waiter, INFINITY);
continue;
}
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);
}
- pthread_mutex_unlock(&sub->lock);
+ demux_set_stream_wakeup_cb(sub->sh, NULL, NULL);
+ talloc_free(demux_waiter);
+
+ mp_mutex_unlock(&sub->lock);
}
static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p)
@@ -275,13 +288,52 @@ 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)
+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;
- pthread_mutex_lock(&sub->lock);
+ *packets_read = true;
+ mp_mutex_lock(&sub->lock);
video_pts = pts_to_subtitle(sub, video_pts);
while (1) {
bool read_more = true;
@@ -299,16 +351,20 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
if (sub->new_segment)
break;
+ // (Use this mechanism only if sub_delay matters to avoid corner cases.)
+ 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(sub->sh, &pkt);
+ int st = demux_read_packet_async_until(sub->sh, min_pts, &pkt);
// Note: "wait" (st==0) happens with non-interleaved streams only, and
// then we should stop the playloop until a new enough packet has been
- // seen (or the subtitle decoder's queue is full). This does not happen
- // for interleaved subtitle streams, which never return "wait" when
- // reading.
+ // seen (or the subtitle decoder's queue is full). This usually does not
+ // 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;
}
@@ -316,9 +372,10 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
mp_recorder_feed_packet(sub->recorder_sink, 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 = pkt;
+ sub->new_segment = demux_copy_packet(pkt);
// Note that this can be delayed to a much later point in time.
update_segment(sub);
break;
@@ -326,39 +383,53 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
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);
+}
- talloc_free(pkt);
+// 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);
+ int index = sub->cached_pkt_pos;
+ while (index < sub->num_cached_pkts) {
+ sub->sd->driver->decode(sub->sd, sub->cached_pkts[index]);
+ ++index;
}
- pthread_mutex_unlock(&sub->lock);
- return r;
+ mp_mutex_unlock(&sub->lock);
}
-// You must call sub_lock/sub_unlock if more than 1 thread access sub.
-// The issue is that *res will contain decoder allocated data, which might
-// be deallocated on the next decoder access.
-void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, int format,
- double pts, struct sub_bitmaps *res)
+// Unref sub_bitmaps.rc to free the result. May return NULL.
+struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim,
+ int format, double pts)
{
- struct mp_subtitle_opts *opts = sub->opts;
+ mp_mutex_lock(&sub->lock);
pts = pts_to_subtitle(sub, pts);
sub->last_vo_pts = pts;
update_segment(sub);
- if (sub->end != MP_NOPTS_VALUE && pts >= sub->end)
- return;
+ struct sub_bitmaps *res = NULL;
+
+ if (!(sub->end != MP_NOPTS_VALUE && pts >= sub->end) &&
+ sub->sd->driver->get_bitmaps)
+ res = sub->sd->driver->get_bitmaps(sub->sd, dim, format, pts);
- if (opts->sub_visibility && sub->sd->driver->get_bitmaps)
- sub->sd->driver->get_bitmaps(sub->sd, dim, format, pts, res);
+ mp_mutex_unlock(&sub->lock);
+ return res;
}
-// See sub_get_bitmaps() for locking requirements.
-// It can be called unlocked too, but then only 1 thread must call this function
-// at a time (unless exclusive access is guaranteed).
-char *sub_get_text(struct dec_sub *sub, double pts)
+// The returned string is talloc'ed.
+char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
char *text = NULL;
pts = pts_to_subtitle(sub, pts);
@@ -367,14 +438,28 @@ char *sub_get_text(struct dec_sub *sub, double pts)
update_segment(sub);
if (sub->sd->driver->get_text)
- text = sub->sd->driver->get_text(sub->sd, pts);
- pthread_mutex_unlock(&sub->lock);
+ text = sub->sd->driver->get_text(sub->sd, pts, type);
+ mp_mutex_unlock(&sub->lock);
return text;
}
+char *sub_ass_get_extradata(struct dec_sub *sub)
+{
+ char *data = NULL;
+ mp_mutex_lock(&sub->lock);
+ if (strcmp(sub->sd->codec->codec, "ass") != 0)
+ goto done;
+ char *extradata = sub->sd->codec->extradata;
+ int extradata_size = sub->sd->codec->extradata_size;
+ data = talloc_strndup(NULL, extradata, extradata_size);
+done:
+ mp_mutex_unlock(&sub->lock);
+ return data;
+}
+
struct sd_times sub_get_times(struct dec_sub *sub, double pts)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
struct sd_times res = { .start = MP_NOPTS_VALUE, .end = MP_NOPTS_VALUE };
pts = pts_to_subtitle(sub, pts);
@@ -385,34 +470,34 @@ struct sd_times sub_get_times(struct dec_sub *sub, double pts)
if (sub->sd->driver->get_times)
res = sub->sd->driver->get_times(sub->sd, pts);
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
return res;
}
void sub_reset(struct dec_sub *sub)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
if (sub->sd->driver->reset)
sub->sd->driver->reset(sub->sd);
sub->last_pkt_pts = MP_NOPTS_VALUE;
sub->last_vo_pts = MP_NOPTS_VALUE;
- talloc_free(sub->new_segment);
- sub->new_segment = NULL;
- pthread_mutex_unlock(&sub->lock);
+ destroy_cached_pkts(sub);
+ TA_FREEP(&sub->new_segment);
+ mp_mutex_unlock(&sub->lock);
}
void sub_select(struct dec_sub *sub, bool selected)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
if (sub->sd->driver->select)
sub->sd->driver->select(sub->sd, selected);
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
}
int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
{
int r = CONTROL_UNKNOWN;
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
bool propagate = false;
switch (cmd) {
case SD_CTRL_SET_VIDEO_DEF_FPS:
@@ -428,10 +513,19 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
if (r == CONTROL_OK)
a[0] = pts_from_subtitle(sub, arg2[0]);
break;
- case SD_CTRL_UPDATE_OPTS:
+ }
+ case SD_CTRL_UPDATE_OPTS: {
+ 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
+ // UPDATE_SUB_HARD will cause a sub reinit
+ // that clears all preloaded sub packets
+ sub->preload_attempted = false;
+ }
break;
}
default:
@@ -439,20 +533,36 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
}
if (propagate && sub->sd->driver->control)
r = sub->sd->driver->control(sub->sd, cmd, arg);
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
return r;
}
void sub_set_recorder_sink(struct dec_sub *sub, struct mp_recorder_sink *sink)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
sub->recorder_sink = sink;
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
}
void sub_set_play_dir(struct dec_sub *sub, int dir)
{
- pthread_mutex_lock(&sub->lock);
+ mp_mutex_lock(&sub->lock);
sub->play_dir = dir;
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
+}
+
+bool sub_is_primary_visible(struct dec_sub *sub)
+{
+ mp_mutex_lock(&sub->lock);
+ bool ret = sub->shared_opts->sub_visibility[0];
+ mp_mutex_unlock(&sub->lock);
+ return ret;
+}
+
+bool sub_is_secondary_visible(struct dec_sub *sub)
+{
+ mp_mutex_lock(&sub->lock);
+ bool ret = sub->shared_opts->sub_visibility[1];
+ mp_mutex_unlock(&sub->lock);
+ return ret;
}