summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-06-25 00:43:04 +0200
committerwm4 <wm4@nowhere>2013-06-25 00:43:04 +0200
commit403a266d466850621397c07e0b96cc2c493b2936 (patch)
tree0835a0908b3df8cca8374d173c34e0bf7df7d842 /sub
parent536871d7e5848eb385b97202a33b9382b5e8ab0e (diff)
parent54851d60614e912fc422658302d72811a31b80f8 (diff)
downloadmpv-403a266d466850621397c07e0b96cc2c493b2936.tar.bz2
mpv-403a266d466850621397c07e0b96cc2c493b2936.tar.xz
Merge branch 'sub_mess2'
...the return.
Diffstat (limited to 'sub')
-rw-r--r--sub/ass_mp.c29
-rw-r--r--sub/ass_mp.h3
-rw-r--r--sub/dec_sub.c331
-rw-r--r--sub/dec_sub.h2
-rw-r--r--sub/sd_ass.c55
-rw-r--r--sub/sd_lavc_conv.c2
-rw-r--r--sub/sd_lavf_srt.c94
-rw-r--r--sub/sd_movtext.c3
-rw-r--r--sub/sub.c1
-rw-r--r--sub/subreader.c1618
-rw-r--r--sub/subreader.h79
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 = {
diff --git a/sub/sub.c b/sub/sub.c
index a0965dc1ec..7eb32d5885 100644
--- a/sub/sub.c
+++ b/sub/sub.c
@@ -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 */
- if (*s == '\0') break;
- if (*s != '<') { state = 3; p = text; continue; } /* not a TAG */
- s++;
- if (*s == 'P' || *s == 'p') { s++; state = 2; continue; } /* found '<P' */
- for (; *s != '>' && *s != '\0'; s++); /* skip remains of non-<P> TAG */
- if (*s == '\0')
- break;
- s++;
- continue;
-
- case 2: /* find ">" */
- if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue