summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
Diffstat (limited to 'sub')
-rw-r--r--sub/dec_sub.c19
-rw-r--r--sub/dec_sub.h2
-rw-r--r--sub/filter_sdh.c61
-rw-r--r--sub/sd.h36
-rw-r--r--sub/sd_ass.c115
5 files changed, 190 insertions, 43 deletions
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index 27cab201d9..f98a478be4 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.c
@@ -413,6 +413,7 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
{
int r = CONTROL_UNKNOWN;
pthread_mutex_lock(&sub->lock);
+ bool propagate = false;
switch (cmd) {
case SD_CTRL_SET_VIDEO_DEF_FPS:
sub->video_fps = *(double *)arg;
@@ -427,23 +428,21 @@ 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:
+ if (m_config_cache_update(sub->opts_cache))
+ update_subtitle_speed(sub);
+ propagate = true;
+ break;
}
default:
- if (sub->sd->driver->control)
- r = sub->sd->driver->control(sub->sd, cmd, arg);
+ propagate = true;
}
+ if (propagate && sub->sd->driver->control)
+ r = sub->sd->driver->control(sub->sd, cmd, arg);
pthread_mutex_unlock(&sub->lock);
return r;
}
-void sub_update_opts(struct dec_sub *sub)
-{
- pthread_mutex_lock(&sub->lock);
- if (m_config_cache_update(sub->opts_cache))
- update_subtitle_speed(sub);
- pthread_mutex_unlock(&sub->lock);
-}
-
void sub_set_recorder_sink(struct dec_sub *sub, struct mp_recorder_sink *sink)
{
pthread_mutex_lock(&sub->lock);
diff --git a/sub/dec_sub.h b/sub/dec_sub.h
index 5449b97ad0..030b8d21e1 100644
--- a/sub/dec_sub.h
+++ b/sub/dec_sub.h
@@ -19,6 +19,7 @@ enum sd_ctrl {
SD_CTRL_SET_VIDEO_PARAMS,
SD_CTRL_SET_TOP,
SD_CTRL_SET_VIDEO_DEF_FPS,
+ SD_CTRL_UPDATE_OPTS,
};
struct sd_times {
@@ -46,7 +47,6 @@ char *sub_get_text(struct dec_sub *sub, double pts);
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_update_opts(struct dec_sub *sub);
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);
diff --git a/sub/filter_sdh.c b/sub/filter_sdh.c
index 72744ec20e..2b544ea222 100644
--- a/sub/filter_sdh.c
+++ b/sub/filter_sdh.c
@@ -15,9 +15,11 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <assert.h>
+#include <stdlib.h>
#include <string.h>
+#include <limits.h>
+
#include "misc/ctype.h"
#include "common/common.h"
#include "common/msg.h"
@@ -43,7 +45,7 @@ static void init_buf(struct buffer *buf, int length)
buf->length = length;
}
-static inline int append(struct sd *sd, struct buffer *buf, char c)
+static inline int append(struct sd_filter *sd, struct buffer *buf, char c)
{
if (buf->pos >= 0 && buf->pos < buf->length) {
buf->string[buf->pos++] = c;
@@ -66,7 +68,7 @@ static inline int append(struct sd *sd, struct buffer *buf, char c)
//
// on return the read pointer is updated to the position after
// the tags.
-static void copy_ass(struct sd *sd, char **rpp, struct buffer *buf)
+static void copy_ass(struct sd_filter *sd, char **rpp, struct buffer *buf)
{
char *rp = *rpp;
@@ -83,7 +85,7 @@ static void copy_ass(struct sd *sd, char **rpp, struct buffer *buf)
return;
}
-static bool skip_bracketed(struct sd *sd, char **rpp, struct buffer *buf);
+static bool skip_bracketed(struct sd_filter *sd, char **rpp, struct buffer *buf);
// check for speaker label, like MAN:
// normal subtitles may include mixed case text with : after so
@@ -101,7 +103,7 @@ static bool skip_bracketed(struct sd *sd, char **rpp, struct buffer *buf);
// if no label was found read pointer and write position in buffer
// will be unchanged
// otherwise they point to next position after label and next write position
-static void skip_speaker_label(struct sd *sd, char **rpp, struct buffer *buf)
+static void skip_speaker_label(struct sd_filter *sd, char **rpp, struct buffer *buf)
{
int filter_harder = sd->opts->sub_filter_SDH_harder;
char *rp = *rpp;
@@ -187,7 +189,7 @@ static void skip_speaker_label(struct sd *sd, char **rpp, struct buffer *buf)
// 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 *sd, char **rpp, struct buffer *buf)
+static bool skip_bracketed(struct sd_filter *sd, char **rpp, struct buffer *buf)
{
char *rp = *rpp;
int old_pos = buf->pos;
@@ -235,7 +237,7 @@ static bool skip_bracketed(struct sd *sd, char **rpp, struct buffer *buf)
// return true if paranthesed 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_parenthesed(struct sd *sd, char **rpp, struct buffer *buf)
+static bool skip_parenthesed(struct sd_filter *sd, char **rpp, struct buffer *buf)
{
int filter_harder = sd->opts->sub_filter_SDH_harder;
char *rp = *rpp;
@@ -289,7 +291,8 @@ static bool skip_parenthesed(struct sd *sd, char **rpp, struct buffer *buf)
//
// when removing characters the following are moved back
//
-static void remove_leading_hyphen_space(struct sd *sd, int start_pos, struct buffer *buf)
+static void remove_leading_hyphen_space(struct sd_filter *sd, int start_pos,
+ struct buffer *buf)
{
int old_pos = buf->pos;
if (start_pos < 0 || start_pos >= old_pos)
@@ -340,7 +343,8 @@ static void remove_leading_hyphen_space(struct sd *sd, int start_pos, struct buf
//
// Returns NULL if filtering resulted in all of ASS data being removed so no
// subtitle should be output
-char *filter_SDH(struct sd *sd, char *format, int n_ignored, char *data, int length)
+static char *filter_SDH(struct sd_filter *sd, char *format, int n_ignored,
+ char *data, int length)
{
if (!format) {
MP_VERBOSE(sd, "SDH filtering not possible - format missing\n");
@@ -462,3 +466,42 @@ char *filter_SDH(struct sd *sd, char *format, int n_ignored, char *data, int len
return NULL;
}
}
+
+static bool sdh_init(struct sd_filter *ft)
+{
+ if (strcmp(ft->codec, "ass") != 0)
+ return false;
+
+ if (!ft->opts->sub_filter_SDH)
+ return false;
+
+ return true;
+}
+
+static struct demux_packet *sdh_filter(struct sd_filter *ft,
+ struct demux_packet *pkt)
+{
+ char *line = (char *)pkt->buffer;
+ size_t len = pkt->len;
+ if (len >= INT_MAX)
+ return NULL;
+
+ line = filter_SDH(ft, ft->event_format, 1, line, len);
+ if (!line)
+ return NULL;
+
+ // Stupidly, this copies it again. One could possibly allocate the packet
+ // for writing in the first place (new_demux_packet()) and use
+ // demux_packet_shorten(). Or not allocate anything on no change.
+ struct demux_packet *npkt = new_demux_packet_from(line, strlen(line));
+ if (npkt)
+ demux_packet_copy_attribs(npkt, pkt);
+
+ talloc_free(line);
+ return npkt;
+}
+
+const struct sd_filter_functions sd_filter_sdh = {
+ .init = sdh_init,
+ .filter = sdh_filter,
+};
diff --git a/sub/sd.h b/sub/sd.h
index 6c3fc4e285..0d361edba6 100644
--- a/sub/sd.h
+++ b/sub/sd.h
@@ -3,6 +3,7 @@
#include "dec_sub.h"
#include "demux/packet.h"
+#include "misc/bstr.h"
// up to 210 ms overlaps or gaps are removed
#define SUB_GAP_THRESHOLD 0.210
@@ -43,6 +44,7 @@ struct sd_functions {
struct sd_times (*get_times)(struct sd *sd, double pts);
};
+// lavc_conv.c
struct lavc_conv;
struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name,
char *extradata, int extradata_len);
@@ -52,6 +54,38 @@ char **lavc_conv_decode(struct lavc_conv *priv, struct demux_packet *packet,
void lavc_conv_reset(struct lavc_conv *priv);
void lavc_conv_uninit(struct lavc_conv *priv);
-char *filter_SDH(struct sd *sd, char *format, int n_ignored, char *data, int length);
+struct sd_filter {
+ struct mpv_global *global;
+ struct mp_log *log;
+ struct mp_sub_filter_opts *opts;
+ const struct sd_filter_functions *driver;
+
+ void *priv;
+
+ // Static codec parameters. Set by sd; cannot be changed by filter.
+ char *codec;
+ char *event_format;
+};
+
+struct sd_filter_functions {
+ bool (*init)(struct sd_filter *ft);
+
+ // Filter an ASS event (usually in the Matroska format, but event_format
+ // can be used to determine details).
+ // Returning NULL is interpreted as dropping the event completely.
+ // Returning pkt makes it no-op.
+ // If the returned packet is not pkt or NULL, it must have been properly
+ // allocated.
+ // pkt is owned by the caller (and freed by the caller when needed).
+ // Note: as by normal demux_packet rules, you must not modify any fields in
+ // it, or the data referenced by it. You must create a new demux_packet
+ // when modifying data.
+ struct demux_packet *(*filter)(struct sd_filter *ft,
+ struct demux_packet *pkt);
+
+ void (*uninit)(struct sd_filter *ft);
+};
+
+extern const struct sd_filter_functions sd_filter_sdh;
#endif
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 5443a688d4..2b0cf13127 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -26,6 +26,8 @@
#include "mpv_talloc.h"
+#include "config.h"
+#include "options/m_config.h"
#include "options/options.h"
#include "common/common.h"
#include "common/msg.h"
@@ -43,6 +45,9 @@ struct sd_ass_priv {
struct ass_track *shadow_track; // for --sub-ass=no rendering
bool is_converted;
struct lavc_conv *converter;
+ struct sd_filter **filters;
+ int num_filters;
+ bool clear_once;
bool on_top;
struct mp_ass_packer *packer;
struct sub_bitmap *bs;
@@ -57,6 +62,12 @@ struct sd_ass_priv {
static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts);
static void fill_plaintext(struct sd *sd, double pts);
+static const struct sd_filter_functions *const filters[] = {
+ // Note: list order defines filter order.
+ &sd_filter_sdh,
+ NULL,
+};
+
// 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)
@@ -130,6 +141,43 @@ static void add_subtitle_fonts(struct sd *sd)
}
}
+static void filters_destroy(struct sd *sd)
+{
+ struct sd_ass_priv *ctx = sd->priv;
+
+ for (int n = 0; n < ctx->num_filters; n++) {
+ struct sd_filter *ft = ctx->filters[n];
+ if (ft->driver->uninit)
+ ft->driver->uninit(ft);
+ talloc_free(ft);
+ }
+ ctx->num_filters = 0;
+}
+
+static void filters_init(struct sd *sd)
+{
+ struct sd_ass_priv *ctx = sd->priv;
+
+ filters_destroy(sd);
+
+ for (int n = 0; filters[n]; n++) {
+ struct sd_filter *ft = talloc_ptrtype(ctx, ft);
+ *ft = (struct sd_filter){
+ .global = sd->global,
+ .log = sd->log,
+ .opts = mp_get_config_group(ft, sd->global, &mp_sub_filter_opts),
+ .driver = filters[n],
+ .codec = "ass",
+ .event_format = ctx->ass_track->event_format,
+ };
+ if (ft->driver->init(ft)) {
+ MP_TARRAY_APPEND(ctx, ctx->filters, ctx->num_filters, ft);
+ } else {
+ talloc_free(ft);
+ }
+ }
+}
+
static void enable_output(struct sd *sd, bool enable)
{
struct sd_ass_priv *ctx = sd->priv;
@@ -198,12 +246,37 @@ static int init(struct sd *sd)
#endif
enable_output(sd, true);
+ filters_init(sd);
ctx->packer = mp_ass_packer_alloc(ctx);
return 0;
}
+// 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;
+
+ for (int n = 0; n < ctx->num_filters; n++) {
+ struct sd_filter *ft = ctx->filters[n];
+ struct demux_packet *npkt = ft->driver->filter(ft, pkt);
+ if (pkt != npkt && pkt != orig_pkt)
+ talloc_free(pkt);
+ pkt = npkt;
+ if (!pkt)
+ return;
+ }
+
+ ass_process_chunk(ctx->ass_track, pkt->buffer, pkt->len,
+ llrint(pkt->pts * 1000),
+ llrint(pkt->duration * 1000));
+
+ 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.
@@ -251,15 +324,13 @@ static void decode(struct sd *sd, struct demux_packet *packet)
}
for (int n = 0; r && r[n]; n++) {
- char *ass_line = r[n];
- if (sd->opts->sub_filter_SDH)
- ass_line = filter_SDH(sd, track->event_format, 1, ass_line, 0);
- if (ass_line)
- ass_process_chunk(track, ass_line, strlen(ass_line),
- llrint(sub_pts * 1000),
- llrint(sub_duration * 1000));
- if (sd->opts->sub_filter_SDH)
- talloc_free(ass_line);
+ struct demux_packet pkt2 = {
+ .pts = sub_pts,
+ .duration = sub_duration,
+ .buffer = r[n],
+ .len = strlen(r[n]),
+ };
+ filter_and_add(sd, &pkt2);
}
if (ctx->duration_unknown) {
for (int n = 0; n < track->n_events - 1; n++) {
@@ -272,18 +343,7 @@ 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.
- char *ass_line = packet->buffer;
- int ass_len = packet->len;
- if (sd->opts->sub_filter_SDH) {
- ass_line = filter_SDH(sd, track->event_format, 1, ass_line, ass_len);
- ass_len = ass_line ? strlen(ass_line) : 0;
- }
- if (ass_line)
- ass_process_chunk(track, ass_line, ass_len,
- llrint(packet->pts * 1000),
- llrint(packet->duration * 1000));
- if (sd->opts->sub_filter_SDH)
- talloc_free(ass_line);
+ filter_and_add(sd, packet);
}
}
@@ -668,10 +728,11 @@ 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) {
+ if (sd->opts->sub_clear_on_seek || ctx->duration_unknown || ctx->clear_once) {
ass_flush_events(ctx->ass_track);
ctx->num_seen_packets = 0;
sd->preload_ok = false;
+ ctx->clear_once = false;
}
if (ctx->converter)
lavc_conv_reset(ctx->converter);
@@ -681,6 +742,7 @@ static void uninit(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
+ filters_destroy(sd);
if (ctx->converter)
lavc_conv_uninit(ctx->converter);
ass_free_track(ctx->ass_track);
@@ -708,6 +770,15 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
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) {
+ filters_destroy(sd);
+ filters_init(sd);
+ ctx->clear_once = true; // allow reloading on seeks
+ }
+ return CONTROL_OK;
+ }
default:
return CONTROL_UNKNOWN;
}