From 60e8a3d404f0640fa5a3f834eae54b4f1fb9127d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 03:13:14 +0200 Subject: Adding upstream version 0.38.0. Signed-off-by: Daniel Baumann --- sub/ass_mp.c | 3 +- sub/ass_mp.h | 2 +- sub/dec_sub.c | 127 ++++++++++++++++++++++-------- sub/dec_sub.h | 4 +- sub/draw_bmp.c | 22 +++--- sub/draw_bmp.h | 2 +- sub/filter_sdh.c | 161 +++++++++++++++++++++----------------- sub/img_convert.c | 3 +- sub/osd.c | 2 +- sub/osd.h | 3 + sub/osd_libass.c | 23 +++--- sub/sd.h | 3 + sub/sd_ass.c | 229 +++++++++++++++++++++++++++++++++++------------------- sub/sd_lavc.c | 24 ++++-- 14 files changed, 389 insertions(+), 219 deletions(-) (limited to 'sub') 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 #include #include +#include #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]; -- cgit v1.2.3