From 04af005c35ee11d227a758d7b4e9e6c9558c8409 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 17 Dec 2015 00:46:07 +0100 Subject: sd_ass: remove dead code With the FFmpeg subtitle decoder used for _all_ non-ASS text subtitle format, this code is simply unused now. Ironically, the FFmpeg subtitle decoder does not handle things correctly in a bunch of cases. Should it turn out they actually matter, they will have to hack back. The extend_event one is a candidate, although even though there were allegedly files which need it, I couldn't get samples from the user who originally reported such files. As such, extend_event was only confirmed to handle trailing events with no (endless) duration like with MicroDVD and LRC, but FFmpeg "fudges" these anyway, so no special handling is needed. This code also had logic to handle seeking with muxed srt subtitles, which made the sub-seek command work. But this has been broken before this commit already. Currently, seeking with muxed srt subs will clear all subtitles, as the broken FFmpeg ASS format output by the libavcodec subtitle converters does not check for duplicates. Since the subtitles are all cleared, ass_step_sub() can not work properly and sub-seek can not seek to already seen subtitles. --- sub/sd_ass.c | 64 +++++++----------------------------------------------------- 1 file changed, 7 insertions(+), 57 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 2948475e7a..4c6296b2f4 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -43,7 +43,6 @@ struct sd_ass_priv { bool on_top; struct sub_bitmap *parts; bool flush_on_seek; - int extend_event; char last_text[500]; struct mp_image_params video_params; struct mp_image_params last_params; @@ -79,12 +78,9 @@ static void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts) static bool supports_format(const char *format) { - // ass-text is produced by converters and the subreader.c ssa parser; this - // format has ASS tags, but doesn't start with any prelude, nor does it - // have extradata. + // "ssa" is used for the FFmpeg subtitle converter output return format && (strcmp(format, "ass") == 0 || - strcmp(format, "ssa") == 0 || - strcmp(format, "ass-text") == 0); + strcmp(format, "ssa") == 0); } static int init(struct sd *sd) @@ -96,7 +92,6 @@ static int init(struct sd *sd) struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv); sd->priv = ctx; - ctx->extend_event = -1; ctx->is_converted = sd->converted_from != NULL; pthread_mutex_lock(sd->ass_lock); @@ -139,59 +134,16 @@ static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; - long long ipts = packet->pts * 1000 + 0.5; - long long iduration = packet->duration * 1000 + 0.5; if (strcmp(sd->codec, "ass") == 0) { + long long ipts = lrint(packet->pts * 1000); + long long iduration = lrint(packet->duration * 1000); ass_process_chunk(track, packet->buffer, packet->len, ipts, iduration); return; - } else if (strcmp(sd->codec, "ssa") == 0) { - // broken ffmpeg ASS packet format + } else { + // "ssa" codec ID ctx->flush_on_seek = true; ass_process_data(track, packet->buffer, packet->len); - return; - } - - // plaintext subs - if (packet->pts == MP_NOPTS_VALUE) { - MP_WARN(sd, "Subtitle without pts, ignored\n"); - return; - } - if (ctx->extend_event >= 0 && ctx->extend_event < track->n_events) { - ASS_Event *event = &track->events[ctx->extend_event]; - if (event->Start <= ipts) - event->Duration = ipts - event->Start; - ctx->extend_event = -1; } - - unsigned char *text = packet->buffer; - if (!sd->no_remove_duplicates) { - for (int i = 0; i < track->n_events; i++) { - if (track->events[i].Start == ipts - && (track->events[i].Duration == iduration) - && strcmp(track->events[i].Text, text) == 0) - return; // We've already added this subtitle - } - } - int eid = ass_alloc_event(track); - ASS_Event *event = track->events + eid; - - if (packet->duration == 0) { - MP_WARN(sd, "Subtitle without duration or " - "duration set to 0 at pts %f.\n", packet->pts); - } - if (packet->duration < 0) { - // Assume unknown duration. The FFmpeg API is very unclear about this. - MP_WARN(sd, "Assuming subtitle without duration at pts %f\n", packet->pts); - // _If_ there's a next subtitle, the duration will be adjusted again. - // If not, show it forever. - iduration = INT_MAX; - ctx->extend_event = eid; - } - - event->Start = ipts; - event->Duration = iduration; - event->Style = track->default_style; - event->Text = strdup(text); } static void configure_ass(struct sd *sd, struct mp_osd_res *dim, @@ -533,10 +485,8 @@ static void fix_events(struct sd *sd) static void reset(struct sd *sd) { struct sd_ass_priv *ctx = sd->priv; - if (ctx->flush_on_seek || sd->opts->sub_clear_on_seek) { + if (ctx->flush_on_seek || sd->opts->sub_clear_on_seek) ass_flush_events(ctx->ass_track); - ctx->extend_event = -1; - } ctx->flush_on_seek = false; } -- cgit v1.2.3 From 2c7db48195b03e93ee3cbf382ccabcedb96a6830 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 18 Dec 2015 00:51:57 +0100 Subject: sub: do not clear subtitle list on seeking This affects non-ASS text subtitles (those which go through libavcodec's subtitle converter), which are muxed with video/audio. (Typically srt subs in mkv.) The problem is that seeking in the file can send a subtitle packet to the decoder multiple times. These packets are interlaved with video, and thus can't be all read when opening the file. Rather, subtitle packets can essentially be randomly skipped or repeated (by seeking). Until recently, this was solved by scanning the libass event list for duplicates. Then our builtin srt-to-ass converter was removed, and the problem was handled by fully clearing the subtitle list on each seek. This resulted in sub-seek not working properly for this type of file. Since the subtitle list was cleared on seek, it was not possible to do e.g. sub-seeks to subtitles before the current playback position. Fix this by not clearing the list, and intead explicitly rejecting duplicate packets. We use the packet file position was unique ID for subtitles; this is confirmed working for most file formats (although it is slightly risky - new demuxers may not necessarily set the file position to something unique, or at all). The list of seen packets is sorted, and the lookup uses binary search. This is to avoid quadratic complexity when subtitles are added in bulks, such as when opening a text subtitle file. In some places, the code has to be adjusted to pass through the packet file position correctly. --- sub/sd_ass.c | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 4c6296b2f4..dc045996cd 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -42,11 +42,12 @@ struct sd_ass_priv { bool is_converted; bool on_top; struct sub_bitmap *parts; - bool flush_on_seek; char last_text[500]; struct mp_image_params video_params; struct mp_image_params last_params; double sub_speed; + int64_t *seen_packets; + int num_seen_packets; }; static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts); @@ -130,19 +131,42 @@ static int init(struct sd *sd) return 0; } +// 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. +static bool check_packet_seen(struct sd *sd, int64_t pos) +{ + struct sd_ass_priv *priv = sd->priv; + int a = 0; + int b = priv->num_seen_packets; + while (a < b) { + int mid = a + (b - a) / 2; + int64_t val = priv->seen_packets[mid]; + if (pos == val) + return true; + if (pos > val) { + a = mid + 1; + } else { + b = mid; + } + } + MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a, pos); + return false; +} + static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; if (strcmp(sd->codec, "ass") == 0) { - long long ipts = lrint(packet->pts * 1000); - long long iduration = lrint(packet->duration * 1000); - ass_process_chunk(track, packet->buffer, packet->len, ipts, iduration); - return; + // Note that for this packet format, libass has an internal mechanism + // for discarding duplicate (already seen) packets. + ass_process_chunk(track, packet->buffer, packet->len, + lrint(packet->pts * 1000), + lrint(packet->duration * 1000)); } else { - // "ssa" codec ID - ctx->flush_on_seek = true; - ass_process_data(track, packet->buffer, packet->len); + if (!check_packet_seen(sd, packet->pos)) + ass_process_data(track, packet->buffer, packet->len); } } @@ -476,18 +500,11 @@ static void fill_plaintext(struct sd *sd, double pts) track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2; } -static void fix_events(struct sd *sd) -{ - struct sd_ass_priv *ctx = sd->priv; - ctx->flush_on_seek = false; -} - static void reset(struct sd *sd) { struct sd_ass_priv *ctx = sd->priv; - if (ctx->flush_on_seek || sd->opts->sub_clear_on_seek) + if (sd->opts->sub_clear_on_seek) ass_flush_events(ctx->ass_track); - ctx->flush_on_seek = false; } static void uninit(struct sd *sd) @@ -530,7 +547,6 @@ const struct sd_functions sd_ass = { .decode = decode, .get_bitmaps = get_bitmaps, .get_text = get_text, - .fix_events = fix_events, .control = control, .reset = reset, .uninit = uninit, -- cgit v1.2.3 From 687b552db186af66e97c58792b2db40294e92bad Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 18 Dec 2015 01:54:14 +0100 Subject: sub: remove subtitle filter chain concept It was stupid. The only thing that still effectively used it was sd_lavc_conv - all other "filters" were the subtitle decoder/renderers for text (sd_ass) and bitmap (sd_lavc) subtitles. While having a subtitle filter chain was interesting (and actually worked in almost the same way as the audio/video ones), I didn't manage to use it in a meaningful way, and I couldn't e.g. factor secondary features like fixing subtitle timing into filters. Refactor the shit and drop unneeded things as it goes. --- sub/sd_ass.c | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index dc045996cd..baf35b5d30 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -40,6 +40,7 @@ struct sd_ass_priv { struct ass_track *ass_track; struct ass_track *shadow_track; // for --sub-ass=no rendering bool is_converted; + struct lavc_conv *converter; bool on_top; struct sub_bitmap *parts; char last_text[500]; @@ -79,21 +80,31 @@ static void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts) static bool supports_format(const char *format) { - // "ssa" is used for the FFmpeg subtitle converter output - return format && (strcmp(format, "ass") == 0 || - strcmp(format, "ssa") == 0); + return (format && strcmp(format, "ass") == 0) || + lavc_conv_supports_format(format); } static int init(struct sd *sd) { struct MPOpts *opts = sd->opts; - if (!sd->ass_library || !sd->ass_renderer || !sd->ass_lock || !sd->codec) + if (!sd->ass_library || !sd->ass_renderer || !sd->ass_lock) return -1; - struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv); + struct sd_ass_priv *ctx = talloc_zero(sd, struct sd_ass_priv); sd->priv = ctx; - ctx->is_converted = sd->converted_from != NULL; + char *extradata = sd->sh->extradata; + int extradata_size = sd->sh->extradata_size; + + if (strcmp(sd->sh->codec, "ass") != 0) { + ctx->is_converted = true; + ctx->converter = lavc_conv_create(sd->log, sd->sh->codec, extradata, + extradata_size); + if (!ctx->converter) + return -1; + extradata = lavc_conv_get_extradata(ctx->converter); + extradata_size = extradata ? strlen(extradata) : 0; + } pthread_mutex_lock(sd->ass_lock); @@ -106,10 +117,8 @@ static int init(struct sd *sd) ctx->shadow_track->PlayResY = 288; mp_ass_add_default_styles(ctx->shadow_track, opts); - if (sd->extradata) { - ass_process_codec_private(ctx->ass_track, sd->extradata, - sd->extradata_len); - } + if (extradata) + ass_process_codec_private(ctx->ass_track, extradata, extradata_size); mp_ass_add_default_styles(ctx->ass_track, opts); @@ -158,15 +167,18 @@ static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; - if (strcmp(sd->codec, "ass") == 0) { + if (ctx->converter) { + if (check_packet_seen(sd, packet->pos)) + return; + char **r = lavc_conv_decode(ctx->converter, packet); + for (int n = 0; r && r[n]; n++) + ass_process_data(track, r[n], strlen(r[n])); + } else { // Note that for this packet format, libass has an internal mechanism // for discarding duplicate (already seen) packets. ass_process_chunk(track, packet->buffer, packet->len, lrint(packet->pts * 1000), lrint(packet->duration * 1000)); - } else { - if (!check_packet_seen(sd, packet->pos)) - ass_process_data(track, packet->buffer, packet->len); } } @@ -505,14 +517,17 @@ static void reset(struct sd *sd) struct sd_ass_priv *ctx = sd->priv; if (sd->opts->sub_clear_on_seek) ass_flush_events(ctx->ass_track); + if (ctx->converter) + lavc_conv_reset(ctx->converter); } static void uninit(struct sd *sd) { struct sd_ass_priv *ctx = sd->priv; + if (ctx->converter) + lavc_conv_uninit(ctx->converter); ass_free_track(ctx->ass_track); - talloc_free(ctx); } static int control(struct sd *sd, enum sd_ctrl cmd, void *arg) -- cgit v1.2.3 From 0a0bb9059f42671c267ea5d0c8faa3ac71a8c742 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 19 Dec 2015 20:04:31 +0100 Subject: video: switch from using display aspect to sample aspect MPlayer traditionally always used the display aspect ratio, e.g. 16:9, while FFmpeg uses the sample (aka pixel) aspect ratio. Both have a bunch of advantages and disadvantages. Actually, it seems using sample aspect ratio is generally nicer. The main reason for the change is making mpv closer to how FFmpeg works in order to make life easier. It's also nice that everything uses integer fractions instead of floats now (except --video-aspect option/property). Note that there is at least 1 user-visible change: vf_dsize now does not set the display size, only the display aspect ratio. This is because the image_params d_w/d_h fields did not just set the display aspect, but also the size (except in encoding mode). --- sub/sd_ass.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index baf35b5d30..32179c843d 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -343,11 +343,9 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts, opts->ass_vsfilter_aspect_compat)) { // Let's use the original video PAR for vsfilter compatibility: - double par = scale - * (ctx->video_params.d_w / (double)ctx->video_params.d_h) - / (ctx->video_params.w / (double)ctx->video_params.h); + double par = ctx->video_params.p_w / (double)ctx->video_params.p_h; if (isnormal(par)) - scale = par; + scale *= par; } configure_ass(sd, &dim, converted, track); ass_set_pixel_aspect(renderer, scale); -- cgit v1.2.3 From f1a2610c4fcb1a943bf64f3a737afcf1b9c0bfdb Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 20 Dec 2015 10:14:14 +0100 Subject: sd_ass: handle --sub-clear-on-seek correctly with non-ASS subs Converted subtitles use a different method to avoid adding repeated packets as duplicate subtitle events. The state for this mechanism must be cleared as well if --sub-clear-on-seek is used. --- sub/sd_ass.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 32179c843d..16275207b7 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -513,8 +513,10 @@ 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) + if (sd->opts->sub_clear_on_seek) { ass_flush_events(ctx->ass_track); + ctx->num_seen_packets = 0; + } if (ctx->converter) lavc_conv_reset(ctx->converter); } -- cgit v1.2.3 From 6d9cb89333782c1c7043f58cc4b0c713c2e871d6 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 25 Dec 2015 12:28:01 +0100 Subject: sub: clear subtitle list when crossing timeline boundary When crossing timeline boundaries (such as switching to a new segment or chapter with ordered chapters), clear the internal text subtitle list. This breaks the sub-seek command, but is otherwise not too harmful. Fixes Sub-OC-test-final7.mkv. (The internal text subtitle list is basically a cache to make subtitles show up at the right time when seeking back.) I suspect this was caused by 76fcef61. The sample file times subtitles slightly before the video frame when it should show up. This is to avoid problems with subtitles showing up a frame later than intended. It also means that a subtitle which is supposed to show up on the start of a timeline part boundary actually might first be shown in a different part. Since we now manipulate the packet timestamps, instead of manipulating timestamps after the subtitle decoder, this means this subtitle event would have 2 timestamps, which our code of course does not handle. If the two parts come one after another, this would actually work (since the subtitle would have the same timestamps in the old and new part), but it breaks if the new part (which follows the old part in the physical file) is has a completely different start time in the timeline. Essentially, the trick used to time subtitles correctly is incompatible with the way we cache subtitles (to make them survive seeks). The simple solution is just clearing the cached subtitles when crossing chapter boundaries. --- sub/sd_ass.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 16275207b7..88aebe8c6a 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -510,13 +510,18 @@ static void fill_plaintext(struct sd *sd, double pts) track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2; } +static void clear(struct sd *sd) +{ + struct sd_ass_priv *ctx = sd->priv; + ass_flush_events(ctx->ass_track); + ctx->num_seen_packets = 0; +} + static void reset(struct sd *sd) { struct sd_ass_priv *ctx = sd->priv; - if (sd->opts->sub_clear_on_seek) { - ass_flush_events(ctx->ass_track); - ctx->num_seen_packets = 0; - } + if (sd->opts->sub_clear_on_seek) + clear(sd); if (ctx->converter) lavc_conv_reset(ctx->converter); } @@ -549,6 +554,9 @@ 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_CLEAR: + clear(sd); + return CONTROL_OK; default: return CONTROL_UNKNOWN; } -- cgit v1.2.3 From ce8524cb479f3b3339c6d2b3e0f5a45051145204 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 26 Dec 2015 18:32:27 +0100 Subject: sub: cache subtitle state per track instead of per demuxer stream Since commit 6d9cb893, subtitle state doesn't survive timeline switches (ordered chapters etc.). So there is no point in caching the state per sh_stream anymore (which would be required to deal with multiple segments). Move the cache to struct track. (Whether it's worth caching the subtitle state just for the situation when subtitle tracks get reselected is questionable. But for now, it's nice to have the subtitles immediately show up when reselecting a subtitle.) --- sub/sd_ass.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 88aebe8c6a..16275207b7 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -510,18 +510,13 @@ static void fill_plaintext(struct sd *sd, double pts) track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2; } -static void clear(struct sd *sd) -{ - struct sd_ass_priv *ctx = sd->priv; - ass_flush_events(ctx->ass_track); - ctx->num_seen_packets = 0; -} - static void reset(struct sd *sd) { struct sd_ass_priv *ctx = sd->priv; - if (sd->opts->sub_clear_on_seek) - clear(sd); + if (sd->opts->sub_clear_on_seek) { + ass_flush_events(ctx->ass_track); + ctx->num_seen_packets = 0; + } if (ctx->converter) lavc_conv_reset(ctx->converter); } @@ -554,9 +549,6 @@ 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_CLEAR: - clear(sd); - return CONTROL_OK; default: return CONTROL_UNKNOWN; } -- cgit v1.2.3 From 8d4a179c144cb3e36762b2c3cef55d1d3bb9f951 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 26 Dec 2015 18:34:18 +0100 Subject: sub: always recreate ASS_Renderer on subtitle decoder reinit This includes the case of switching ordered chapter boundaries. It will now be recreated on each timeline part switch. This shouldn't be much of a problem with modern libass. (Older libass versions use fontconfig for memory fonts, and will be very slow to reinitialize memory fonts.) --- sub/sd_ass.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 16 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 16275207b7..5a6be1bf1b 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -29,7 +29,7 @@ #include "options/options.h" #include "common/common.h" #include "common/msg.h" -#include "demux/stheader.h" +#include "demux/demux.h" #include "video/csputils.h" #include "video/mp_image.h" #include "dec_sub.h" @@ -37,6 +37,8 @@ #include "sd.h" struct sd_ass_priv { + struct ass_library *ass_library; + struct ass_renderer *ass_renderer; struct ass_track *ass_track; struct ass_track *shadow_track; // for --sub-ass=no rendering bool is_converted; @@ -78,6 +80,51 @@ static void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts) ass_process_force_style(track); } +static const char *const font_mimetypes[] = { + "application/x-truetype-font", + "application/vnd.ms-opentype", + "application/x-font-ttf", + "application/x-font", // probably incorrect + NULL +}; + +static const char *const font_exts[] = {".ttf", ".ttc", ".otf", NULL}; + +static bool attachment_is_font(struct mp_log *log, struct demux_attachment *f) +{ + if (!f->name || !f->type || !f->data || !f->data_size) + return false; + for (int n = 0; font_mimetypes[n]; n++) { + if (strcmp(font_mimetypes[n], f->type) == 0) + return true; + } + // fallback: match against file extension + char *ext = strlen(f->name) > 4 ? f->name + strlen(f->name) - 4 : ""; + for (int n = 0; font_exts[n]; n++) { + if (strcasecmp(ext, font_exts[n]) == 0) { + mp_warn(log, "Loading font attachment '%s' with MIME type %s. " + "Assuming this is a broken Matroska file, which was " + "muxed without setting a correct font MIME type.\n", + f->name, f->type); + return true; + } + } + return false; +} + +static void add_subtitle_fonts(struct sd *sd) +{ + struct sd_ass_priv *ctx = sd->priv; + struct MPOpts *opts = sd->opts; + if (!opts->ass_enabled || !sd->demuxer) + return; + for (int i = 0; i < sd->demuxer->num_attachments; i++) { + struct demux_attachment *f = &sd->demuxer->attachments[i]; + if (opts->use_embedded_fonts && attachment_is_font(sd->log, f)) + ass_add_font(ctx->ass_library, f->name, f->data, f->data_size); + } +} + static bool supports_format(const char *format) { return (format && strcmp(format, "ass") == 0) || @@ -87,9 +134,6 @@ static bool supports_format(const char *format) static int init(struct sd *sd) { struct MPOpts *opts = sd->opts; - if (!sd->ass_library || !sd->ass_renderer || !sd->ass_lock) - return -1; - struct sd_ass_priv *ctx = talloc_zero(sd, struct sd_ass_priv); sd->priv = ctx; @@ -106,13 +150,23 @@ static int init(struct sd *sd) extradata_size = extradata ? strlen(extradata) : 0; } - pthread_mutex_lock(sd->ass_lock); + ctx->ass_library = mp_ass_init(sd->global, sd->log); + + add_subtitle_fonts(sd); + + if (opts->ass_style_override) + ass_set_style_overrides(ctx->ass_library, opts->ass_force_style_list); - ctx->ass_track = ass_new_track(sd->ass_library); + ctx->ass_renderer = ass_renderer_init(ctx->ass_library); + + mp_ass_configure_fonts(ctx->ass_renderer, opts->sub_text_style, + sd->global, sd->log); + + ctx->ass_track = ass_new_track(ctx->ass_library); if (!ctx->is_converted) ctx->ass_track->track_type = TRACK_TYPE_ASS; - ctx->shadow_track = ass_new_track(sd->ass_library); + ctx->shadow_track = ass_new_track(ctx->ass_library); ctx->shadow_track->PlayResX = 384; ctx->shadow_track->PlayResY = 288; mp_ass_add_default_styles(ctx->shadow_track, opts); @@ -122,8 +176,6 @@ static int init(struct sd *sd) mp_ass_add_default_styles(ctx->ass_track, opts); - pthread_mutex_unlock(sd->ass_lock); - ctx->sub_speed = 1.0; if (sd->video_fps && sd->sh && sd->sh->sub->frame_based > 0) { @@ -186,7 +238,8 @@ static void configure_ass(struct sd *sd, struct mp_osd_res *dim, bool converted, ASS_Track *track) { struct MPOpts *opts = sd->opts; - ASS_Renderer *priv = sd->ass_renderer; + struct sd_ass_priv *ctx = sd->priv; + ASS_Renderer *priv = ctx->ass_renderer; ass_set_frame_size(priv, dim->w, dim->h); ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr); @@ -332,12 +385,10 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts, bool converted = ctx->is_converted || no_ass; ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track; - if (pts == MP_NOPTS_VALUE || !sd->ass_renderer) + if (pts == MP_NOPTS_VALUE) return; - pthread_mutex_lock(sd->ass_lock); - - ASS_Renderer *renderer = sd->ass_renderer; + ASS_Renderer *renderer = ctx->ass_renderer; double scale = dim.display_par; if (!converted && (!opts->ass_style_override || opts->ass_vsfilter_aspect_compat)) @@ -364,8 +415,6 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts, if (!converted) mangle_colors(sd, res); - - pthread_mutex_unlock(sd->ass_lock); } struct buf { @@ -528,6 +577,8 @@ static void uninit(struct sd *sd) if (ctx->converter) lavc_conv_uninit(ctx->converter); ass_free_track(ctx->ass_track); + ass_renderer_done(ctx->ass_renderer); + ass_library_done(ctx->ass_library); } static int control(struct sd *sd, enum sd_ctrl cmd, void *arg) -- cgit v1.2.3 From 190dea149aea07bc4be41c684a50db4231ccb0da Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 26 Dec 2015 18:35:36 +0100 Subject: sub: destroy/recreate ASS_Renderer when disabling/enablings subs Keeping ASS_Renderers around for a potentially large number of subtitle tracks could lead to excessive memory usage, especially since the libass cache is broken (caches even unneeded data), and might consume up to ~500MB of memory for no reason. --- sub/sd_ass.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 5a6be1bf1b..869d8eaf1f 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -131,6 +131,22 @@ static bool supports_format(const char *format) lavc_conv_supports_format(format); } +static void enable_output(struct sd *sd, bool enable) +{ + struct sd_ass_priv *ctx = sd->priv; + if (enable == !!ctx->ass_renderer) + return; + if (ctx->ass_renderer) { + ass_renderer_done(ctx->ass_renderer); + ctx->ass_renderer = NULL; + } else { + ctx->ass_renderer = ass_renderer_init(ctx->ass_library); + + mp_ass_configure_fonts(ctx->ass_renderer, sd->opts->sub_text_style, + sd->global, sd->log); + } +} + static int init(struct sd *sd) { struct MPOpts *opts = sd->opts; @@ -157,11 +173,6 @@ static int init(struct sd *sd) if (opts->ass_style_override) ass_set_style_overrides(ctx->ass_library, opts->ass_force_style_list); - ctx->ass_renderer = ass_renderer_init(ctx->ass_library); - - mp_ass_configure_fonts(ctx->ass_renderer, opts->sub_text_style, - sd->global, sd->log); - ctx->ass_track = ass_new_track(ctx->ass_library); if (!ctx->is_converted) ctx->ass_track->track_type = TRACK_TYPE_ASS; @@ -189,6 +200,8 @@ static int init(struct sd *sd) ctx->sub_speed *= opts->sub_speed; + enable_output(sd, true); + return 0; } @@ -384,11 +397,11 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts, bool no_ass = !opts->ass_enabled || ctx->on_top; bool converted = ctx->is_converted || no_ass; ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track; + ASS_Renderer *renderer = ctx->ass_renderer; - if (pts == MP_NOPTS_VALUE) + if (pts == MP_NOPTS_VALUE || !renderer) return; - ASS_Renderer *renderer = ctx->ass_renderer; double scale = dim.display_par; if (!converted && (!opts->ass_style_override || opts->ass_vsfilter_aspect_compat)) @@ -577,7 +590,7 @@ static void uninit(struct sd *sd) if (ctx->converter) lavc_conv_uninit(ctx->converter); ass_free_track(ctx->ass_track); - ass_renderer_done(ctx->ass_renderer); + enable_output(sd, false); ass_library_done(ctx->ass_library); } @@ -615,6 +628,7 @@ const struct sd_functions sd_ass = { .get_text = get_text, .control = control, .reset = reset, + .select = enable_output, .uninit = uninit, }; -- cgit v1.2.3 From 50c379e2d8a2cee0fcdbadbcbf5d0a0617fdafec Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 27 Dec 2015 01:25:32 +0100 Subject: sub: minor refactor how video FPS for MicroDVD is set So that the video FPs is not required at initialization, and can be set later. (As for whether this MicroDVD crap is worth the trouble to handle it "correctly": MicroDVD files are unfortunately still around, and in at least one case using the video FPS seemed to help indeed.) --- sub/sd_ass.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 869d8eaf1f..d798b1f4f4 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -48,7 +48,7 @@ struct sd_ass_priv { char last_text[500]; struct mp_image_params video_params; struct mp_image_params last_params; - double sub_speed; + double sub_speed, video_fps, frame_fps; int64_t *seen_packets; int num_seen_packets; }; @@ -147,6 +147,24 @@ static void enable_output(struct sd *sd, bool enable) } } +static void update_subtitle_speed(struct sd *sd) +{ + struct MPOpts *opts = sd->opts; + struct sd_ass_priv *ctx = sd->priv; + ctx->sub_speed = 1.0; + + if (ctx->video_fps > 0 && ctx->frame_fps > 0) { + MP_VERBOSE(sd, "Frame based format, dummy FPS: %f, video FPS: %f\n", + ctx->frame_fps, ctx->video_fps); + ctx->sub_speed *= ctx->frame_fps / ctx->video_fps; + } + + if (opts->sub_fps && ctx->video_fps) + ctx->sub_speed *= opts->sub_fps / ctx->video_fps; + + ctx->sub_speed *= opts->sub_speed; +} + static int init(struct sd *sd) { struct MPOpts *opts = sd->opts; @@ -187,18 +205,8 @@ static int init(struct sd *sd) mp_ass_add_default_styles(ctx->ass_track, opts); - ctx->sub_speed = 1.0; - - if (sd->video_fps && sd->sh && sd->sh->sub->frame_based > 0) { - MP_VERBOSE(sd, "Frame based format, dummy FPS: %f, video FPS: %f\n", - sd->sh->sub->frame_based, sd->video_fps); - ctx->sub_speed *= sd->sh->sub->frame_based / sd->video_fps; - } - - if (opts->sub_fps && sd->video_fps) - ctx->sub_speed *= opts->sub_fps / sd->video_fps; - - ctx->sub_speed *= opts->sub_speed; + ctx->frame_fps = sd->sh->sub->frame_based; + update_subtitle_speed(sd); enable_output(sd, true); @@ -613,6 +621,10 @@ 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_SET_VIDEO_DEF_FPS: + ctx->video_fps = *(double *)arg; + update_subtitle_speed(sd); + return CONTROL_OK; default: return CONTROL_UNKNOWN; } -- cgit v1.2.3 From d85753b79e4ce0fa7a5ddac5b2ed0cf65f7aecd8 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 27 Dec 2015 02:07:01 +0100 Subject: sub: refactor initialization Just simplify by removing parts not needed anymore. This includes merging dec_sub allocation and initialization (since things making initialization complicated were removed), or format support queries (it simply tries to create a decoder, and if that fails, tries the next one). --- sub/sd_ass.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index d798b1f4f4..564a62133d 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -125,12 +125,6 @@ static void add_subtitle_fonts(struct sd *sd) } } -static bool supports_format(const char *format) -{ - return (format && strcmp(format, "ass") == 0) || - lavc_conv_supports_format(format); -} - static void enable_output(struct sd *sd, bool enable) { struct sd_ass_priv *ctx = sd->priv; @@ -633,7 +627,6 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg) const struct sd_functions sd_ass = { .name = "ass", .accept_packets_in_advance = true, - .supports_format = supports_format, .init = init, .decode = decode, .get_bitmaps = get_bitmaps, -- cgit v1.2.3 From ad072b17ce7d4ae8f5e6605cf7237c5fd15d283d Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 31 Dec 2015 16:43:57 +0100 Subject: sub: do not check for duplicates if --sub-clear-on-seek is set --- sub/sd_ass.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 564a62133d..b44878da5a 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -199,6 +199,10 @@ static int init(struct sd *sd) mp_ass_add_default_styles(ctx->ass_track, opts); +#if LIBASS_VERSION >= 0x01302000 + ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1); +#endif + ctx->frame_fps = sd->sh->sub->frame_based; update_subtitle_speed(sd); @@ -235,7 +239,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; if (ctx->converter) { - if (check_packet_seen(sd, packet->pos)) + if (!sd->opts->sub_clear_on_seek && check_packet_seen(sd, packet->pos)) return; char **r = lavc_conv_decode(ctx->converter, packet); for (int n = 0; r && r[n]; n++) -- cgit v1.2.3 From 1f38c2107d793f28139c3de1c318bfe38e087275 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 4 Jan 2016 15:29:29 +0100 Subject: sd_ass: fix memory leaks with --sub-ass=no This affects only the codepath which forcibly disables any ASS tags. --- sub/sd_ass.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index b44878da5a..767ad927e2 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -564,7 +564,7 @@ static void fill_plaintext(struct sd *sd, double pts) text++; } - if (!dst.start || !dst.start[0]) + if (!dst.start) return; int n = ass_alloc_event(track); @@ -576,6 +576,8 @@ static void fill_plaintext(struct sd *sd, double pts) if (track->default_style < track->n_styles) track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2; + + talloc_free(dst.start); } static void reset(struct sd *sd) @@ -596,6 +598,7 @@ static void uninit(struct sd *sd) if (ctx->converter) lavc_conv_uninit(ctx->converter); ass_free_track(ctx->ass_track); + ass_free_track(ctx->shadow_track); enable_output(sd, false); ass_library_done(ctx->ass_library); } -- cgit v1.2.3 From ea442fa047819ec2e48a3dbe8ea21959ac3d70b0 Mon Sep 17 00:00:00 2001 From: "Dmitrij D. Czarkoff" Date: Mon, 11 Jan 2016 19:03:40 +0100 Subject: mpv_talloc.h: rename from talloc.h This change helps avoiding conflict with talloc.h from libtalloc. --- sub/sd_ass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 767ad927e2..658e952c28 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -24,7 +24,7 @@ #include #include -#include "talloc.h" +#include "mpv_talloc.h" #include "options/options.h" #include "common/common.h" -- cgit v1.2.3 From 671df54e4dcf0675c335483d26f7f6ff9baaf76a Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 12 Jan 2016 23:48:19 +0100 Subject: demux: merge sh_video/sh_audio/sh_sub This is mainly a refactor. I'm hoping it will make some things easier in the future due to cleanly separating codec metadata and stream metadata. Also, declare that the "codec" field can not be NULL anymore. demux.c will set it to "" if it's NULL when added. This gets rid of a corner case everything had to handle, but which rarely happened. --- sub/sd_ass.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sub/sd_ass.c') diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 658e952c28..2358ba45f6 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -165,12 +165,12 @@ static int init(struct sd *sd) struct sd_ass_priv *ctx = talloc_zero(sd, struct sd_ass_priv); sd->priv = ctx; - char *extradata = sd->sh->extradata; - int extradata_size = sd->sh->extradata_size; + char *extradata = sd->codec->extradata; + int extradata_size = sd->codec->extradata_size; - if (strcmp(sd->sh->codec, "ass") != 0) { + if (strcmp(sd->codec->codec, "ass") != 0) { ctx->is_converted = true; - ctx->converter = lavc_conv_create(sd->log, sd->sh->codec, extradata, + ctx->converter = lavc_conv_create(sd->log, sd->codec->codec, extradata, extradata_size); if (!ctx->converter) return -1; @@ -203,7 +203,7 @@ static int init(struct sd *sd) ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1); #endif - ctx->frame_fps = sd->sh->sub->frame_based; + ctx->frame_fps = sd->codec->frame_based; update_subtitle_speed(sd); enable_output(sd, true); -- cgit v1.2.3