diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | sub/dec_sub.c | 8 | ||||
-rw-r--r-- | sub/sd_lavf_srt.c | 94 |
3 files changed, 102 insertions, 1 deletions
@@ -236,6 +236,7 @@ SOURCES = talloc.c \ sub/img_convert.c \ sub/sd_lavc.c \ sub/sd_lavc_conv.c \ + sub/sd_lavf_srt.c \ sub/sd_microdvd.c \ sub/sd_movtext.c \ sub/sd_spu.c \ diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 01ba109ad0..56ed2a3d6f 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -36,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[] = { @@ -47,6 +48,7 @@ static const struct sd_functions *sd_list[] = { &sd_movtext, &sd_srt, &sd_microdvd, + &sd_lavf_srt, &sd_lavc_conv, NULL }; @@ -134,7 +136,7 @@ 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"); @@ -371,6 +373,10 @@ bool sub_read_all_packets(struct dec_sub *sub, struct sh_sub *sh) 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); 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, +}; |