diff options
Diffstat (limited to 'src/libsputext')
-rw-r--r-- | src/libsputext/Makefile.am | 14 | ||||
-rw-r--r-- | src/libsputext/demux_sputext.c | 1493 | ||||
-rw-r--r-- | src/libsputext/xine_sputext_decoder.c | 1215 |
3 files changed, 0 insertions, 2722 deletions
diff --git a/src/libsputext/Makefile.am b/src/libsputext/Makefile.am deleted file mode 100644 index ad6c1b776..000000000 --- a/src/libsputext/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -include $(top_builddir)/misc/Makefile.plugins -include $(top_srcdir)/misc/Makefile.common - -AM_LDFLAGS = $(xineplug_ldflags) - -xineplug_LTLIBRARIES = xineplug_decode_sputext.la xineplug_dmx_sputext.la - -xineplug_dmx_sputext_la_SOURCES = demux_sputext.c -xineplug_dmx_sputext_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) -xineplug_dmx_sputext_la_CFLAGS = $(VISIBILITY_FLAG) - -xineplug_decode_sputext_la_SOURCES = xine_sputext_decoder.c -xineplug_decode_sputext_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) -xineplug_decode_sputext_la_CFLAGS = $(VISIBILITY_FLAG) diff --git a/src/libsputext/demux_sputext.c b/src/libsputext/demux_sputext.c deleted file mode 100644 index dd5958044..000000000 --- a/src/libsputext/demux_sputext.c +++ /dev/null @@ -1,1493 +0,0 @@ -/* - * Copyright (C) 2000-2003 the xine project - * - * This file is part of xine, a free video player. - * - * xine 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. - * - * xine 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * code based on old libsputext/xine_decoder.c - * - * code based on mplayer module: - * - * Subtitle reader with format autodetection - * - * Written by laaz - * Some code cleanup & realloc() by A'rpi/ESP-team - * dunnowhat sub format by szabi - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#define LOG_MODULE "demux_sputext" -#define LOG_VERBOSE -/* -#define LOG -*/ - -#include "xine_internal.h" -#include "xineutils.h" -#include "../demuxers/demux.h" - -#define ERR (void *)-1 -#define SUB_MAX_TEXT 5 -#define SUB_BUFSIZE 1024 -#define LINE_LEN 1000 -#define LINE_LEN_QUOT "1000" - -/* - * Demuxer typedefs - */ - -typedef struct { - - int lines; - - long start; /* csecs */ - long end; /* csecs */ - - char *text[SUB_MAX_TEXT]; - -} subtitle_t; - - -typedef struct { - - demux_plugin_t demux_plugin; - xine_stream_t *stream; - input_plugin_t *input; - - int status; - - char buf[SUB_BUFSIZE]; - off_t buflen; - - float mpsub_position; - - int uses_time; - int errs; - subtitle_t *subtitles; - int num; /* number of subtitle structs */ - int cur; /* current subtitle */ - int format; /* constants see below */ - char next_line[SUB_BUFSIZE]; /* a buffer for next line read from file */ - -} demux_sputext_t; - -typedef struct demux_sputext_class_s { - - demux_class_t demux_class; - - int max_timeout; /* default timeout of hidding subtitles */ - -} demux_sputext_class_t; - -/* - * Demuxer code start - */ - -#define FORMAT_UNKNOWN -1 -#define FORMAT_MICRODVD 0 -#define FORMAT_SUBRIP 1 -#define FORMAT_SUBVIEWER 2 -#define FORMAT_SAMI 3 -#define FORMAT_VPLAYER 4 -#define FORMAT_RT 5 -#define FORMAT_SSA 6 /* Sub Station Alpha */ -#define FORMAT_PJS 7 -#define FORMAT_MPSUB 8 -#define FORMAT_AQTITLE 9 -#define FORMAT_JACOBSUB 10 -#define FORMAT_SUBVIEWER2 11 -#define FORMAT_SUBRIP09 12 -#define FORMAT_MPL2 13 /*Mplayer sub 2 ?*/ - -static int eol(char p) { - return (p=='\r' || p=='\n' || p=='\0'); -} - -static inline void trail_space(char *s) { - while (isspace(*s)) { - char *copy = s; - do { - copy[0] = copy[1]; - copy++; - } while(*copy); - } - size_t i = strlen(s) - 1; - while (i > 0 && isspace(s[i])) - s[i--] = '\0'; -} - -/* - * Reimplementation of fgets() using the input->read() method. - */ -static char *read_line_from_input(demux_sputext_t *this, char *line, off_t len) { - off_t nread = 0; - - if ((len - this->buflen) > 512 && len < SUB_BUFSIZE) { - if((nread = this->input->read(this->input, - &this->buf[this->buflen], len - this->buflen)) < 0) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "read failed.\n"); - return NULL; - } - } - - this->buflen += nread; - this->buf[this->buflen] = '\0'; - - char *s = strchr(this->buf, '\n'); - - if (line && (s || this->buflen)) { - - size_t linelen = s ? (s - this->buf) + 1 : this->buflen; - - memcpy(line, this->buf, linelen); - line[linelen] = '\0'; - - memmove(this->buf, &this->buf[linelen], SUB_BUFSIZE - linelen); - this->buflen -= linelen; - - return line; - } - - return NULL; -} - - -static subtitle_t *sub_read_line_sami(demux_sputext_t *this, subtitle_t *current) { - - static char line[LINE_LEN + 1]; - static char *s = NULL; - char text[LINE_LEN + 1]; - - char *p = NULL; - current->lines = current->start = 0; - current->end = -1; - int state = 0; - - /* read the first line */ - if (!s) - if (!(s = read_line_from_input(this, line, LINE_LEN))) return 0; - - do { - switch (state) { - - case 0: /* find "START=" */ - s = strstr (s, "Start="); - if (s) { - current->start = strtol (s + 6, &s, 0) / 10; - state = 1; continue; - } - break; - - case 1: /* find "<P" */ - if ((s = strstr (s, "<P"))) { s += 2; state = 2; continue; } - break; - - case 2: /* find ">" */ - if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue; } - break; - - case 3: /* get all text until '<' appears */ - if (*s == '\0') { break; } - else if (*s == '<') { state = 4; } - else if (!strncasecmp (s, " ", 6)) { *p++ = ' '; s += 6; } - else if (*s == '\r') { s++; } - else if (!strncasecmp (s, "<br>", 4) || *s == '\n') { - *p = '\0'; p = text; trail_space (text); - if (text[0] != '\0') - current->text[current->lines++] = strdup (text); - if (*s == '\n') s++; else s += 4; - } - else *p++ = *s++; - continue; - - case 4: /* get current->end or skip <TAG> */ - { - char *q = strstr (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; - } - - /* read next line */ - if (state != 99 && !(s = read_line_from_input (this, line, LINE_LEN))) - return 0; - - } while (state != 99); - - return current; -} - - -static char *sub_readtext(char *source, char **dest) { - size_t len=0; - char *p=source; - - while ( !eol(*p) && *p!= '|' ) { - p++,len++; - } - - *dest = strndup(source, len); - - while (*p=='\r' || *p=='\n' || *p=='|') - p++; - - if (*p) return p; /* not-last text field */ - else return NULL; /* last text field */ -} - -static subtitle_t *sub_read_line_microdvd(demux_sputext_t *this, subtitle_t *current) { - - char line[LINE_LEN + 1]; - char line2[LINE_LEN + 1]; - - memset (current, 0, sizeof(subtitle_t)); - - current->end=-1; - do { - if (!read_line_from_input (this, line, LINE_LEN)) return NULL; - } while ((sscanf (line, "{%ld}{}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), line2) !=2) && - (sscanf (line, "{%ld}{%ld}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), &(current->end),line2) !=3) - ); - - char *p=line2; - - char *next=p; - size_t i=0; - while ((next =sub_readtext (next, &(current->text[i])))) { - if (current->text[i]==ERR) return ERR; - i++; - if (i>=SUB_MAX_TEXT) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; - } - } - current->lines= ++i; - - return current; -} - -static subtitle_t *sub_read_line_subviewer(demux_sputext_t *this, subtitle_t *current) { - char line[LINE_LEN + 1]; - - memset (current, 0, sizeof(subtitle_t)); - - while (1) { - if (!read_line_from_input(this, line, LINE_LEN)) return NULL; - - { - int a1,a2,a3,a4,b1,b2,b3,b4; - if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) { - 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 (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - - char *p = line, *q = line; - for (current->lines=1; current->lines <= SUB_MAX_TEXT; current->lines++) { - size_t len; - for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' && strncasecmp(p,"[br]",4); p++,len++); - current->text[current->lines-1] = strndup(q, len); - if (!current->text[current->lines-1]) return ERR; - if (!*p || *p=='\r' || *p=='\n') break; - if (*p=='[') while (*p++!=']'); - if (*p=='|') p++; - } - if (current->lines > SUB_MAX_TEXT) current->lines = SUB_MAX_TEXT; - break; - } - return current; -} - -static subtitle_t *sub_read_line_subrip(demux_sputext_t *this,subtitle_t *current) { - memset(current,0,sizeof(subtitle_t)); - - { - int a1,a2,a3,a4,b1,b2,b3,b4,i; - do { - char line[LINE_LEN + 1]; - if(!read_line_from_input(this,line,LINE_LEN)) - return NULL; - i = sscanf(line,"%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4); - } while(i < 8); - current->start = a1*360000+a2*6000+a3*100+a4/10; - current->end = b1*360000+b2*6000+b3*100+b4/10; - } - - int i=0; - int end_sub=0; - do { - char *p; /* pointer to the curently read char */ - char line[LINE_LEN + 1]; - char temp_line[SUB_BUFSIZE] = { 0, }; /* subtitle line that will be transfered to current->text[i] */ - size_t temp_index; /* ... and its index wich 'points' to the first EMPTY place -> last read char is at temp_index-1 if temp_index>0 */ - if(!read_line_from_input(this,line,LINE_LEN)) { - if(i) - break; /* if something was read, transmit it */ - else - return NULL; /* if not, repport EOF */ - } - for(temp_index=0,p=line;*p!='\0' && !end_sub && temp_index<SUB_BUFSIZE && i<SUB_MAX_TEXT;p++) { - switch(*p) { - case '\\': - if(*(p+1)=='N' || *(p+1)=='n') { - temp_line[temp_index++]='\0'; /* end of curent line */ - p++; - } else - temp_line[temp_index++]=*p; - break; - case '\r': /* just ignore '\r's */ - break; - case '\n': - temp_line[temp_index++]='\0'; - break; - default: - temp_line[temp_index++]=*p; - break; - } - if(temp_index>0) { - if(temp_index==SUB_BUFSIZE) - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many characters in a subtitle line\n"); - if(temp_line[temp_index-1]=='\0' || temp_index==SUB_BUFSIZE) { - if(temp_index>1) { /* more than 1 char (including '\0') -> that is a valid one */ - /* temp_index<=SUB_BUFSIZE is always true here */ - current->text[i] = strndup(temp_line, temp_index); - if(!current->text[i]) - return ERR; - i++; - temp_index=0; - } else - end_sub=1; - } - } - } - } while(i<SUB_MAX_TEXT && !end_sub); - if(i>=SUB_MAX_TEXT) - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; -} - -static subtitle_t *sub_read_line_vplayer(demux_sputext_t *this,subtitle_t *current) { - char line[LINE_LEN + 1]; - - memset (current, 0, sizeof(subtitle_t)); - - while (!current->text[0]) { - if( this->next_line[0] == '\0' ) { /* if the buffer is empty.... */ - if( !read_line_from_input(this, line, LINE_LEN) ) return NULL; - } else { - /* ... get the current line from buffer. */ - strncpy( line, this->next_line, LINE_LEN); - line[LINE_LEN] = '\0'; /* I'm scared. This makes me feel better. */ - this->next_line[0] = '\0'; /* mark the buffer as empty. */ - } - /* Initialize buffer with next line */ - if( ! read_line_from_input( this, this->next_line, LINE_LEN) ) { - this->next_line[0] = '\0'; - return NULL; - } - - { - int a1,a2,a3,b1,b2,b3; - if( (sscanf( line, "%d:%d:%d:", &a1, &a2, &a3) < 3) || - (sscanf( this->next_line, "%d:%d:%d:", &b1, &b2, &b3) < 3) ) - continue; - current->start = a1*360000+a2*6000+a3*100; - current->end = b1*360000+b2*6000+b3*100; - } - - if ((current->end - current->start) > LINE_LEN) - current->end = current->start + LINE_LEN; /* not too long though. */ - /* teraz czas na wkopiowanie stringu */ - char *p=line; - /* finds the body of the subtitle_t */ - { - int i; - for (i=0; i<3; i++){ - char *p2=strchr( p, ':'); - if( p2 == NULL ) break; - p=p2+1; - } - } - - char *next=p; - int i=0; - while( (next = sub_readtext( next, &(current->text[i]))) ) { - if (current->text[i]==ERR) - return ERR; - i++; - if (i>=SUB_MAX_TEXT) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; - } - } - current->lines=++i; - } - return current; -} - -static subtitle_t *sub_read_line_rt(demux_sputext_t *this,subtitle_t *current) { - /* - * 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 - */ - memset (current, 0, sizeof(subtitle_t)); - - while (!current->text[0]) { - char line[LINE_LEN + 1]; - if (!read_line_from_input(this, line, LINE_LEN)) return NULL; - - char *p = line; - /* - * 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. - */ - { - int a1,a2,a3,a4,b1,b2,b3,b4,len,plen; - if ((len=sscanf (line, "<Time Begin=\"%d:%d:%d.%d\" End=\"%d:%d:%d.%d\"",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4)) < 8) - - a1=a2=a3=a4=b1=b2=b3=b4=0; - if ( - ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&plen)) < 4) && - ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&b4,&plen)) < 5) && - /* ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&plen)) < 5) && */ - ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&b4,&plen)) < 6) && - ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d:%d.%d\" %*[Ee]nd=\"%d:%d:%d.%d\"%*[^<]<clear/>%n",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4,&plen)) < 8) - ) - continue; - - current->start = a1*360000+a2*6000+a3*100+a4/10; - current->end = b1*360000+b2*6000+b3*100+b4/10; - - p += plen; - } - - int i=0; - /* TODO: I don't know what kind of convention is here for marking multiline subs, maybe <br/> like in xml? */ - char *next = strstr(line,"<clear/>")+8;i=0; - while ((next =sub_readtext (next, &(current->text[i])))) { - if (current->text[i]==ERR) - return ERR; - i++; - if (i>=SUB_MAX_TEXT) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; - } - } - current->lines=i+1; - } - return current; -} - -static subtitle_t *sub_read_line_ssa(demux_sputext_t *this,subtitle_t *current) { - static int max_comma = 32; /* let's use 32 for the case that the */ - /* amount of commas increase with newer SSA versions */ - - int hour1, min1, sec1, hunsec1, hour2, min2, sec2, hunsec2, nothing; - char line3[LINE_LEN + 1]; - - { - char line[LINE_LEN + 1]; - do { - if (!read_line_from_input(this, line, LINE_LEN)) 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 ); - } - - char *line2=strchr(line3, ','); - if (!line2) - return NULL; - - int comma; - for (comma = 4; comma < max_comma; comma ++) { - char *tmp = line2; - if(!(tmp=strchr(++tmp, ','))) break; - if(*(++tmp) == ' ') break; - /* a space after a comma means we're already in a sentence */ - line2 = tmp; - } - - if(comma < max_comma)max_comma = comma; - /* eliminate the trailing comma */ - if(*line2 == ',') line2++; - - current->lines=0; - current->start = 360000*hour1 + 6000*min1 + 100*sec1 + hunsec1; - current->end = 360000*hour2 + 6000*min2 + 100*sec2 + hunsec2; - - int num=0; - char *tmp; - while (((tmp=strstr(line2, "\\n")) != NULL) || ((tmp=strstr(line2, "\\N")) != NULL) ){ - current->text[num] = strndup(line2, tmp-line2); - line2=tmp+2; - num++; - current->lines++; - if (current->lines >= SUB_MAX_TEXT) return current; - } - - current->text[num]=strdup(line2); - current->lines++; - - return current; -} - -/* Sylvain "Skarsnik" Colinet <scolinet@gmail.com> - * From MPlayer subreader.c : - * - * PJS subtitles reader. - * That's the "Phoenix Japanimation Society" format. - * I found some of them in http://www.scriptsclub.org/ (used for anime). - * The time is in tenths of second. - * - * by set, based on code by szabi (dunnowhat sub format ;-) - */ - -static subtitle_t *sub_read_line_pjs (demux_sputext_t *this, subtitle_t *current) { - char line[LINE_LEN + 1]; - char *s; - - memset (current, 0, sizeof(subtitle_t)); - - if (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - for (s = line; *s && isspace(*s); s++); - if (*s == 0) - return NULL; - if (sscanf (line, "%ld,%ld,", &(current->start), - &(current->end)) <2) - return ERR; - /* the files I have are in tenths of second */ - current->start *= 10; - current->end *= 10; - - /* walk to the beggining of the string */ - for (; *s; s++) if (*s==',') break; - if (*s) { - for (s++; *s; s++) if (*s==',') break; - if (*s) s++; - } - if (*s!='"') { - return ERR; - } - /* copy the string to the text buffer */ - char text[LINE_LEN + 1]; - char *d = NULL; - for (s++, d=text; *s && *s!='"'; s++, d++) - *d=*s; - *d=0; - current->text[0] = strdup(text); - current->lines = 1; - - return current; -} - -static subtitle_t *sub_read_line_mpsub (demux_sputext_t *this, subtitle_t *current) { - char line[LINE_LEN + 1]; - - { - float a,b; - do { - if (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - } while (sscanf (line, "%f %f", &a, &b) !=2); - - this->mpsub_position += (a*100.0); - current->start = (int) this->mpsub_position; - this->mpsub_position += (b*100.0); - current->end = (int) this->mpsub_position; - } - - int num = 0; - while (num < SUB_MAX_TEXT) { - if (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - - char *p=line; - while (isspace(*p)) - p++; - - if (eol(*p) && num > 0) - return current; - - if (eol(*p)) - return NULL; - - char *q; - for (q=p; !eol(*q); q++); - *q='\0'; - if (*p) { - current->text[num]=strdup(p); - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, ">%s<\n",p); - current->lines = ++num; - } else { - return num ? current : NULL; - } - } - - return NULL; -} - -static subtitle_t *sub_read_line_aqt (demux_sputext_t *this, subtitle_t *current) { - memset (current, 0, sizeof(subtitle_t)); - - while (1) { - char line[LINE_LEN + 1]; - /* try to locate next subtitle_t */ - if (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - if (!(sscanf (line, "-->> %ld", &(current->start)) <1)) - break; - } - - char line[LINE_LEN + 1]; - if (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - - sub_readtext((char *) &line,¤t->text[0]); - current->lines = 1; - current->end = -1; - - if (!read_line_from_input(this, line, LINE_LEN)) - return current;; - - sub_readtext((char *) &line,¤t->text[1]); - current->lines = 2; - - if ((current->text[0][0]==0) && (current->text[1][0]==0)) { - return NULL; - } - - return current; -} - -static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *this, subtitle_t *current) { - char line1[LINE_LEN] = { 0, }, line2[LINE_LEN] = { 0, }, directive[LINE_LEN] = { 0, }, *p, *q; - unsigned a1, a2, a3, a4, b1, b2, b3, b4, comment = 0; - static unsigned jacoTimeres = 30; - static int jacoShift = 0; - - memset(current, 0, sizeof(subtitle_t)); - while (!current->text[0]) { - if (!read_line_from_input(this, line1, LINE_LEN)) { - return NULL; - } - if (sscanf - (line1, "%u:%u:%u.%u %u:%u:%u.%u %" LINE_LEN_QUOT "[^\n\r]", &a1, &a2, &a3, &a4, - &b1, &b2, &b3, &b4, line2) < 9) { - if (sscanf(line1, "@%u @%u %" LINE_LEN_QUOT "[^\n\r]", &a4, &b4, line2) < 3) { - if (line1[0] == '#') { - int hours = 0, minutes = 0, seconds, delta, inverter = - 1; - unsigned units = jacoShift; - switch (toupper(line1[1])) { - case 'S': - if (isalpha(line1[2])) { - delta = 6; - } else { - delta = 2; - } - if (sscanf(&line1[delta], "%d", &hours)) { - if (hours < 0) { - hours *= -1; - inverter = -1; - } - if (sscanf(&line1[delta], "%*d:%d", &minutes)) { - if (sscanf - (&line1[delta], "%*d:%*d:%d", - &seconds)) { - sscanf(&line1[delta], "%*d:%*d:%*d.%d", - &units); - } else { - hours = 0; - sscanf(&line1[delta], "%d:%d.%d", - &minutes, &seconds, &units); - minutes *= inverter; - } - } else { - hours = minutes = 0; - sscanf(&line1[delta], "%d.%d", &seconds, - &units); - seconds *= inverter; - } - jacoShift = - ((hours * 3600 + minutes * 60 + - seconds) * jacoTimeres + - units) * inverter; - } - break; - case 'T': - if (isalpha(line1[2])) { - delta = 8; - } else { - delta = 2; - } - sscanf(&line1[delta], "%u", &jacoTimeres); - break; - } - } - continue; - } else { - current->start = - (unsigned long) ((a4 + jacoShift) * 100.0 / - jacoTimeres); - current->end = - (unsigned long) ((b4 + jacoShift) * 100.0 / - jacoTimeres); - } - } else { - current->start = - (unsigned - long) (((a1 * 3600 + a2 * 60 + a3) * jacoTimeres + a4 + - jacoShift) * 100.0 / jacoTimeres); - current->end = - (unsigned - long) (((b1 * 3600 + b2 * 60 + b3) * jacoTimeres + b4 + - jacoShift) * 100.0 / jacoTimeres); - } - current->lines = 0; - p = line2; - while ((*p == ' ') || (*p == '\t')) { - ++p; - } - if (isalpha(*p)||*p == '[') { - if (sscanf(p, "%s %" LINE_LEN_QUOT "[^\n\r]", directive, line1) < 2) - return ERR; - if ((strcasestr(directive, "RDB") != NULL) - || (strcasestr(directive, "RDC") != NULL) - || (strcasestr(directive, "RLB") != NULL) - || (strcasestr(directive, "RLG") != NULL)) { - continue; - } - /* no alignment */ -#if 0 - if (strcasestr(directive, "JL") != NULL) { - current->alignment = SUB_ALIGNMENT_HLEFT; - } else if (strcasestr(directive, "JR") != NULL) { - current->alignment = SUB_ALIGNMENT_HRIGHT; - } else { - current->alignment = SUB_ALIGNMENT_HCENTER; - } -#endif - strcpy(line2, line1); - p = line2; - } - for (q = line1; (!eol(*p)) && (current->lines < SUB_MAX_TEXT); ++p) { - switch (*p) { - case '{': - comment++; - break; - case '}': - if (comment) { - --comment; - /* the next line to get rid of a blank after the comment */ - if ((*(p + 1)) == ' ') - p++; - } - break; - case '~': - if (!comment) { - *q = ' '; - ++q; - } - break; - case ' ': - case '\t': - if ((*(p + 1) == ' ') || (*(p + 1) == '\t')) - break; - if (!comment) { - *q = ' '; - ++q; - } - break; - case '\\': - if (*(p + 1) == 'n') { - *q = '\0'; - q = line1; - current->text[current->lines++] = strdup(line1); - ++p; - break; - } - if ((toupper(*(p + 1)) == 'C') - || (toupper(*(p + 1)) == 'F')) { - ++p,++p; - break; - } - if ((*(p + 1) == 'B') || (*(p + 1) == 'b') || - /* actually this means "insert current date here" */ - (*(p + 1) == 'D') || - (*(p + 1) == 'I') || (*(p + 1) == 'i') || - (*(p + 1) == 'N') || - /* actually this means "insert current time here" */ - (*(p + 1) == 'T') || - (*(p + 1) == 'U') || (*(p + 1) == 'u')) { - ++p; - break; - } - if ((*(p + 1) == '\\') || - (*(p + 1) == '~') || (*(p + 1) == '{')) { - ++p; - } else if (eol(*(p + 1))) { - if (!read_line_from_input(this, directive, LINE_LEN)) - return NULL; - trail_space(directive); - strncat(line2, directive, - ((LINE_LEN > 511) ? LINE_LEN-1 : 511) - - strlen(line2)); - break; - } - default: - if (!comment) { - *q = *p; - ++q; - } - } - } - *q = '\0'; - if (current->lines < SUB_MAX_TEXT) - current->text[current->lines] = strdup(line1); - else - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - } - current->lines++; - return current; -} - -static subtitle_t *sub_read_line_subviewer2(demux_sputext_t *this, subtitle_t *current) { - char *p=NULL; - - while (!current->text[0]) { - char line[LINE_LEN+1]; - if (!read_line_from_input(this, line, LINE_LEN)) return NULL; - if (line[0]!='{') - continue; - - { - int a1,a2,a3,a4; - const int len = sscanf (line, "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4); - if (len < 4) - continue; - current->start = a1*360000+a2*6000+a3*100+a4/10; - } - - int i; - for (i=0; i<SUB_MAX_TEXT;) { - if (!read_line_from_input(this, line, LINE_LEN)) break; - if (line[0]=='}') break; - size_t len=0; - for (p=line; *p!='\n' && *p!='\r' && *p; ++p,++len); - if (len) { - current->text[i] = strndup(line, len); - if (!current->text[i]) return ERR; - ++i; - } else { - break; - } - } - current->lines=i; - } - return current; -} - -static subtitle_t *sub_read_line_subrip09 (demux_sputext_t *this, subtitle_t *current) { - memset (current, 0, sizeof(subtitle_t)); - - int h, m, s; - { - char line[LINE_LEN + 1]; - do { - if (!read_line_from_input (this, line, LINE_LEN)) return NULL; - } while (sscanf (line, "[%d:%d:%d]", &h, &m, &s) != 3); - } - - char line[LINE_LEN + 1]; - if (!read_line_from_input (this, line, LINE_LEN)) return NULL; - - current->start = 360000 * h + 6000 * m + 100 * s; - current->end = -1; - - char *next = line; - int i=0; - while ((next = sub_readtext (next, &(current->text[i])))) { - if (current->text[i]==ERR) return ERR; - i++; - if (i>=SUB_MAX_TEXT) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; - } - } - current->lines= ++i; - - return current; -} - -/* Code from subreader.c of MPlayer -** Sylvain "Skarsnik" Colinet <scolinet@gmail.com> -*/ - -static subtitle_t *sub_read_line_mpl2(demux_sputext_t *this, subtitle_t *current) { - char line2[LINE_LEN+1]; - - memset (current, 0, sizeof(subtitle_t)); - - { - char line[LINE_LEN+1]; - long start, end; - - do { - if (!read_line_from_input (this, line, LINE_LEN)) return NULL; - } while ((sscanf (line, - "[%ld][%ld]%[^\r\n]", - &start, &end, line2) < 3)); - - current->start = start * 10; - current->end = end * 10; - } - - char *p = line2, *next = p; - int i = 0; - - while ((next = sub_readtext (next, &(current->text[i])))) { - if (current->text[i] == ERR) {return ERR;} - i++; - if (i >= SUB_MAX_TEXT) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines = i; - return current; - } - } - current->lines= ++i; - - return current; -} - - - -static int sub_autodetect (demux_sputext_t *this) { - int j; - for(j = 1; j < 100; j++) { - char line[LINE_LEN + 1]; - if (!read_line_from_input(this, line, LINE_LEN)) - return FORMAT_UNKNOWN; - - int i; - if ((sscanf (line, "{%d}{}", &i)==1) || - (sscanf (line, "{%d}{%d}", &i, &i)==2)) { - this->uses_time=0; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "microdvd subtitle format detected\n"); - return FORMAT_MICRODVD; - } - - if (sscanf (line, "%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d", &i, &i, &i, &i, &i, &i, &i, &i)==8) { - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "subrip subtitle format detected\n"); - return FORMAT_SUBRIP; - } - - if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){ - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "subviewer subtitle format detected\n"); - return FORMAT_SUBVIEWER; - } - - if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){ - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "subviewer subtitle format detected\n"); - return FORMAT_SUBVIEWER; - } - - if (strstr (line, "<SAMI>")) { - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "sami subtitle format detected\n"); - return FORMAT_SAMI; - } - if (sscanf (line, "%d:%d:%d:", &i, &i, &i )==3) { - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "vplayer subtitle format detected\n"); - return FORMAT_VPLAYER; - } - /* - * A RealText format is a markup language, starts with <window> tag, - * options (behaviour modifiers) are possible. - */ - if ( !strcasecmp(line, "<window") ) { - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "rt subtitle format detected\n"); - return FORMAT_RT; - } - if ((!memcmp(line, "Dialogue: Marked", 16)) || (!memcmp(line, "Dialogue: ", 10))) { - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "ssa subtitle format detected\n"); - return FORMAT_SSA; - } - if (sscanf (line, "%d,%d,\"%c", &i, &i, (char *) &i) == 3) { - this->uses_time=0; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "pjs subtitle format detected\n"); - return FORMAT_PJS; - } - if (sscanf (line, "FORMAT=%d", &i) == 1) { - this->uses_time=0; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "mpsub subtitle format detected\n"); - return FORMAT_MPSUB; - } - - char p; - if (sscanf (line, "FORMAT=TIM%c", &p)==1 && p=='E') { - this->uses_time=1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "mpsub subtitle format detected\n"); - return FORMAT_MPSUB; - } - - if (strstr (line, "-->>")) { - this->uses_time=0; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "aqtitle subtitle format detected\n"); - return FORMAT_AQTITLE; - } - if (sscanf(line, "@%d @%d", &i, &i) == 2 || - sscanf(line, "%d:%d:%d.%d %d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) { - this->uses_time = 1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "jacobsub subtitle format detected\n"); - return FORMAT_JACOBSUB; - } - if (sscanf(line, "{T %d:%d:%d:%d",&i, &i, &i, &i) == 4) { - this->uses_time = 1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "subviewer 2.0 subtitle format detected\n"); - return FORMAT_SUBVIEWER2; - } - if (sscanf(line, "[%d:%d:%d]", &i, &i, &i) == 3) { - this->uses_time = 1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "subrip 0.9 subtitle format detected\n"); - return FORMAT_SUBRIP09; - } - - if (sscanf (line, "[%d][%d]", &i, &i) == 2) { - this->uses_time = 1; - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "mpl2 subtitle format detected\n"); - return FORMAT_MPL2; - } - } - return FORMAT_UNKNOWN; /* too many bad lines */ -} - -static subtitle_t *sub_read_file (demux_sputext_t *this) { - - subtitle_t * (*func[])(demux_sputext_t *this,subtitle_t *dest)= - { - sub_read_line_microdvd, - sub_read_line_subrip, - sub_read_line_subviewer, - sub_read_line_sami, - sub_read_line_vplayer, - sub_read_line_rt, - sub_read_line_ssa, - sub_read_line_pjs, - sub_read_line_mpsub, - sub_read_line_aqt, - sub_read_line_jacobsub, - sub_read_line_subviewer2, - sub_read_line_subrip09, - sub_read_line_mpl2, - }; - - /* Rewind (sub_autodetect() needs to read input from the beginning) */ - if(this->input->seek(this->input, 0, SEEK_SET) == -1) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "seek failed.\n"); - return NULL; - } - this->buflen = 0; - - this->format=sub_autodetect (this); - if (this->format==FORMAT_UNKNOWN) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Could not determine file format\n"); - return NULL; - } - - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Detected subtitle file format: %d\n",this->format); - - /* Rewind */ - if(this->input->seek(this->input, 0, SEEK_SET) == -1) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "seek failed.\n"); - return NULL; - } - this->buflen = 0; - - this->num=0; - int n_max=32; - subtitle_t *first = calloc(n_max, sizeof(subtitle_t)); - if(!first) return NULL; - - const int timeout = - (((demux_sputext_class_t *) - (this->demux_plugin.demux_class))->max_timeout) * - (this->uses_time ? 100 : 10); - - while(1) { - if(this->num>=n_max){ - n_max+=16; - first=realloc(first,n_max*sizeof(subtitle_t)); - } - - subtitle_t *sub = func[this->format] (this, &first[this->num]); - - if (!sub) - break; /* EOF */ - - if (sub==ERR) - ++this->errs; - else { - if (this->num > 0 && first[this->num-1].end == -1) { - /* end time not defined in the subtitle */ - if (timeout > 0) { - /* timeout */ - if (timeout > sub->start - first[this->num-1].start) { - first[this->num-1].end = sub->start; - } else - first[this->num-1].end = first[this->num-1].start + timeout; - } else { - /* no timeout */ - first[this->num-1].end = sub->start; - } - } - ++this->num; /* Error vs. Valid */ - } - } - /* timeout of last subtitle */ - if (this->num > 0 && first[this->num-1].end == -1) - if (timeout > 0) { - first[this->num-1].end = first[this->num-1].start + timeout; - } - - if(this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) { - char buffer[1024]; - - sprintf(buffer, "Read %i subtitles", this->num); - - if(this->errs) - sprintf(buffer + strlen(buffer), ", %i bad line(s).\n", this->errs); - else - strcat(buffer, "\n"); - - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s", buffer); - } - - return first; -} - -static int demux_sputext_next (demux_sputext_t *this_gen) { - demux_sputext_t *this = (demux_sputext_t *) this_gen; - - if (this->cur >= this->num) - return 0; - - subtitle_t *sub = &this->subtitles[this->cur]; - - buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo); - buf->type = BUF_SPU_TEXT; - buf->pts = 0; - - uint32_t *val = (uint32_t * )buf->content; - *val++ = sub->lines; - *val++ = this->uses_time; - *val++ = (this->uses_time) ? sub->start * 10 : sub->start; - *val++ = (this->uses_time) ? sub->end * 10 : sub->end; - char *str = (char *)val; - - /** @FIXME The way this works seems wrong, SUB_BUFSIZE-1 is not the - right maximum, I think. */ - int line; - for (line = 0; line < sub->lines; line++, str+=strlen(str)+1) { - strncpy(str, sub->text[line], SUB_BUFSIZE-1); - str[SUB_BUFSIZE-1] = '\0'; - } - - this->stream->video_fifo->put(this->stream->video_fifo, buf); - this->cur++; - - return 1; -} - -static void demux_sputext_dispose (demux_plugin_t *this_gen) { - demux_sputext_t *this = (demux_sputext_t *) this_gen; - - int i; - for (i = 0; i < this->num; i++) { - int l; - for (l = 0; l < this->subtitles[i].lines; l++) - free(this->subtitles[i].text[l]); - } - free(this->subtitles); - free(this); -} - -static int demux_sputext_get_status (demux_plugin_t *this_gen) { - demux_sputext_t *this = (demux_sputext_t *) this_gen; - return this->status; -} - -static int demux_sputext_get_stream_length (demux_plugin_t *this_gen) { - demux_sputext_t *this = (demux_sputext_t *) this_gen; - - return ( this->uses_time && this->num ) ? - this->subtitles[this->num-1].end * 10 : - 0; -} - -static int demux_sputext_send_chunk (demux_plugin_t *this_gen) { - demux_sputext_t *this = (demux_sputext_t *) this_gen; - - if (!demux_sputext_next (this)) { - this->status = DEMUX_FINISHED; - } - - return this->status; -} - -static int demux_sputext_seek (demux_plugin_t *this_gen, - off_t start_pos, int start_time, int playing) { - demux_sputext_t *this = (demux_sputext_t*)this_gen; - - lprintf("seek() called\n"); - - /* simple seeking approach: just go back to start. - * decoder will discard subtitles until the desired position. - */ - this->cur = 0; - this->status = DEMUX_OK; - - _x_demux_flush_engine (this->stream); - _x_demux_control_newpts(this->stream, 0, 0); - - return this->status; -} - -static void demux_sputext_send_headers(demux_plugin_t *this_gen) { - demux_sputext_t *this = (demux_sputext_t*)this_gen; - - lprintf("send_headers() called\n"); - - _x_demux_control_start(this->stream); - _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 0); - _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 0); - - /* enable the SPU channel */ - buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo); - buf->type = BUF_CONTROL_SPU_CHANNEL; - buf->decoder_info[0] = buf->decoder_info[1] = buf->decoder_info[2] = 0; - this->stream->video_fifo->put(this->stream->video_fifo, buf); - - this->status = DEMUX_OK; -} - -static uint32_t demux_sputext_get_capabilities(demux_plugin_t *this_gen) { - return DEMUX_CAP_NOCAP; -} - -static int demux_sputext_get_optional_data(demux_plugin_t *this_gen, - void *data, int data_type) { - int channel = *((int *)data); - - switch (data_type) { - case DEMUX_OPTIONAL_DATA_SPULANG: - if (channel == -1 || channel == 0) { - strcpy(data, "sub"); - return DEMUX_OPTIONAL_SUCCESS; - } - default: - return DEMUX_OPTIONAL_UNSUPPORTED; - } -} - -static demux_plugin_t *open_demux_plugin (demux_class_t *class_gen, xine_stream_t *stream, - input_plugin_t *input_gen) { - - input_plugin_t *input = (input_plugin_t *) input_gen; - demux_sputext_t *this; - - lprintf("open_plugin() called\n"); - - this = calloc(1, sizeof (demux_sputext_t)); - this->stream = stream; - this->input = input; - - this->demux_plugin.send_headers = demux_sputext_send_headers; - this->demux_plugin.send_chunk = demux_sputext_send_chunk; - this->demux_plugin.seek = demux_sputext_seek; - this->demux_plugin.dispose = demux_sputext_dispose; - this->demux_plugin.get_status = demux_sputext_get_status; - this->demux_plugin.get_stream_length = demux_sputext_get_stream_length; - this->demux_plugin.get_capabilities = demux_sputext_get_capabilities; - this->demux_plugin.get_optional_data = demux_sputext_get_optional_data; - this->demux_plugin.demux_class = class_gen; - - this->buflen = 0; - - switch (stream->content_detection_method) { - case METHOD_BY_EXTENSION: - { - const char *const mrl = input->get_mrl(input); - const char *const ending = strrchr(mrl, '.'); - - if (!ending || ( - (strncasecmp(ending, ".asc", 4) != 0) && - (strncasecmp(ending, ".txt", 4) != 0) && - (strncasecmp(ending, ".sub", 4) != 0) && - (strncasecmp(ending, ".srt", 4) != 0) && - (strncasecmp(ending, ".smi", 4) != 0) && - (strncasecmp(ending, ".ssa", 4) != 0) && - (strncasecmp(ending, ".ass", 4) != 0))) { - free (this); - return NULL; - } - } - /* falling through is intended */ - - case METHOD_EXPLICIT: - /* case METHOD_BY_CONTENT: */ - - /* FIXME: for now this demuxer only works when requested explicitly - * to make sure it does not interfere with others; - * If this is found too inconvenient, this may be changed after making - * sure the content detection does not produce any false positives. - */ - - if ((input->get_capabilities(input) & INPUT_CAP_SEEKABLE) != 0) { - - this->subtitles = sub_read_file (this); - - this->cur = 0; - - if (this->subtitles) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "subtitle format %s time.\n", - this->uses_time ? "uses" : "doesn't use"); - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "read %i subtitles, %i errors.\n", this->num, this->errs); - return &this->demux_plugin; - } - } - /* falling through is intended */ - } - - free (this); - return NULL; -} - -static const char *get_demux_description (demux_class_t *this_gen) { - return "sputext demuxer plugin"; -} - -static const char *get_demux_identifier (demux_class_t *this_gen) { - return "sputext"; -} - -static const char *get_demux_extensions (demux_class_t *this_gen) { - return "asc txt sub srt smi ssa ass"; -} - -static const char *get_demux_mimetypes (demux_class_t *this_gen) { - return NULL; - - /* do not report this mimetype, it might confuse browsers. */ - /* "text/plain: asc txt sub srt: VIDEO subtitles;" */ -} - -static void demux_class_dispose (demux_class_t *this_gen) { - demux_sputext_class_t *this = (demux_sputext_class_t *) this_gen; - - free (this); -} - -static void config_timeout_cb(void *this_gen, xine_cfg_entry_t *entry) { - demux_sputext_class_t *this = (demux_sputext_class_t *)this_gen; - - this->max_timeout = entry->num_value; -} - -static void *init_sputext_demux_class (xine_t *xine, void *data) { - - demux_sputext_class_t *this ; - - lprintf("initializing\n"); - - this = calloc(1, sizeof (demux_sputext_class_t)); - - this->demux_class.open_plugin = open_demux_plugin; - this->demux_class.get_description = get_demux_description; - this->demux_class.get_identifier = get_demux_identifier; - this->demux_class.get_mimetypes = get_demux_mimetypes; - this->demux_class.get_extensions = get_demux_extensions; - this->demux_class.dispose = demux_class_dispose; - - /* - * Some subtitling formats, namely AQT and Subrip09, define the end of a - * subtitle as the beginning of the following. From end-user view it's - * better define timeout of hidding. Setting to zero means "no timeout". - */ - this->max_timeout = xine->config->register_num(xine->config, - "subtitles.separate.timeout", 4, - _("default duration of subtitle display in seconds"), - _("Some subtitle formats do not explicitly give a duration for each subtitle. " - "For these, you can set a default duration here. Setting to zero will result " - "in the subtitle being shown until the next one takes over."), - 20, config_timeout_cb, this); - - return this; -} - -const plugin_info_t xine_plugin_info[] EXPORTED = { - /* type, API, "name", version, special_info, init_function */ - { PLUGIN_DEMUX, 26, "sputext", XINE_VERSION_CODE, NULL, &init_sputext_demux_class }, - { PLUGIN_NONE, 0, "", 0, NULL, NULL } -}; diff --git a/src/libsputext/xine_sputext_decoder.c b/src/libsputext/xine_sputext_decoder.c deleted file mode 100644 index e2e37bc37..000000000 --- a/src/libsputext/xine_sputext_decoder.c +++ /dev/null @@ -1,1215 +0,0 @@ -/* - * Copyright (C) 2000-2004 the xine project - * - * This file is part of xine, a free video player. - * - * xine 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. - * - * xine 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#define LOG_MODULE "libsputext" -#define LOG_VERBOSE -/* -#define LOG -*/ - -#include "buffer.h" -#include "xine_internal.h" -#include "xineutils.h" -#include "osd.h" - -#define SUB_MAX_TEXT 5 /* lines */ -#define SUB_BUFSIZE 256 /* chars per line */ - -/* alignment in SSA codes */ -#define ALIGN_LEFT 1 -#define ALIGN_CENTER 2 -#define ALIGN_RIGHT 3 -#define ALIGN_BOTTOM 0 -#define ALIGN_TOP 4 -#define ALIGN_MIDDLE 8 -#define GET_X_ALIGNMENT(a) ((a) & 3) -#define GET_Y_ALIGNMENT(a) ((a) - ((a) & 3)) - -/* subtitles projection */ -/* for subrip file with SSA tags, those values are always correct.*/ -/* But for SSA files, those values are the default ones. we have */ -/* to use PlayResX and PlayResY defined in [Script Info] section. */ -/* not implemented yet... */ -#define SPU_PROJECTION_X 384 -#define SPU_PROJECTION_Y 288 - - - -#define rgb2yuv(R,G,B) ((((((66*R+129*G+25*B+128)>>8)+16)<<8)|(((112*R-94*G-18*B+128)>>8)+128))<<8|(((-38*R-74*G+112*B+128)>>8)+128)) - -static const uint32_t sub_palette[22]={ -/* RED */ - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(50,10,10), - rgb2yuv(120,20,20), - rgb2yuv(185,50,50), - rgb2yuv(255,70,70), -/* BLUE */ - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,0,0), - rgb2yuv(0,30,50), - rgb2yuv(0,90,120), - rgb2yuv(0,140,185), - rgb2yuv(0,170,255) -}; - -static const uint8_t sub_trans[22]={ - 0, 0, 3, 6, 8, 10, 12, 14, 15, 15, 15, - 0, 0, 3, 6, 8, 10, 12, 14, 15, 15, 15 -}; - -typedef enum { - SUBTITLE_SIZE_TINY = 0, - SUBTITLE_SIZE_SMALL, - SUBTITLE_SIZE_NORMAL, - SUBTITLE_SIZE_LARGE, - SUBTITLE_SIZE_VERY_LARGE, - SUBTITLE_SIZE_HUGE, - - SUBTITLE_SIZE_NUM /* number of values in enum */ -} subtitle_size; - -#define FONTNAME_SIZE 100 - -typedef struct sputext_class_s { - spu_decoder_class_t class; - - subtitle_size subtitle_size; /* size of subtitles */ - int vertical_offset; - char font[FONTNAME_SIZE]; /* subtitle font */ -#ifdef HAVE_FT2 - char font_ft[FILENAME_MAX]; /* subtitle font */ - int use_font_ft; /* use Freetype */ -#endif - const char *src_encoding; /* encoding of subtitle file */ - int use_unscaled; /* use unscaled OSD if possible */ - - xine_t *xine; - -} sputext_class_t; - - -/* Convert subtiles coordinates in window coordinates. */ -/* (a, b) --> (x + a * dx, y + b * dy) */ -typedef struct video2wnd_s { - int x; - int y; - double dx; - double dy; -} video2wnd_t; - -typedef struct sputext_decoder_s { - spu_decoder_t spu_decoder; - - sputext_class_t *class; - xine_stream_t *stream; - - int ogm; - int lines; - char text[SUB_MAX_TEXT][SUB_BUFSIZE]; - - /* below 3 variables are the same from class. use to detect - * when something changes. - */ - subtitle_size subtitle_size; /* size of subtitles */ - int vertical_offset; - char font[FILENAME_MAX]; /* subtitle font */ - char *buf_encoding; /* encoding of subtitle buffer */ - - int width; /* frame width */ - int height; /* frame height */ - int font_size; - int line_height; - int started; - int finished; - - osd_renderer_t *renderer; - osd_object_t *osd; - int current_osd_text; - uint32_t spu_palette[OVL_PALETTE_SIZE]; - uint8_t spu_trans[OVL_PALETTE_SIZE]; - - int64_t img_duration; - int64_t last_subtitle_end; /* no new subtitle before this vpts */ - int unscaled; /* use unscaled OSD */ - - int last_y; /* location of the previous subtitle */ - int last_lines; /* number of lines of the previous subtitle */ - video2wnd_t video2wnd; -} sputext_decoder_t; - -static inline char *get_font (sputext_class_t *class) -{ -#ifdef HAVE_FT2 - return class->use_font_ft ? class->font_ft : class->font; -#else - return class->font; -#endif -} - -static void update_font_size (sputext_decoder_t *this, int force_update) { - static const int sizes[SUBTITLE_SIZE_NUM] = { 16, 20, 24, 32, 48, 64 }; - - if ((this->subtitle_size != this->class->subtitle_size) || - (this->vertical_offset != this->class->vertical_offset) || - force_update) { - - this->subtitle_size = this->class->subtitle_size; - this->vertical_offset = this->class->vertical_offset; - this->last_lines = 0; - - this->font_size = sizes[this->class->subtitle_size]; - - this->line_height = this->font_size + 10; - - /* Create a full-window OSD */ - if( this->osd ) - this->renderer->free_object (this->osd); - - this->osd = this->renderer->new_object (this->renderer, - this->width, - this->height); - - this->renderer->set_font (this->osd, get_font (this->class), this->font_size); - - this->renderer->set_position (this->osd, 0, 0); - } -} - -static void update_output_size (sputext_decoder_t *this) { - const int unscaled = this->class->use_unscaled && - (this->stream->video_out->get_capabilities(this->stream->video_out) & - VO_CAP_UNSCALED_OVERLAY); - - if( unscaled != this->unscaled ) { - this->unscaled = unscaled; - this->width = 0; /* force update */ - } - - /* initialize decoder if needed */ - if( this->unscaled ) { - if( this->width != this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_WINDOW_WIDTH) || - this->height != this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_WINDOW_HEIGHT) || - !this->img_duration || !this->osd ) { - - int width = 0, height = 0; - - this->stream->video_out->status(this->stream->video_out, NULL, - &width, &height, &this->img_duration ); - if( width && height ) { - - this->width = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_WINDOW_WIDTH); - this->height = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_WINDOW_HEIGHT); - - if(!this->osd || (this->width && this->height)) { - - /* in unscaled mode, we have to convert subtitle position in window coordinates. */ - /* we have a scale factor because video may be zommed */ - /* and a displacement factor because video may have blacks lines. */ - int output_width, output_height, output_xoffset, output_yoffset; - - output_width = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_OUTPUT_WIDTH); - output_height = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_OUTPUT_HEIGHT); - output_xoffset = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_OUTPUT_XOFFSET); - output_yoffset = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_OUTPUT_YOFFSET); - - /* driver don't seen to be capable to give us those values */ - /* fallback to a default full-window values */ - if (output_width <= 0 || output_height <= 0) { - output_width = this->width; - output_height = this->height; - output_xoffset = 0; - output_yoffset = 0; - } - - this->video2wnd.x = output_xoffset; - this->video2wnd.y = output_yoffset; - this->video2wnd.dx = (double)output_width / SPU_PROJECTION_X; - this->video2wnd.dy = (double)output_height / SPU_PROJECTION_Y; - - this->renderer = this->stream->osd_renderer; - update_font_size (this, 1); - } - } - } - } else { - if( !this->width || !this->height || !this->img_duration || !this->osd ) { - - this->width = 0; - this->height = 0; - - this->stream->video_out->status(this->stream->video_out, NULL, - &this->width, &this->height, &this->img_duration ); - - if(!this->osd || ( this->width && this->height)) { - this->renderer = this->stream->osd_renderer; - - /* in scaled mode, we have to convert subtitle position in film coordinates. */ - this->video2wnd.x = 0; - this->video2wnd.y = 0; - this->video2wnd.dx = (double)this->width / SPU_PROJECTION_X; - this->video2wnd.dy = (double)this->height / SPU_PROJECTION_Y; - - update_font_size (this, 1); - } - } - } -} - -static int parse_utf8_size(const uint8_t *c) -{ - if ( c[0]<0x80 ) - return 1; - - if( c[1]==0 ) - return 1; - if ( (c[0]>=0xC2 && c[0]<=0xDF) && (c[1]>=0x80 && c[1]<=0xBF) ) - return 2; - - if( c[2]==0 ) - return 2; - else if ( c[0]==0xE0 && (c[1]>=0xA0 && c[1]<=0xBF) && (c[2]>=0x80 && c[1]<=0xBF) ) - return 3; - else if ( (c[0]>=0xE1 && c[0]<=0xEC) && (c[1]>=0x80 && c[1]<=0xBF) && (c[2]>=0x80 && c[1]<=0xBF) ) - return 3; - else if ( c[0]==0xED && (c[1]>=0x80 && c[1]<=0x9F) && (c[2]>=0x80 && c[1]<=0xBF) ) - return 3; - else if ( c[0]==0xEF && (c[1]>=0xA4 && c[1]<=0xBF) && (c[2]>=0x80 && c[1]<=0xBF) ) - return 3; - else - return 1; -} - -static int ogm_render_line_internal(sputext_decoder_t *this, int x, int y, const char *text, int render) -{ - const size_t length = strlen (text); - size_t i = 0; - - while (i <= length) { - - if (text[i] == '<') { - if (!strncmp("<b>", text+i, 3)) { - /* enable Bold color */ - if (render) - this->current_osd_text = OSD_TEXT2; - i=i+3; - continue; - } else if (!strncmp("</b>", text+i, 4)) { - /* disable BOLD */ - if (render) - this->current_osd_text = OSD_TEXT1; - i=i+4; - continue; - } else if (!strncmp("<i>", text+i, 3)) { - /* enable italics color */ - if (render) - this->current_osd_text = OSD_TEXT3; - i=i+3; - continue; - } else if (!strncmp("</i>", text+i, 4)) { - /* disable italics */ - if (render) - this->current_osd_text = OSD_TEXT1; - i=i+4; - continue; - } else if (!strncmp("<font>", text+i, 6)) { - /*Do somethink to disable typing - fixme - no teststreams*/ - i=i+6; - continue; - } else if (!strncmp("</font>", text+i, 7)) { - /*Do somethink to enable typing - fixme - no teststreams*/ - i=i+7; - continue; - } - } - if (text[i] == '{') { - - if (!strncmp("{\\", text+i, 2)) { - int value; - - if (sscanf(text+i, "{\\b%d}", &value) == 1) { - if (render) { - if (value) - this->current_osd_text = OSD_TEXT2; - else - this->current_osd_text = OSD_TEXT1; - } - } else if (sscanf(text+i, "{\\i%d}", &value) == 1) { - if (render) { - if (value) - this->current_osd_text = OSD_TEXT3; - else - this->current_osd_text = OSD_TEXT1; - } - } - char *const end = strstr(text+i+2, "}"); - if (end) { - i=end-text+1; - continue; - } - } - } - - char letter[5]; - const char *const encoding = this->buf_encoding ? : this->class->src_encoding; - const int isutf8 = !strcmp(encoding, "utf-8"); - const size_t shift = isutf8 ? parse_utf8_size (&text[i]) : 1; - memcpy(letter,&text[i],shift); - letter[shift]=0; - - if (render) - this->renderer->render_text(this->osd, x, y, letter, this->current_osd_text); - - int w, dummy; - this->renderer->get_text_size(this->osd, letter, &w, &dummy); - x += w; - i += shift; - } - - return x; -} - -static inline int ogm_get_width(sputext_decoder_t *this, char* text) { - return ogm_render_line_internal (this, 0, 0, text, 0); -} - -static inline void ogm_render_line(sputext_decoder_t *this, int x, int y, char* text) { - ogm_render_line_internal (this, x, y, text, 1); -} - -/* read SSA tags at begening of text. Suported tags are : */ -/* \a : alignment in SSA code (see #defines) */ -/* \an : alignment in 'numpad code' */ -/* \pos : absolute position of subtitles. Alignment define origin. */ -static void read_ssa_tag(sputext_decoder_t *this, const char* text, - int* alignment, int* sub_x, int* sub_y, int* max_width) { - - int in_tag = 0; - - (*alignment) = 2; - (*sub_x) = -1; - (*sub_y) = -1; - - while (*text) { - - /* wait for tag begin, allow space and tab */ - if (in_tag == 0) { - if (*text == '{') in_tag = 1; - else if ((*text != ' ') && (*text != '\t')) break; - - /* parse SSA command */ - } else { - if (*text == '\\') { - if (sscanf(text, "\\pos(%d,%d)", sub_x, sub_y) == 2) { - text += 8; /* just for speed up, 8 is the minimal with */ - } - - if (sscanf(text, "\\a%d", alignment) == 1) { - text += 2; - } - - if (sscanf(text, "\\an%d", alignment) == 1) { - text += 3; - if ((*alignment) > 6) (*alignment) = (*alignment) - 2; - else if ((*alignment) > 3) (*alignment) = (*alignment) + 5; - } - } - - if (*text == '}') in_tag = 0; - } - - text++; - } - - - /* check alignment validity */ - if ((*alignment) < 1 || (*alignment) > 11) { - (*alignment) = 2; - } - - /* convert to window coordinates */ - if ((*sub_x) >= 0 && (*sub_y) >= 0) { - (*sub_x) = this->video2wnd.x + this->video2wnd.dx * (*sub_x); - (*sub_y) = this->video2wnd.y + this->video2wnd.dy * (*sub_y); - } - - /* check validity, compute max width */ - if ( (*sub_x) < 0 || (*sub_x) >= this->width || - (*sub_y) < 0 || (*sub_y) >= this->height ) { - (*sub_x) = -1; - (*sub_y) = -1; - (*max_width) = this->width; - } else { - switch (GET_X_ALIGNMENT(*alignment)) { - case ALIGN_LEFT: - (*max_width) = this->width - (*sub_x); - break; - case ALIGN_CENTER: - (*max_width) = this->width; - break; - case ALIGN_RIGHT: - (*max_width) = (*sub_x); - break; - } - } - - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libsputext: position : (%d, %d), max width : %d, alignment : %d\n", - (*sub_x), (*sub_y), (*max_width), (*alignment)); -} - -static int is_cjk_encoding(const char *enc) { - /* CJK charset strings defined in iconvdata/gconv-modules of glibc */ - static const char cjk_encoding_strings[][16] = { - "SJIS", - "CP932", - "EUC-KR", - "UHC", - "JOHAB", - "BIG5", - "BIG5HKSCS", - "EUC-JP-MS", - "EUC-JP", - "EUC-CN", - "GBBIG5", - "GBK", - "GBGBK", - "EUC-TW", - "ISO-2022-JP", - "ISO-2022-JP-2", - "ISO-2022-JP-3", - "ISO-2022-KR", - "ISO-2022-CN", - "ISO-2022-CN-EXT", - "GB18030", - "EUC-JISX0213", - "SHIFT_JISX0213", - }; - - int pstr; - - /* return 1 if encoding string is one of the CJK(Chinese,Jananese,Korean) - * character set strings. */ - for (pstr = 0; pstr < sizeof (cjk_encoding_strings) / sizeof (cjk_encoding_strings[0]); pstr++) - if (strcasecmp (enc, cjk_encoding_strings[pstr]) == 0) - return 1; - - return 0; -} - -static void draw_subtitle(sputext_decoder_t *this, int64_t sub_start, int64_t sub_end ) { - - int y; - int sub_x, sub_y, max_width = this->width; - int alignment; - - _x_assert(this->renderer != NULL); - if ( ! this->renderer ) - return; - - read_ssa_tag(this, this->text[0], &alignment, &sub_x, &sub_y, &max_width); - - update_font_size(this, 0); - - const char *const font = get_font (this->class); - if( strcmp(this->font, font) ) { - strncpy(this->font, font, FILENAME_MAX); - this->font[FILENAME_MAX - 1] = '\0'; - this->renderer->set_font (this->osd, font, this->font_size); - } - - int font_size = this->font_size; - - const char *const encoding = this->buf_encoding ? : this->class->src_encoding; - this->renderer->set_encoding(this->osd, encoding); - - int rebuild_all = 0; - int line; - for (line = 0; line < this->lines; line++) { - int line_width = ogm_get_width(this, this->text[line]); - - /* line too long */ - if (line_width > max_width) { - char *current_cut, *best_cut; - int a; - - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libsputext: Line too long: %d > %d, split at max size.\n", - line_width, max_width); - - /* can't fit with keeping existing lines */ - if (this->lines + 1 > SUB_MAX_TEXT) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libsputext: Can't fit with keeping existing line, we have to rebuild all the subtitle\n"); - rebuild_all = 1; - break; - } - - /* find the longest sequence witch fit */ - line_width = 0; - current_cut = this->text[line]; - best_cut = NULL; - while (line_width < max_width) { - while (*current_cut && *current_cut != ' ') current_cut++; - if (*current_cut == ' ') { - *current_cut = 0; - line_width = ogm_get_width(this, this->text[line]); - *current_cut = ' '; - if (line_width < max_width) best_cut = current_cut; - current_cut++; - } else { - break; /* end of line */ - } - } - - if (best_cut == NULL) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "libsputext: Can't wrap line: a word is too long, abort.\n"); - break; - } - - /* move other lines */ - for (a = this->lines - 1; a > line; a--) - memcpy(this->text[a + 1], this->text[a], SUB_BUFSIZE); - - /* split current one */ - strncpy(this->text[line + 1], best_cut + 1, SUB_BUFSIZE); - *best_cut = 0; - - this->lines = this->lines + 1; - } - } - - /* regenerate all the lines to find something that better fits */ - if (rebuild_all) { - char buf[SUB_BUFSIZE * SUB_MAX_TEXT] = { 0, }; - - int line; - for(line = 0; line < this->lines; line++) { - const size_t len = strlen(buf); - if (len) - buf[len] = ' '; - - strncat(buf, this->text[line], SUB_BUFSIZE-len-1); - } - - char *stream = buf; - this->lines = 0; - - char *current_cut, *best_cut; - do { - - if (this->lines + 1 < SUB_MAX_TEXT) { - - /* find the longest sequence witch fit */ - int line_width = 0; - current_cut = stream; - best_cut = NULL; - while (line_width < max_width) { - while (*current_cut && *current_cut != ' ') current_cut++; - if (*current_cut == ' ') { - *current_cut = 0; - line_width = ogm_get_width(this, stream); - *current_cut = ' '; - if (line_width < max_width) best_cut = current_cut; - current_cut++; - } else { - line_width = ogm_get_width(this, stream); - if (line_width < max_width) best_cut = current_cut; - break; /* end of line */ - } - } - } - - /* line maybe too long, but we have reached last subtitle line */ - else { - best_cut = current_cut = stream + strlen(stream); - } - - /* copy current line */ - if (best_cut != NULL) *best_cut = 0; - strncpy(this->text[this->lines], stream, SUB_BUFSIZE); - this->lines = this->lines + 1; - - stream = best_cut + 1; - - } while (best_cut != current_cut); - - } - - - /* Erase subtitle : use last_y and last_lines saved last turn. */ - if (this->last_lines) { - this->renderer->filled_rect (this->osd, 0, this->last_y, - this->width - 1, this->last_y + this->last_lines * this->line_height, - 0); - } - - switch (GET_Y_ALIGNMENT(alignment)) { - case ALIGN_TOP: - if (sub_y >= 0) y = sub_y; - else y = 5; - break; - - case ALIGN_MIDDLE: - if (sub_y >= 0) y = sub_y - (this->lines * this->line_height) / 2; - else y = (this->height - this->lines * this->line_height) / 2; - break; - - case ALIGN_BOTTOM: - default: - if (sub_y >= 0) y = sub_y - this->lines * this->line_height; - else y = this->height - this->lines * this->line_height - this->class->vertical_offset; - break; - } - if (y < 0 || y >= this->height) - y = this->height - this->line_height * this->lines; - - this->last_lines = this->lines; - this->last_y = y; - - - for (line = 0; line < this->lines; line++) { - int w, x; - - while(1) { - w = ogm_get_width( this, this->text[line]); - - switch (GET_X_ALIGNMENT(alignment)) { - case ALIGN_LEFT: - if (sub_x >= 0) x = sub_x; - else x = 5; - break; - - case ALIGN_RIGHT: - if (sub_x >= 0) x = sub_x - w; - else x = max_width - w - 5; - break; - - case ALIGN_CENTER: - default: - if (sub_x >= 0) x = sub_x - w / 2; - else x = (max_width - w) / 2; - break; - } - - - if( w > max_width && font_size > 16 ) { - font_size -= 4; - this->renderer->set_font (this->osd, get_font (this->class), font_size); - } else { - break; - } - } - - if( is_cjk_encoding(encoding) ) { - this->renderer->render_text (this->osd, x, y + line * this->line_height, - this->text[line], OSD_TEXT1); - } else { - ogm_render_line(this, x, y + line*this->line_height, this->text[line]); - } - } - - if( font_size != this->font_size ) - this->renderer->set_font (this->osd, get_font (this->class), this->font_size); - - if( this->last_subtitle_end && sub_start < this->last_subtitle_end ) { - sub_start = this->last_subtitle_end; - } - this->last_subtitle_end = sub_end; - - this->renderer->set_text_palette (this->osd, -1, OSD_TEXT1); - this->renderer->get_palette(this->osd, this->spu_palette, this->spu_trans); - /* append some colors for colored typeface tag */ - memcpy(this->spu_palette+OSD_TEXT2, sub_palette, sizeof(sub_palette)); - memcpy(this->spu_trans+OSD_TEXT2, sub_trans, sizeof(sub_trans)); - this->renderer->set_palette(this->osd, this->spu_palette, this->spu_trans); - - if (this->unscaled) - this->renderer->show_unscaled (this->osd, sub_start); - else - this->renderer->show (this->osd, sub_start); - - this->renderer->hide (this->osd, sub_end); - - lprintf ("scheduling subtitle >%s< at %"PRId64" until %"PRId64", current time is %"PRId64"\n", - this->text[0], sub_start, sub_end, - this->stream->xine->clock->get_current_time (this->stream->xine->clock)); -} - - -static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { - - sputext_decoder_t *this = (sputext_decoder_t *) this_gen; - int uses_time; - int32_t start, end, diff; - int64_t start_vpts, end_vpts; - int64_t spu_offset; - int i; - uint32_t *val; - char *str; - extra_info_t extra_info; - int master_status, slave_status; - int vo_discard; - - /* filter unwanted streams */ - if (buf->decoder_flags & BUF_FLAG_HEADER) { - return; - } - if (buf->decoder_flags & BUF_FLAG_PREVIEW) - return; - - if ((this->stream->spu_channel & 0x1f) != (buf->type & 0x1f)) - return; - - if ( (buf->decoder_flags & BUF_FLAG_SPECIAL) && - (buf->decoder_info[1] == BUF_SPECIAL_CHARSET_ENCODING) ) - this->buf_encoding = buf->decoder_info_ptr[2]; - else - this->buf_encoding = NULL; - - this->current_osd_text = OSD_TEXT1; - - if( (buf->type & 0xFFFF0000) == BUF_SPU_OGM ) { - - this->ogm = 1; - uses_time = 1; - val = (uint32_t * )buf->content; - start = *val++; - end = *val++; - str = (char *)val; - - if (!*str) return; - /* Empty ogm packets (as created by ogmmux) clears out old messages. We already respect the end time. */ - - this->lines = 0; - - i = 0; - while (*str && (this->lines < SUB_MAX_TEXT) && (i < SUB_BUFSIZE)) { - if (*str == '\r' || *str == '\n') { - if (i) { - this->text[ this->lines ][i] = 0; - this->lines++; - i = 0; - } - } else { - this->text[ this->lines ][i] = *str; - if (i < SUB_BUFSIZE-1) - i++; - } - str++; - } - if (i == SUB_BUFSIZE) - i--; - - if (i) { - this->text[ this->lines ][i] = 0; - this->lines++; - } - - } else { - - this->ogm = 0; - val = (uint32_t * )buf->content; - - this->lines = *val++; - uses_time = *val++; - start = *val++; - end = *val++; - str = (char *)val; - for (i = 0; i < this->lines; i++, str += strlen(str) + 1) { - strncpy( this->text[i], str, SUB_BUFSIZE - 1); - this->text[i][SUB_BUFSIZE - 1] = '\0'; - } - - } - - xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, - "libsputext: decoder data [%s]\n", this->text[0]); - xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, - "libsputext: mode %d timing %d->%d\n", uses_time, start, end); - - if( end <= start ) { - xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, - "libsputext: discarding subtitle with invalid timing\n"); - return; - } - - spu_offset = this->stream->master->metronom->get_option (this->stream->master->metronom, - METRONOM_SPU_OFFSET); - if( uses_time ) { - start += (spu_offset / 90); - end += (spu_offset / 90); - } else { - if( this->osd && this->img_duration ) { - start += spu_offset / this->img_duration; - end += spu_offset / this->img_duration; - } - } - - while( !this->finished ) { - - master_status = xine_get_status (this->stream->master); - slave_status = xine_get_status (this->stream); - vo_discard = this->stream->video_out->get_property(this->stream->video_out, - VO_PROP_DISCARD_FRAMES); - - _x_get_current_info (this->stream->master, &extra_info, sizeof(extra_info) ); - - lprintf("master: %d slave: %d input_normpos: %d vo_discard: %d\n", - master_status, slave_status, extra_info.input_normpos, vo_discard); - - if( !this->started && (master_status == XINE_STATUS_PLAY && - slave_status == XINE_STATUS_PLAY && - extra_info.input_normpos) ) { - lprintf("started\n"); - - this->width = this->height = 0; - - update_output_size( this ); - if( this->width && this->height ) { - this->started = 1; - } - } - - if( this->started ) { - - if( master_status != XINE_STATUS_PLAY || - slave_status != XINE_STATUS_PLAY || - vo_discard ) { - lprintf("finished\n"); - - this->width = this->height = 0; - this->finished = 1; - return; - } - - if( this->osd ) { - - /* try to use frame number mode */ - if( !uses_time && extra_info.frame_number ) { - - diff = end - extra_info.frame_number; - - /* discard old subtitles */ - if( diff < 0 ) { - xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, - "libsputext: discarding old subtitle\n"); - return; - } - - diff = start - extra_info.frame_number; - - start_vpts = extra_info.vpts + diff * this->img_duration; - end_vpts = start_vpts + (end-start) * this->img_duration; - - } else { - - if( !uses_time ) { - start = start * this->img_duration / 90; - end = end * this->img_duration / 90; - uses_time = 1; - } - - diff = end - extra_info.input_time; - - /* discard old subtitles */ - if( diff < 0 ) { - xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, - "libsputext: discarding old subtitle\n"); - return; - } - - diff = start - extra_info.input_time; - - start_vpts = extra_info.vpts + diff * 90; - end_vpts = start_vpts + (end-start) * 90; - } - - _x_spu_decoder_sleep(this->stream, start_vpts); - update_output_size( this ); - draw_subtitle(this, start_vpts, end_vpts); - - return; - } - } - - if (_x_spu_decoder_sleep(this->stream, 0)) - xine_usec_sleep (50000); - else - return; - } -} - - -static void spudec_reset (spu_decoder_t *this_gen) { - sputext_decoder_t *this = (sputext_decoder_t *) this_gen; - - lprintf("i guess we just seeked\n"); - this->width = this->height = 0; - this->started = this->finished = 0; - this->last_subtitle_end = 0; -} - -static void spudec_discontinuity (spu_decoder_t *this_gen) { - /* sputext_decoder_t *this = (sputext_decoder_t *) this_gen; */ - -} - -static void spudec_dispose (spu_decoder_t *this_gen) { - sputext_decoder_t *this = (sputext_decoder_t *) this_gen; - - if (this->osd) { - this->renderer->free_object (this->osd); - this->osd = NULL; - } - free(this); -} - -static void update_vertical_offset(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - class->vertical_offset = entry->num_value; -} - -static void update_osd_font(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - strncpy(class->font, entry->str_value, FONTNAME_SIZE); - class->font[FONTNAME_SIZE - 1] = '\0'; - - xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_font = %s\n", class->font ); -} - -#ifdef HAVE_FT2 -static void update_osd_font_ft(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - strncpy(class->font_ft, entry->str_value, FILENAME_MAX); - class->font_ft[FILENAME_MAX - 1] = '\0'; - - xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_font_ft = %s\n", class->font_ft); -} - -static void update_osd_use_font_ft(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - class->use_font_ft = entry->num_value; - - xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_use_font_ft = %d\n", class->use_font_ft); -} -#endif - -static void update_subtitle_size(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - class->subtitle_size = entry->num_value; -} - -static void update_use_unscaled(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - class->use_unscaled = entry->num_value; -} - -static spu_decoder_t *sputext_class_open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { - - sputext_class_t *class = (sputext_class_t *)class_gen; - sputext_decoder_t *this ; - - this = (sputext_decoder_t *) calloc(1, sizeof(sputext_decoder_t)); - - this->spu_decoder.decode_data = spudec_decode_data; - this->spu_decoder.reset = spudec_reset; - this->spu_decoder.discontinuity = spudec_discontinuity; - this->spu_decoder.get_interact_info = NULL; - this->spu_decoder.set_button = NULL; - this->spu_decoder.dispose = spudec_dispose; - - this->class = class; - this->stream = stream; - - return (spu_decoder_t *) this; -} - -static void sputext_class_dispose (spu_decoder_class_t *class_gen) { - sputext_class_t *this = (sputext_class_t *)class_gen; - - this->xine->config->unregister_callback(this->xine->config, - "subtitles.separate.src_encoding"); - this->xine->config->unregister_callback(this->xine->config, - "subtitles.separate.subtitle_size"); - this->xine->config->unregister_callback(this->xine->config, - "subtitles.separate.vertical_offset"); - this->xine->config->unregister_callback(this->xine->config, - "subtitles.separate.use_unscaled_osd"); - free (this); -} - -static char *sputext_class_get_identifier (spu_decoder_class_t *this) { - return "sputext"; -} - -static char *sputext_class_get_description (spu_decoder_class_t *this) { - return "external subtitle decoder plugin"; -} - -static void update_src_encoding(void *class_gen, xine_cfg_entry_t *entry) -{ - sputext_class_t *class = (sputext_class_t *)class_gen; - - class->src_encoding = entry->str_value; - xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_src_encoding = %s\n", class->src_encoding ); -} - -static void *init_spu_decoder_plugin (xine_t *xine, void *data) { - - static const char *subtitle_size_strings[] = { - "tiny", "small", "normal", "large", "very large", "huge", NULL - }; - sputext_class_t *this ; - - lprintf("init class\n"); - - this = (sputext_class_t *) calloc(1, sizeof(sputext_class_t)); - - this->class.open_plugin = sputext_class_open_plugin; - this->class.get_identifier = sputext_class_get_identifier; - this->class.get_description = sputext_class_get_description; - this->class.dispose = sputext_class_dispose; - - this->xine = xine; - - this->subtitle_size = xine->config->register_enum(xine->config, - "subtitles.separate.subtitle_size", - 1, - subtitle_size_strings, - _("subtitle size"), - _("You can adjust the subtitle size here. The setting will " - "be evaluated relative to the window size."), - 0, update_subtitle_size, this); - this->vertical_offset = xine->config->register_num(xine->config, - "subtitles.separate.vertical_offset", - 0, - _("subtitle vertical offset"), - _("You can adjust the vertical position of the subtitle. " - "The setting will be evaluated relative to the window size."), - 0, update_vertical_offset, this); - strncpy(this->font, xine->config->register_string(xine->config, - "subtitles.separate.font", - "sans", - _("font for subtitles"), - _("A font from the xine font directory to be used for the " - "subtitle text."), - 10, update_osd_font, this), FONTNAME_SIZE); - this->font[FONTNAME_SIZE - 1] = '\0'; -#ifdef HAVE_FT2 - strncpy(this->font_ft, xine->config->register_filename(xine->config, - "subtitles.separate.font_freetype", - "", XINE_CONFIG_STRING_IS_FILENAME, - _("font for subtitles"), - _("An outline font file (e.g. a .ttf) to be used for the subtitle text."), - 10, update_osd_font_ft, this), FILENAME_MAX); - this->font_ft[FILENAME_MAX - 1] = '\0'; - this->use_font_ft = xine->config->register_bool(xine->config, - "subtitles.separate.font_use_freetype", - 0, - _("whether to use a freetype font"), - NULL, - 10, update_osd_use_font_ft, this); -#endif - this->src_encoding = xine->config->register_string(xine->config, - "subtitles.separate.src_encoding", - xine_guess_spu_encoding(), - _("encoding of the subtitles"), - _("The encoding of the subtitle text in the stream. This setting " - "is used to render non-ASCII characters correctly. If non-ASCII " - "characters are not displayed as you expect, ask the " - "creator of the subtitles what encoding was used."), - 10, update_src_encoding, this); - this->use_unscaled = xine->config->register_bool(xine->config, - "subtitles.separate.use_unscaled_osd", - 1, - _("use unscaled OSD if possible"), - _("The unscaled OSD will be rendered independently of the video " - "frame and will always be sharp, even if the video is magnified. " - "This will look better, but does not work with all graphics " - "hardware. The alternative is the scaled OSD, which will become " - "blurry, if you enlarge a low resolution video to fullscreen, but " - "it works with all graphics cards."), - 10, update_use_unscaled, this); - - return &this->class; -} - - -/* plugin catalog information */ -static uint32_t supported_types[] = { BUF_SPU_TEXT, BUF_SPU_OGM, 0 }; - -static const decoder_info_t spudec_info = { - supported_types, /* supported types */ - 1 /* priority */ -}; - -const plugin_info_t xine_plugin_info[] EXPORTED = { - /* type, API, "name", version, special_info, init_function */ - { PLUGIN_SPU_DECODER | PLUGIN_MUST_PRELOAD, 16, "sputext", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, - { PLUGIN_NONE, 0, "", 0, NULL, NULL } -}; |