diff options
author | wm4 <wm4@nowhere> | 2013-06-25 00:43:04 +0200 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2013-06-25 00:43:04 +0200 |
commit | 403a266d466850621397c07e0b96cc2c493b2936 (patch) | |
tree | 0835a0908b3df8cca8374d173c34e0bf7df7d842 /sub | |
parent | 536871d7e5848eb385b97202a33b9382b5e8ab0e (diff) | |
parent | 54851d60614e912fc422658302d72811a31b80f8 (diff) | |
download | mpv-403a266d466850621397c07e0b96cc2c493b2936.tar.bz2 mpv-403a266d466850621397c07e0b96cc2c493b2936.tar.xz |
Merge branch 'sub_mess2'
...the return.
Diffstat (limited to 'sub')
-rw-r--r-- | sub/ass_mp.c | 29 | ||||
-rw-r--r-- | sub/ass_mp.h | 3 | ||||
-rw-r--r-- | sub/dec_sub.c | 331 | ||||
-rw-r--r-- | sub/dec_sub.h | 2 | ||||
-rw-r--r-- | sub/sd_ass.c | 55 | ||||
-rw-r--r-- | sub/sd_lavc_conv.c | 2 | ||||
-rw-r--r-- | sub/sd_lavf_srt.c | 94 | ||||
-rw-r--r-- | sub/sd_movtext.c | 3 | ||||
-rw-r--r-- | sub/sub.c | 1 | ||||
-rw-r--r-- | sub/subreader.c | 1618 | ||||
-rw-r--r-- | sub/subreader.h | 79 |
11 files changed, 384 insertions, 1833 deletions
diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 258dd57688..5e87223041 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -33,7 +33,6 @@ #include "core/mp_msg.h" #include "core/path.h" #include "ass_mp.h" -#include "subreader.h" #include "sub/sub.h" #include "stream/stream.h" #include "core/options.h" @@ -113,34 +112,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) return track; } -ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, - char *charset) -{ - ASS_Track *track; - - struct stream *s = open_stream(fname, NULL, NULL); - if (!s) - // Stream code should have printed an error already - return NULL; - struct bstr content = stream_read_complete(s, NULL, 100000000, 1); - if (content.start == NULL) - mp_tmsg(MSGT_ASS, MSGL_ERR, "Refusing to load subtitle file " - "larger than 100 MB: %s\n", fname); - free_stream(s); - if (content.len == 0) { - talloc_free(content.start); - return NULL; - } - content.start[content.len] = 0; - track = ass_read_memory(library, content.start, content.len, charset); - if (track) { - free(track->name); - track->name = strdup(fname); - } - talloc_free(content.start); - return track; -} - void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, struct mp_osd_res *dim) { diff --git a/sub/ass_mp.h b/sub/ass_mp.h index 9f40b34166..c0fba934eb 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -25,7 +25,6 @@ #include <stdbool.h> #include "config.h" -#include "subreader.h" // This is probably arbitrary. // sd_lavc_conv might indirectly still assume this PlayResY, though. @@ -50,8 +49,6 @@ void mp_ass_set_style(ASS_Style *style, int res_y, struct osd_style_opts *opts); void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts); ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts); -ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, - char *charset); struct MPOpts; void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, diff --git a/sub/dec_sub.c b/sub/dec_sub.c index b72630470c..a1392017a2 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -18,16 +18,17 @@ #include <stdlib.h> #include <stdbool.h> +#include <string.h> #include <assert.h> #include "config.h" -#include "demux/stheader.h" +#include "demux/demux.h" #include "sd.h" #include "sub.h" #include "dec_sub.h" -#include "subreader.h" #include "core/options.h" #include "core/mp_msg.h" +#include "core/charset_conv.h" extern const struct sd_functions sd_ass; extern const struct sd_functions sd_lavc; @@ -35,6 +36,7 @@ extern const struct sd_functions sd_spu; extern const struct sd_functions sd_movtext; extern const struct sd_functions sd_srt; extern const struct sd_functions sd_microdvd; +extern const struct sd_functions sd_lavf_srt; extern const struct sd_functions sd_lavc_conv; static const struct sd_functions *sd_list[] = { @@ -46,6 +48,7 @@ static const struct sd_functions *sd_list[] = { &sd_movtext, &sd_srt, &sd_microdvd, + &sd_lavf_srt, &sd_lavc_conv, NULL }; @@ -56,10 +59,18 @@ struct dec_sub { struct MPOpts *opts; struct sd init_sd; + double video_fps; + const char *charset; + struct sd *sd[MAX_NUM_SD]; int num_sd; }; +struct packet_list { + struct demux_packet **packets; + int num_packets; +}; + struct dec_sub *sub_create(struct MPOpts *opts) { struct dec_sub *sub = talloc_zero(NULL, struct dec_sub); @@ -102,6 +113,11 @@ void sub_set_video_res(struct dec_sub *sub, int w, int h) sub->init_sd.sub_video_h = h; } +void sub_set_video_fps(struct dec_sub *sub, double fps) +{ + sub->video_fps = fps; +} + void sub_set_extradata(struct dec_sub *sub, void *data, int data_len) { sub->init_sd.extradata = data_len ? talloc_memdup(sub, data, data_len) : NULL; @@ -120,74 +136,12 @@ static void print_chain(struct dec_sub *sub) mp_msg(MSGT_OSD, MSGL_V, "Subtitle filter chain: "); for (int n = 0; n < sub->num_sd; n++) { struct sd *sd = sub->sd[n]; - mp_msg(MSGT_OSD, MSGL_V, "%s%s (%s)", n > 0 ? " -> " : "", + mp_msg(MSGT_OSD, MSGL_V, "%s%s (%s)", n > 0 ? " -> " : "", sd->driver->name, sd->codec); } mp_msg(MSGT_OSD, MSGL_V, "\n"); } -// Subtitles read with subreader.c -static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) -{ - assert(sub_accept_packets_in_advance(sub)); - char *temp = NULL; - - struct sd *sd = sub_get_last_sd(sub); - - sd->no_remove_duplicates = true; - - for (int i = 0; i < subdata->sub_num; i++) { - subtitle *st = &subdata->subtitles[i]; - // subdata is in 10 ms ticks, pts is in seconds - double t = subdata->sub_uses_time ? 0.01 : (1 / subdata->fallback_fps); - - int len = 0; - for (int j = 0; j < st->lines; j++) - len += st->text[j] ? strlen(st->text[j]) : 0; - - len += 2 * st->lines; // '\N', including the one after the last line - len += 6; // {\anX} - len += 1; // '\0' - - if (talloc_get_size(temp) < len) { - talloc_free(temp); - temp = talloc_array(NULL, char, len); - } - - char *p = temp; - char *end = p + len; - - if (st->alignment) - p += snprintf(p, end - p, "{\\an%d}", st->alignment); - - for (int j = 0; j < st->lines; j++) - p += snprintf(p, end - p, "%s\\N", st->text[j]); - - if (st->lines > 0) - p -= 2; // remove last "\N" - *p = 0; - - struct demux_packet pkt = {0}; - pkt.pts = st->start * t; - pkt.duration = (st->end - st->start) * t; - pkt.buffer = temp; - pkt.len = strlen(temp); - - sub_decode(sub, &pkt); - } - - // Hack for broken FFmpeg packet format: make sd_ass keep the subtitle - // events on reset(), even though broken FFmpeg ASS packets were received - // (from sd_lavc_conv.c). Normally, these events are removed on seek/reset, - // but this is obviously unwanted in this case. - if (sd && sd->driver->fix_events) - sd->driver->fix_events(sd); - - sd->no_remove_duplicates = false; - - talloc_free(temp); -} - static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) { sd->driver = NULL; @@ -230,8 +184,6 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) // Try adding new converters until a decoder is reached if (sd->driver->get_bitmaps || sd->driver->get_text) { print_chain(sub); - if (sh->sub_data) - read_sub_data(sub, sh->sub_data); return; } init_sd = (struct sd) { @@ -249,32 +201,245 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) sh->gsh->codec ? sh->gsh->codec : "<unknown>"); } -bool sub_accept_packets_in_advance(struct dec_sub *sub) +static struct demux_packet *get_decoded_packet(struct sd *sd) { - // 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; + return sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL; } -static void decode_next(struct dec_sub *sub, int n, struct demux_packet *packet) +static void decode_chain(struct sd **sd, int num_sd, struct demux_packet *packet) { - struct sd *sd = sub->sd[n]; - sd->driver->decode(sd, packet); - if (n + 1 >= sub->num_sd || !sd->driver->get_converted) + if (num_sd == 0) return; - while (1) { - struct demux_packet *next = - sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL; - if (!next) - break; - decode_next(sub, n + 1, next); + struct sd *dec = sd[0]; + dec->driver->decode(dec, packet); + if (num_sd > 1) { + while (1) { + struct demux_packet *next = get_decoded_packet(dec); + if (!next) + break; + decode_chain(sd + 1, num_sd - 1, next); + } + } +} + +static struct demux_packet *recode_packet(struct demux_packet *in, + const char *charset) +{ + struct demux_packet *pkt = NULL; + bstr in_buf = {in->buffer, in->len}; + bstr conv = mp_iconv_to_utf8(in_buf, charset, MP_ICONV_VERBOSE); + if (conv.start && conv.start != in_buf.start) { + pkt = talloc_ptrtype(NULL, pkt); + talloc_steal(pkt, conv.start); + *pkt = (struct demux_packet) { + .buffer = conv.start, + .len = conv.len, + .pts = in->pts, + .duration = in->duration, + .avpacket = in->avpacket, // questionable, but gives us sidedata + }; + } + return pkt; +} + +static void decode_chain_recode(struct dec_sub *sub, struct sd **sd, int num_sd, + struct demux_packet *packet) +{ + if (num_sd > 0) { + struct demux_packet *recoded = NULL; + if (sub->charset) + recoded = recode_packet(packet, sub->charset); + decode_chain(sd, num_sd, recoded ? recoded : packet); } } void sub_decode(struct dec_sub *sub, struct demux_packet *packet) { - if (sub->num_sd > 0) - decode_next(sub, 0, packet); + decode_chain_recode(sub, sub->sd, sub->num_sd, packet); +} + +static const char *guess_sub_cp(struct packet_list *subs, const char *usercp) +{ + if (!mp_charset_requires_guess(usercp)) + return usercp; + + // Concat all subs into a buffer. We can't probably do much better without + // having the original data (which we don't, not anymore). + int max_size = 2 * 1024 * 1024; + const char *sep = "\n\n"; // In utf-16: U+0A0A GURMUKHI LETTER UU + int sep_len = strlen(sep); + int num_pkt = 0; + int size = 0; + for (int n = 0; n < subs->num_packets; n++) { + struct demux_packet *pkt = subs->packets[n]; + if (size + pkt->len > max_size) + break; + size += pkt->len + sep_len; + num_pkt++; + } + bstr text = {talloc_size(NULL, size), 0}; + for (int n = 0; n < num_pkt; n++) { + struct demux_packet *pkt = subs->packets[n]; + memcpy(text.start + text.len, pkt->buffer, pkt->len); + memcpy(text.start + text.len + pkt->len, sep, sep_len); + text.len += pkt->len + sep_len; + } + const char *guess = mp_charset_guess(text, usercp); + talloc_free(text.start); + return guess; +} + +static void multiply_timings(struct packet_list *subs, double factor) +{ + for (int n = 0; n < subs->num_packets; n++) { + struct demux_packet *pkt = subs->packets[n]; + if (pkt->pts != MP_NOPTS_VALUE) + pkt->pts *= factor; + if (pkt->duration > 0) + pkt->duration *= factor; + } +} + +// Remove overlaps and fill gaps between adjacent subtitle packets. This is done +// by adjusting the duration of the earlier packet. If the gaps or overlap are +// larger than the threshold, or if the durations are close to the threshold, +// don't change the events. +// The algorithm is maximally naive and doesn't work if there are multiple +// overlapping lines. (It's not worth the trouble.) +static void fix_overlaps_and_gaps(struct packet_list *subs) +{ + double threshold = 0.2; // up to 200 ms overlaps or gaps are removed + double keep = threshold * 2;// don't change timings if durations are smaller + for (int i = 0; i < subs->num_packets - 1; i++) { + struct demux_packet *cur = subs->packets[i]; + struct demux_packet *next = subs->packets[i + 1]; + if (cur->pts != MP_NOPTS_VALUE && cur->duration > 0 && + next->pts != MP_NOPTS_VALUE && next->duration > 0) + { + double end = cur->pts + cur->duration; + if (fabs(next->pts - end) <= threshold && cur->duration >= keep && + next->duration >= keep) + { + cur->duration = next->pts - cur->pts; + } + } + } +} + +static void add_sub_list(struct dec_sub *sub, int at, struct packet_list *subs) +{ + struct sd *sd = sub_get_last_sd(sub); + assert(sd); + + sd->no_remove_duplicates = true; + + for (int n = 0; n < subs->num_packets; n++) + decode_chain_recode(sub, sub->sd + at, sub->num_sd - at, subs->packets[n]); + + // Hack for broken FFmpeg packet format: make sd_ass keep the subtitle + // events on reset(), even if broken FFmpeg ASS packets were received + // (from sd_lavc_conv.c). Normally, these events are removed on seek/reset, + // but this is obviously unwanted in this case. + if (sd->driver->fix_events) + sd->driver->fix_events(sd); + + sd->no_remove_duplicates = false; +} + +static void add_packet(struct packet_list *subs, struct demux_packet *pkt) +{ + pkt = demux_copy_packet(pkt); + talloc_steal(subs, pkt); + MP_TARRAY_APPEND(subs, subs->packets, subs->num_packets, pkt); +} + +// Read all packets from the demuxer and decode/add them. Returns false if +// there are circumstances which makes this not possible. +bool sub_read_all_packets(struct dec_sub *sub, struct sh_sub *sh) +{ + struct MPOpts *opts = sub->opts; + + if (!sub_accept_packets_in_advance(sub) || sh->track || sub->num_sd < 1) + return false; + + struct packet_list *subs = talloc_zero(NULL, struct packet_list); + + // In some cases, we want to put the packets through a decoder first. + // Preprocess until sub->sd[preprocess]. + int preprocess = 0; + + // movtext is currently the only subtitle format that has text output, + // but binary input. Do charset conversion after converting to text. + if (sub->sd[0]->driver == &sd_movtext) + preprocess = 1; + + // Broken Libav libavformat srt packet format (fix timestamps first). + if (sub->sd[0]->driver == &sd_lavf_srt) + preprocess = 1; + + for (;;) { + ds_get_next_pts(sh->ds); + struct demux_packet *pkt = ds_get_packet_sub(sh->ds); + if (!pkt) + break; + if (preprocess) { + decode_chain(sub->sd, preprocess, pkt); + while (1) { + pkt = get_decoded_packet(sub->sd[preprocess - 1]); + if (!pkt) + break; + add_packet(subs, pkt); + } + } else { + add_packet(subs, pkt); + } + } + + if (opts->sub_cp && !sh->is_utf8) + sub->charset = guess_sub_cp(subs, opts->sub_cp); + + if (sub->charset) + mp_msg(MSGT_OSD, MSGL_INFO, "Using subtitle charset: %s\n", sub->charset); + + double sub_speed = 1.0; + + // 23.976 FPS is used as default timebase for frame based formats + if (sub->video_fps && sh->frame_based) + sub_speed *= sub->video_fps / 23.976; + + if (opts->sub_fps && sub->video_fps) + sub_speed *= opts->sub_fps / sub->video_fps; + + sub_speed *= opts->sub_speed; + + if (sub_speed != 1.0) + multiply_timings(subs, sub_speed); + + if (!opts->suboverlap_enabled) + fix_overlaps_and_gaps(subs); + + if (sh->gsh->codec && strcmp(sh->gsh->codec, "microdvd") == 0) { + // The last subtitle event in MicroDVD subs can have duration unset, + // which means show the subtitle until end of video. + // See FFmpeg FATE MicroDVD_capability_tester.sub + if (subs->num_packets) { + struct demux_packet *last = subs->packets[subs->num_packets - 1]; + if (last->duration <= 0) + last->duration = 10; // arbitrary + } + } + + add_sub_list(sub, preprocess, subs); + + talloc_free(subs); + return true; +} + +bool sub_accept_packets_in_advance(struct dec_sub *sub) +{ + // 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; } void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 805a87ef5c..c285449f94 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -20,6 +20,7 @@ struct dec_sub *sub_create(struct MPOpts *opts); void sub_destroy(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); void sub_set_extradata(struct dec_sub *sub, void *data, int data_len); void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library, struct ass_renderer *ass_renderer); @@ -27,6 +28,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh); bool sub_is_initialized(struct dec_sub *sub); +bool sub_read_all_packets(struct dec_sub *sub, struct sh_sub *sh); bool sub_accept_packets_in_advance(struct dec_sub *sub); void sub_decode(struct dec_sub *sub, struct demux_packet *packet); void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, diff --git a/sub/sd_ass.c b/sub/sd_ass.c index c46c55c1ab..9c51398f33 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -32,6 +32,10 @@ #include "ass_mp.h" #include "sd.h" +// Enable code that treats subtitle events with duration 0 specially, and +// adjust their duration so that they will disappear with the next event. +#define INCOMPLETE_EVENTS 0 + struct sd_ass_priv { struct ass_track *ass_track; bool vsfilter_aspect; @@ -41,17 +45,14 @@ struct sd_ass_priv { char last_text[500]; }; -static bool is_native_ass(const char *t) -{ - return strcmp(t, "ass") == 0 || strcmp(t, "ssa") == 0; -} - 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. - return format && (is_native_ass(format) || strcmp(format, "ass-text") == 0); + return format && (strcmp(format, "ass") == 0 || + strcmp(format, "ssa") == 0 || + strcmp(format, "ass-text") == 0); } static void free_last_event(ASS_Track *track) @@ -64,7 +65,7 @@ static void free_last_event(ASS_Track *track) static int init(struct sd *sd) { struct MPOpts *opts = sd->opts; - if (!sd->ass_library || !sd->ass_renderer) + if (!sd->ass_library || !sd->ass_renderer || !sd->codec) return -1; bool is_converted = sd->converted_from != NULL; @@ -99,16 +100,15 @@ static void decode(struct sd *sd, struct demux_packet *packet) unsigned char *text = data; struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; - if (is_native_ass(sd->codec)) { - if (bstr_startswith0((bstr){data, data_len}, "Dialogue: ")) { - // broken ffmpeg ASS packet format - ctx->flush_on_seek = true; - ass_process_data(track, data, data_len); - } else { - ass_process_chunk(track, data, data_len, - (long long)(pts*1000 + 0.5), - (long long)(duration*1000 + 0.5)); - } + if (strcmp(sd->codec, "ass") == 0) { + ass_process_chunk(track, data, data_len, + (long long)(pts*1000 + 0.5), + (long long)(duration*1000 + 0.5)); + return; + } else if (strcmp(sd->codec, "ssa") == 0) { + // broken ffmpeg ASS packet format + ctx->flush_on_seek = true; + ass_process_data(track, data, data_len); return; } // plaintext subs @@ -118,6 +118,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) } long long ipts = pts * 1000 + 0.5; long long iduration = duration * 1000 + 0.5; +#if INCOMPLETE_EVENTS if (ctx->incomplete_event) { ctx->incomplete_event = false; ASS_Event *event = track->events + track->n_events - 1; @@ -148,6 +149,21 @@ static void decode(struct sd *sd, struct demux_packet *packet) iduration = 10000; ctx->incomplete_event = true; } +#else + if (duration <= 0) { + mp_msg(MSGT_SUBREADER, MSGL_WARN, "Subtitle without duration or " + "duration set to 0 at pts %f, ignored\n", pts); + return; + } + 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 + } + } +#endif int eid = ass_alloc_event(track); ASS_Event *event = track->events + eid; event->Start = ipts; @@ -259,8 +275,11 @@ static char *get_text(struct sd *sd, double pts) if (event->Text) { int start = b.len; ass_to_plaintext(&b, event->Text); - if (!is_whitespace_only(&b.start[b.len], b.len - start)) + if (is_whitespace_only(&b.start[start], b.len - start)) { + b.len = start; + } else { append(&b, '\n'); + } } } } diff --git a/sub/sd_lavc_conv.c b/sub/sd_lavc_conv.c index 1fc0262f96..4f24e20709 100644 --- a/sub/sd_lavc_conv.c +++ b/sub/sd_lavc_conv.c @@ -87,7 +87,7 @@ static int init(struct sd *sd) avctx->time_base = (AVRational) {1, 1000}; priv->avctx = avctx; sd->priv = priv; - sd->output_codec = "ass"; + sd->output_codec = "ssa"; sd->output_extradata = avctx->subtitle_header; sd->output_extradata_len = avctx->subtitle_header_size; if (sd->output_extradata) { diff --git a/sub/sd_lavf_srt.c b/sub/sd_lavf_srt.c new file mode 100644 index 0000000000..0d34b489c7 --- /dev/null +++ b/sub/sd_lavf_srt.c @@ -0,0 +1,94 @@ +/* + * This file is part of mpv. + * + * SRT timestamp parsing code lifted from FFmpeg srtdec.c (LGPL). + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <assert.h> + +#include "core/bstr.h" +#include "sd.h" + +/* + * Background: + * + * Libav's .srt demuxer outputs packets that contain parts of the subtitle + * event header. Also, the packet duration is not set (they don't parse it + * on the demuxer side). As a result, the srt demuxer is useless. + * + * However, we can fix it by parsing the header, which spares us from writing + * a full SRT demuxer. + * + * Newer versions of FFmpeg do not have this problem. To avoid compatibility + * problems, they changed the codec name from "srt" to "subrip". + * + * Summary: this is a hack for broken SRT stuff in Libav. + * + */ + +static bool supports_format(const char *format) +{ + return format && strcmp(format, "srt") == 0; +} + +static int init(struct sd *sd) +{ + sd->output_codec = "subrip"; + return 0; +} + +static bool parse_pts(bstr header, double *duration) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "%.*s", BSTR_P(header)); + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + if (sscanf(buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d", + &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) >= 8) + { + int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1; + int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2; + *duration = (end - start) / 1000.0; + return true; + } + return false; +} + +static void decode(struct sd *sd, struct demux_packet *packet) +{ + bstr data = {packet->buffer, packet->len}; + // Remove the broken header. It's usually on the second or first line. + bstr left = data; + while (left.len) { + bstr line = bstr_getline(left, &left); + if (parse_pts(line, &packet->duration)) { + data = left; + break; + } + } + sd_conv_add_packet(sd, data.start, data.len, packet->pts, packet->duration); +} + +const struct sd_functions sd_lavf_srt = { + .name = "lavf_srt", + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_converted = sd_conv_def_get_converted, + .reset = sd_conv_def_reset, +}; diff --git a/sub/sd_movtext.c b/sub/sd_movtext.c index a6ef120ec7..3038a4c132 100644 --- a/sub/sd_movtext.c +++ b/sub/sd_movtext.c @@ -42,7 +42,8 @@ static void decode(struct sd *sd, struct demux_packet *packet) return; len = FFMIN(len - 2, AV_RB16(data)); data += 2; - sd_conv_add_packet(sd, data, len, packet->pts, packet->duration); + if (len > 0) + sd_conv_add_packet(sd, data, len, packet->pts, packet->duration); } const struct sd_functions sd_movtext = { @@ -37,7 +37,6 @@ #include "dec_sub.h" #include "img_convert.h" #include "draw_bmp.h" -#include "subreader.h" #include "video/mp_image.h" #include "video/mp_image_pool.h" diff --git a/sub/subreader.c b/sub/subreader.c deleted file mode 100644 index f3821ba5ab..0000000000 --- a/sub/subreader.c +++ /dev/null @@ -1,1618 +0,0 @@ -/* - * Subtitle reader with format autodetection - * - * Copyright (c) 2001 laaz - * Some code cleanup & realloc() by A'rpi/ESP-team - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <dirent.h> -#include <ctype.h> - -#include "config.h" -#include "core/mp_msg.h" -#include "subreader.h" -#include "core/mp_common.h" -#include "core/options.h" -#include "stream/stream.h" -#include "libavutil/common.h" -#include "libavutil/avstring.h" - -#ifdef CONFIG_ENCA -#include <enca.h> -#endif - -#define ERR ((void *) -1) - -#ifdef CONFIG_ICONV -#include <iconv.h> -#endif - -// Parameter struct for the format-specific readline functions -struct readline_args { - int utf16; - struct MPOpts *opts; - - // subtitle reader state used by some formats - - float mpsub_multiplier; - float mpsub_position; - int sub_slacktime; - - /* - Some subtitling formats, namely AQT and Subrip09, define the end of a - subtitle as the beginning of the following. Since currently we read one - subtitle at time, for these format we keep two global *subtitle, - previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle, - so we can change its end when we read current subtitle starting time. - We use a single global unsigned long, - previous_sub_end, for both (and even future) formats, to store the end of - the previous sub: it is initialized to 0 in sub_read_file and eventually - modified by sub_read_aqt_line or sub_read_subrip09_line. - */ - unsigned long previous_sub_end; -}; - -/* Maximal length of line of a subtitle */ -#define LINE_LEN 1000 - -static int eol(char p) { - return p=='\r' || p=='\n' || p=='\0'; -} - -/* Remove leading and trailing space */ -static void trail_space(char *s) { - int i = 0; - while (isspace(s[i])) ++i; - if (i) strcpy(s, s + i); - i = strlen(s) - 1; - while (i > 0 && isspace(s[i])) s[i--] = '\0'; -} - -static char *stristr(const char *haystack, const char *needle) { - int len = 0; - const char *p = haystack; - - if (!(haystack && needle)) return NULL; - - len=strlen(needle); - while (*p != '\0') { - if (strncasecmp(p, needle, len) == 0) return (char*)p; - p++; - } - - return NULL; -} - -static void sami_add_line(subtitle *current, char *buffer, char **pos) { - char *p = *pos; - *p = 0; - trail_space(buffer); - if (*buffer && current->lines < SUB_MAX_TEXT) - current->text[current->lines++] = strdup(buffer); - *pos = buffer; -} - -static subtitle *sub_read_line_sami(stream_t* st, subtitle *current, - struct readline_args *args) -{ - int utf16 = args->utf16; - static char line[LINE_LEN+1]; - static char *s = NULL, *slacktime_s; - char text[LINE_LEN+1], *p=NULL, *q; - int state; - - current->lines = current->start = current->end = 0; - current->alignment = SUB_ALIGNMENT_BOTTOMCENTER; - state = 0; - - /* read the first line */ - if (!s) - if (!(s = stream_read_line(st, line, LINE_LEN, utf16))) return 0; - - do { - switch (state) { - - case 0: /* find "START=" or "Slacktime:" */ - slacktime_s = stristr (s, "Slacktime:"); - if (slacktime_s) - args->sub_slacktime = strtol (slacktime_s+10, NULL, 0) / 10; - - s = stristr (s, "Start="); - if (s) { - current->start = strtol (s + 6, &s, 0) / 10; - /* eat '>' */ - for (; *s != '>' && *s != '\0'; s++); - s++; - state = 1; continue; - } - break; - - case 1: /* find (optional) "<P", skip other TAGs */ - for (; *s == ' ' || *s == '\t'; s++); /* strip blanks, if any */ |