diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index af80052b1d..be48644cfd 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -2461,6 +2461,19 @@ Property list This property is experimental and might be removed in the future. +``sub-text-ass`` + Like ``sub-text``, but return the text in ASS format. Text subtitles in + other formats are converted. For native ASS subtitles, events that do + not contain any text (but vector drawings etc.) are not filtered out. If + multiple events match with the current playback time, they are concatenated + with line breaks. Contains only the "Text" part of the events. + + This property is not enough to render ASS subtitles correctly, because ASS + header and per-event metadata are not returned. You likely need to do + further filtering on the returned string to make it useful. + + This property is experimental and might be removed in the future. + ``sub-start`` Return the current subtitle start time (in seconds). If there's multiple current subtitles, returns the first start time. If no current subtitle is diff --git a/player/command.c b/player/command.c index 2c44a3f095..322ce973c9 100644 --- a/player/command.c +++ b/player/command.c @@ -2695,6 +2695,7 @@ static int mp_property_sub_pos(void *ctx, struct m_property *prop, static int mp_property_sub_text(void *ctx, struct m_property *prop, int action, void *arg) { + int type = *(int *)prop->priv; MPContext *mpctx = ctx; struct track *track = mpctx->current_track[0][STREAM_SUB]; struct dec_sub *sub = track ? track->d_sub : NULL; @@ -2702,11 +2703,19 @@ static int mp_property_sub_text(void *ctx, struct m_property *prop, if (!sub || pts == MP_NOPTS_VALUE) return M_PROPERTY_UNAVAILABLE; - char *text = sub_get_text(sub, pts); - if (!text) - text = ""; - - return m_property_strdup_ro(action, arg, text); + switch (action) { + case M_PROPERTY_GET: { + char *text = sub_get_text(sub, pts, type); + if (!text) + text = talloc_strdup(NULL, ""); + *(char **)arg = text; + return M_PROPERTY_OK; + } + case M_PROPERTY_GET_TYPE: + *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING}; + return M_PROPERTY_OK; + } + return M_PROPERTY_NOT_IMPLEMENTED; } static struct sd_times get_times(void *ctx, struct m_property *prop, @@ -3489,7 +3498,10 @@ static const struct m_property mp_properties_base[] = { {"sub-delay", mp_property_sub_delay}, {"sub-speed", mp_property_sub_speed}, {"sub-pos", mp_property_sub_pos}, - {"sub-text", mp_property_sub_text}, + {"sub-text", mp_property_sub_text, + .priv = (void *)&(const int){SD_TEXT_TYPE_PLAIN}}, + {"sub-text-ass", mp_property_sub_text, + .priv = (void *)&(const int){SD_TEXT_TYPE_ASS}}, {"sub-start", mp_property_sub_start}, {"sub-end", mp_property_sub_end}, diff --git a/player/sub.c b/player/sub.c index a40a6d5e9d..ae2a85ac1c 100644 --- a/player/sub.c +++ b/player/sub.c @@ -105,8 +105,11 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts, return false; // Handle displaying subtitles on terminal; never done for secondary subs - if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out) - term_osd_set_subs(mpctx, sub_get_text(dec_sub, video_pts)); + if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out) { + char *text = sub_get_text(dec_sub, video_pts, SD_TEXT_TYPE_PLAIN); + term_osd_set_subs(mpctx, text); + talloc_free(text); + } // Handle displaying subtitles on VO with no video being played. This is // quite different, because normally subtitles are redrawn on new video diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 76e96bc16b..aedb836235 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -348,10 +348,10 @@ struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, 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) +// This can only be called by the main thread, due to the returned text pointing +// to a buffer bound to the sub object. The main thread is the designated +// "outside" owner of the buffer. +char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type) { pthread_mutex_lock(&sub->lock); char *text = NULL; @@ -362,7 +362,7 @@ 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); + text = sub->sd->driver->get_text(sub->sd, pts, type); pthread_mutex_unlock(&sub->lock); return text; } diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 8d0c76cd14..f998b59f6b 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -22,6 +22,11 @@ enum sd_ctrl { SD_CTRL_UPDATE_OPTS, }; +enum sd_text_type { + SD_TEXT_TYPE_PLAIN, + SD_TEXT_TYPE_ASS, +}; + struct sd_times { double start; double end; @@ -41,7 +46,7 @@ void sub_preload(struct dec_sub *sub); bool sub_read_packets(struct dec_sub *sub, double video_pts); 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); +char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type); 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); diff --git a/sub/sd.h b/sub/sd.h index d3d70bf594..38bab3aa7a 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -40,7 +40,7 @@ struct sd_functions { struct sub_bitmaps *(*get_bitmaps)(struct sd *sd, struct mp_osd_res dim, int format, double pts); - char *(*get_text)(struct sd *sd, double pts); + char *(*get_text)(struct sd *sd, double pts, enum sd_text_type type); struct sd_times (*get_times)(struct sd *sd, double pts); }; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index d51f892dd4..46f16908b0 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -627,7 +627,7 @@ static bool is_whitespace_only(char *s, int len) return true; } -static char *get_text(struct sd *sd, double pts) +static char *get_text_buf(struct sd *sd, double pts, enum sd_text_type type) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; @@ -643,7 +643,13 @@ static char *get_text(struct sd *sd, double pts) if (ipts >= event->Start && ipts < event->Start + event->Duration) { if (event->Text) { int start = b.len; - ass_to_plaintext(&b, event->Text); + if (type == SD_TEXT_TYPE_PLAIN) { + ass_to_plaintext(&b, event->Text); + } else { + char *t = event->Text; + while (*t) + append(&b, *t++); + } if (is_whitespace_only(&b.start[start], b.len - start)) { b.len = start; } else { @@ -661,6 +667,11 @@ static char *get_text(struct sd *sd, double pts) return ctx->last_text; } +static char *get_text(struct sd *sd, double pts, enum sd_text_type type) +{ + return talloc_strdup(NULL, get_text_buf(sd, pts, type)); +} + static struct sd_times get_times(struct sd *sd, double pts) { struct sd_ass_priv *ctx = sd->priv; @@ -697,7 +708,7 @@ static void fill_plaintext(struct sd *sd, double pts) ass_flush_events(track); - char *text = get_text(sd, pts); + char *text = get_text_buf(sd, pts, SD_TEXT_TYPE_PLAIN); if (!text) return;