diff options
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 28 | ||||
-rw-r--r-- | demux/demux.h | 1 | ||||
-rw-r--r-- | demux/demux_disc.c | 9 | ||||
-rw-r--r-- | demux/demux_lavf.c | 4 | ||||
-rw-r--r-- | demux/demux_libass.c | 111 | ||||
-rw-r--r-- | demux/demux_mkv.c | 3 | ||||
-rw-r--r-- | demux/demux_subreader.c | 837 |
7 files changed, 30 insertions, 963 deletions
diff --git a/demux/demux.c b/demux/demux.c index 3233541be2..dcad692be5 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -48,8 +48,6 @@ extern const demuxer_desc_t demuxer_desc_tv; extern const demuxer_desc_t demuxer_desc_mf; extern const demuxer_desc_t demuxer_desc_matroska; extern const demuxer_desc_t demuxer_desc_lavf; -extern const demuxer_desc_t demuxer_desc_libass; -extern const demuxer_desc_t demuxer_desc_subreader; extern const demuxer_desc_t demuxer_desc_playlist; extern const demuxer_desc_t demuxer_desc_disc; extern const demuxer_desc_t demuxer_desc_rar; @@ -68,9 +66,6 @@ const demuxer_desc_t *const demuxer_list[] = { #if HAVE_TV &demuxer_desc_tv, #endif -#if HAVE_LIBASS - &demuxer_desc_libass, -#endif &demuxer_desc_matroska, #if HAVE_LIBARCHIVE &demuxer_desc_libarchive, @@ -79,8 +74,6 @@ const demuxer_desc_t *const demuxer_list[] = { &demuxer_desc_lavf, &demuxer_desc_mf, &demuxer_desc_playlist, - // Pretty aggressive, so should be last. - &demuxer_desc_subreader, NULL }; @@ -128,6 +121,8 @@ struct demux_internal { bool refresh_seeks_enabled; bool start_refresh_seek; + double ts_offset; // timestamp offset to apply everything + // Cached state. bool force_cache_update; double time_length; @@ -168,6 +163,8 @@ struct demux_stream { #define MP_PTS_MIN(a, b) MPMIN(PTS_OR_DEF(a, b), PTS_OR_DEF(b, a)) #define MP_PTS_MAX(a, b) MPMAX(PTS_OR_DEF(a, b), PTS_OR_DEF(b, a)) +#define MP_ADD_PTS(a, b) ((a) == MP_NOPTS_VALUE ? (a) : ((a) + (b))) + static void demuxer_sort_chapters(demuxer_t *demuxer); static void *demux_thread(void *pctx); static void update_cache(struct demux_internal *in); @@ -193,6 +190,14 @@ static void ds_flush(struct demux_stream *ds) ds->last_pos = -1; } +void demux_set_ts_offset(struct demuxer *demuxer, double offset) +{ + struct demux_internal *in = demuxer->in; + pthread_mutex_lock(&in->lock); + in->ts_offset = offset; + pthread_mutex_unlock(&in->lock); +} + struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type) { assert(demuxer == demuxer->in->d_thread); @@ -607,6 +612,9 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds) if (pkt->pos >= ds->in->d_user->filepos) ds->in->d_user->filepos = pkt->pos; + pkt->pts = MP_ADD_PTS(pkt->pts, ds->in->ts_offset); + pkt->dts = MP_ADD_PTS(pkt->dts, ds->in->ts_offset); + return pkt; } @@ -668,7 +676,7 @@ double demux_get_next_pts(struct sh_stream *sh) pthread_mutex_lock(&sh->ds->in->lock); ds_get_packets(sh->ds); if (sh->ds->head) - res = sh->ds->head->pts; + res = MP_ADD_PTS(sh->ds->head->pts, sh->ds->in->ts_offset); pthread_mutex_unlock(&sh->ds->in->lock); } return res; @@ -1152,6 +1160,8 @@ int demux_seek(demuxer_t *demuxer, double rel_seek_secs, int flags) in->seeking = true; in->seek_flags = flags; in->seek_pts = rel_seek_secs; + if ((flags & SEEK_ABSOLUTE) && !(flags & SEEK_FACTOR)) + in->seek_pts = MP_ADD_PTS(in->seek_pts, -in->ts_offset); if (!in->threading) execute_seek(in); @@ -1423,6 +1433,8 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg) r->ts_duration = MPMAX(0, r->ts_range[1] - r->ts_range[0]); if (!num_packets || in->seeking) r->ts_duration = 0; + r->ts_range[0] = MP_ADD_PTS(r->ts_range[0], in->ts_offset); + r->ts_range[1] = MP_ADD_PTS(r->ts_range[1], in->ts_offset); return DEMUXER_CTRL_OK; } } diff --git a/demux/demux.h b/demux/demux.h index 7823d50233..96717d5eeb 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -271,6 +271,7 @@ bool demux_cancel_test(struct demuxer *demuxer); void demux_flush(struct demuxer *demuxer); int demux_seek(struct demuxer *demuxer, double rel_seek_secs, int flags); void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled); +void demux_set_ts_offset(struct demuxer *demuxer, double offset); int demux_control(struct demuxer *demuxer, int cmd, void *arg); diff --git a/demux/demux_disc.c b/demux/demux_disc.c index a816a74fa8..b994b3cf11 100644 --- a/demux/demux_disc.c +++ b/demux/demux_disc.c @@ -93,17 +93,16 @@ static void add_dvd_streams(demuxer_t *demuxer) // emulate the extradata struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS; - csp.int_bits_in = 8; - csp.int_bits_out = 8; struct mp_cmat cmatrix; - mp_get_yuv2rgb_coeffs(&csp, &cmatrix); + mp_get_csp_matrix(&csp, &cmatrix); char *s = talloc_strdup(sh, ""); s = talloc_asprintf_append(s, "palette: "); for (int i = 0; i < 16; i++) { int color = info.palette[i]; - int c[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff}; - mp_map_int_color(&cmatrix, 8, c); + int y[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff}; + int c[3]; + mp_map_fixp_color(&cmatrix, 8, y, 8, c); color = (c[2] << 16) | (c[1] << 8) | c[0]; if (i != 0) diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index eeed7a1b9a..2d12f5cfa2 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -612,6 +612,10 @@ static void handle_stream(demuxer_t *demuxer, int i) sh_sub->frame_based = 23.976; } } + + if (matches_avinputformat_name(priv, "ass")) + sh_sub->is_utf8 = true; + break; } case AVMEDIA_TYPE_ATTACHMENT: { diff --git a/demux/demux_libass.c b/demux/demux_libass.c deleted file mode 100644 index ef09f042ed..0000000000 --- a/demux/demux_libass.c +++ /dev/null @@ -1,111 +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: 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 "options/options.h" -#include "common/msg.h" -#include "misc/charset_conv.h" -#include "stream/stream.h" -#include "demux.h" - -#define PROBE_SIZE (8 * 1024) - -static void message_callback(int level, const char *format, va_list va, void *ctx) -{ - // ignore -} - -static int d_check_file(struct demuxer *demuxer, enum demux_check check) -{ - const char *user_cp = demuxer->opts->sub_cp; - struct stream *s = demuxer->stream; - struct mp_log *log = demuxer->log; - - if (!demuxer->params || !demuxer->params->expect_subtitle) - return -1; - - if (check >= DEMUX_CHECK_UNSAFE) { - ASS_Library *lib = ass_library_init(); - if (!lib) - return -1; - ass_set_message_cb(lib, message_callback, NULL); - - // 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(log, 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); - bool ok = !!track; - if (cbuf.start != buf.start) - talloc_free(cbuf.start); - talloc_free(buf.start); - if (track) - ass_free_track(track); - ass_library_done(lib); - if (!ok) - return -1; - } - - // Actually load the full thing. - - bstr buf = stream_read_complete(s, NULL, 100000000); - if (!buf.start) { - MP_ERR(demuxer, "Refusing to load subtitle file " - "larger than 100 MB: %s\n", demuxer->filename); - return -1; - } - bstr cbuf = mp_charset_guess_and_conv_to_utf8(log, buf, user_cp, - MP_ICONV_VERBOSE); - if (cbuf.start == NULL) - cbuf = buf; - if (cbuf.start != buf.start) - talloc_free(buf.start); - talloc_steal(demuxer, cbuf.start); - - struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB); - sh->codec = "ass"; - sh->extradata = cbuf.start; - sh->extradata_size = cbuf.len; - - demuxer->seekable = true; - demuxer->fully_read = true; - - return 0; -} - -const struct demuxer_desc demuxer_desc_libass = { - .name = "libass", - .desc = "ASS/SSA subtitles (libass)", - .open = d_check_file, -}; diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c index 733e441feb..a11691b317 100644 --- a/demux/demux_mkv.c +++ b/demux/demux_mkv.c @@ -2659,8 +2659,7 @@ static int create_index_until(struct demuxer *demuxer, uint64_t timecode) mkv_index_t *index = get_highest_index_entry(demuxer); if (!index || index->timecode * mkv_d->tc_scale < timecode) { - if (index) - stream_seek(s, index->filepos); + stream_seek(s, index ? index->filepos : mkv_d->cluster_start); MP_VERBOSE(demuxer, "creating index until TC %" PRIu64 "\n", timecode); for (;;) { int res; diff --git a/demux/demux_subreader.c b/demux/demux_subreader.c deleted file mode 100644 index ecceb5d13d..0000000000 --- a/demux/demux_subreader.c +++ /dev/null @@ -1,837 +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 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/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <dirent.h> - -#include <libavutil/common.h> -#include <libavutil/avstring.h> - -#include "config.h" -#include "common/msg.h" -#include "common/common.h" -#include "options/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_SSA 3 -#define SUB_SUBVIEWER2 4 -#define SUB_SUBRIP09 5 -#define SUB_MPL2 6 - -#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 { - struct mp_log *log; - 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 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; -} - -static void demux_packet_list_seek(struct demux_packet **pkts, int num_pkts, - int *current, double 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; -} - -static 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; -} - -static int eol(char p) { - return p=='\r' || p=='\n' || p=='\0'; -} - -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(struct readline_args *arg, 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_WARN(arg, "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(args, 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(args, 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; - if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) continue; - current->start = a1*360000+a2*6000+a3*100+a4; - current->end = b1*360000+b2*6000+b3*100+b4; - - if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; - - p=q=line; - for (current->lines=1; current->lines < SUB_MAX_TEXT; current->lines++) { - for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' && strncmp(p,"[br]",4); p++,len++); - current->text[current->lines-1]=malloc (len+1); - if (!current->text[current->lines-1]) return ERR; - strncpy (current->text[current->lines-1], q, len); - current->text[current->lines-1][len]='\0'; - if (!*p || *p=='\r' || *p=='\n') break; - if (*p=='|') p++; - else while (*p++!=']'); - } - break; - } - return current; -} - -static subtitle *sub_read_line_subviewer(stream_t *st, subtitle *current, - struct readline_args *args) -{ - int utf16 = args->utf16; - int a1, a2, a3, a4, b1, b2, b3, b4, j = 0; - - while (!current->text[0]) { - char line[LINE_LEN + 1], full_line[LINE_LEN + 1]; - int i; - - /* Parse SubRip header */ - if (!stream_read_line(st, line, LINE_LEN, utf16)) - return NULL; - if (sscanf(line, "%d:%d:%d%*1[,.:]%d --> %d:%d:%d%*1[,.:]%d", - &a1, &a2, &a3, &a4, &b1, &b2, &b3, &b4) < 8) - continue; - - current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4 / 10; - current->end = b1 * 360000 + b2 * 6000 + b3 * 100 + b4 / 10; - - /* Concatenate lines */ - full_line[0] = 0; - for (i = 0; i < SUB_MAX_TEXT; i++) { - int blank = 1, len = 0; - char *p; - - if (!stream_read_line(st, line, LINE_LEN, utf16)) - break; - - for (p = line; *p != '\n' && *p != '\r' && *p; p++, len++) - if (*p != ' ' && *p != '\t') - blank = 0; - - if (blank) - break; - - *p = 0; - - if (!(j + 1 + len < sizeof(full_line) - 1)) - break; - - if (j != 0) - full_line[j++] = '\n'; - snprintf(&full_line[j], sizeof(full_line) - j, "%s", line); - full_line[LINE_LEN] = '\0'; - j += len; - } - - if (full_line[0]) { - current->text[0] = strdup(full_line); - current->lines = 1; - } - } - return current; -} - -static subtitle *sub_read_line_subviewer2(stream_t *st,subtitle *current, - struct readline_args *args) -{ - int utf16 = args->utf16; - char line[LINE_LEN+1]; - int a1,a2,a3,a4; - char *p=NULL; - int i,len; - - while (!current->text[0]) { - if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; - if (line[0]!='{') - continue; - if ((len=sscanf (line, "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4)) < 4) - continue; - current->start = a1*360000+a2*6000+a3*100+a4/10; - for (i=0; i<SUB_MAX_TEXT;) { - if (!stream_read_line (st, line, LINE_LEN, utf16)) break; - if (line[0]=='}') break; - len=0; - for (p=line; *p!='\n' && *p!='\r' && *p; ++p,++len); - if (len) { - current->text[i]=malloc (len+1); - if (!current->text[i]) return ERR; - strncpy (current->text[i], line, len); current->text[i][len]='\0'; - ++i; - } else { - break; - } - } - current->lines=i; - } - return current; -} - -static subtitle *sub_read_line_ssa(stream_t *st,subtitle *current, - struct readline_args *args) -{ - /* Instead of hardcoding the expected fields and their order on - * each dialogue line, this code should parse the "Format: " line - * which lists the fields used in the script. As is, this may not - * work correctly with all scripts. - */ - - int utf16 = args->utf16; - int comma; - - int hour1, min1, sec1, hunsec1, - hour2, min2, sec2, hunsec2, nothing; - int num; - - char line[LINE_LEN+1], - line3[LINE_LEN+1], - *line2; - char *tmp; - - do { - if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; - } while (sscanf (line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d" - "%[^\n\r]", ¬hing, - &hour1, &min1, &sec1, &hunsec1, - &hour2, &min2, &sec2, &hunsec2, - line3) < 9 - && - sscanf (line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d" - "%[^\n\r]", ¬hing, - &hour1, &min1, &sec1, &hunsec1, - &hour2, &min2, &sec2, &hunsec2, - line3) < 9 ); - - line2=strchr(line3, ','); - if (!line2) return NULL; - - for (comma = 3; comma < 9; comma ++) - if (!(line2 = strchr(++line2, ','))) - return NULL; - line2++; - - current->lines=0;num=0; - current->start = 360000*hour1 + 6000*min1 + 100*sec1 + hunsec1; - current->end = 360000*hour2 + 6000*min2 + 100*sec2 + hunsec2; - - while (((tmp=strstr(line2, "\\n")) != NULL) || ((tmp=strstr(line2, "\\N")) != NULL) ){ - current->text[num]=malloc(tmp-line2+1); - strncpy (current->text[num], line2, tmp-line2); - current->text[num][tmp-line2]='\0'; - line2=tmp+2; - num++; - current->lines++; - if (current->lines >= SUB_MAX_TEXT) return current; - } - - current->text[num]=strdup(line2); - current->lines++; - - return current; -} - -static subtitle *sub_read_line_subrip09(stream_t *st,subtitle *current, - struct readline_args *args) -{ - int utf16 = args->utf16; - char line[LINE_LEN+1]; - int a1,a2,a3; - int len; - -retry: - while (1) { - // try to locate next subtitle - if (!stream_read_line (st, line, LINE_LEN, utf16)) - return NULL; - if (!((len=sscanf (line, "[%d:%d:%d]",&a1,&a2,&a3)) < 3)) - break; - } - - current->start = a1*360000+a2*6000+a3*100; - - if (!args->previous_sub_end) - args->previous_sub_end = (current->start) ? current->start - 1 : 0; - - if (!stream_read_line (st, line, LINE_LEN, utf16)) - return NULL; - - current->text[0]=""; // just to be sure that string is clear - - if (set_multiline_text(args, current, line, 0) == ERR) - return ERR; - - if (!strlen(current->text[0]) && current->lines <= 1) - goto retry; - - return current; -} - -static int sub_autodetect (stream_t* st, int *uses_time, int utf16) { - char line[LINE_LEN+1]; - int i,j=0; - - while (j < 100) { - j++; - if (!stream_read_line (st, line, LINE_LEN, utf16)) - return SUB_INVALID; - - if (sscanf (line, "{%d}{%d}", &i, &i)==2) - {*uses_time=0;return SUB_MICRODVD;} - if (sscanf (line, "{%d}{}", &i)==1) - {*uses_time=0;return SUB_MICRODVD;} - if (sscanf (line, "[%d][%d]", &i, &i)==2) - {*uses_time=1;return SUB_MPL2;} - if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8) - {*uses_time=1;return SUB_SUBRIP;} - if (sscanf (line, "%d:%d:%d%*1[,.:]%d --> %d:%d:%d%*1[,.:]%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) - {*uses_time=1;return SUB_SUBVIEWER;} - if (sscanf (line, "{T %d:%d:%d:%d",&i, &i, &i, &i)==4) - {*uses_time=1;return SUB_SUBVIEWER2;} - if (!memcmp(line, "Dialogue: Marked", 16)) - {*uses_time=1; return SUB_SSA;} - if (!memcmp(line, "Dialogue: ", 10)) - {*uses_time=1; return SUB_SSA;} - if (sscanf (line, "[%d:%d:%d]", &i, &i, &i)==3) - {*uses_time=1;return SUB_SUBRIP09;} - } - - return SUB_INVALID; // too many bad lines -} - -struct subreader { - subtitle * (*read)(stream_t *st, subtitle *dest, - struct readline_args *args); - void (*post)(subtitle *dest); - const char *name; - const char *codec_name; - struct readline_args args; -}; - -static void adjust_subs_time(struct subreader *srp, subtitle* sub, - float subtime, float fps, - float sub_fps, int block, - int sub_num, int sub_uses_time) { - int n,m; - subtitle* nextsub; - int i = sub_num; - unsigned long subfms = (sub_uses_time ? 100 : fps) * subtime; - - n=m=0; - if (i) for (;;){ - if (sub->end <= sub->start){ - sub->end = sub->start + subfms; - m++; - n++; - } - if (!--i) break; - nextsub = sub + 1; - if(block){ - if (sub->end >= nextsub->start){ - sub->end = nextsub->start - 1; - if (sub->end - sub->start > subfms) - sub->end = sub->start + subfms; - if (!m) - n++; - } - } - - sub = nextsub; - m = 0; - } - if (n) MP_VERBOSE(&srp->args, "Adjusted %d subtitle(s).\n", n); -} - -static bool subreader_autodetect(stream_t *fd, struct MPOpts *opts, - struct mp_log *log, struct subreader *out) -{ - static const struct subreader sr[]= - { - { sub_read_line_microdvd, NULL, "microdvd", "microdvd" }, - { sub_read_line_subrip, NULL, "subviewer" }, - { sub_read_line_subviewer, NULL, "subrip", "subrip" }, - { sub_read_line_ssa, NULL, "ssa", "ass-text" }, - { sub_read_line_subviewer2, NULL, "subviewer2" }, - { sub_read_line_subrip09, NULL, "subrip9" }, - { sub_read_line_mpl2, NULL, "mpl2" } - }; - const struct subreader *srp; - - int sub_format = SUB_INVALID; - int utf16; - int uses_time = 0; - for (utf16 = 0; sub_format == SUB_INVALID && utf16 < 3; utf16++) { - sub_format=sub_autodetect (fd, &uses_time, utf16); - stream_seek(fd,0); - } - utf16--; - - if (sub_format==SUB_INVALID) { - mp_verbose(log, "Could not determine file format\n"); - return false; - } - srp=sr+sub_format; - mp_verbose(log, "Detected subtitle file format: %s\n", srp->name); - - *out = *srp; - out->args = (struct readline_args) { - .log = log, - .utf16 = utf16, - .opts = opts, - .sub_slacktime = 20000, //20 sec - .mpsub_multiplier = (uses_time ? 100.0 : 1.0), - .uses_time = uses_time, - }; - - return true; -} - -static sub_data* sub_read_file(stream_t *fd, struct subreader *srp) -{ - struct MPOpts *opts = fd->opts; - float fps = 23.976; - int n_max, i, j; - subtitle *first, *sub, *return_sub, *alloced_sub = NULL; - sub_data *subt_data; - int sub_num = 0, sub_errs = 0; - struct readline_args args = srp->args; - - sub_num=0;n_max=32; - first=malloc(n_max*sizeof(subtitle)); - if (!first) - abort(); - - alloced_sub = - sub = malloc(sizeof(subtitle)); - //This is to deal with those formats (AQT & Subrip) which define the end of a subtitle - //as the beginning of the following - args.previous_sub_end = 0; - while(1){ - if(sub_num>=n_max){ - n_max+=16; - first=realloc(first,n_max*sizeof(subtitle)); - } - memset(sub, '\0', sizeof(subtitle)); - sub=srp->read(fd, sub, &args); - if(!sub) break; // EOF - - if ( sub == ERR ) - { - free(first); - free(alloced_sub); - return NULL; - } - // Apply any post processing that needs recoding first - if ((sub!=ERR) && srp->post) srp->post(sub); - if(!sub_num || (first[sub_num - 1].start <= sub->start)){ - first[sub_num].start = sub->start; - first[sub_num].end = sub->end; - first[sub_num].lines = sub->lines; - first[sub_num].alignment = sub->alignment; - for(i = 0; i < sub->lines; ++i){ - first[sub_num].text[i] = sub->text[i]; - } - if (args.previous_sub_end){ - first[sub_num - 1].end = args.previous_sub_end; - args.previous_sub_end = 0; - } - } else { - for(j = sub_num - 1; j >= 0; --j){ - first[j + 1].start = first[j].start; - first[j + 1].end = first[j].end; - first[j + 1].lines = first[j].lines; - first[j + 1].alignment = first[j].alignment; - for(i = 0; i < first[j].lines; ++i){ - first[j + 1].text[i] = first[j].text[i]; - } - if(!j || (first[j - 1].start <= sub->start)){ - first[j].start = sub->start; - first[j].end = sub->end; - first[j].lines = sub->lines; - first[j].alignment = sub->alignment; - for(i = 0; i < SUB_MAX_TEXT; ++i){ - first[j].text[i] = sub->text[i]; - } - if (args.previous_sub_end){ - first[j].end = first[j - 1].end; - first[j - 1].end = args.previous_sub_end; - args.previous_sub_end = 0; - } - break; - } - } - } - if(sub==ERR) ++sub_errs; else ++sub_num; // Error vs. Valid - } - - free(alloced_sub); - -// printf ("Subtitle format %s time.\n", uses_time?"uses":"doesn't use"); - MP_VERBOSE(&srp->args, "Read %i subtitles, %i bad line(s).\n", - sub_num, sub_errs); - - if(sub_num<=0){ - free(first); - return NULL; - } - - adjust_subs_time(srp, first, 6.0, fps, opts->sub_fps, 1, sub_num |