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 /demux | |
parent | 536871d7e5848eb385b97202a33b9382b5e8ab0e (diff) | |
parent | 54851d60614e912fc422658302d72811a31b80f8 (diff) | |
download | mpv-403a266d466850621397c07e0b96cc2c493b2936.tar.bz2 mpv-403a266d466850621397c07e0b96cc2c493b2936.tar.xz |
Merge branch 'sub_mess2'
...the return.
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 139 | ||||
-rw-r--r-- | demux/demux.h | 19 | ||||
-rw-r--r-- | demux/demux_cue.c | 8 | ||||
-rw-r--r-- | demux/demux_edl.c | 8 | ||||
-rw-r--r-- | demux/demux_lavf.c | 10 | ||||
-rw-r--r-- | demux/demux_libass.c | 121 | ||||
-rw-r--r-- | demux/demux_mf.c | 2 | ||||
-rw-r--r-- | demux/demux_sub.c | 38 | ||||
-rw-r--r-- | demux/demux_subreader.c | 1402 | ||||
-rw-r--r-- | demux/stheader.h | 3 |
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, " ", 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, |