diff options
Diffstat (limited to 'sub')
-rw-r--r-- | sub/ass_mp.c | 48 | ||||
-rw-r--r-- | sub/ass_mp.h | 14 | ||||
-rw-r--r-- | sub/dec_sub.c | 282 | ||||
-rw-r--r-- | sub/dec_sub.h | 28 | ||||
-rw-r--r-- | sub/draw_bmp.c | 1270 | ||||
-rw-r--r-- | sub/draw_bmp.h | 53 | ||||
-rw-r--r-- | sub/filter_jsre.c | 140 | ||||
-rw-r--r-- | sub/filter_regex.c | 28 | ||||
-rw-r--r-- | sub/filter_sdh.c | 244 | ||||
-rw-r--r-- | sub/img_convert.c | 3 | ||||
-rw-r--r-- | sub/lavc_conv.c | 94 | ||||
-rw-r--r-- | sub/meson.build | 6 | ||||
-rw-r--r-- | sub/osd.c | 327 | ||||
-rw-r--r-- | sub/osd.h | 44 | ||||
-rw-r--r-- | sub/osd_dummy.c | 39 | ||||
-rw-r--r-- | sub/osd_libass.c | 138 | ||||
-rw-r--r-- | sub/osd_state.h | 18 | ||||
-rw-r--r-- | sub/sd.h | 34 | ||||
-rw-r--r-- | sub/sd_ass.c | 580 | ||||
-rw-r--r-- | sub/sd_lavc.c | 128 |
20 files changed, 2421 insertions, 1097 deletions
diff --git a/sub/ass_mp.c b/sub/ass_mp.c index d5d9e3e7d6..3e9f83313f 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -23,6 +23,7 @@ #include <stdarg.h> #include <stdbool.h> #include <assert.h> +#include <math.h> #include <ass/ass.h> #include <ass/ass_types.h> @@ -131,9 +132,14 @@ static void message_callback(int level, const char *format, va_list va, void *ct mp_msg(log, level, "\n"); } -ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log) +ASS_Library *mp_ass_init(struct mpv_global *global, + struct osd_style_opts *opts, struct mp_log *log) { - char *path = mp_find_config_file(NULL, global, "fonts"); + char *path = opts->fonts_dir && opts->fonts_dir[0] ? + mp_get_user_path(NULL, global, opts->fonts_dir) : + mp_find_config_file(NULL, global, "fonts"); + mp_dbg(log, "ASS library version: 0x%x (runtime 0x%x)\n", + (unsigned)LIBASS_VERSION, ass_library_version()); ASS_Library *priv = ass_library_init(); if (!priv) abort(); @@ -237,6 +243,11 @@ static bool pack(struct mp_ass_packer *p, struct sub_bitmaps *res, int imgfmt) talloc_steal(p, p->cached_img); } + if (!mp_image_make_writeable(p->cached_img)) { + packer_reset(p->packer); + return false; + } + res->packed = p->cached_img; for (int n = 0; n < res->num_parts; n++) { @@ -277,7 +288,7 @@ static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res) struct sub_bitmaps imgs = { .change_id = res->change_id, - .format = SUBBITMAP_RGBA, + .format = SUBBITMAP_BGRA, .parts = p->rgba_imgs, .num_parts = num_bb, }; @@ -330,10 +341,10 @@ static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res) // repacks all images). preferred_osd_format can be set to a desired // sub_bitmap_format. Currently, only SUBBITMAP_LIBASS is supported. void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists, - int num_image_lists, bool image_lists_changed, + int num_image_lists, bool image_lists_changed, bool video_color_space, int preferred_osd_format, struct sub_bitmaps *out) { - int format = preferred_osd_format == SUBBITMAP_RGBA ? SUBBITMAP_RGBA + int format = preferred_osd_format == SUBBITMAP_BGRA ? SUBBITMAP_BGRA : SUBBITMAP_LIBASS; if (p->cached_subs_valid && !image_lists_changed && @@ -350,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++) { @@ -371,7 +383,7 @@ void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists, } bool r = false; - if (format == SUBBITMAP_RGBA) { + if (format == SUBBITMAP_BGRA) { r = pack_rgba(p, &res); } else { r = pack_libass(p, &res); @@ -385,3 +397,27 @@ void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists, p->cached_subs.change_id = 0; p->cached_subs_valid = true; } + +// Set *out_rc to [x0, y0, x1, y1] of the graphical bounding box in script +// coordinates. +// Set it to [inf, inf, -inf, -inf] if empty. +void mp_ass_get_bb(ASS_Image *image_list, ASS_Track *track, + struct mp_osd_res *res, double *out_rc) +{ + double rc[4] = {INFINITY, INFINITY, -INFINITY, -INFINITY}; + + for (ASS_Image *img = image_list; img; img = img->next) { + if (img->w == 0 || img->h == 0) + continue; + rc[0] = MPMIN(rc[0], img->dst_x); + rc[1] = MPMIN(rc[1], img->dst_y); + rc[2] = MPMAX(rc[2], img->dst_x + img->w); + rc[3] = MPMAX(rc[3], img->dst_y + img->h); + } + + double scale = track->PlayResY / (double)MPMAX(res->h, 1); + if (scale > 0) { + for (int i = 0; i < 4; i++) + out_rc[i] = rc[i] * scale; + } +} diff --git a/sub/ass_mp.h b/sub/ass_mp.h index 4ebb7f39a6..b4e16d6650 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -26,8 +26,10 @@ #include <ass/ass.h> #include <ass/ass_types.h> -// This is probably arbitrary. -// sd_lavc_conv might indirectly still assume this PlayResY, though. +// These PlayResX and PlayResY values are arbitrary and taken from lavc. +// lavc assumes these values when converting to ass generally. Moreover, these +// values are also used by default in VSFilter, so it isn't that arbitrary. +#define MP_ASS_FONT_PLAYRESX 384 #define MP_ASS_FONT_PLAYRESY 288 #define MP_ASS_RGBA(r, g, b, a) \ @@ -40,6 +42,7 @@ struct MPOpts; struct mpv_global; struct mp_osd_res; struct osd_style_opts; +struct mp_log; void mp_ass_flush_old_events(ASS_Track *track, long long ts); void mp_ass_set_style(ASS_Style *style, double res_y, @@ -47,13 +50,16 @@ void mp_ass_set_style(ASS_Style *style, double res_y, void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts, struct mpv_global *global, struct mp_log *log); -ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log); +ASS_Library *mp_ass_init(struct mpv_global *global, + struct osd_style_opts *opts, struct mp_log *log); struct sub_bitmaps; struct mp_ass_packer; struct mp_ass_packer *mp_ass_packer_alloc(void *ta_parent); void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists, - int num_image_lists, bool changed, + int num_image_lists, bool changed, bool video_color_space, int preferred_osd_format, struct sub_bitmaps *out); +void mp_ass_get_bb(ASS_Image *image_list, ASS_Track *track, + struct mp_osd_res *res, double *out_rc); #endif /* MPLAYER_ASS_MP_H */ diff --git a/sub/dec_sub.c b/sub/dec_sub.c index f98a478be4..c03393a007 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -20,9 +20,8 @@ #include <string.h> #include <math.h> #include <assert.h> -#include <pthread.h> +#include <limits.h> -#include "config.h" #include "demux/demux.h" #include "sd.h" #include "dec_sub.h" @@ -39,19 +38,19 @@ extern const struct sd_functions sd_lavc; static const struct sd_functions *const sd_list[] = { &sd_lavc, -#if HAVE_LIBASS &sd_ass, -#endif NULL }; struct dec_sub { - pthread_mutex_t lock; + mp_mutex lock; struct mp_log *log; struct mpv_global *global; struct mp_subtitle_opts *opts; + struct mp_subtitle_shared_opts *shared_opts; struct m_config_cache *opts_cache; + struct m_config_cache *shared_opts_cache; struct mp_recorder_sink *recorder_sink; @@ -59,10 +58,12 @@ struct dec_sub { struct sh_stream *sh; int play_dir; + int order; double last_pkt_pts; bool preload_attempted; double video_fps; double sub_speed; + bool sub_visible; struct mp_codec_params *codec; double start, end; @@ -71,8 +72,9 @@ struct dec_sub { struct sd *sd; struct demux_packet *new_segment; - - struct mp_dispatch_queue *demux_waiter; + struct demux_packet **cached_pkts; + int cached_pkt_pos; + int num_cached_pkts; }; static void update_subtitle_speed(struct dec_sub *sub) @@ -95,40 +97,43 @@ static void update_subtitle_speed(struct dec_sub *sub) // Return the subtitle PTS used for a given video PTS. static double pts_to_subtitle(struct dec_sub *sub, double pts) { - struct mp_subtitle_opts *opts = sub->opts; + struct mp_subtitle_shared_opts *opts = sub->shared_opts; + float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order]; if (pts != MP_NOPTS_VALUE) - pts = (pts * sub->play_dir - opts->sub_delay) / sub->sub_speed; + pts = (pts * sub->play_dir - delay) / sub->sub_speed; return pts; } static double pts_from_subtitle(struct dec_sub *sub, double pts) { - struct mp_subtitle_opts *opts = sub->opts; + struct mp_subtitle_shared_opts *opts = sub->shared_opts; + float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order]; if (pts != MP_NOPTS_VALUE) - pts = (pts * sub->sub_speed + opts->sub_delay) * sub->play_dir; + pts = (pts * sub->sub_speed + delay) * sub->play_dir; return pts; } -void sub_lock(struct dec_sub *sub) -{ - pthread_mutex_lock(&sub->lock); -} - -void sub_unlock(struct dec_sub *sub) -{ - pthread_mutex_unlock(&sub->lock); -} - static void wakeup_demux(void *ctx) { struct mp_dispatch_queue *q = ctx; mp_dispatch_interrupt(q); } +static void destroy_cached_pkts(struct dec_sub *sub) +{ + int index = 0; + while (index < sub->num_cached_pkts) { + TA_FREEP(&sub->cached_pkts[index]); + ++index; + } + sub->cached_pkt_pos = 0; + sub->num_cached_pkts = 0; +} + void sub_destroy(struct dec_sub *sub) { if (!sub) @@ -139,7 +144,7 @@ void sub_destroy(struct dec_sub *sub) sub->sd->driver->uninit(sub->sd); } talloc_free(sub->sd); - pthread_mutex_destroy(&sub->lock); + mp_mutex_destroy(&sub->lock); talloc_free(sub); } @@ -152,7 +157,9 @@ static struct sd *init_decoder(struct dec_sub *sub) .global = sub->global, .log = mp_log_new(sd, sub->log, driver->name), .opts = sub->opts, + .shared_opts = sub->shared_opts, .driver = driver, + .order = sub->order, .attachments = sub->attachments, .codec = sub->codec, .preload_ok = true, @@ -172,32 +179,32 @@ static struct sd *init_decoder(struct dec_sub *sub) // Thread-safety of the returned object: all functions are thread-safe, // except sub_get_bitmaps() and sub_get_text(). Decoder backends (sd_*) // do not need to acquire locks. -// Ownership of attachments goes to the caller, and is released with +// Ownership of attachments goes to the callee, and is released with // talloc_free() (even on failure). -struct dec_sub *sub_create(struct mpv_global *global, struct sh_stream *sh, - struct attachment_list *attachments) +struct dec_sub *sub_create(struct mpv_global *global, struct track *track, + struct attachment_list *attachments, int order) { - assert(sh && sh->type == STREAM_SUB); + assert(track->stream && track->stream->type == STREAM_SUB); struct dec_sub *sub = talloc(NULL, struct dec_sub); *sub = (struct dec_sub){ .log = mp_log_new(sub, global->log, "sub"), .global = global, .opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_sub_opts), - .sh = sh, - .codec = sh->codec, + .shared_opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_shared_sub_opts), + .sh = track->stream, + .codec = track->stream->codec, .attachments = talloc_steal(sub, attachments), .play_dir = 1, + .order = order, .last_pkt_pts = MP_NOPTS_VALUE, .last_vo_pts = MP_NOPTS_VALUE, .start = MP_NOPTS_VALUE, .end = MP_NOPTS_VALUE, - .demux_waiter = mp_dispatch_create(sub), }; sub->opts = sub->opts_cache->opts; - mpthread_mutex_init_recursive(&sub->lock); - - demux_set_stream_wakeup_cb(sub->sh, wakeup_demux, sub->demux_waiter); + sub->shared_opts = sub->shared_opts_cache->opts; + mp_mutex_init(&sub->lock); sub->sd = init_decoder(sub); if (sub->sd) { @@ -241,15 +248,18 @@ static void update_segment(struct dec_sub *sub) bool sub_can_preload(struct dec_sub *sub) { bool r; - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); r = sub->sd->driver->accept_packets_in_advance && !sub->preload_attempted; - pthread_mutex_unlock(&sub->lock); + mp_mutex_unlock(&sub->lock); return r; } void sub_preload(struct dec_sub *sub) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); + + struct mp_dispatch_queue *demux_waiter = mp_dispatch_create(NULL); + demux_set_stream_wakeup_cb(sub->sh, wakeup_demux, demux_waiter); sub->preload_attempted = true; @@ -257,16 +267,19 @@ void sub_preload(struct dec_sub *sub) struct demux_packet *pkt = NULL; int r = demux_read_packet_async(sub->sh, &pkt); if (r == 0) { - mp_dispatch_queue_process(sub->demux_waiter, INFINITY); + mp_dispatch_queue_process(demux_waiter, INFINITY); continue; } if (!pkt) break; sub->sd->driver->decode(sub->sd, pkt); - talloc_free(pkt); + MP_TARRAY_APPEND(sub, sub->cached_pkts, sub->num_cached_pkts, pkt); } - pthread_mutex_unlock(&sub->lock); + demux_set_stream_wakeup_cb(sub->sh, NULL, NULL); + talloc_free(demux_waiter); + + mp_mutex_unlock(&sub->lock); } static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p) @@ -275,13 +288,52 @@ static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p) (p->start != sub->start || p->end != sub->end || p->codec != sub->codec); } -// Read packets from the demuxer stream passed to sub_create(). Return true if -// enough packets were read, false if the player should wait until the demuxer -// signals new packets available (and then should retry). -bool sub_read_packets(struct dec_sub *sub, double video_pts) +static bool is_packet_visible(struct demux_packet *p, double video_pts) +{ + return p && p->pts <= video_pts && (video_pts <= p->pts + p->sub_duration || + p->sub_duration < 0); +} + +static bool update_pkt_cache(struct dec_sub *sub, double video_pts) +{ + if (!sub->cached_pkts[sub->cached_pkt_pos]) + return false; + + struct demux_packet *pkt = sub->cached_pkts[sub->cached_pkt_pos]; + struct demux_packet *next_pkt = sub->cached_pkt_pos + 1 < sub->num_cached_pkts ? + sub->cached_pkts[sub->cached_pkt_pos + 1] : NULL; + if (!pkt) + return false; + + double pts = video_pts + sub->shared_opts->sub_delay[sub->order]; + double next_pts = next_pkt ? next_pkt->pts : INT_MAX; + double end_pts = pkt->sub_duration >= 0 ? pkt->pts + pkt->sub_duration : INT_MAX; + + if (next_pts < pts || end_pts < pts) { + if (sub->cached_pkt_pos + 1 < sub->num_cached_pkts) { + TA_FREEP(&sub->cached_pkts[sub->cached_pkt_pos]); + pkt = NULL; + sub->cached_pkt_pos++; + } + if (next_pts < pts) + return true; + } + + if (pkt && pkt->animated) + return true; + + return false; +} + +// Read packets from the demuxer stream passed to sub_create(). Signals if +// enough packets were read and if the subtitle state updated in anyway. If +// packets_read is false, the player should wait until the demuxer signals new +// packets and retry. +void sub_read_packets(struct dec_sub *sub, double video_pts, bool force, + bool *packets_read, bool *sub_updated) { - bool r = true; - pthread_mutex_lock(&sub->lock); + *packets_read = true; + mp_mutex_lock(&sub->lock); video_pts = pts_to_subtitle(sub, video_pts); while (1) { bool read_more = true; @@ -299,16 +351,20 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts) if (sub->new_segment) break; + // (Use this mechanism only if sub_delay matters to avoid corner cases.) + float delay = sub->order < 0 ? 0.0f : sub->shared_opts->sub_delay[sub->order]; + double min_pts = delay < 0 || force ? video_pts : MP_NOPTS_VALUE; + struct demux_packet *pkt; - int st = demux_read_packet_async(sub->sh, &pkt); + int st = demux_read_packet_async_until(sub->sh, min_pts, &pkt); // Note: "wait" (st==0) happens with non-interleaved streams only, and // then we should stop the playloop until a new enough packet has been - // seen (or the subtitle decoder's queue is full). This does not happen - // for interleaved subtitle streams, which never return "wait" when - // reading. + // seen (or the subtitle decoder's queue is full). This usually does not + // happen for interleaved subtitle streams, which never return "wait" + // when reading, unless min_pts is set. if (st <= 0) { - r = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE && - sub->last_pkt_pts > video_pts); + *packets_read = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE && + sub->last_pkt_pts > video_pts); break; } @@ -316,9 +372,10 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts) mp_recorder_feed_packet(sub->recorder_sink, pkt); sub->last_pkt_pts = pkt->pts; + MP_TARRAY_APPEND(sub, sub->cached_pkts, sub->num_cached_pkts, pkt); if (is_new_segment(sub, pkt)) { - sub->new_segment = pkt; + sub->new_segment = demux_copy_packet(pkt); // Note that this can be delayed to a much later point in time. update_segment(sub); break; @@ -326,39 +383,53 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts) if (!(sub->preload_attempted && sub->sd->preload_ok)) sub->sd->driver->decode(sub->sd, pkt); + } + if (sub->cached_pkts && sub->num_cached_pkts) { + bool visible = is_packet_visible(sub->cached_pkts[sub->cached_pkt_pos], video_pts); + *sub_updated = update_pkt_cache(sub, video_pts) || sub->sub_visible != visible; + sub->sub_visible = visible; + } + mp_mutex_unlock(&sub->lock); +} - talloc_free(pkt); +// Redecode all cached packets if needed. +// Used with UPDATE_SUB_HARD and UPDATE_SUB_FILT. +void sub_redecode_cached_packets(struct dec_sub *sub) +{ + mp_mutex_lock(&sub->lock); + int index = sub->cached_pkt_pos; + while (index < sub->num_cached_pkts) { + sub->sd->driver->decode(sub->sd, sub->cached_pkts[index]); + ++index; } - pthread_mutex_unlock(&sub->lock); - return r; + mp_mutex_unlock(&sub->lock); } -// You must call sub_lock/sub_unlock if more than 1 thread access sub. -// The issue is that *res will contain decoder allocated data, which might -// be deallocated on the next decoder access. -void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, int format, - double pts, struct sub_bitmaps *res) +// Unref sub_bitmaps.rc to free the result. May return NULL. +struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, + int format, double pts) { - struct mp_subtitle_opts *opts = sub->opts; + mp_mutex_lock(&sub->lock); pts = pts_to_subtitle(sub, pts); sub->last_vo_pts = pts; update_segment(sub); - if (sub->end != MP_NOPTS_VALUE && pts >= sub->end) - return; + struct sub_bitmaps *res = NULL; + + if (!(sub->end != MP_NOPTS_VALUE && pts >= sub->end) && + sub->sd->driver->get_bitmaps) + res = sub->sd->driver->get_bitmaps(sub->sd, dim, format, pts); - if (opts->sub_visibility && sub->sd->driver->get_bitmaps) - sub->sd->driver->get_bitmaps(sub->sd, dim, format, pts, res); + mp_mutex_unlock(&sub->lock); + return res; } -// See sub_get_bitmaps() for locking requirements. -// It can be called unlocked too, but then only 1 thread must call this function -// at a time (unless exclusive access is guaranteed). -char *sub_get_text(struct dec_sub *sub, double pts) +// The returned string is talloc'ed. +char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); char *text = NULL; pts = pts_to_subtitle(sub, pts); @@ -367,14 +438,28 @@ char *sub_get_text(struct dec_sub *sub, double pts) update_segment(sub); if (sub->sd->driver->get_text) - text = sub->sd->driver->get_text(sub->sd, pts); - pthread_mutex_unlock(&sub->lock); + text = sub->sd->driver->get_text(sub->sd, pts, type); + mp_mutex_unlock(&sub->lock); return text; } +char *sub_ass_get_extradata(struct dec_sub *sub) +{ + char *data = NULL; + mp_mutex_lock(&sub->lock); + if (strcmp(sub->sd->codec->codec, "ass") != 0) + goto done; + char *extradata = sub->sd->codec->extradata; + int extradata_size = sub->sd->codec->extradata_size; + data = talloc_strndup(NULL, extradata, extradata_size); +done: + mp_mutex_unlock(&sub->lock); + return data; +} + struct sd_times sub_get_times(struct dec_sub *sub, double pts) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); struct sd_times res = { .start = MP_NOPTS_VALUE, .end = MP_NOPTS_VALUE }; pts = pts_to_subtitle(sub, pts); @@ -385,34 +470,34 @@ struct sd_times sub_get_times(struct dec_sub *sub, double pts) if (sub->sd->driver->get_times) res = sub->sd->driver->get_times(sub->sd, pts); - pthread_mutex_unlock(&sub->lock); + mp_mutex_unlock(&sub->lock); return res; } void sub_reset(struct dec_sub *sub) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); if (sub->sd->driver->reset) sub->sd->driver->reset(sub->sd); sub->last_pkt_pts = MP_NOPTS_VALUE; sub->last_vo_pts = MP_NOPTS_VALUE; - talloc_free(sub->new_segment); - sub->new_segment = NULL; - pthread_mutex_unlock(&sub->lock); + destroy_cached_pkts(sub); + TA_FREEP(&sub->new_segment); + mp_mutex_unlock(&sub->lock); } void sub_select(struct dec_sub *sub, bool selected) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); if (sub->sd->driver->select) sub->sd->driver->select(sub->sd, selected); - pthread_mutex_unlock(&sub->lock); + mp_mutex_unlock(&sub->lock); } int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg) { int r = CONTROL_UNKNOWN; - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); bool propagate = false; switch (cmd) { case SD_CTRL_SET_VIDEO_DEF_FPS: @@ -428,10 +513,19 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg) if (r == CONTROL_OK) a[0] = pts_from_subtitle(sub, arg2[0]); break; - case SD_CTRL_UPDATE_OPTS: + } + case SD_CTRL_UPDATE_OPTS: { + int flags = (uintptr_t)arg; if (m_config_cache_update(sub->opts_cache)) update_subtitle_speed(sub); + m_config_cache_update(sub->shared_opts_cache); propagate = true; + if (flags & UPDATE_SUB_HARD) { + // forget about the previous preload because + // UPDATE_SUB_HARD will cause a sub reinit + // that clears all preloaded sub packets + sub->preload_attempted = false; + } break; } default: @@ -439,20 +533,36 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg) } if (propagate && sub->sd->driver->control) r = sub->sd->driver->control(sub->sd, cmd, arg); - pthread_mutex_unlock(&sub->lock); + mp_mutex_unlock(&sub->lock); return r; } void sub_set_recorder_sink(struct dec_sub *sub, struct mp_recorder_sink *sink) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); sub->recorder_sink = sink; - pthread_mutex_unlock(&sub->lock); + mp_mutex_unlock(&sub->lock); } void sub_set_play_dir(struct dec_sub *sub, int dir) { - pthread_mutex_lock(&sub->lock); + mp_mutex_lock(&sub->lock); sub->play_dir = dir; - pthread_mutex_unlock(&sub->lock); + mp_mutex_unlock(&sub->lock); +} + +bool sub_is_primary_visible(struct dec_sub *sub) +{ + mp_mutex_lock(&sub->lock); + bool ret = sub->shared_opts->sub_visibility[0]; + mp_mutex_unlock(&sub->lock); + return ret; +} + +bool sub_is_secondary_visible(struct dec_sub *sub) +{ + mp_mutex_lock(&sub->lock); + bool ret = sub->shared_opts->sub_visibility[1]; + mp_mutex_unlock(&sub->lock); + return ret; } diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 030b8d21e1..a40aa9bbfd 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -4,24 +4,29 @@ #include <stdbool.h> #include <stdint.h> +#include "player/core.h" #include "osd.h" struct sh_stream; struct mpv_global; struct demux_packet; struct mp_recorder_sink; - struct dec_sub; 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, }; +enum sd_text_type { + SD_TEXT_TYPE_PLAIN, + SD_TEXT_TYPE_ASS, + SD_TEXT_TYPE_ASS_FULL, +}; + struct sd_times { double start; double end; @@ -32,23 +37,26 @@ struct attachment_list { int num_entries; }; -struct dec_sub *sub_create(struct mpv_global *global, struct sh_stream *sh, - struct attachment_list *attachments); +struct dec_sub *sub_create(struct mpv_global *global, struct track *track, + struct attachment_list *attachments, int order); void sub_destroy(struct dec_sub *sub); -void sub_lock(struct dec_sub *sub); -void sub_unlock(struct dec_sub *sub); bool sub_can_preload(struct dec_sub *sub); void sub_preload(struct dec_sub *sub); -bool sub_read_packets(struct dec_sub *sub, double video_pts); -void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, int format, - double pts, struct sub_bitmaps *res); -char *sub_get_text(struct dec_sub *sub, double pts); +void sub_redecode_cached_packets(struct dec_sub *sub); +void sub_read_packets(struct dec_sub *sub, double video_pts, bool force, + bool *packets_read, bool *sub_updated); +struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, + int format, double pts); +char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type); +char *sub_ass_get_extradata(struct dec_sub *sub); struct sd_times sub_get_times(struct dec_sub *sub, double pts); void sub_reset(struct dec_sub *sub); void sub_select(struct dec_sub *sub, bool selected); void sub_set_recorder_sink(struct dec_sub *sub, struct mp_recorder_sink *sink); void sub_set_play_dir(struct dec_sub *sub, int dir); +bool sub_is_primary_visible(struct dec_sub *sub); +bool sub_is_secondary_visible(struct dec_sub *sub); int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg); diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c index a4847b1534..78e29f491b 100644 --- a/sub/draw_bmp.c +++ b/sub/draw_bmp.c @@ -21,525 +21,1019 @@ #include <math.h> #include <inttypes.h> -#include <libswscale/swscale.h> - #include "common/common.h" #include "draw_bmp.h" #include "img_convert.h" #include "video/mp_image.h" +#include "video/repack.h" #include "video/sws_utils.h" #include "video/img_format.h" #include "video/csputils.h" const bool mp_draw_sub_formats[SUBBITMAP_COUNT] = { [SUBBITMAP_LIBASS] = true, - [SUBBITMAP_RGBA] = true, -}; - -struct sub_cache { - struct mp_image *i, *a; + [SUBBITMAP_BGRA] = true, }; struct part { int change_id; - int imgfmt; - enum mp_csp colorspace; - enum mp_csp_levels levels; + // Sub-bitmaps scaled to final sizes. int num_imgs; - struct sub_cache *imgs; + struct mp_image **imgs; +}; + +// Must be a power of 2. Height is 1, but mark_rect() effectively operates on +// multiples of chroma sized macro-pixels. (E.g. 4:2:0 -> every second line is +// the same as the previous one, and x0%2==x1%2==0.) +#define SLICE_W 256u + +// Whether to scale in tiles. Faster, but can't use correct chroma position. +// Should be a runtime option. SLICE_W is used as tile width. The tile size +// should probably be small; too small or too big will cause overhead when +// scaling. +#define SCALE_IN_TILES 1 +#define TILE_H 4u + +struct slice { + uint16_t x0, x1; }; struct mp_draw_sub_cache { - struct part *parts[MAX_OSD_PARTS]; - struct mp_image *upsample_img; - struct mp_image upsample_temp; + struct mpv_global *global; + + // Possibly cached parts. Also implies what's in the video_overlay. + struct part parts[MAX_OSD_PARTS]; + int64_t change_id; + + struct mp_image_params params; // target image params + + int w, h; // like params.w/h, but rounded up to chroma + unsigned align_x, align_y; // alignment for all video pixels + + struct mp_image *rgba_overlay; // all OSD in RGBA + struct mp_image *video_overlay; // rgba_overlay converted to video colorspace + struct mp_image *alpha_overlay; // alpha plane ref. to video_overlay + struct mp_image *calpha_overlay; // alpha_overlay scaled to chroma plane size + + unsigned s_w; // number of slices per line + struct slice *slices; // slices[y * s_w + x / SLICE_W] + bool any_osd; + + struct mp_sws_context *rgba_to_overlay; // scaler for rgba -> video csp. + struct mp_sws_context *alpha_to_calpha; // scaler for overlay -> calpha + bool scale_in_tiles; + + struct mp_sws_context *sub_scale; // scaler for SUBBITMAP_BGRA + + struct mp_repack *overlay_to_f32; // convert video_overlay to float + struct mp_image *overlay_tmp; // slice in float32 + + struct mp_repack *calpha_to_f32; // convert video_overlay to float + struct mp_image *calpha_tmp; // slice in float32 + + struct mp_repack *video_to_f32; // convert video to float + struct mp_repack *video_from_f32; // convert float back to video + struct mp_image *video_tmp; // slice in float32 + + struct mp_sws_context *premul; // video -> premultiplied video + struct mp_sws_context *unpremul; // reverse + struct mp_image *premul_tmp; + + // Function that works on the _f32 data. + void (*blend_line)(void *dst, void *src, void *src_a, int w); + + struct mp_image res_overlay; // returned by mp_draw_sub_overlay() }; +static void blend_line_f32(void *dst, void *src, void *src_a, int w) +{ + float *dst_f = dst; + float *src_f = src; + float *src_a_f = src_a; -static struct part *g |