summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
Diffstat (limited to 'sub')
-rw-r--r--sub/ass_mp.c3
-rw-r--r--sub/ass_mp.h2
-rw-r--r--sub/dec_sub.c127
-rw-r--r--sub/dec_sub.h4
-rw-r--r--sub/draw_bmp.c22
-rw-r--r--sub/draw_bmp.h2
-rw-r--r--sub/filter_sdh.c161
-rw-r--r--sub/img_convert.c3
-rw-r--r--sub/osd.c2
-rw-r--r--sub/osd.h3
-rw-r--r--sub/osd_libass.c23
-rw-r--r--sub/sd.h3
-rw-r--r--sub/sd_ass.c229
-rw-r--r--sub/sd_lavc.c24
14 files changed, 389 insertions, 219 deletions
diff --git a/sub/ass_mp.c b/sub/ass_mp.c
index 634681f..3e9f833 100644
--- a/sub/ass_mp.c
+++ b/sub/ass_mp.c
@@ -341,7 +341,7 @@ 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_BGRA ? SUBBITMAP_BGRA
@@ -361,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++) {
diff --git a/sub/ass_mp.h b/sub/ass_mp.h
index dc83e31..b4e16d6 100644
--- a/sub/ass_mp.h
+++ b/sub/ass_mp.h
@@ -57,7 +57,7 @@ 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 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];
}
diff --git a/sub/dec_sub.h b/sub/dec_sub.h
index 9de6760..eb8406c 100644
--- a/sub/dec_sub.h
+++ b/sub/dec_sub.h
@@ -17,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,
};
@@ -44,7 +43,8 @@ void sub_destroy(struct dec_sub *sub);
bool sub_can_preload(struct dec_sub *sub);
void sub_preload(struct dec_sub *sub);
void sub_redecode_cached_packets(struct dec_sub *sub);
-bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force);
+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);
diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c
index 58db162..78e29f4 100644
--- a/sub/draw_bmp.c
+++ b/sub/draw_bmp.c
@@ -431,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)
@@ -525,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:
@@ -546,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 &&
@@ -660,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) {
@@ -675,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;
@@ -724,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);
@@ -762,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;
@@ -787,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);
diff --git a/sub/draw_bmp.h b/sub/draw_bmp.h
index fda7797..b4c7378 100644
--- a/sub/draw_bmp.h
+++ b/sub/draw_bmp.h
@@ -15,7 +15,7 @@ struct mp_draw_sub_cache *mp_draw_sub_alloc_test(struct mp_image *dst);
// Render the sub-bitmaps in sbs_list to dst. sbs_list must have been rendered
// for an OSD resolution equivalent to dst's size (UB if not).
-// Warning: if dst is a format with alpha, and dst is not set to MP_ALPHA_PREMUL
+// Warning: if dst is a format with alpha, and dst is not set to PL_ALPHA_PREMULTIPLIED
// (not done by default), this will be extremely slow.
// Warning: the caller is responsible for ensuring that dst is writable.
// cache: allocated instance; caches non-changing OSD parts etc.
diff --git a/sub/filter_sdh.c b/sub/filter_sdh.c
index 69fca9f..5adc1f9 100644
--- a/sub/filter_sdh.c
+++ b/sub/filter_sdh.c
@@ -33,6 +33,13 @@
// all SDH parts.
// It is for filtering ASS encoded subtitles
+static const char *const enclosure_pair[][2] = {
+ {"(", ")"},
+ {"[", "]"},
+ {"\uFF08", "\uFF09"},
+ {0},
+};
+
struct buffer {
char *string;
int length;
@@ -58,6 +65,47 @@ static inline int append(struct sd_filter *sd, struct buffer *buf, char c)
return c;
}
+static int get_char_bytes(char *str)
+{
+ // In case the first character is non-ASCII.
+ // Will only work with UTF-8 but you shouldn't be
+ // using anything else anyway.
+ if (str && str[0]) {
+ if (!(str[0] >> 7 & 1)) {
+ return strnlen(str, 1);
+ } else if (!(str[0] >> 5 & 1)) {
+ return strnlen(str, 2);
+ } else if (!(str[0] >> 4 & 1)) {
+ return strnlen(str, 3);
+ } else if (!(str[0] >> 3 & 1)) {
+ return strnlen(str, 4);
+ }
+ }
+ return 0;
+}
+
+static const char *get_right_enclosure(char *left)
+{
+ // See if the right hand character is mapped. If not, just return the same thing.
+ for (int i = 0; enclosure_pair[i][0]; i++) {
+ if (strcmp(left, enclosure_pair[i][0]) == 0)
+ return enclosure_pair[i][1];
+ }
+ return left;
+}
+
+static bool valid_left_enclosure(struct sd_filter *sd, char *str)
+{
+ // All characters in this string are valid left hand enclosure characters.
+ char *enclosures = sd->opts->sub_filter_SDH_enclosures;
+ int len = strlen(enclosures);
+ for (int i = 0; i < len; i++) {
+ if (str && str[0] && str[0] == enclosures[i])
+ return true;
+ }
+ return false;
+}
+
// copy ass override tags, if they exist att current position,
// from source string to destination buffer stopping at first
@@ -86,7 +134,8 @@ static void copy_ass(struct sd_filter *sd, char **rpp, struct buffer *buf)
return;
}
-static bool skip_bracketed(struct sd_filter *sd, char **rpp, struct buffer *buf);
+static bool skip_enclosed(struct sd_filter *sd, char **rpp, struct buffer *buf,
+ const char *left, const char *right);
// check for speaker label, like MAN:
// normal subtitles may include mixed case text with : after so
@@ -128,7 +177,7 @@ static void skip_speaker_label(struct sd_filter *sd, char **rpp, struct buffer *
copy_ass(sd, &rp, buf);
} else if (rp[0] == '[') {
// not uncommon with [xxxx]: which should also be skipped
- if (!skip_bracketed(sd, &rp, buf)) {
+ if (!skip_enclosed(sd, &rp, buf, "[", "]")) {
buf->pos = old_pos;
return;
}
@@ -174,94 +223,56 @@ static void skip_speaker_label(struct sd_filter *sd, char **rpp, struct buffer *
return;
}
-// check for bracketed text, like [SOUND]
-// and skip it while preserving ass tags
-// any characters are allowed, brackets are seldom used in normal text
+// Check for text enclosed in symbols, like (SOUND)
+// and skip it while preserving ass tags.
+// Parentheses are a special case since normal subtitles may have
+// them so only upper case is accepted and lower case l which for
+// some looks like upper case I. If sub_filter_SDH_harder is used,
+// both upper and lower case is accepted.
//
-// Parameters:
-// rpp read pointer pointer to source string, updated on return
-// buf write buffer
-//
-// scan in source string
-// the first character in source string must by the starting '['
-// and copy ass tags to destination string but
-// skipping bracketed text if it looks like SDH
-//
-// return true if bracketed text was removed.
-// if not valid SDH read pointer and write buffer position will be unchanged
-// otherwise they point to next position after text and next write position
-static bool skip_bracketed(struct sd_filter *sd, char **rpp, struct buffer *buf)
-{
- char *rp = *rpp;
- int old_pos = buf->pos;
-
- rp++; // skip past '['
- // skip past valid data searching for ]
- while (*rp && rp[0] != ']') {
- if (rp[0] == '{') {
- copy_ass(sd, &rp, buf);
- } else {
- rp++;
- }
- }
- if (!*rp) {
- // ] was not found
- buf->pos = old_pos;
- return false;
- }
- rp++; // skip ]
- // skip trailing spaces
- while (rp[0] == ' ') {
- rp++;
- }
- *rpp = rp;
-
- return true;
-}
-
-// check for parenthesized text, like (SOUND)
-// and skip it while preserving ass tags
-// normal subtitles may include mixed case text in parentheses so
-// only upper case is accepted and lower case l which for some
-// looks like upper case I but if requested harder filtering
-// both upper and lower case is accepted
+// For other symbols, all text in between is removed.
//
// Parameters:
// rpp read pointer pointer to source string, updated on return
// buf write buffer
//
// scan in source string
-// the first character in source string must be the starting '('
+// the first character in source string must be the starting left symbol
// and copy ass tags to destination string but
-// skipping parenthesized text if it looks like SDH
+// skipping enclosed text if it looks like SDH
//
-// return true if parenthesized text was removed.
+// return true if enclosed text was removed.
// if not valid SDH read pointer and write buffer position will be unchanged
// otherwise they point to next position after text and next write position
-static bool skip_parenthesized(struct sd_filter *sd, char **rpp, struct buffer *buf)
+static bool skip_enclosed(struct sd_filter *sd, char **rpp, struct buffer *buf,
+ const char *left, const char *right)
{
- int filter_harder = sd->opts->sub_filter_SDH_harder;
+ bool filter_harder = sd->opts->sub_filter_SDH_harder;
char *rp = *rpp;
int old_pos = buf->pos;
+ bool parenthesis = strcmp(left, "(") == 0 || strcmp(left, "\uFF08") == 0;
- rp++; // skip past '('
- // skip past valid data searching for )
- bool only_digits = true;
- while (*rp && rp[0] != ')') {
+ // skip past the left character
+ rp += get_char_bytes(rp);
+ // skip past valid data searching for the right character
+ bool only_digits = parenthesis;
+ while (*rp && rp[0] != right[0]) {
if (rp[0] == '{') {
copy_ass(sd, &rp, buf);
- } else if ((mp_isalpha(rp[0]) &&
+ } else if (parenthesis && ((mp_isalpha(rp[0]) &&
(filter_harder || mp_isupper(rp[0]) || rp[0] == 'l')) ||
mp_isdigit(rp[0]) ||
rp[0] == ' ' || rp[0] == '\'' || rp[0] == '#' ||
rp[0] == '.' || rp[0] == ',' ||
- rp[0] == '-' || rp[0] == '"' || rp[0] == '\\') {
+ rp[0] == '-' || rp[0] == '"' || rp[0] == '\\')) {
if (!mp_isdigit(rp[0]))
only_digits = false;
rp++;
- } else {
+ } else if (parenthesis) {
buf->pos = old_pos;
return false;
+ } else {
+ rp++;
}
}
if (!*rp) {
@@ -274,7 +285,8 @@ static bool skip_parenthesized(struct sd_filter *sd, char **rpp, struct buffer *
buf->pos = old_pos;
return false;
}
- rp++; // skip )
+ // skip past the right character
+ rp += get_char_bytes(rp);
// skip trailing spaces
while (rp[0] == ' ') {
rp++;
@@ -371,14 +383,17 @@ static char *filter_SDH(struct sd_filter *sd, char *data, int length, ptrdiff_t
// go through the rest of the line looking for SDH in () or []
while (*rp && !(rp[0] == '\\' && rp[1] == 'N')) {
copy_ass(sd, &rp, buf);
- if (rp[0] == '[') {
- if (!skip_bracketed(sd, &rp, buf)) {
- append(sd, buf, rp[0]);
- rp++;
- line_with_text = true;
- }
- } else if (rp[0] == '(') {
- if (!skip_parenthesized(sd, &rp, buf)) {
+ char left[5] = {0};
+ const char *right = NULL;
+ if (valid_left_enclosure(sd, rp)) {
+ int bytes = get_char_bytes(rp);
+ for (int i = 0; i < bytes; i++)
+ left[i] = rp[i];
+ left[bytes] = '\0';
+ right = get_right_enclosure(left);
+ }
+ if (left[0] && right && right[0]) {
+ if (!skip_enclosed(sd, &rp, buf, left, right)) {
append(sd, buf, rp[0]);
rp++;
line_with_text = true;
diff --git a/sub/img_convert.c b/sub/img_convert.c
index a70bb0a..3c18e17 100644
--- a/sub/img_convert.c
+++ b/sub/img_convert.c
@@ -31,7 +31,8 @@
void mp_blur_rgba_sub_bitmap(struct sub_bitmap *d, double gblur)
{
struct mp_image *tmp1 = mp_image_alloc(IMGFMT_BGRA, d->w, d->h);
- if (tmp1) { // on OOM, skip region
+ MP_HANDLE_OOM(tmp1);
+ {
struct mp_image s = {0};
mp_image_setfmt(&s, IMGFMT_BGRA);
mp_image_set_size(&s, d->w, d->h);
diff --git a/sub/osd.c b/sub/osd.c
index 9d6926d..d33aabd 100644
--- a/sub/osd.c
+++ b/sub/osd.c
@@ -119,7 +119,7 @@ bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
struct osd_state *osd_create(struct mpv_global *global)
{
- assert(MAX_OSD_PARTS >= OSDTYPE_COUNT);
+ static_assert(MAX_OSD_PARTS >= OSDTYPE_COUNT, "");
struct osd_state *osd = talloc_zero(NULL, struct osd_state);
*osd = (struct osd_state) {
diff --git a/sub/osd.h b/sub/osd.h
index 39a88ea..2677847 100644
--- a/sub/osd.h
+++ b/sub/osd.h
@@ -74,6 +74,8 @@ struct sub_bitmaps {
int packed_w, packed_h;
int change_id; // Incremented on each change (0 is never used)
+
+ bool video_color_space; // True if the bitmap is in video color space
};
struct sub_bitmap_list {
@@ -243,5 +245,6 @@ void osd_set_external(struct osd_state *osd, struct osd_external_ass *ov);
void osd_set_external_remove_owner(struct osd_state *osd, void *owner);
void osd_get_text_size(struct osd_state *osd, int *out_screen_h, int *out_font_h);
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function);
+void osd_mangle_ass(bstr *dst, const char *in, bool replace_newlines);
#endif /* MPLAYER_SUB_H */
diff --git a/sub/osd_libass.c b/sub/osd_libass.c
index a3b19c9..f2de27b 100644
--- a/sub/osd_libass.c
+++ b/sub/osd_libass.c
@@ -193,7 +193,7 @@ void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function)
snprintf(buffer, buffer_size, "\xFF%c", osd_function);
}
-static void mangle_ass(bstr *dst, const char *in)
+void osd_mangle_ass(bstr *dst, const char *in, bool replace_newlines)
{
const char *start = in;
bool escape_ass = true;
@@ -213,6 +213,14 @@ static void mangle_ass(bstr *dst, const char *in)
}
if (escape_ass && *in == '{')
bstr_xappend(NULL, dst, bstr0("\\"));
+ // Replace newlines with \N for escape-ass. This is necessary to apply
+ // ASS tags past newlines and to preserve consecutive newlines with
+ // osd-overlay because update_external() adds a ASS event per line.
+ if (replace_newlines && *in == '\n') {
+ bstr_xappend(NULL, dst, bstr0("\\N"));
+ in += 1;
+ continue;
+ }
// Libass will strip leading whitespace
if (in[0] == ' ' && (in == start || in[-1] == '\n')) {
bstr_xappend(NULL, dst, bstr0("\\h"));
@@ -231,7 +239,7 @@ static ASS_Event *add_osd_ass_event_escaped(ASS_Track *track, const char *style,
const char *text)
{
bstr buf = {0};
- mangle_ass(&buf, text);
+ osd_mangle_ass(&buf, text, false);
ASS_Event *e = add_osd_ass_event(track, style, buf.start);
talloc_free(buf.start);
return e;
@@ -378,12 +386,7 @@ static void get_osd_bar_box(struct osd_state *osd, struct osd_object *obj,
*o_w = track->PlayResX * (opts->osd_bar_w / 100.0);
*o_h = track->PlayResY * (opts->osd_bar_h / 100.0);
- float base_size = 0.03125;
- style->Outline *= *o_h / track->PlayResY / base_size;
- // So that the chapter marks have space between them
- style->Outline = MPMIN(style->Outline, *o_h / 5.2);
- // So that the border is not 0
- style->Outline = MPMAX(style->Outline, *o_h / 32.0);
+ style->Outline = opts->osd_bar_border_size;
// Rendering with shadow is broken (because there's more than one shape)
style->Shadow = 0;
@@ -471,7 +474,7 @@ static void update_progbar(struct osd_state *osd, struct osd_object *obj)
// chapter marks
for (int n = 0; n < obj->progbar_state.num_stops; n++) {
float s = obj->progbar_state.stops[n] * width;
- float dent = border * 1.3;
+ float dent = MPMAX(border * 1.3, 1.6);
if (s > dent && s < width - dent) {
ass_draw_move_to(d, s + dent, 0);
@@ -683,7 +686,7 @@ struct sub_bitmaps *osd_object_get_bitmaps(struct osd_state *osd,
struct sub_bitmaps out_imgs = {0};
mp_ass_packer_pack(obj->ass_packer, obj->ass_imgs, obj->num_externals + 1,
- obj->changed, format, &out_imgs);
+ obj->changed, false, format, &out_imgs);
obj->changed = false;
diff --git a/sub/sd.h b/sub/sd.h
index 11a90fe..459e8c0 100644
--- a/sub/sd.h
+++ b/sub/sd.h
@@ -11,14 +11,17 @@
#define SUB_GAP_KEEP 0.4
// slight offset when sub seeking or sub stepping
#define SUB_SEEK_OFFSET 0.01
+#define SUB_SEEK_WITHOUT_VIDEO_OFFSET 0.1
struct sd {
struct mpv_global *global;
struct mp_log *log;
struct mp_subtitle_opts *opts;
+ struct mp_subtitle_shared_opts *shared_opts;
const struct sd_functions *driver;
void *priv;
+ int order;
struct attachment_list *attachments;
struct mp_codec_params *codec;
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 6742f6f..6fa4d1b 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -49,18 +49,24 @@ struct sd_ass_priv {
struct sd_filter **filters;
int num_filters;
bool clear_once;
- bool on_top;
struct mp_ass_packer *packer;
struct sub_bitmap_copy_cache *copy_cache;
char last_text[500];
struct mp_image_params video_params;
struct mp_image_params last_params;
struct mp_osd_res osd;
- int64_t *seen_packets;
+ struct seen_packet *seen_packets;
int num_seen_packets;
+ bool *packets_animated;
+ int num_packets_animated;
bool duration_unknown;
};
+struct seen_packet {
+ int64_t pos;
+ double pts;
+};
+
static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts);
static void fill_plaintext(struct sd *sd, double pts);
@@ -78,9 +84,10 @@ static const struct sd_filter_functions *const filters[] = {
// Add default styles, if the track does not have any styles yet.
// Apply style overrides if the user provides any.
-static void mp_ass_add_default_styles(ASS_Track *track, struct mp_subtitle_opts *opts)
+static void mp_ass_add_default_styles(ASS_Track *track, struct mp_subtitle_opts *opts,
+ struct mp_subtitle_shared_opts *shared_opts, int order)
{
- if (opts->ass_styles_file && opts->ass_style_override)
+ if (opts->ass_styles_file && shared_opts->ass_style_override[order])
ass_read_styles(track, opts->ass_styles_file, NULL);
if (track->n_styles == 0) {
@@ -96,7 +103,7 @@ static void mp_ass_add_default_styles(ASS_Track *track, struct mp_subtitle_opts
mp_ass_set_style(style, track->PlayResY, opts->sub_style);
}
- if (opts->ass_style_override)
+ if (shared_opts->ass_style_override[order])
ass_process_force_style(track);
}
@@ -177,7 +184,7 @@ static void filters_init(struct sd *sd)
.opts = mp_get_config_group(ft, sd->global, &mp_sub_filter_opts),
.driver = filters[n],
.codec = "ass",
- .event_format = ctx->ass_track->event_format,
+ .event_format = talloc_strdup(ft, ctx->ass_track->event_format),
};
if (ft->driver->init(ft)) {
MP_TARRAY_APPEND(ctx, ctx->filters, ctx->num_filters, ft);
@@ -207,13 +214,14 @@ static void assobjects_init(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
struct mp_subtitle_opts *opts = sd->opts;
+ struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts;
ctx->ass_library = mp_ass_init(sd->global, sd->opts->sub_style, sd->log);
ass_set_extract_fonts(ctx->ass_library, opts->use_embedded_fonts);
add_subtitle_fonts(sd);
- if (opts->ass_style_override)
+ if (shared_opts->ass_style_override[sd->order])
ass_set_style_overrides(ctx->ass_library, opts->ass_style_override_list);
ctx->ass_track = ass_new_track(ctx->ass_library);
@@ -222,7 +230,7 @@ static void assobjects_init(struct sd *sd)
ctx->shadow_track = ass_new_track(ctx->ass_library);
ctx->shadow_track->PlayResX = MP_ASS_FONT_PLAYRESX;
ctx->shadow_track->PlayResY = MP_ASS_FONT_PLAYRESY;
- mp_ass_add_default_styles(ctx->shadow_track, opts);
+ mp_ass_add_default_styles(ctx->shadow_track, opts, shared_opts, sd->order);
char *extradata = sd->codec->extradata;
int extradata_size = sd->codec->extradata_size;
@@ -233,7 +241,7 @@ static void assobjects_init(struct sd *sd)
if (extradata)
ass_process_codec_private(ctx->ass_track, extradata, extradata_size);
- mp_ass_add_default_styles(ctx->ass_track, opts);
+ mp_ass_add_default_styles(ctx->ass_track, opts, shared_opts, sd->order);
#if LIBASS_VERSION >= 0x01302000
ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1);
@@ -279,11 +287,55 @@ static int init(struct sd *sd)
return 0;
}
+// Check if subtitle has events that would cause it to be animated inside {}
+static bool is_animated(char *s)
+{
+ bool in_tag = false;
+ bool valid_event = false;
+ bool valid_tag = false;
+ while (*s) {
+ if (!in_tag && s[0] == '{')
+ in_tag = true;
+ if (s[0] == '\\') {
+ s++;
+ if (!s[0])
+ break;
+ if (s[0] == 'k' || s[0] == 'K' || s[0] == 't') {
+ valid_event = true;
+ continue;
+ // just bruteforce the multi-letter ones
+ } else if (s[0] == 'f') {
+ if (!strncmp(s, "fad", 3)) {
+ valid_event = true;
+ continue;
+ }
+ } else if (s[0] == 'm') {
+ if (!strncmp(s, "move", 4)) {
+ valid_event = true;
+ continue;
+ }
+ }
+ }
+ if (in_tag && valid_event && s[0] == '}') {
+ valid_tag = true;
+ break;
+ } else if (s[0] == '}') {
+ in_tag = false;
+ valid_event = false;
+ valid_tag = false;
+ }
+ s++;
+ }
+ return valid_tag;
+}
+
// Note: pkt is not necessarily a fully valid refcounted packet.
static void filter_and_add(struct sd *sd, struct demux_packet *pkt)
{
struct sd_ass_priv *ctx = sd->priv;
struct demux_packet *orig_pkt = pkt;
+ ASS_Track *track = ctx->ass_track;
+ int old_n_events = track->n_events;
for (int n = 0; n < ctx->num_filters; n++) {
struct sd_filter *ft = ctx->filters[n];
@@ -299,30 +351,51 @@ static void filter_and_add(struct sd *sd, struct demux_packet *pkt)
llrint(pkt->pts * 1000),
llrint(pkt->duration * 1000));
+ // This bookkeeping is only ever needed for ASS subs
+ if (!ctx->is_converted) {
+ if (!pkt->seen) {
+ for (int n = track->n_events - 1; n >= 0; n--) {
+ if (n + 1 == old_n_events || pkt->animated)
+ break;
+ ASS_Event *event = &track->events[n];
+ pkt->animated = (event->Effect && event->Effect[0]) ||
+ is_animated(event->Text);
+ }
+ MP_TARRAY_APPEND(ctx, ctx->packets_animated, ctx->num_packets_animated, pkt->animated);
+ } else {
+ pkt->animated = ctx->packets_animated[pkt->seen_pos];
+ }
+ }
+
if (pkt != orig_pkt)
talloc_free(pkt);
}
-// Test if the packet with the given file position (used as unique ID) was
-// already consumed. Return false if the packet is new (and add it to the
-// internal list), and return true if it was already seen.
-static bool check_packet_seen(struct sd *sd, int64_t pos)
+// Test if the packet with the given file position and pts was already consumed.
+// Return false if the packet is new (and add it to the internal list), and
+// return true if it was already seen.
+static bool check_packet_seen(struct sd *sd, struct demux_packet *packet)
{
struct sd_ass_priv *priv = sd->priv;
int a = 0;
int b = priv->num_seen_packets;
while (a < b) {
int mid = a + (b - a) / 2;
- int64_t val = priv->seen_packets[mid];
- if (pos == val)
+ struct seen_packet *seen_packet = &priv->seen_packets[mid];
+ if (packet->pos == seen_packet->pos && packet->pts == seen_packet->pts) {
+ packet->seen_pos = mid;
return true;
- if (pos > val) {
+ }
+ if (packet->pos > seen_packet->pos ||
+ (packet->pos == seen_packet->pos && packet->pts > seen_packet->pts)) {
a = mid + 1;
} else {
b = mid;
}
}
- MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a, pos);
+ packet->seen_pos = a;
+ MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a,
+ (struct seen_packet){packet->pos, packet->pts});
return false;
}
@@ -332,9 +405,12 @@ static void decode(struct sd *sd, struct demux_packet *packet)
{
struct sd_ass_priv *ctx = sd->priv;
ASS_Track *track = ctx->ass_track;
+
+ packet->sub_duration = packet->duration;
+
if (ctx->converter) {
if (!sd->opts->sub_clear_on_seek && packet->pos >= 0 &&
- check_packet_seen(sd, packet->pos))
+ check_packet_seen(sd, packet))
return;
double sub_pts = 0;
@@ -373,7 +449,9 @@ static void decode(struct sd *sd, struct demux_packet *packet)
}
} else {
// Note that for this packet format, libass has an internal mechanism
- // for discarding duplicate (already seen) packets.
+ // for discarding duplicate (already seen) packets but we check this
+ // anyways for our purposes for ASS subtitles.
+ packet->seen = check_packet_seen(sd, packet);
filter_and_add(sd, packet);
}
}
@@ -382,6 +460,7 @@ static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
bool converted, ASS_Track *track)
{
struct mp_subtitle_opts *opts = sd->opts;
+ struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts;
struct sd_ass_priv *ctx = sd->priv;
ASS_Renderer *priv = ctx->ass_renderer;
@@ -397,7 +476,7 @@ static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
bool set_scale_by_window = true;
bool total_override = false;
// With forced overrides, apply the --sub-* specific options
- if (converted || opts->ass_style_override == 3) { // 'force'
+ if (converted || shared_opts->ass_style_override[sd->order] == 3) { // 'force'
set_scale_with_window = opts->sub_scale_with_window;
set_use_margins = opts->sub_use_margins;
set_scale_by_window = opts->sub_scale_by_window;
@@ -406,8 +485,8 @@ static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
set_scale_with_window = opts->ass_scale_with_window;
set_use_margins = opts->ass_use_margins;
}
- if (converted || opts->ass_style_override) {
- set_sub_pos = 100.0f - opts->sub_pos;
+ if (converted || shared_opts->ass_style_override[sd->order]) {
+ set_sub_pos = 100.0f - shared_opts->sub_pos[sd->order];
set_line_spacing = opts->ass_line_spacing;
set_hinting = opts->ass_hinting;
set_font_scale = opts->sub_scale;
@@ -427,12 +506,12 @@ static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
int set_force_flags = 0;
if (total_override)
set_force_flags |= ASS_OVERRIDE_BIT_STYLE | ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
- if (opts->ass_style_override == 4) // 'scale'
+ if (shared_opts->ass_style_override[sd->order] == 4) // 'scale'
set_force_flags |= ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
if (converted)
set_force_flags |= ASS_OVERRIDE_BIT_ALIGNMENT;
#ifdef ASS_JUSTIFY_AUTO
- if ((converted || opts->ass_style_override) && opts->ass_justify)
+ if ((converted || shared_opts->ass_style_override[sd->order]) && opts->ass_justify)
set_force_flags |= ASS_OVERRIDE_BIT_JUSTIFY;
#endif
ass_set_selective_style_override_enabled(priv, set_force_flags);
@@ -499,7 +578,7 @@ static long long find_timestamp(struct sd *sd, double pts)
long long ts = llrint(pts * 1000);
- if (!sd->opts->sub_fix_timing || sd->opts->ass_style_override == 0)
+ if (!sd->opts->sub_fix_timing || sd->shared_opts->ass_style_override[sd->order] == 0)
return ts;
// Try to fix small gaps and overlaps.
@@ -561,8 +640,8 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res dim,
{
struct sd_ass_priv *ctx = sd->priv;
struct mp_subtitle_opts *opts = sd->opts;
- bool no_ass = !opts->ass_enabled || ctx->on_top ||
- opts->ass_style_override == 5;
+ struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts;
+ bool no_ass = !opts->ass_enabled || shared_opts->ass_style_override[sd->order] == 5;
bool converted = ctx->is_converted || no_ass;
ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track;
ASS_Renderer *renderer = ctx->ass_renderer;
@@ -582,7 +661,7 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res dim,
goto done;
double scale = dim.display_par;
- if (!converted && (!opts->ass_style_override ||
+ if (!converted && (!shared_opts->ass_style_override[sd->order] ||
opts->ass_vsfilter_aspect_compat))
{
// Let's use the original video PAR for vsfilter compatibility:
@@ -595,7 +674,7 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res dim,
ctx->ass_configured = true;
}
ass_set_pixel_aspect(renderer, scale);
- if (!converted && (!opts->ass_style_override ||
+ if (!converted && (!shared_opts->ass_style_override[sd->order] ||
opts->ass_vsfilter_blur_compat))
{
ass_set_storage_size(renderer, ctx->video_params.w, ctx->video_params.h);
@@ -603,18 +682,13 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res dim,
ass_set_storage_size(renderer, 0, 0);
}
long long ts = find_timestamp(sd, pts);
- if (ctx->duration_unknown && pts != MP_NOPTS_VALUE) {
- mp_ass_flush_old_events(track, ts);
- ctx->num_seen_packets = 0;
- sd->preload_ok = false;
- }
if (no_ass)
fill_plaintext(sd, pts);
int changed;
ASS_Image *imgs = ass_render_frame(renderer, track, ts, &changed);
- mp_ass_packer_pack(ctx->packer, &imgs, 1, changed, format, res);
+ mp_ass_packer_pack(ctx->packer, &imgs, 1, changed, !converted, format, res);
done:
// mangle_colors() modifies the color field, so copy the thing _before_.
@@ -650,7 +724,7 @@ static void ass_to_plaintext(struct buf *b, const char *in)
if (in[0] == '}') {
in += 1;
in_tag = false;
- } else if (in[0] == '\\' && in[1] == 'p') {
+ } else if (in[0] == '\\' && in[1] == 'p' && in[2] != 'o') {
in += 2;
// Skip text between \pN and \p0 tags. A \p without a number
// is the same as \p0, and leading 0s are also allowed.
@@ -785,9 +859,6 @@ static void fill_plaintext(struct sd *sd, double pts)
bstr dst = {0};
- if (ctx->on_top)
- bstr_xappend(NULL, &dst, bstr0("{\\a6}"));
-
while (*text) {
if (*text == '{')
bstr_xappend(NULL, &dst, bstr0("\\"));
@@ -814,7 +885,7 @@ static void fill_plaintext(struct sd *sd, double pts)
static void reset(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
- if (sd->opts->sub_clear_on_seek || ctx->duration_unknown || ctx->clear_once) {
+ if (sd->opts->sub_clear_on_seek || ctx->clear_once) {
ass_flush_events(ctx->ass_track);
ctx->num_seen_packets = 0;
sd->preload_ok = false;
@@ -852,9 +923,6 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
case SD_CTRL_SET_VIDEO_PARAMS:
ctx->video_params = *(struct mp_image_params *)arg;
return CONTROL_OK;
- case SD_CTRL_SET_TOP:
- ctx->on_top = *(bool *)arg;
- return CONTROL_OK;
case SD_CTRL_UPDATE_OPTS: {
int flags = (uintptr_t)arg;
if (flags & UPDATE_SUB_FILT) {
@@ -897,27 +965,27 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
{
struct mp_subtitle_opts *opts = sd->opts;
struct sd_ass_priv *ctx = sd->priv;
- enum mp_csp csp = 0;
- enum mp_csp_levels levels = 0;
+ enum pl_color_system csp = 0;
+ enum pl_color_levels levels = 0;
if (opts->ass_vsfilter_color_compat == 0) // "no"
return;
bool force_601 = opts->ass_vsfilter_color_compat == 3;
ASS_Track *track = ctx->ass_track;
static const int ass_csp[] = {
- [YCBCR_BT601_TV] = MP_CSP_BT_601,
- [YCBCR_BT601_PC] = MP_CSP_BT_601,
- [YCBCR_BT709_TV] = MP_CSP_BT_709,
- [YCBCR_BT709_PC] = MP_CSP_BT_709,
- [YCBCR_SMPTE240M_TV] = MP_CSP_SMPTE_240M,
- [YCBCR_SMPTE240M_PC] = MP_CSP_SMPTE_240M,
+ [YCBCR_BT601_TV] = PL_COLOR_SYSTEM_BT_601,
+ [YCBCR_BT601_PC] = PL_COLOR_SYSTEM_BT_601,
+ [YCBCR_BT709_TV] = PL_COLOR_SYSTEM_BT_709,
+ [YCBCR_BT709_PC] = PL_COLOR_SYSTEM_BT_709,
+ [YCBCR_SMPTE240M_TV] = PL_COLOR_SYSTEM_SMPTE_240M,
+ [YCBCR_SMPTE240M_PC] = PL_COLOR_SYSTEM_SMPTE_240M,
};
static const int ass_levels[] = {
- [YCBCR_BT601_TV] = MP_CSP_LEVELS_TV,
- [YCBCR_BT601_PC] = MP_CSP_LEVELS_PC,
- [YCBCR_BT709_TV] = MP_CSP_LEVELS_TV,
- [YCBCR_BT709_PC] = MP_CSP_LEVELS_PC,
- [YCBCR_SMPTE240M_TV] = MP_CSP_LEVELS_TV,
- [YCBCR_SMPTE240M_PC] = MP_CSP_LEVELS_PC,
+ [YCBCR_BT601_TV] = PL_COLOR_LEVELS_LIMITED,
+ [YCBCR_BT601_PC] = PL_COLOR_LEVELS_FULL,
+ [YCBCR_BT709_TV] = PL_COLOR_LEVELS_LIMITED,
+ [YCBCR_BT709_PC] = PL_COLOR_LEVELS_FULL,
+ [YCBCR_SMPTE240M_TV] = PL_COLOR_LEVELS_LIMITED,
+ [YCBCR_SMPTE240M_PC] = PL_COLOR_LEVELS_FULL,
};
int trackcsp = track->YCbCrMatrix;
if (force_601)
@@ -930,8 +998,8 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
if (trackcsp < sizeof(ass_levels) / sizeof(ass_levels[0]))
levels = ass_levels[trackcsp];
if (trackcsp == YCBCR_DEFAULT) {
- csp = MP_CSP_BT_601;
- levels = MP_CSP_LEVELS_TV;
+ csp = PL_COLOR_SYSTEM_BT_601;
+ levels = PL_COLOR_LEVELS_LIMITED;
}
// Unknown colorspace (either YCBCR_UNKNOWN, or a valid value unknown to us)
if (!csp || !levels)
@@ -940,50 +1008,51 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
struct mp_image_params params = ctx->video_params;
if (force_601) {
- params.color = (struct mp_colorspace){
- .space = MP_CSP_BT_709,
- .levels = MP_CSP_LEVELS_TV,
+ params.repr = (struct pl_color_repr){
+ .sys = PL_COLOR_SYSTEM_BT_709,
+ .levels = PL_COLOR_LEVELS_LIMITED,
};
}
- if ((csp == params.color.space && levels == params.color.levels) ||
- params.color.space == MP_CSP_RGB) // Even VSFilter doesn't mangle on RGB video
+ if ((csp == params.repr.sys && levels == params.repr.levels) ||
+ params.repr.sys == PL_COLOR_SYSTEM_RGB) // Even VSFilter doesn't mangle on RGB video
return;
- bool basic_conv = params.color.space == MP_CSP_BT_709 &&
- params.color.levels == MP_CSP_LEVELS_TV &&
- csp == MP_CSP_BT_601 &&
- levels == MP_CSP_LEVELS_TV;
+ bool basic_conv = params.repr.sys == PL_COLOR_SYSTEM_BT_709 &&
+ params.repr.levels == PL_COLOR_LEVELS_LIMITED &&
+ csp == PL_COLOR_SYSTEM_BT_601 &&
+ levels == PL_COLOR_LEVELS_LIMITED;
// With "basic", only do as much as needed for basic compatibility.
if (opts->ass_vsfilter_color_compat == 1 && !basic_conv)
return;
- if (params.color.space != ctx->last_params.color.space ||
- params.color.levels != ctx->last_params.color.levels)
+ if (params.repr.sys != ctx->last_params.repr.sys ||
+ params.repr.levels != ctx->last_params.repr.levels)
{
int msgl = basic_conv ? MSGL_V : MSGL_WARN;
ctx->last_params = params;
MP_MSG(sd, msgl, "mangling colors like vsfilter: "
"RGB -> %s %s -> %s %s -> RGB\n",
- m_opt_choice_str(mp_csp_names, csp),
- m_opt_choice_str(mp_csp_levels_names, levels),
- m_opt_choice_str(mp_csp_names, params.color.space),
- m_opt_choice_str(mp_csp_names, params.color.levels));
+ m_opt_choice_str(pl_csp_names, csp),
+ m_opt_choice_str(pl_csp_levels_names, levels),
+ m_opt_choice_str(pl_csp_names, params.repr.sys),
+ m_opt_choice_str(pl_csp_names, params.repr.levels));
}
// Conversion that VSFilter would use
struct mp_csp_params vs_params = MP_CSP_PARAMS_DEFAULTS;
- vs_params.color.space = csp;
- vs_params.color.levels = levels;
- struct mp_cmat vs_yuv2rgb, vs_rgb2yuv;
+ vs_params.repr.sys = csp;
+ vs_params.repr.levels = levels;
+ struct pl_transform3x3 vs_yuv2rgb;
mp_get_csp_matrix(&vs_params, &vs_yuv2rgb);
- mp_invert_cmat(&vs_rgb2yuv, &vs_yuv2rgb);
+ pl_transform3x3_invert(&vs_yuv2rgb);
// Proper conversion to RGB
struct mp_csp_params rgb_params = MP_CSP_PARAMS_DEFAULTS;
+ rgb_params.repr = params.repr;
rgb_params.color = params.color;
- struct mp_cmat vs2rgb;
+ struct pl_transform3x3 vs2rgb;
mp_get_csp_matrix(&rgb_params, &vs2rgb);
for (int n = 0; n < parts->num_parts; n++) {
@@ -994,7 +1063,7 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
int b = (color >> 8u) & 0xff;
int a = 0xff - (color & 0xff);
int rgb[3] = {r, g, b}, yuv[3];
- mp_map_fixp_color(&vs_rgb2yuv, 8, rgb, 8, yuv);
+ mp_map_fixp_color(&vs_yuv2rgb, 8, rgb, 8, yuv);
mp_map_fixp_color(&vs2rgb, 8, yuv, 8, rgb);
sb->libass.color = MP_ASS_RGBA(rgb[0], rgb[1], rgb[2], a);
}
diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c
index 30aa641..cf49f2d 100644
--- a/sub/sd_lavc.c
+++ b/sub/sd_lavc.c
@@ -96,6 +96,9 @@ static int init(struct sd *sd)
ctx = avcodec_alloc_context3(sub_codec);
if (!ctx)
goto error;
+
+ mp_set_avopts(sd->log, ctx, sd->opts->sub_avopts);
+
priv->avpkt = av_packet_alloc();
if (!priv->avpkt)
goto error;
@@ -308,9 +311,15 @@ static void decode(struct sd *sd, struct demux_packet *packet)
mp_set_av_packet(priv->avpkt, packet, &priv->pkt_timebase);
if (ctx->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
- char page[4];
- snprintf(page, sizeof(page), "%d", opts->teletext_page);
- av_opt_set(ctx, "txt_page", page, AV_OPT_SEARCH_CHILDREN);
+ if (!opts->teletext_page) {
+ av_opt_set(ctx, "txt_page", "subtitle", AV_OPT_SEARCH_CHILDREN);
+ } else if (opts->teletext_page == -1) {
+ av_opt_set(ctx, "txt_page", "*", AV_OPT_SEARCH_CHILDREN);
+ } else {
+ char page[4];
+ snprintf(page, sizeof(page), "%d", opts->teletext_page);
+ av_opt_set(ctx, "txt_page", page, AV_OPT_SEARCH_CHILDREN);
+ }
}
int got_sub;
@@ -318,6 +327,8 @@ static void decode(struct sd *sd, struct demux_packet *packet)
if (res < 0 || !got_sub)
return;
+ packet->sub_duration = sub.end_display_time;
+
if (sub.pts != AV_NOPTS_VALUE)
pts = sub.pts / (double)AV_TIME_BASE;
@@ -403,6 +414,7 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res d,
{
struct sd_lavc_priv *priv = sd->priv;
struct mp_subtitle_opts *opts = sd->opts;
+ struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts;
priv->current_pts = pts;
@@ -450,8 +462,8 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res d,
h = MPMAX(priv->video_params.h, current->src_h);
}
- if (opts->sub_pos != 100.0f && opts->ass_style_override) {
- float offset = (100.0f - opts->sub_pos) / 100.0f * h;
+ if (shared_opts->sub_pos[sd->order] != 100.0f && shared_opts->ass_style_override[sd->order]) {
+ float offset = (100.0f - shared_opts->sub_pos[sd->order]) / 100.0f * h;
for (int n = 0; n < res->num_parts; n++) {
struct sub_bitmap *sub = &res->parts[n];
@@ -469,7 +481,7 @@ static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res d,
osd_rescale_bitmaps(res, w, h, d, video_par);
- if (opts->sub_scale != 1.0 && opts->ass_style_override) {
+ if (opts->sub_scale != 1.0 && shared_opts->ass_style_override[sd->order]) {
for (int n = 0; n < res->num_parts; n++) {
struct sub_bitmap *sub = &res->parts[n];