summaryrefslogtreecommitdiffstats
path: root/demux
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 /demux
parent536871d7e5848eb385b97202a33b9382b5e8ab0e (diff)
parent54851d60614e912fc422658302d72811a31b80f8 (diff)
downloadmpv-403a266d466850621397c07e0b96cc2c493b2936.tar.bz2
mpv-403a266d466850621397c07e0b96cc2c493b2936.tar.xz
Merge branch 'sub_mess2'
...the return.
Diffstat (limited to 'demux')
-rw-r--r--demux/demux.c139
-rw-r--r--demux/demux.h19
-rw-r--r--demux/demux_cue.c8
-rw-r--r--demux/demux_edl.c8
-rw-r--r--demux/demux_lavf.c10
-rw-r--r--demux/demux_libass.c121
-rw-r--r--demux/demux_mf.c2
-rw-r--r--demux/demux_sub.c38
-rw-r--r--demux/demux_subreader.c1402
-rw-r--r--demux/stheader.h3
10 files changed, 1659 insertions, 91 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 5de6b84745..b66f5e3e61 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -40,7 +40,8 @@
#include "audio/format.h"
-#include "libavcodec/avcodec.h"
+#include <libavcodec/avcodec.h>
+
#if MP_INPUT_BUFFER_PADDING_SIZE < FF_INPUT_BUFFER_PADDING_SIZE
#error MP_INPUT_BUFFER_PADDING_SIZE is too small!
#endif
@@ -66,7 +67,8 @@ extern const demuxer_desc_t demuxer_desc_mpeg_es;
extern const demuxer_desc_t demuxer_desc_mpeg4_es;
extern const demuxer_desc_t demuxer_desc_h264_es;
extern const demuxer_desc_t demuxer_desc_mpeg_ts;
-extern const demuxer_desc_t demuxer_desc_sub;
+extern const demuxer_desc_t demuxer_desc_libass;
+extern const demuxer_desc_t demuxer_desc_subreader;
/* Please do not add any new demuxers here. If you want to implement a new
* demuxer, add it to libavformat, except for wrappers around external
@@ -80,8 +82,10 @@ const demuxer_desc_t *const demuxer_list[] = {
#ifdef CONFIG_TV
&demuxer_desc_tv,
#endif
+ &demuxer_desc_libass,
&demuxer_desc_matroska,
&demuxer_desc_lavf,
+ &demuxer_desc_subreader,
&demuxer_desc_avi,
&demuxer_desc_asf,
#ifdef CONFIG_MNG
@@ -96,8 +100,6 @@ const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_mpeg_ts,
// auto-probe last, because it checks file-extensions only
&demuxer_desc_mf,
- // no auto-probe
- &demuxer_desc_sub,
/* Please do not add any new demuxers here. If you want to implement a new
* demuxer, add it to libavformat, except for wrappers around external
* libraries and demuxers requiring binary support. */
@@ -183,6 +185,41 @@ void free_demux_packet(struct demux_packet *dp)
talloc_free(dp);
}
+static int destroy_avpacket(void *pkt)
+{
+ av_free_packet(pkt);
+ return 0;
+}
+
+struct demux_packet *demux_copy_packet(struct demux_packet *dp)
+{
+ struct demux_packet *new = NULL;
+ // No av_copy_packet() in Libav
+#if LIBAVCODEC_VERSION_MICRO >= 100
+ if (dp->avpacket) {
+ assert(dp->buffer == dp->avpacket->data);
+ assert(dp->len == dp->avpacket->size);
+ AVPacket *newavp = talloc_zero(NULL, AVPacket);
+ talloc_set_destructor(newavp, destroy_avpacket);
+ av_init_packet(newavp);
+ if (av_copy_packet(newavp, dp->avpacket) < 0)
+ abort();
+ new = new_demux_packet_fromdata(newavp->data, newavp->size);
+ new->avpacket = newavp;
+ }
+#endif
+ if (!new) {
+ new = new_demux_packet(dp->len);
+ memcpy(new->buffer, dp->buffer, new->len);
+ }
+ new->pts = dp->pts;
+ new->duration = dp->duration;
+ new->stream_pts = dp->stream_pts;
+ new->pos = dp->pos;
+ new->keyframe = dp->keyframe;
+ return new;
+}
+
static void free_demuxer_stream(struct demux_stream *ds)
{
ds_free_packs(ds);
@@ -251,18 +288,6 @@ static demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
return d;
}
-// for demux_sub.c
-demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts)
-{
- struct stream *s = open_stream("null://", NULL, NULL);
- assert(s);
- struct demuxer *d = new_demuxer(opts, s, DEMUXER_TYPE_SUB,
- -1, -1, -1, NULL);
- new_sh_stream(d, STREAM_SUB);
- talloc_steal(d, s);
- return d;
-}
-
static struct sh_stream *new_sh_stream_id(demuxer_t *demuxer,
enum stream_type type,
int stream_index,
@@ -446,13 +471,16 @@ void free_demuxer(demuxer_t *demuxer)
talloc_free(demuxer);
}
-void demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
- demux_packet_t *dp)
+// Returns the same value as demuxer->fill_buffer: 1 ok, 0 EOF/not selected.
+int demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
+ demux_packet_t *dp)
{
- if (!demuxer_stream_is_selected(demuxer, stream)) {
+ if (!dp || !demuxer_stream_is_selected(demuxer, stream)) {
free_demux_packet(dp);
+ return 0;
} else {
ds_add_packet(demuxer->ds[stream->type], dp);
+ return 1;
}
}
@@ -599,7 +627,7 @@ static bool demux_check_queue_full(demuxer_t *demux)
int demux_fill_buffer(demuxer_t *demux, demux_stream_t *ds)
{
// Note: parameter 'ds' can be NULL!
- return demux->desc->fill_buffer(demux, ds);
+ return demux->desc->fill_buffer ? demux->desc->fill_buffer(demux, ds) : 0;
}
// return value:
@@ -940,11 +968,6 @@ static struct demuxer *open_given_type(struct MPOpts *opts,
demuxer = demux2;
}
demuxer->file_format = fformat;
- opts->correct_pts = opts->user_correct_pts;
- if (opts->correct_pts < 0)
- opts->correct_pts =
- demux_control(demuxer, DEMUXER_CTRL_CORRECT_PTS,
- NULL) == DEMUXER_CTRL_OK;
if (stream_manages_timeline(demuxer->stream)) {
// Incorrect, but fixes some behavior with DVD/BD
demuxer->ts_resets_possible = false;
@@ -1482,3 +1505,69 @@ int demuxer_set_angle(demuxer_t *demuxer, int angle)
return angle;
}
+
+static int packet_sort_compare(const void *p1, const void *p2)
+{
+ struct demux_packet *c1 = *(struct demux_packet **)p1;
+ struct demux_packet *c2 = *(struct demux_packet **)p2;
+
+ if (c1->pts > c2->pts)
+ return 1;
+ else if (c1->pts < c2->pts)
+ return -1;
+ return 0;
+}
+
+void demux_packet_list_sort(struct demux_packet **pkts, int num_pkts)
+{
+ qsort(pkts, num_pkts, sizeof(struct demux_packet *), packet_sort_compare);
+}
+
+void demux_packet_list_seek(struct demux_packet **pkts, int num_pkts,
+ int *current, float rel_seek_secs, int flags)
+{
+ double ref_time = 0;
+ if (*current >= 0 && *current < num_pkts) {
+ ref_time = pkts[*current]->pts;
+ } else if (*current == num_pkts && num_pkts > 0) {
+ ref_time = pkts[num_pkts - 1]->pts + pkts[num_pkts - 1]->duration;
+ }
+
+ if (flags & SEEK_ABSOLUTE)
+ ref_time = 0;
+
+ if (flags & SEEK_FACTOR) {
+ ref_time += demux_packet_list_duration(pkts, num_pkts) * rel_seek_secs;
+ } else {
+ ref_time += rel_seek_secs;
+ }
+
+ // Could do binary search, but it's probably not worth the complexity.
+ int last_index = 0;
+ for (int n = 0; n < num_pkts; n++) {
+ if (pkts[n]->pts > ref_time)
+ break;
+ last_index = n;
+ }
+ *current = last_index;
+}
+
+double demux_packet_list_duration(struct demux_packet **pkts, int num_pkts)
+{
+ if (num_pkts > 0)
+ return pkts[num_pkts - 1]->pts + pkts[num_pkts - 1]->duration;
+ return 0;
+}
+
+struct demux_packet *demux_packet_list_fill(struct demux_packet **pkts,
+ int num_pkts, int *current)
+{
+ if (*current < 0)
+ *current = 0;
+ if (*current >= num_pkts)
+ return NULL;
+ struct demux_packet *new = talloc(NULL, struct demux_packet);
+ *new = *pkts[*current];
+ *current += 1;
+ return new;
+}
diff --git a/demux/demux.h b/demux/demux.h
index 9ec6d0c6f0..f49a236b80 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -71,13 +71,14 @@ enum demuxer_type {
DEMUXER_TYPE_MNG,
DEMUXER_TYPE_EDL,
DEMUXER_TYPE_CUE,
+ DEMUXER_TYPE_SUBREADER,
+ DEMUXER_TYPE_LIBASS,
/* Values after this are for internal use and can not be selected
* as demuxer type by the user (-demuxer option). */
DEMUXER_TYPE_END,
DEMUXER_TYPE_PLAYLIST,
- DEMUXER_TYPE_SUB,
};
enum timestamp_type {
@@ -216,6 +217,7 @@ struct demuxer_params {
unsigned char (*matroska_wanted_uids)[16];
int matroska_wanted_segment;
bool *matroska_was_valid;
+ struct ass_library *ass_library;
};
typedef struct demuxer {
@@ -291,6 +293,7 @@ struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
struct demux_packet *new_demux_packet_from(void *data, size_t len);
void resize_demux_packet(struct demux_packet *dp, size_t len);
void free_demux_packet(struct demux_packet *dp);
+struct demux_packet *demux_copy_packet(struct demux_packet *dp);
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
@@ -305,13 +308,10 @@ static inline void *realloc_struct(void *ptr, size_t nmemb, size_t size)
return realloc(ptr, nmemb * size);
}
-demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts);
-
-
void free_demuxer(struct demuxer *demuxer);
-void demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
- demux_packet_t *dp);
+int demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
+ demux_packet_t *dp);
void ds_add_packet(struct demux_stream *ds, struct demux_packet *dp);
void ds_read_packet(struct demux_stream *ds, struct stream *stream, int len,
double pts, int64_t pos, bool keyframe);
@@ -425,4 +425,11 @@ struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
bool demuxer_stream_is_selected(struct demuxer *d, struct sh_stream *stream);
+void demux_packet_list_sort(struct demux_packet **pkts, int num_pkts);
+void demux_packet_list_seek(struct demux_packet **pkts, int num_pkts,
+ int *current, float rel_seek_secs, int flags);
+double demux_packet_list_duration(struct demux_packet **pkts, int num_pkts);
+struct demux_packet *demux_packet_list_fill(struct demux_packet **pkts,
+ int num_pkts, int *current);
+
#endif /* MPLAYER_DEMUXER_H */
diff --git a/demux/demux_cue.c b/demux/demux_cue.c
index 31a3e00e40..073fa9d336 100644
--- a/demux/demux_cue.c
+++ b/demux/demux_cue.c
@@ -39,7 +39,7 @@ static int try_open_file(struct demuxer *demuxer)
if (!mp_probe_cue((struct bstr) { buf, len }))
return 0;
stream_seek(s, 0);
- demuxer->file_contents = stream_read_complete(s, demuxer, 1000000, 0);
+ demuxer->file_contents = stream_read_complete(s, demuxer, 1000000);
if (demuxer->file_contents.start == NULL)
return 0;
if (!mp_probe_cue((struct bstr) { buf, len }))
@@ -47,11 +47,6 @@ static int try_open_file(struct demuxer *demuxer)
return DEMUXER_TYPE_CUE;
}
-static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds)
-{
- return 0;
-}
-
const struct demuxer_desc demuxer_desc_cue = {
.info = "CUE file demuxer",
.name = "cue",
@@ -61,5 +56,4 @@ const struct demuxer_desc demuxer_desc_cue = {
.type = DEMUXER_TYPE_CUE,
.safe_check = true,
.check_file = try_open_file, // no separate .open
- .fill_buffer = dummy_fill_buffer,
};
diff --git a/demux/demux_edl.c b/demux/demux_edl.c
index 1e1db5be93..c35137ffb2 100644
--- a/demux/demux_edl.c
+++ b/demux/demux_edl.c
@@ -34,17 +34,12 @@ static int try_open_file(struct demuxer *demuxer)
if (strncmp(buf, header, len))
return 0;
stream_seek(s, 0);
- demuxer->file_contents = stream_read_complete(s, demuxer, 1000000, 0);
+ demuxer->file_contents = stream_read_complete(s, demuxer, 1000000);
if (demuxer->file_contents.start == NULL)
return 0;
return DEMUXER_TYPE_EDL;
}
-static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds)
-{
- return 0;
-}
-
const struct demuxer_desc demuxer_desc_edl = {
.info = "EDL file demuxer",
.name = "edl",
@@ -54,5 +49,4 @@ const struct demuxer_desc demuxer_desc_edl = {
.type = DEMUXER_TYPE_EDL,
.safe_check = true,
.check_file = try_open_file, // no separate .open
- .fill_buffer = dummy_fill_buffer,
};
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index d8e8109c93..48e79e949b 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -267,12 +267,11 @@ static int lavf_check_file(demuxer_t *demuxer)
while (avpd.buf_size < PROBE_BUF_SIZE) {
int nsize = av_clip(avpd.buf_size * 2, INITIAL_PROBE_SIZE,
PROBE_BUF_SIZE);
- int read_size = stream_read(s, avpd.buf + avpd.buf_size,
- nsize - avpd.buf_size);
- if (read_size <= 0)
+ bstr buf = stream_peek(s, nsize);
+ if (buf.len <= avpd.buf_size)
break;
-
- avpd.buf_size += read_size;
+ memcpy(avpd.buf, buf.start, buf.len);
+ avpd.buf_size = buf.len;
int score = 0;
priv->avif = av_probe_input_format2(&avpd, avpd.buf_size > 0, &score);
@@ -294,7 +293,6 @@ static int lavf_check_file(demuxer_t *demuxer)
priv->avif = NULL;
}
- stream_unread_buffer(s, avpd.buf, avpd.buf_size);
av_free(avpd.buf);
if (!priv->avif) {
diff --git a/demux/demux_libass.c b/demux/demux_libass.c
new file mode 100644
index 0000000000..cbc85b3abe
--- /dev/null
+++ b/demux/demux_libass.c
@@ -0,0 +1,121 @@
+/*
+ * This file is part of mpv.
+ *
+ * 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/>.
+ */
+
+// Note: just wraps libass, and makes the subtitle track available though
+// sh_sub->track. It doesn't produce packets and doesn't support seeking.
+
+#include <ass/ass.h>
+#include <ass/ass_types.h>
+
+#include "core/options.h"
+#include "core/mp_msg.h"
+#include "core/charset_conv.h"
+#include "stream/stream.h"
+#include "demux.h"
+
+#define PROBE_SIZE (8 * 1024)
+
+struct priv {
+ ASS_Track *track;
+};
+
+static int d_check_file(struct demuxer *demuxer)
+{
+ const char *user_cp = demuxer->opts->sub_cp;
+ struct stream *s = demuxer->stream;
+ // Older versions of libass will behave strange if renderer and track
+ // library handles mismatch, so make sure everything uses a global handle.
+ ASS_Library *lib = demuxer->params ? demuxer->params->ass_library : NULL;
+ if (!lib)
+ return 0;
+
+ // Probe by loading a part of the beginning of the file with libass.
+ // Incomplete scripts are usually ok, and we hope libass is not verbose
+ // when dealing with (from its perspective) completely broken binary
+ // garbage.
+
+ bstr buf = stream_peek(s, PROBE_SIZE);
+ // Older versions of libass will overwrite the input buffer, and despite
+ // passing length, expect a 0 termination.
+ void *tmp = talloc_size(NULL, buf.len + 1);
+ memcpy(tmp, buf.start, buf.len);
+ buf.start = tmp;
+ buf.start[buf.len] = '\0';
+ bstr cbuf =
+ mp_charset_guess_and_conv_to_utf8(buf, user_cp, MP_ICONV_ALLOW_CUTOFF);
+ if (cbuf.start == NULL)
+ cbuf = buf;
+ ASS_Track *track = ass_read_memory(lib, cbuf.start, cbuf.len, NULL);
+ if (cbuf.start != buf.start)
+ talloc_free(cbuf.start);
+ talloc_free(buf.start);
+ if (!track)
+ return 0;
+ ass_free_track(track);
+
+ // Actually load the full thing.
+
+ buf = stream_read_complete(s, NULL, 100000000);
+ if (!buf.start) {
+ mp_tmsg(MSGT_ASS, MSGL_ERR, "Refusing to load subtitle file "
+ "larger than 100 MB: %s\n", demuxer->filename);
+ return 0;
+ }
+ cbuf = mp_charset_guess_and_conv_to_utf8(buf, user_cp, MP_ICONV_VERBOSE);
+ if (cbuf.start == NULL)
+ cbuf = buf;
+ track = ass_read_memory(lib, cbuf.start, cbuf.len, NULL);
+ if (cbuf.start != buf.start)
+ talloc_free(cbuf.start);
+ talloc_free(buf.start);
+ if (!track)
+ return 0;
+
+ track->name = strdup(demuxer->filename);
+
+ struct priv *p = talloc_ptrtype(demuxer, p);
+ *p = (struct priv) {
+ .track = track,
+ };
+
+ struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
+ sh->sub->track = track;
+ sh->codec = "ass";
+
+ return DEMUXER_TYPE_LIBASS;
+}
+
+static void d_close(struct demuxer *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ if (p) {
+ if (p->track)
+ ass_free_track(p->track);
+ }
+}
+
+const struct demuxer_desc demuxer_desc_libass = {
+ .info = "Read subtitles with libass",
+ .name = "libass",
+ .shortdesc = "ASS/SSA subtitles (libass)",
+ .author = "",
+ .comment = "",
+ .safe_check = 1,
+ .type = DEMUXER_TYPE_LIBASS,
+ .check_file = d_check_file,
+ .close = d_close,
+};
diff --git a/demux/demux_mf.c b/demux/demux_mf.c
index 0db3fb8add..127ee8474e 100644
--- a/demux/demux_mf.c
+++ b/demux/demux_mf.c
@@ -78,7 +78,7 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds){
if (stream) {
stream_seek(stream, 0);
- bstr data = stream_read_complete(stream, NULL, MF_MAX_FILE_SIZE, 0);
+ bstr data = stream_read_complete(stream, NULL, MF_MAX_FILE_SIZE);
if (data.len) {
demux_packet_t *dp = new_demux_packet(data.len);
memcpy(dp->buffer, data.start, data.len);
diff --git a/demux/demux_sub.c b/demux/demux_sub.c
deleted file mode 100644
index ab99091215..0000000000
--- a/demux/demux_sub.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * 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/>.
- */
-
-// Note: not a real demuxer. The frontend has its own code to open subtitle
-// code, and then creates a new dummy demuxer with new_sub_demuxer().
-// But eventually, all subtitles should be opened this way, and this
-// file can be removed.
-
-#include "demux.h"
-
-static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds)
-{
- return 0;
-}
-
-const struct demuxer_desc demuxer_desc_sub = {
- .info = "External subtitles pseudo demuxer",
- .name = "sub",
- .shortdesc = "sub",
- .author = "",
- .comment = "",
- .type = DEMUXER_TYPE_SUB,
- .fill_buffer = dummy_fill_buffer,
-};
diff --git a/demux/demux_subreader.c b/demux/demux_subreader.c
new file mode 100644
index 0000000000..ca03e73c33
--- /dev/null
+++ b/demux/demux_subreader.c
@@ -0,0 +1,1402 @@
+/*
+ * 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 <libavutil/common.h>
+#include <libavutil/avstring.h>
+
+#include "config.h"
+#include "core/mp_msg.h"
+#include "core/mp_common.h"
+#include "core/options.h"
+#include "stream/stream.h"
+#include "demux/demux.h"
+
+#define ERR ((void *) -1)
+
+// subtitle formats
+#define SUB_INVALID -1
+#define SUB_MICRODVD 0
+#define SUB_SUBRIP 1
+#define SUB_SUBVIEWER 2
+#define SUB_SAMI 3
+#define SUB_VPLAYER 4
+#define SUB_RT 5
+#define SUB_SSA 6
+#define SUB_PJS 7
+#define SUB_MPSUB 8
+#define SUB_AQTITLE 9
+#define SUB_SUBVIEWER2 10
+#define SUB_SUBRIP09 11
+#define SUB_JACOSUB 12
+#define SUB_MPL2 13
+
+#define SUB_MAX_TEXT 12
+#define SUB_ALIGNMENT_BOTTOMLEFT 1
+#define SUB_ALIGNMENT_BOTTOMCENTER 2
+#define SUB_ALIGNMENT_BOTTOMRIGHT 3
+#define SUB_ALIGNMENT_MIDDLELEFT 4
+#define SUB_ALIGNMENT_MIDDLECENTER 5
+#define SUB_ALIGNMENT_MIDDLERIGHT 6
+#define SUB_ALIGNMENT_TOPLEFT 7
+#define SUB_ALIGNMENT_TOPCENTER 8
+#define SUB_ALIGNMENT_TOPRIGHT 9
+
+typedef struct subtitle {
+
+ int lines;
+
+ unsigned long start;
+ unsigned long end;
+
+ char *text[SUB_MAX_TEXT];
+ unsigned char alignment;
+} subtitle;
+
+typedef struct sub_data {
+ const char *codec;
+ subtitle *subtitles;
+ int sub_uses_time;
+ int sub_num; // number of subtitle structs
+ int sub_errs;
+ double fallback_fps;
+} sub_data;
+
+// 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;
+
+ int uses_time;
+
+ /*
+ 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; }
+ break;
+
+ case 3: /* get all text until '<' appears */
+ if (p - text >= LINE_LEN)
+ sami_add_line(current, text, &p);
+ if (*s == '\0') break;
+ else if (!strncasecmp (s, "<br>", 4)) {
+ sami_add_line(current, text, &p);
+ s += 4;
+ }
+ else if ((*s == '{')) { state = 5; ++s; continue; }
+ else if (*s == '<') { state = 4; }
+ else if (!strncasecmp (s, "&nbsp;", 6)) { *p++ = ' '; s += 6; }
+ else if (*s == '\t') { *p++ = ' '; s++; }
+ else if (*s == '\r' || *s == '\n') { s++; }
+ else *p++ = *s++;
+
+ /* skip duplicated space */
+ if (p > text + 2) if (*(p-1) == ' ' && *(p-2) == ' ') p--;
+
+ continue;
+
+ case 4: /* get current->end or skip <TAG> */
+ q = stristr (s, "Start=");
+ if (q) {
+ current->end = strtol (q + 6, &q, 0) / 10 - 1;
+ *p = '\0'; trail_space (text);
+ if (text[0] != '\0')
+ current->text[current->lines++] = strdup (text);
+ if (current->lines > 0) { state = 99; break; }
+ state = 0; continue;
+ }
+ s = strchr (s, '>');
+ if (s) { s++; state = 3; continue; }
+ break;
+ case 5: /* get rid of {...} text, but read the alignment code */
+ if ((*s == '\\') && (*(s + 1) == 'a')) {
+ if (stristr(s, "\\a1") != NULL) {
+ current->alignment = SUB_ALIGNMENT_BOTTOMLEFT;
+ s = s + 3;
+ }
+ if (stristr(s, "\\a2") != NULL) {
+ current->alignment = SUB_ALIGNMENT_BOTTOMCENTER;
+ s = s + 3;
+ } else if (stristr(s, "\\a3") != NULL) {
+ current->alignment = SUB_ALIGNMENT_BOTTOMRIGHT;
+ s = s + 3;
+ } else if ((stristr(s, "\\a4") != NULL) || (stristr(s, "\\a5") != NULL) || (stristr(s, "\\a8") != NULL)) {
+ current->alignment = SUB_ALIGNMENT_TOPLEFT;
+ s = s + 3;
+ } else if (stristr(s, "\\a6") != NULL) {
+ current->alignment = SUB_ALIGNMENT_TOPCENTER;
+ s = s + 3;
+ } else if (stristr(s, "\\a7") != NULL) {
+ current->alignment = SUB_ALIGNMENT_TOPRIGHT;
+ s = s + 3;
+ } else if (stristr(s, "\\a9") != NULL) {
+ current->alignment = SUB_ALIGNMENT_MIDDLELEFT;
+ s = s + 3;
+ } else if (stristr(s, "\\a10") != NULL) {
+ current->alignment = SUB_ALIGNMENT_MIDDLECENTER;
+ s = s + 4;
+ } else if (stristr(s, "\\a11") != NULL) {
+ current->alignment = SUB_ALIGNMENT_MIDDLERIGHT;
+ s = s + 4;
+ }
+ }
+ if (*s == '}') state = 3;
+ ++s;
+ continue;
+ }
+
+ /* read next line */
+ if (state != 99 && !(s = stream_read_line (st, line, LINE_LEN, utf16))) {
+ if (current->start > 0) {
+ break; // if it is the last subtitle
+ } else {
+ return 0;
+ }
+ }
+
+ } while (state != 99);
+
+ // For the last subtitle
+ if (current->end <= 0) {
+ current->end = current->start + args->sub_slacktime;
+ sami_add_line(current, text, &p);
+ }
+
+ return current;
+}
+
+
+static const char *sub_readtext(const char *source, char **dest) {
+ int len=0;
+ const char *p=source;
+
+// printf("src=%p dest=%p \n",source,dest);
+
+ while ( !eol(*p) && *p!= '|' ) {
+ p++,len++;
+ }
+
+ *dest= malloc (len+1);
+ if (!*dest) {return ERR;}
+
+ strncpy(*dest, source, len);
+ (*dest)[len]=0;
+
+ while (*p=='\r' || *p=='\n' || *p=='|') p++;
+
+ if (*p) return p; // not-last text field
+ else return NULL; // last text field
+}
+
+static subtitle *set_multiline_text(subtitle *current, const char *text, int start)
+{
+ int i = start;
+ while ((text = sub_readtext(text, current->text + i))) {
+ if (current->text[i] == ERR) return ERR;
+ i++;
+ if (i >= SUB_MAX_TEXT) {
+ mp_msg(MSGT_SUBREADER, MSGL_WARN, "Too many lines in a subtitle\n");
+ current->lines = i;
+ return current;
+ }
+ }
+ current->lines = i + 1;
+ return current;
+}
+
+static subtitle *sub_read_line_microdvd(stream_t *st,subtitle *current,
+ struct readline_args *args)
+{
+ int utf16 = args->utf16;
+ char line[LINE_LEN+1];
+ char line2[LINE_LEN+1];
+
+ do {
+ if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;
+ } while ((sscanf (line,
+ "{%ld}{}%[^\r\n]",
+ &(current->start), line2) < 2) &&
+ (sscanf (line,
+ "{%ld}{%ld}%[^\r\n]",
+ &(current->start), &(current->end), line2) < 3));
+
+ return set_multiline_text(current, line2, 0);
+}
+
+static subtitle *sub_read_line_mpl2(stream_t *st,subtitle *current,
+ struct readline_args *args)
+{
+ int utf16 = args->utf16;
+ char line[LINE_LEN+1];
+ char line2[LINE_LEN+1];
+
+ do {
+ if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;
+ } while ((sscanf (line,
+ "[%ld][%ld]%[^\r\n]",
+ &(current->start), &(current->end), line2) < 3));
+ current->start *= 10;
+ current->end *= 10;
+
+ return set_multiline_text(current, line2, 0);
+}
+
+static subtitle *sub_read_line_subrip(stream_t* st, subtitle *current,
+ struct readline_args *args)
+{
+ int utf16 = args->utf16;
+ char line[LINE_LEN+1];
+ int a1,a2,a3,a4,b1,b2,b3,b4;
+ char *p=NULL, *q=NULL;
+ int len;
+
+ while (1) {
+ if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;