From d064c69e1c958514c3ef4d7871bcd92c8ff92de9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 11 Jun 2013 12:07:04 +0200 Subject: demux: don't require fill_buffer callback --- demux/demux.c | 2 +- demux/demux_cue.c | 6 ------ demux/demux_edl.c | 6 ------ 3 files changed, 1 insertion(+), 13 deletions(-) (limited to 'demux') diff --git a/demux/demux.c b/demux/demux.c index 5de6b84745..d9b41e2c99 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -599,7 +599,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: diff --git a/demux/demux_cue.c b/demux/demux_cue.c index 31a3e00e40..04cc3229f3 100644 --- a/demux/demux_cue.c +++ b/demux/demux_cue.c @@ -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..71d75e7f38 100644 --- a/demux/demux_edl.c +++ b/demux/demux_edl.c @@ -40,11 +40,6 @@ static int try_open_file(struct demuxer *demuxer) 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, }; -- cgit v1.2.3 From 4f5e12136de717896bf322e75d42de1af09e1c3e Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 11 Jun 2013 12:16:42 +0200 Subject: stream: remove padding parameter from stream_read_complete() Seems like a completely unnecessary complication. Instead, always add a 1 byte padding (could be extended if a caller needs it), and clear it. Also add some documentation. There was some, but it was outdated and incomplete. --- demux/demux_cue.c | 2 +- demux/demux_edl.c | 2 +- demux/demux_mf.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'demux') diff --git a/demux/demux_cue.c b/demux/demux_cue.c index 04cc3229f3..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 })) diff --git a/demux/demux_edl.c b/demux/demux_edl.c index 71d75e7f38..c35137ffb2 100644 --- a/demux/demux_edl.c +++ b/demux/demux_edl.c @@ -34,7 +34,7 @@ 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; 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); -- cgit v1.2.3 From a70d575291d48289669ee8989e0597a94189dd8d Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 11 Jun 2013 21:39:54 +0200 Subject: sub: preload external text subtitles If a subtitle is external, read it completely and add all subtitle events in advance when the subtitle track is selected. This is done for text subtitles only. (Note that subreader.c and subtitles loaded with libass are different and don't have anything to do with this commit.) --- demux/demux.c | 38 +++++++++++++++++++++++++++++++++++++- demux/demux.h | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'demux') diff --git a/demux/demux.c b/demux/demux.c index d9b41e2c99..23c4a28706 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -40,7 +40,8 @@ #include "audio/format.h" -#include "libavcodec/avcodec.h" +#include + #if MP_INPUT_BUFFER_PADDING_SIZE < FF_INPUT_BUFFER_PADDING_SIZE #error MP_INPUT_BUFFER_PADDING_SIZE is too small! #endif @@ -183,6 +184,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); diff --git a/demux/demux.h b/demux/demux.h index 9ec6d0c6f0..a82fbea91a 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -291,6 +291,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) -- cgit v1.2.3 From db2e1ef4d210f5a8a4a2555d0a78b0a4dea103ec Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 21 Jun 2013 00:26:05 +0200 Subject: Move/rename subreader.c --- demux/demux_subreader.c | 1378 +++++++++++++++++++++++++++++++++++++++++++++++ demux/subreader.h | 79 +++ 2 files changed, 1457 insertions(+) create mode 100644 demux/demux_subreader.c create mode 100644 demux/subreader.h (limited to 'demux') diff --git a/demux/demux_subreader.c b/demux/demux_subreader.c new file mode 100644 index 0000000000..befac689a4 --- /dev/null +++ b/demux/demux_subreader.c @@ -0,0 +1,1378 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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 +#endif + +#define ERR ((void *) -1) + +#ifdef CONFIG_ICONV +#include +#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) " 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, "
", 4)) { + sami_add_line(current, text, &p); + s += 4; + } + else if ((*s == '{') && !args->opts->sub_no_text_pp) { 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 */ + 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') && !args->opts->sub_no_text_pp) { + 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; + 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; + + /* Concat 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'; + strcpy(&full_line[j], line); + 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; itext[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_vplayer(stream_t *st,subtitle *current, + struct readline_args *args) +{ + int utf16 = args->utf16; + char line[LINE_LEN+1]; + int a1,a2,a3; + char *p=NULL, separator; + int len,plen; + + while (!current->text[0]) { + if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; + if ((len=sscanf (line, "%d:%d:%d%c%n",&a1,&a2,&a3,&separator,&plen)) < 4) + continue; + + if (!(current->start = a1*360000+a2*6000+a3*100)) + continue; + /* removed by wodzu + p=line; + // finds the body of the subtitle + for (i=0; i<3; i++){ + p=strchr(p,':'); + if (p==NULL) break; + ++p; + } + if (p==NULL) { + printf("SUB: Skipping incorrect subtitle line!\n"); + continue; + } + */ + // by wodzu: hey! this time we know what length it has! what is + // that magic for? it can't deal with space instead of third + // colon! look, what simple it can be: + p = &line[ plen ]; + + if (*p!='|') { + // + return set_multiline_text(current, p, 0); + } + } + return current; +} + +static subtitle *sub_read_line_rt(stream_t *st,subtitle *current, + struct readline_args *args) +{ + int utf16 = args->utf16; + + //TODO: This format uses quite rich (sub/super)set of xhtml + // I couldn't check it since DTD is not included. + // WARNING: full XML parses can be required for proper parsing + char line[LINE_LEN+1]; + int a1,a2,a3,a4,b1,b2,b3,b4; + char *p=NULL,*next=NULL; + int len,plen; + + while (!current->text[0]) { + if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; + //TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0 + //to describe the same moment in time. Maybe there are even more formats in use. + //if ((len=sscanf (line, "