diff --git a/player/sub.c b/player/sub.c index 734cbcf7e4..6d39ef2299 100644 --- a/player/sub.c +++ b/player/sub.c @@ -136,10 +136,15 @@ static void update_subtitle(struct MPContext *mpctx, int order) // Handle displaying subtitles on terminal; never done for secondary subs if (order == 0) { - if (!osd_obj->render_bitmap_subs || !mpctx->video_out) + if (!osd_obj->render_bitmap_subs || !mpctx->video_out) { + sub_lock(dec_sub); set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s)); + sub_unlock(dec_sub); + } } else if (order == 1) { + sub_lock(dec_sub); osd_set_sub(mpctx->osd, osd_obj, sub_get_text(dec_sub, curpts_s)); + sub_unlock(dec_sub); } } diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 102a048da3..64d285f568 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "config.h" #include "demux/demux.h" @@ -57,6 +58,8 @@ static const struct sd_functions *sd_list[] = { #define MAX_NUM_SD 3 struct dec_sub { + pthread_mutex_t lock; + struct mp_log *log; struct MPOpts *opts; struct sd init_sd; @@ -73,11 +76,32 @@ struct packet_list { int num_packets; }; + +void sub_lock(struct dec_sub *sub) +{ + pthread_mutex_lock(&sub->lock); +} + +void sub_unlock(struct dec_sub *sub) +{ + pthread_mutex_unlock(&sub->lock); +} + +// 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. struct dec_sub *sub_create(struct mpv_global *global) { struct dec_sub *sub = talloc_zero(NULL, struct dec_sub); sub->log = mp_log_new(sub, global->log, "sub"); sub->opts = global->opts; + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&sub->lock, &attr); + pthread_mutexattr_destroy(&attr); + return sub; } @@ -97,12 +121,16 @@ void sub_destroy(struct dec_sub *sub) if (!sub) return; sub_uninit(sub); + pthread_mutex_destroy(&sub->lock); talloc_free(sub); } bool sub_is_initialized(struct dec_sub *sub) { - return !!sub->num_sd; + pthread_mutex_lock(&sub->lock); + bool r = !!sub->num_sd; + pthread_mutex_unlock(&sub->lock); + return r; } static struct sd *sub_get_last_sd(struct dec_sub *sub) @@ -112,26 +140,34 @@ static struct sd *sub_get_last_sd(struct dec_sub *sub) void sub_set_video_res(struct dec_sub *sub, int w, int h) { + pthread_mutex_lock(&sub->lock); sub->init_sd.sub_video_w = w; sub->init_sd.sub_video_h = h; + pthread_mutex_unlock(&sub->lock); } void sub_set_video_fps(struct dec_sub *sub, double fps) { + pthread_mutex_lock(&sub->lock); sub->video_fps = fps; + pthread_mutex_unlock(&sub->lock); } void sub_set_extradata(struct dec_sub *sub, void *data, int data_len) { + pthread_mutex_lock(&sub->lock); sub->init_sd.extradata = data_len ? talloc_memdup(sub, data, data_len) : NULL; sub->init_sd.extradata_len = data_len; + pthread_mutex_unlock(&sub->lock); } void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library, struct ass_renderer *ass_renderer) { + pthread_mutex_lock(&sub->lock); sub->init_sd.ass_library = ass_library; sub->init_sd.ass_renderer = ass_renderer; + pthread_mutex_unlock(&sub->lock); } static void print_chain(struct dec_sub *sub) @@ -170,6 +206,8 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh) assert(!sub->num_sd); assert(sh && sh->sub); + pthread_mutex_lock(&sub->lock); + if (sh->sub->extradata && !sub->init_sd.extradata) sub_set_extradata(sub, sh->sub->extradata, sh->sub->extradata_len); struct sd init_sd = sub->init_sd; @@ -189,6 +227,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh) // Try adding new converters until a decoder is reached if (sd->driver->get_bitmaps || sd->driver->get_text) { print_chain(sub); + pthread_mutex_unlock(&sub->lock); return; } init_sd = (struct sd) { @@ -204,6 +243,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh) sub_uninit(sub); MP_ERR(sub, "Could not find subtitle decoder for format '%s'.\n", sh->codec ? sh->codec : ""); + pthread_mutex_unlock(&sub->lock); } static struct demux_packet *get_decoded_packet(struct sd *sd) @@ -262,7 +302,9 @@ static void decode_chain_recode(struct dec_sub *sub, struct sd **sd, int num_sd, void sub_decode(struct dec_sub *sub, struct demux_packet *packet) { + pthread_mutex_lock(&sub->lock); decode_chain_recode(sub, sub->sd, sub->num_sd, packet); + pthread_mutex_unlock(&sub->lock); } static const char *guess_sub_cp(struct mp_log *log, struct packet_list *subs, @@ -373,8 +415,13 @@ bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh) assert(sh && sh->sub); struct MPOpts *opts = sub->opts; + pthread_mutex_lock(&sub->lock); + if (!sub_accept_packets_in_advance(sub) || sh->sub->track || sub->num_sd < 1) + { + pthread_mutex_unlock(&sub->lock); return false; + } struct packet_list *subs = talloc_zero(NULL, struct packet_list); @@ -446,17 +493,24 @@ bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh) add_sub_list(sub, preprocess, subs); + pthread_mutex_unlock(&sub->lock); talloc_free(subs); return true; } bool sub_accept_packets_in_advance(struct dec_sub *sub) { + pthread_mutex_lock(&sub->lock); // Converters are assumed to always accept packets in advance struct sd *sd = sub_get_last_sd(sub); - return sd && sd->driver->accept_packets_in_advance; + bool r = sd && sd->driver->accept_packets_in_advance; + pthread_mutex_unlock(&sub->lock); + return r; } +// 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, double pts, struct sub_bitmaps *res) { @@ -472,12 +526,17 @@ void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, bool sub_has_get_text(struct dec_sub *sub) { + pthread_mutex_lock(&sub->lock); struct sd *sd = sub_get_last_sd(sub); - return sd && sd->driver->get_text; + bool r = sd && sd->driver->get_text; + pthread_mutex_unlock(&sub->lock); + return r; } +// See sub_get_bitmaps() for locking requirements. char *sub_get_text(struct dec_sub *sub, double pts) { + pthread_mutex_lock(&sub->lock); struct MPOpts *opts = sub->opts; struct sd *sd = sub_get_last_sd(sub); char *text = NULL; @@ -485,27 +544,33 @@ char *sub_get_text(struct dec_sub *sub, double pts) if (sd->driver->get_text) text = sd->driver->get_text(sd, pts); } + pthread_mutex_unlock(&sub->lock); return text; } void sub_reset(struct dec_sub *sub) { + pthread_mutex_lock(&sub->lock); for (int n = 0; n < sub->num_sd; n++) { if (sub->sd[n]->driver->reset) sub->sd[n]->driver->reset(sub->sd[n]); } + pthread_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); for (int n = 0; n < sub->num_sd; n++) { if (sub->sd[n]->driver->control) { - int r = sub->sd[n]->driver->control(sub->sd[n], cmd, arg); + r = sub->sd[n]->driver->control(sub->sd[n], cmd, arg); if (r != CONTROL_UNKNOWN) - return r; + break; } } - return CONTROL_UNKNOWN; + pthread_mutex_unlock(&sub->lock); + return r; } #define MAX_PACKETS 10 diff --git a/sub/dec_sub.h b/sub/dec_sub.h index c659458c04..495ac46402 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -24,6 +24,8 @@ enum sd_ctrl { struct dec_sub *sub_create(struct mpv_global *global); void sub_destroy(struct dec_sub *sub); +void sub_lock(struct dec_sub *sub); +void sub_unlock(struct dec_sub *sub); void sub_set_video_res(struct dec_sub *sub, int w, int h); void sub_set_video_fps(struct dec_sub *sub, double fps); diff --git a/sub/osd.c b/sub/osd.c index 16c1288f5d..56d845b97f 100644 --- a/sub/osd.c +++ b/sub/osd.c @@ -246,6 +246,9 @@ void osd_draw(struct osd_state *osd, struct mp_osd_res res, if ((draw_flags & OSD_DRAW_SUB_ONLY) && !obj->is_sub) continue; + if (obj->dec_sub) + sub_lock(obj->dec_sub); + struct sub_bitmaps imgs; render_object(osd, obj, res, video_pts, formats, &imgs); if (imgs.num_parts > 0) { @@ -256,6 +259,9 @@ void osd_draw(struct osd_state *osd, struct mp_osd_res res, obj->type, imgs.format); } } + + if (obj->dec_sub) + sub_unlock(obj->dec_sub); } }