mirror of
https://github.com/mpv-player/mpv.git
synced 2026-10-16 22:40:53 +08:00
sub: add sub-lines property
Return all subtitle lines in memory with text, start and end. I did not name this sub-all so it cannot be misinterpreted as referring to all subtitle tracks. Closes #12810.
This commit is contained in:
committed by
Kacper Michajłow
parent
c662594f05
commit
5f02e1b8a2
@@ -0,0 +1 @@
|
||||
add `sub-lines` and `secondary-sub-lines` properties
|
||||
@@ -3290,6 +3290,29 @@ Property list
|
||||
``secondary-sub-end``
|
||||
Same as ``sub-end``, but for the secondary subtitles.
|
||||
|
||||
``sub-lines``
|
||||
The list of subtitle lines in memory. Not available if the subtitle is not
|
||||
text-based (i.e. DVD/BD subtitles).
|
||||
|
||||
When querying the property with the client API using ``MPV_FORMAT_NODE``,
|
||||
or with Lua ``mp.get_property_native``, this will return a mpv_node with
|
||||
the following contents:
|
||||
|
||||
::
|
||||
|
||||
MPV_FORMAT_NODE_ARRAY
|
||||
MPV_FORMAT_NODE_MAP (for each subtitle line)
|
||||
"text" MPV_FORMAT_STRING
|
||||
"start" MPV_FORMAT_DOUBLE
|
||||
"end" MPV_FORMAT_DOUBLE
|
||||
|
||||
ASS tags are stripped from ``text``.
|
||||
|
||||
``end`` is absent if unknown.
|
||||
|
||||
``secondary-sub-lines``
|
||||
Same as ``sub-lines``, but for the secondary subtitles.
|
||||
|
||||
``playlist-pos`` (RW)
|
||||
Current position on playlist. The first entry is on position 0. Writing to
|
||||
this property may start playback at the new position.
|
||||
|
||||
+43
-2
@@ -3455,6 +3455,43 @@ static int mp_property_sub_end(void *ctx, struct m_property *prop,
|
||||
return property_time(action, arg, end);
|
||||
}
|
||||
|
||||
static int mp_property_sub_lines(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
int sub_index = *(int *)prop->priv;
|
||||
struct track *track = mpctx->current_track[sub_index][STREAM_SUB];
|
||||
struct dec_sub *sub = track ? track->d_sub : NULL;
|
||||
if (!sub)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET_TYPE:
|
||||
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_GET:
|
||||
case M_PROPERTY_GET_NODE: {
|
||||
struct sub_lines *lines = sub_get_lines(sub);
|
||||
if (!lines)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
struct mpv_node *node = arg;
|
||||
node_init(node, MPV_FORMAT_NODE_ARRAY, NULL);
|
||||
for (int i = 0; i < lines->num_entries; i++) {
|
||||
struct sub_line *line = &lines->entries[i];
|
||||
struct mpv_node *entry = node_array_add(node, MPV_FORMAT_NODE_MAP);
|
||||
node_map_add_string(entry, "text", line->text);
|
||||
node_map_add_double(entry, "start", line->start);
|
||||
if (line->end != MP_NOPTS_VALUE)
|
||||
node_map_add_double(entry, "end", line->end);
|
||||
}
|
||||
talloc_free(lines);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int mp_property_playlist_current_pos(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
@@ -4650,6 +4687,10 @@ static const struct m_property mp_properties_base[] = {
|
||||
.priv = (void *)&(const int){0}},
|
||||
{"secondary-sub-end", mp_property_sub_end,
|
||||
.priv = (void *)&(const int){1}},
|
||||
{"sub-lines", mp_property_sub_lines,
|
||||
.priv = (void *)&(const int){0}},
|
||||
{"secondary-sub-lines", mp_property_sub_lines,
|
||||
.priv = (void *)&(const int){1}},
|
||||
|
||||
{"vf", mp_property_vf},
|
||||
{"af", mp_property_af},
|
||||
@@ -4734,8 +4775,8 @@ static const char *const *const mp_event_property_change[] = {
|
||||
"secondary-sub-text", "audio-bitrate", "video-bitrate", "sub-bitrate",
|
||||
"decoder-frame-drop-count", "frame-drop-count", "video-frame-info",
|
||||
"vf-metadata", "af-metadata", "sub-start", "sub-end", "secondary-sub-start",
|
||||
"secondary-sub-end", "video-out-params", "video-dec-params", "video-params",
|
||||
"deinterlace-active", "video-target-params"),
|
||||
"secondary-sub-end", "sub-lines", "secondary-sub-lines", "video-out-params",
|
||||
"video-dec-params", "video-params", "deinterlace-active", "video-target-params"),
|
||||
E(MP_EVENT_DURATION_UPDATE, "duration"),
|
||||
E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params",
|
||||
"video-format", "video-codec", "video-bitrate", "dwidth", "dheight",
|
||||
|
||||
@@ -580,3 +580,24 @@ bool sub_is_secondary_visible(struct dec_sub *sub)
|
||||
mp_mutex_unlock(&sub->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sub_line_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct sub_line *la = a, *lb = b;
|
||||
if (la->start < lb->start) return -1;
|
||||
if (la->start > lb->start) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sub_lines *sub_get_lines(struct dec_sub *sub)
|
||||
{
|
||||
mp_mutex_lock(&sub->lock);
|
||||
struct sub_lines *res = NULL;
|
||||
if (sub->sd->driver->get_lines) {
|
||||
res = sub->sd->driver->get_lines(sub->sd);
|
||||
qsort(res->entries, res->num_entries, sizeof(res->entries[0]),
|
||||
sub_line_cmp);
|
||||
}
|
||||
mp_mutex_unlock(&sub->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,17 @@ struct attachment_list {
|
||||
int num_entries;
|
||||
};
|
||||
|
||||
struct sub_line {
|
||||
char *text;
|
||||
double start;
|
||||
double end;
|
||||
};
|
||||
|
||||
struct sub_lines {
|
||||
struct sub_line *entries;
|
||||
int num_entries;
|
||||
};
|
||||
|
||||
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);
|
||||
@@ -53,6 +64,9 @@ struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim,
|
||||
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);
|
||||
// Return subtitle lines in memory. Call talloc_free() on the return value.
|
||||
struct sub_lines *sub_get_lines(struct dec_sub *sub);
|
||||
|
||||
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);
|
||||
|
||||
@@ -52,6 +52,7 @@ struct sd_functions {
|
||||
int format, 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);
|
||||
struct sub_lines *(*get_lines)(struct sd *sd);
|
||||
};
|
||||
|
||||
// lavc_conv.c
|
||||
|
||||
@@ -1017,6 +1017,44 @@ static void uninit(struct sd *sd)
|
||||
talloc_free(ctx->copy_cache);
|
||||
}
|
||||
|
||||
static struct sub_lines *get_lines(struct sd *sd)
|
||||
{
|
||||
struct sd_ass_priv *ctx = sd->priv;
|
||||
ASS_Track *track = ctx->ass_track;
|
||||
struct sub_lines *res = talloc_zero(NULL, struct sub_lines);
|
||||
|
||||
for (int i = 0; i < track->n_events; i++) {
|
||||
ASS_Event *event = &track->events[i];
|
||||
if (!event->Text)
|
||||
continue;
|
||||
|
||||
char *plain = NULL;
|
||||
bstr result = sd_ass_to_plaintext(&plain, event->Text);
|
||||
|
||||
// ASS subtitle lines can have many empty lines after stripping tags,
|
||||
// but empty lines are useful in LRC.
|
||||
if (is_whitespace_only(result)) {
|
||||
talloc_free(plain);
|
||||
if (!strcmp(sd->codec->codec, "ass"))
|
||||
continue;
|
||||
plain = talloc_strdup(res, "");
|
||||
} else {
|
||||
talloc_steal(res, plain);
|
||||
}
|
||||
|
||||
struct sub_line line = {
|
||||
.text = plain,
|
||||
.start = event->Start / 1000.0,
|
||||
.end = event->Duration == UNKNOWN_DURATION * 1000
|
||||
? MP_NOPTS_VALUE
|
||||
: (event->Start + event->Duration) / 1000.0,
|
||||
};
|
||||
MP_TARRAY_APPEND(res, res->entries, res->num_entries, line);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
|
||||
{
|
||||
struct sd_ass_priv *ctx = sd->priv;
|
||||
@@ -1072,6 +1110,7 @@ const struct sd_functions sd_ass = {
|
||||
.get_bitmaps = get_bitmaps,
|
||||
.get_text = get_text,
|
||||
.get_times = get_times,
|
||||
.get_lines = get_lines,
|
||||
.control = control,
|
||||
.reset = reset,
|
||||
.select = enable_output,
|
||||
|
||||
Reference in New Issue
Block a user