summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
Diffstat (limited to 'sub')
-rw-r--r--sub/ass_mp.c16
-rw-r--r--sub/ass_mp.h12
-rw-r--r--sub/dec_sub.c217
-rw-r--r--sub/dec_sub.h10
-rw-r--r--sub/draw_bmp.c56
-rw-r--r--sub/draw_bmp.h5
-rw-r--r--sub/filter_jsre.c5
-rw-r--r--sub/filter_sdh.c165
-rw-r--r--sub/img_convert.c3
-rw-r--r--sub/lavc_conv.c68
-rw-r--r--sub/meson.build6
-rw-r--r--sub/osd.c80
-rw-r--r--sub/osd.h16
-rw-r--r--sub/osd_libass.c55
-rw-r--r--sub/osd_state.h7
-rw-r--r--sub/sd.h8
-rw-r--r--sub/sd_ass.c325
-rw-r--r--sub/sd_lavc.c86
18 files changed, 755 insertions, 385 deletions
diff --git a/sub/ass_mp.c b/sub/ass_mp.c
index 45328fb920..3e9f83313f 100644
--- a/sub/ass_mp.c
+++ b/sub/ass_mp.c
@@ -132,9 +132,12 @@ static void message_callback(int level, const char *format, va_list va, void *ct
mp_msg(log, level, "\n");
}
-ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log)
+ASS_Library *mp_ass_init(struct mpv_global *global,
+ struct osd_style_opts *opts, struct mp_log *log)
{
- char *path = mp_find_config_file(NULL, global, "fonts");
+ char *path = opts->fonts_dir && opts->fonts_dir[0] ?
+ mp_get_user_path(NULL, global, opts->fonts_dir) :
+ mp_find_config_file(NULL, global, "fonts");
mp_dbg(log, "ASS library version: 0x%x (runtime 0x%x)\n",
(unsigned)LIBASS_VERSION, ass_library_version());
ASS_Library *priv = ass_library_init();
@@ -285,7 +288,7 @@ static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res)
struct sub_bitmaps imgs = {
.change_id = res->change_id,
- .format = SUBBITMAP_RGBA,
+ .format = SUBBITMAP_BGRA,
.parts = p->rgba_imgs,
.num_parts = num_bb,
};
@@ -338,10 +341,10 @@ static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res)
// repacks all images). preferred_osd_format can be set to a desired
// sub_bitmap_format. Currently, only SUBBITMAP_LIBASS is supported.
void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists,
- int num_image_lists, bool image_lists_changed,
+ int num_image_lists, bool image_lists_changed, bool video_color_space,
int preferred_osd_format, struct sub_bitmaps *out)
{
- int format = preferred_osd_format == SUBBITMAP_RGBA ? SUBBITMAP_RGBA
+ int format = preferred_osd_format == SUBBITMAP_BGRA ? SUBBITMAP_BGRA
: SUBBITMAP_LIBASS;
if (p->cached_subs_valid && !image_lists_changed &&
@@ -358,6 +361,7 @@ void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists,
.change_id = image_lists_changed,
.format = SUBBITMAP_LIBASS,
.parts = p->cached_parts,
+ .video_color_space = video_color_space,
};
for (int n = 0; n < num_image_lists; n++) {
@@ -379,7 +383,7 @@ void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists,
}
bool r = false;
- if (format == SUBBITMAP_RGBA) {
+ if (format == SUBBITMAP_BGRA) {
r = pack_rgba(p, &res);
} else {
r = pack_libass(p, &res);
diff --git a/sub/ass_mp.h b/sub/ass_mp.h
index f4488bf8c7..b4e16d6650 100644
--- a/sub/ass_mp.h
+++ b/sub/ass_mp.h
@@ -26,8 +26,10 @@
#include <ass/ass.h>
#include <ass/ass_types.h>
-// This is probably arbitrary.
-// sd_lavc_conv might indirectly still assume this PlayResY, though.
+// These PlayResX and PlayResY values are arbitrary and taken from lavc.
+// lavc assumes these values when converting to ass generally. Moreover, these
+// values are also used by default in VSFilter, so it isn't that arbitrary.
+#define MP_ASS_FONT_PLAYRESX 384
#define MP_ASS_FONT_PLAYRESY 288
#define MP_ASS_RGBA(r, g, b, a) \
@@ -40,6 +42,7 @@ struct MPOpts;
struct mpv_global;
struct mp_osd_res;
struct osd_style_opts;
+struct mp_log;
void mp_ass_flush_old_events(ASS_Track *track, long long ts);
void mp_ass_set_style(ASS_Style *style, double res_y,
@@ -47,13 +50,14 @@ void mp_ass_set_style(ASS_Style *style, double res_y,
void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts,
struct mpv_global *global, struct mp_log *log);
-ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log);
+ASS_Library *mp_ass_init(struct mpv_global *global,
+ struct osd_style_opts *opts, struct mp_log *log);
struct sub_bitmaps;
struct mp_ass_packer;
struct mp_ass_packer *mp_ass_packer_alloc(void *ta_parent);
void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists,
- int num_image_lists, bool changed,
+ int num_image_lists, bool changed, bool video_color_space,
int preferred_osd_format, struct sub_bitmaps *out);
void mp_ass_get_bb(ASS_Image *image_list, ASS_Track *track,
struct mp_osd_res *res, double *out_rc);
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index a283a5151d..1e316687d8 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"
@@ -44,12 +43,14 @@ static const struct sd_functions *const sd_list[] = {
};
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;
@@ -62,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;
@@ -70,6 +72,9 @@ struct dec_sub {
struct sd *sd;
struct demux_packet *new_segment;
+ struct demux_packet **cached_pkts;
+ int cached_pkt_pos;
+ int num_cached_pkts;
};
static void update_subtitle_speed(struct dec_sub *sub)
@@ -92,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;
}
@@ -116,6 +123,17 @@ static void wakeup_demux(void *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)
@@ -126,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);
}
@@ -139,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,
@@ -161,18 +181,19 @@ static struct sd *init_decoder(struct dec_sub *sub)
// do not need to acquire locks.
// 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 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,
@@ -182,7 +203,8 @@ struct dec_sub *sub_create(struct mpv_global *global, struct sh_stream *sh,
.end = MP_NOPTS_VALUE,
};
sub->opts = sub->opts_cache->opts;
- mpthread_mutex_init_recursive(&sub->lock);
+ sub->shared_opts = sub->shared_opts_cache->opts;
+ mp_mutex_init(&sub->lock);
sub->sd = init_decoder(sub);
if (sub->sd) {
@@ -212,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).
@@ -227,15 +248,15 @@ 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);
@@ -252,13 +273,13 @@ 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);
talloc_free(demux_waiter);
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
}
static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p)
@@ -267,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;
@@ -292,7 +352,8 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
break;
// (Use this mechanism only if sub_delay matters to avoid corner cases.)
- double min_pts = sub->opts->sub_delay < 0 ? 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);
@@ -302,8 +363,8 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
// 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;
}
@@ -311,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;
@@ -321,20 +383,33 @@ 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);
}
// 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)
{
- pthread_mutex_lock(&sub->lock);
-
- struct mp_subtitle_opts *opts = sub->opts;
+ mp_mutex_lock(&sub->lock);
pts = pts_to_subtitle(sub, pts);
@@ -344,17 +419,17 @@ struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim,
struct sub_bitmaps *res = NULL;
if (!(sub->end != MP_NOPTS_VALUE && pts >= sub->end) &&
- opts->sub_visibility && sub->sd->driver->get_bitmaps)
+ sub->sd->driver->get_bitmaps)
res = sub->sd->driver->get_bitmaps(sub->sd, dim, format, pts);
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
return res;
}
// 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);
@@ -364,13 +439,25 @@ char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type)
if (sub->sd->driver->get_text)
text = sub->sd->driver->get_text(sub->sd, pts, type);
- pthread_mutex_unlock(&sub->lock);
+ mp_mutex_unlock(&sub->lock);
return text;
}
+char *sub_ass_get_extradata(struct dec_sub *sub)
+{
+ mp_mutex_lock(&sub->lock);
+ if (strcmp(sub->sd->codec->codec, "ass") != 0)
+ return NULL;
+ char *extradata = sub->sd->codec->extradata;
+ int extradata_size = sub->sd->codec->extradata_size;
+ char *data = talloc_strndup(NULL, extradata, extradata_size);
+ 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);
@@ -381,34 +468,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:
@@ -424,10 +511,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:
@@ -435,25 +531,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)
{
- return !!sub->opts->sec_sub_visibility;
+ mp_mutex_lock(&sub->lock);
+ bool ret = sub->shared_opts->sub_visibility[1];
+ mp_mutex_unlock(&sub->lock);
+ return ret;
}
diff --git a/sub/dec_sub.h b/sub/dec_sub.h
index 6257e74c65..eb8406cb14 100644
--- a/sub/dec_sub.h
+++ b/sub/dec_sub.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include "player/core.h"
#include "osd.h"
struct sh_stream;
@@ -16,7 +17,6 @@ struct sd;
enum sd_ctrl {
SD_CTRL_SUB_STEP,
SD_CTRL_SET_VIDEO_PARAMS,
- SD_CTRL_SET_TOP,
SD_CTRL_SET_VIDEO_DEF_FPS,
SD_CTRL_UPDATE_OPTS,
};
@@ -36,21 +36,25 @@ struct attachment_list {
int num_entries;
};
-struct dec_sub *sub_create(struct mpv_global *global, struct sh_stream *sh,
+struct dec_sub *sub_create(struct mpv_global *global, struct track *track,
struct attachment_list *attachments, int order);
void sub_destroy(struct dec_sub *sub);
bool sub_can_preload(struct dec_sub *sub);
void sub_preload(struct dec_sub *sub);
-bool sub_read_packets(struct dec_sub *sub, double video_pts);
+void sub_redecode_cached_packets(struct dec_sub *sub);
+void sub_read_packets(struct dec_sub *sub, double video_pts, bool force,
+ bool *packets_read, bool *sub_updated);
struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim,
int format, double pts);
char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type);
+char *sub_ass_get_extradata(struct dec_sub *sub);
struct sd_times sub_get_times(struct dec_sub *sub, double pts);
void sub_reset(struct dec_sub *sub);
void sub_select(struct dec_sub *sub, bool selected);
void sub_set_recorder_sink(struct dec_sub *sub, struct mp_recorder_sink *sink);
void sub_set_play_dir(struct dec_sub *sub, int dir);
+bool sub_is_primary_visible(struct dec_sub *sub);
bool sub_is_secondary_visible(struct dec_sub *sub);
int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg);
diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c
index f7f8982051..78e29f491b 100644
--- a/sub/draw_bmp.c
+++ b/sub/draw_bmp.c
@@ -32,7 +32,7 @@
const bool mp_draw_sub_formats[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = true,
- [SUBBITMAP_RGBA] = true,
+ [SUBBITMAP_BGRA] = true,
};
struct part {
@@ -84,7 +84,7 @@ struct mp_draw_sub_cache
struct mp_sws_context *alpha_to_calpha; // scaler for overlay -> calpha
bool scale_in_tiles;
- struct mp_sws_context *sub_scale; // scaler for SUBBITMAP_RGBA
+ struct mp_sws_context *sub_scale; // scaler for SUBBITMAP_BGRA
struct mp_repack *overlay_to_f32; // convert video_overlay to float
struct mp_image *overlay_tmp; // slice in float32
@@ -260,8 +260,8 @@ static void mark_rect(struct mp_draw_sub_cache *p, int x0, int y0, int x1, int y
assert(x0 >= 0 && x0 <= x1 && x1 <= p->w);
assert(y0 >= 0 && y0 <= y1 && y1 <= p->h);
- int sx0 = x0 / SLICE_W;
- int sx1 = x1 / SLICE_W;
+ const int sx0 = x0 / SLICE_W;
+ const int sx1 = MPMIN(x1 / SLICE_W, p->s_w - 1);
for (int y = y0; y < y1; y++) {
struct slice *line = &p->slices[y * p->s_w];
@@ -270,7 +270,7 @@ static void mark_rect(struct mp_draw_sub_cache *p, int x0, int y0, int x1, int y
struct slice *s1 = &line[sx1];
s0->x0 = MPMIN(s0->x0, x0 % SLICE_W);
- s1->x1 = MPMAX(s1->x1, x1 % SLICE_W);
+ s1->x1 = MPMAX(s1->x1, ((x1 - 1) % SLICE_W) + 1);
if (s0 != s1) {
s0->x1 = SLICE_W;
@@ -283,6 +283,11 @@ static void mark_rect(struct mp_draw_sub_cache *p, int x0, int y0, int x1, int y
}
}
+ // Ensure the very last slice does not extend
+ // beyond the total width.
+ struct slice *last_s = &line[p->s_w - 1];
+ last_s->x1 = MPMIN(p->w - ((p->s_w - 1) * SLICE_W), last_s->x1);
+
p->any_osd = true;
}
}
@@ -363,7 +368,7 @@ static void draw_rgba(uint8_t *dst, ptrdiff_t dst_stride,
static bool render_rgba(struct mp_draw_sub_cache *p, struct part *part,
struct sub_bitmaps *sb)
{
- assert(sb->format == SUBBITMAP_RGBA);
+ assert(sb->format == SUBBITMAP_BGRA);
if (part->change_id != sb->change_id) {
for (int n = 0; n < part->num_imgs; n++)
@@ -426,7 +431,7 @@ static bool render_rgba(struct mp_draw_sub_cache *p, struct part *part,
mp_image_set_size(&src_img, sw, sh);
src_img.planes[0] = s_ptr;
src_img.stride[0] = s_stride;
- src_img.params.alpha = MP_ALPHA_PREMUL;
+ src_img.params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
scaled = mp_image_alloc(IMGFMT_BGRA, dw, dh);
if (!scaled)
@@ -462,7 +467,7 @@ static bool render_sb(struct mp_draw_sub_cache *p, struct sub_bitmaps *sb)
case SUBBITMAP_LIBASS:
render_ass(p, sb);
return true;
- case SUBBITMAP_RGBA:
+ case SUBBITMAP_BGRA:
return render_rgba(p, part, sb);
}
@@ -480,6 +485,10 @@ static void clear_rgba_overlay(struct mp_draw_sub_cache *p)
for (int sx = 0; sx < p->s_w; sx++) {
struct slice *s = &line[sx];
+ // Ensure this final slice doesn't extend beyond the width of p->s_w
+ if (s->x1 == SLICE_W && sx == p->s_w - 1 && y == p->rgba_overlay->h - 1)
+ s->x1 = MPMIN(p->w - ((p->s_w - 1) * SLICE_W), s->x1);
+
if (s->x0 <= s->x1) {
memset(px + s->x0, 0, (s->x1 - s->x0) * 4);
*s = (struct slice){SLICE_W, 0};
@@ -516,7 +525,7 @@ static bool reinit_to_video(struct mp_draw_sub_cache *p)
struct mp_image_params *params = &p->params;
mp_image_params_guess_csp(params);
- bool need_premul = params->alpha != MP_ALPHA_PREMUL &&
+ bool need_premul = params->repr.alpha != PL_ALPHA_PREMULTIPLIED &&
(mp_imgfmt_get_desc(params->imgfmt).flags & MP_IMGFLAG_ALPHA);
// Intermediate format for video_overlay. Requirements:
@@ -537,7 +546,7 @@ static bool reinit_to_video(struct mp_draw_sub_cache *p)
mp_get_regular_imgfmt(&vfdesc, mp_repack_get_format_dst(p->video_to_f32));
assert(vfdesc.num_planes); // must have succeeded
- if (params->color.space == MP_CSP_RGB && vfdesc.num_planes >= 3) {
+ if (params->repr.sys == PL_COLOR_SYSTEM_RGB && vfdesc.num_planes >= 3) {
use_shortcut = true;
if (vfdesc.component_type == MP_COMPONENT_TYPE_UINT &&
@@ -651,9 +660,11 @@ static bool reinit_to_video(struct mp_draw_sub_cache *p)
return false;
mp_image_params_guess_csp(&p->rgba_overlay->params);
- p->rgba_overlay->params.alpha = MP_ALPHA_PREMUL;
+ p->rgba_overlay->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
+ p->overlay_tmp->params.repr = params->repr;
p->overlay_tmp->params.color = params->color;
+ p->video_tmp->params.repr = params->repr;
p->video_tmp->params.color = params->color;
if (p->rgba_overlay->imgfmt == overlay_fmt) {
@@ -666,12 +677,13 @@ static bool reinit_to_video(struct mp_draw_sub_cache *p)
if (!p->video_overlay)
return false;
+ p->video_overlay->params.repr = params->repr;
p->video_overlay->params.color = params->color;
p->video_overlay->params.chroma_location = params->chroma_location;
- p->video_overlay->params.alpha = MP_ALPHA_PREMUL;
+ p->video_overlay->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
if (p->scale_in_tiles)
- p->video_overlay->params.chroma_location = MP_CHROMA_CENTER;
+ p->video_overlay->params.chroma_location = PL_CHROMA_CENTER;
p->rgba_to_overlay = alloc_scaler(p);
p->rgba_to_overlay->allow_zimg = true;
@@ -715,13 +727,14 @@ static bool reinit_to_video(struct mp_draw_sub_cache *p)
p->alpha_overlay->stride[0] = p->video_overlay->stride[aplane];
// Full range gray always has the same range as alpha.
- p->alpha_overlay->params.color.levels = MP_CSP_LEVELS_PC;
+ p->alpha_overlay->params.repr.levels = PL_COLOR_LEVELS_FULL;
mp_image_params_guess_csp(&p->alpha_overlay->params);
p->calpha_overlay =
talloc_steal(p, mp_image_alloc(calpha_fmt, w >> xs, h >> ys));
if (!p->calpha_overlay)
return false;
+ p->calpha_overlay->params.repr = p->alpha_overlay->params.repr;
p->calpha_overlay->params.color = p->alpha_overlay->params.color;
p->calpha_to_f32 = mp_repack_create_planar(calpha_fmt, false, rflags);
@@ -753,7 +766,7 @@ static bool reinit_to_video(struct mp_draw_sub_cache *p)
if (!p->premul_tmp)
return false;
mp_image_set_params(p->premul_tmp, params);
- p->premul_tmp->params.alpha = MP_ALPHA_PREMUL;
+ p->premul_tmp->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
// Only zimg supports this.
p->premul->force_scaler = MP_SWS_ZIMG;
@@ -778,7 +791,7 @@ static bool reinit_to_overlay(struct mp_draw_sub_cache *p)
return false;
mp_image_params_guess_csp(&p->rgba_overlay->params);
- p->rgba_overlay->params.alpha = MP_ALPHA_PREMUL;
+ p->rgba_overlay->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
// Some non-sense with the intention to somewhat isolate the returned image.
mp_image_setfmt(&p->res_overlay, p->rgba_overlay->imgfmt);
@@ -835,6 +848,14 @@ struct mp_draw_sub_cache *mp_draw_sub_alloc(void *ta_parent, struct mpv_global *
return c;
}
+// For tests.
+struct mp_draw_sub_cache *mp_draw_sub_alloc_test(struct mp_image *dst)
+{
+ struct mp_draw_sub_cache *c = talloc_zero(NULL, struct mp_draw_sub_cache);
+ reinit_to_video(c);
+ return c;
+}
+
bool mp_draw_sub_bitmaps(struct mp_draw_sub_cache *p, struct mp_image *dst,
struct sub_bitmap_list *sbs_list)
{
@@ -951,7 +972,8 @@ static void mark_rcs(struct mp_draw_sub_cache *p, struct rc_grid *gr)
rc->y0 = MPMIN(rc->y0, y);
rc->y1 = MPMAX(rc->y1, y + 1);
rc->x0 = MPMIN(rc->x0, xpos + s->x0);
- rc->x1 = MPMAX(rc->x1, xpos + s->x1);
+ // Ensure this does not extend beyond the total width
+ rc->x1 = MPCLAMP(xpos + s->x1, rc->x1, p->w);
}
}
}
diff --git a/sub/draw_bmp.h b/sub/draw_bmp.h
index e9560e1ca5..b4c7378290 100644
--- a/sub/draw_bmp.h
+++ b/sub/draw_bmp.h
@@ -10,9 +10,12 @@ struct mp_draw_sub_cache;
struct mp_draw_sub_cache *mp_draw_sub_alloc(void *ta_parent, struct mpv_global *g);
+// Only for use in tests.
+struct mp_draw_sub_cache *mp_draw_sub_alloc_test(struct mp_image