MythTV  master
xine_demux_sputext.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2000-2003 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * code based on old libsputext/xine_decoder.c
21  *
22  * code based on mplayer module:
23  *
24  * Subtitle reader with format autodetection
25  *
26  * Written by laaz
27  * Some code cleanup & realloc() by A'rpi/ESP-team
28  * dunnowhat sub format by szabi
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include <cctype>
36 #include <cstdio>
37 #include <cstdlib>
38 #include <cstring>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include "xine_demux_sputext.h"
44 
45 #define LOG_MODULE "demux_sputext"
46 #define LOG_VERBOSE
47 /*
48 #define LOG
49 */
50 
51 #define ERR ((void *)-1)
52 #define LINE_LEN 1000
53 #define LINE_LEN_QUOT "1000"
54 
55 /*
56  * Demuxer code start
57  */
58 
59 #define FORMAT_UNKNOWN (-1)
60 #define FORMAT_MICRODVD 0
61 #define FORMAT_SUBRIP 1
62 #define FORMAT_SUBVIEWER 2
63 #define FORMAT_SAMI 3
64 #define FORMAT_VPLAYER 4
65 #define FORMAT_RT 5
66 #define FORMAT_SSA 6 /* Sub Station Alpha */
67 #define FORMAT_PJS 7
68 #define FORMAT_MPSUB 8
69 #define FORMAT_AQTITLE 9
70 #define FORMAT_JACOBSUB 10
71 #define FORMAT_SUBVIEWER2 11
72 #define FORMAT_SUBRIP09 12
73 #define FORMAT_MPL2 13 /*Mplayer sub 2 ?*/
74 
75 static bool eol(char p) {
76  return (p=='\r' || p=='\n' || p=='\0');
77 }
78 
79 static inline void trail_space(char *s) {
80  while (isspace(*s)) {
81  char *copy = s;
82  do {
83  copy[0] = copy[1];
84  copy++;
85  } while(*copy);
86  }
87  int i = strlen(s) - 1;
88  while (i > 0 && isspace(s[i]))
89  s[i--] = '\0';
90 }
91 
92 /*
93  * Reimplementation of fgets() using the input->read() method.
94  */
95 static char *read_line_from_input(demux_sputext_t *demuxstr, char *line, off_t len) {
96  off_t nread = 0;
97 
98  // Since our RemoteFile code sleeps 200ms whenever we get back less data
99  // than requested, but this code just keeps trying to read until it gets
100  // an error back, we check for empty reads so that we can stop reading
101  // when there is no more data to read
102  if (demuxstr->emptyReads == 0 && (len - demuxstr->buflen) > 512) {
103  nread = len - demuxstr->buflen;
104  if (nread > demuxstr->rbuffer_len - demuxstr->rbuffer_cur)
105  nread = demuxstr->rbuffer_len - demuxstr->rbuffer_cur;
106  if (nread < 0) {
107  printf("read failed.\n");
108  return nullptr;
109  }
110  memcpy(&demuxstr->buf[demuxstr->buflen],
111  &demuxstr->rbuffer_text[demuxstr->rbuffer_cur],
112  nread);
113  demuxstr->rbuffer_cur += nread;
114  }
115 
116  if (!nread)
117  demuxstr->emptyReads++;
118 
119  demuxstr->buflen += nread;
120  demuxstr->buf[demuxstr->buflen] = '\0';
121 
122  char *s = strchr(demuxstr->buf, '\n');
123 
124  if (line && (s || demuxstr->buflen)) {
125 
126  int linelen = s ? (s - demuxstr->buf) + 1 : demuxstr->buflen;
127 
128  memcpy(line, demuxstr->buf, linelen);
129  line[linelen] = '\0';
130 
131  memmove(demuxstr->buf, &demuxstr->buf[linelen], SUB_BUFSIZE - linelen);
132  demuxstr->buflen -= linelen;
133 
134  return line;
135  }
136 
137  return nullptr;
138 }
139 
140 
142 
143  static char s_line[LINE_LEN + 1];
144  static char *s_s = nullptr;
145  char text[LINE_LEN + 1];
146 
147  char *p = nullptr;
148  current->lines = current->start = 0;
149  current->end = -1;
150  int state = 0;
151 
152  /* read the first line */
153  if (!s_s)
154  if (!(s_s = read_line_from_input(demuxstr, s_line, LINE_LEN))) return nullptr;
155 
156  do {
157  switch (state) {
158 
159  case 0: /* find "START=" */
160  s_s = strstr (s_s, "Start=");
161  if (s_s) {
162  current->start = strtol (s_s + 6, &s_s, 0) / 10;
163  state = 1; continue;
164  }
165  break;
166 
167  case 1: /* find "<P" */
168  if ((s_s = strstr (s_s, "<P"))) { s_s += 2; state = 2; continue; }
169  break;
170 
171  case 2: /* find ">" */
172  if ((s_s = strchr (s_s, '>'))) { s_s++; state = 3; p = text; continue; }
173  break;
174 
175  case 3: /* get all text until '<' appears */
176  if (*s_s == '\0') { break; }
177  else if (*s_s == '<') { state = 4; }
178  else if (strncasecmp (s_s, "&nbsp;", 6) == 0) { *p++ = ' '; s_s += 6; }
179  else if (*s_s == '\r') { s_s++; }
180  else if (strncasecmp (s_s, "<br>", 4) == 0 || *s_s == '\n') {
181  *p = '\0'; p = text; trail_space (text);
182  if (text[0] != '\0')
183  current->text[current->lines++] = strdup (text);
184  if (*s_s == '\n') s_s++; else s_s += 4;
185  }
186  else *p++ = *s_s++;
187  continue;
188 
189  case 4: /* get current->end or skip <TAG> */
190  char *q = strstr (s_s, "Start=");
191  if (q) {
192  current->end = strtol (q + 6, &q, 0) / 10 - 1;
193  *p = '\0'; trail_space (text);
194  if (text[0] != '\0')
195  current->text[current->lines++] = strdup (text);
196  if (current->lines > 0) { state = 99; break; }
197  state = 0; continue;
198  }
199  s_s = strchr (s_s, '>');
200  if (s_s) { s_s++; state = 3; continue; }
201  break;
202  }
203 
204  /* read next line */
205  if (state != 99 && !(s_s = read_line_from_input (demuxstr, s_line, LINE_LEN)))
206  return nullptr;
207 
208  } while (state != 99);
209 
210  return current;
211 }
212 
213 
214 
227 static char *sub_readtext(char *source, char **dest) {
228  int len=0;
229  char *p=source;
230 
231  while ( !eol(*p) && *p!= '|' ) {
232  p++,len++;
233  }
234 
235  if (!dest)
236  return (char*)ERR;
237 
238  *dest= (char *)malloc (len+1);
239  if (!(*dest))
240  return (char*)ERR;
241 
242  strncpy(*dest, source, len);
243  (*dest)[len]=0;
244 
245  while (*p=='\r' || *p=='\n' || *p=='|')
246  p++;
247 
248  if (*p) return p; /* not-last text field */
249  return (char*)nullptr; /* last text field */
250 }
251 
253 
254  char line[LINE_LEN + 1];
255  char line2[LINE_LEN + 1];
256 
257  memset (current, 0, sizeof(subtitle_t));
258 
259  current->end=-1;
260  do {
261  if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
262  } while ((sscanf (line, "{%ld}{}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), line2) !=2) &&
263  (sscanf (line, "{%ld}{%ld}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), &(current->end),line2) !=3)
264  );
265 
266  char *p=line2;
267  char *next=p;
268  int i=0;
269  while ((next =sub_readtext (next, &(current->text[i])))) {
270  if (next==ERR) return (subtitle_t *)ERR;
271  i++;
272  if (i>=SUB_MAX_TEXT) {
273  printf ("Too many lines in a subtitle\n");
274  current->lines=i;
275  return current;
276  }
277  }
278  current->lines= ++i;
279 
280  return current;
281 }
282 
284 
285  char line[LINE_LEN + 1];
286  int a1=0,a2=0,a3=0,a4=0,b1=0,b2=0,b3=0,b4=0; // NOLINT(readability-isolate-declaration)
287 
288  memset (current, 0, sizeof(subtitle_t));
289 
290  while (true) {
291  if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
292  if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) {
293  if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8)
294  continue;
295  }
296  current->start = a1*360000+a2*6000+a3*100+a4;
297  current->end = b1*360000+b2*6000+b3*100+b4;
298 
299  if (!read_line_from_input(demuxstr, line, LINE_LEN))
300  return nullptr;
301 
302  char *p=line;
303  for (current->lines=1; current->lines <= SUB_MAX_TEXT; current->lines++) {
304  char *q=nullptr;
305  int len = 0;
306  for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' &&
307  (strncasecmp(p,"[br]",4) != 0); p++,len++);
308  current->text[current->lines-1]=(char *)malloc (len+1);
309  if (!current->text[current->lines-1]) return (subtitle_t *)ERR;
310  strncpy (current->text[current->lines-1], q, len);
311  current->text[current->lines-1][len]='\0';
312  if (!*p || *p=='\r' || *p=='\n') break;
313  if (*p=='[') while (*p++!=']');
314  if (*p=='|') p++;
315  }
316  if (current->lines > SUB_MAX_TEXT) current->lines = SUB_MAX_TEXT;
317  break;
318  }
319  return current;
320 }
321 
323  char line[LINE_LEN + 1];
324  int a1=0,a2=0,a3=0,a4=0,b1=0,b2=0,b3=0,b4=0; // NOLINT(readability-isolate-declaration)
325  int i = 0;
326 
327  memset(current,0,sizeof(subtitle_t));
328  do {
329  if(!read_line_from_input(demuxstr,line,LINE_LEN))
330  return nullptr;
331  i = sscanf(line,"%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4);
332  } while(i < 8);
333  current->start = a1*360000+a2*6000+a3*100+a4/10;
334  current->end = b1*360000+b2*6000+b3*100+b4/10;
335  i=0;
336  int end_sub=0;
337  do {
338  char *p = nullptr; /* pointer to the curently read char */
339  char temp_line[SUB_BUFSIZE]; /* subtitle line that will be transfered to current->text[i] */
340  int temp_index = 0; /* ... and its index wich 'points' to the first EMPTY place -> last read char is at temp_index-1 if temp_index>0 */
341  temp_line[SUB_BUFSIZE-1]='\0'; /* just in case... */
342  if(!read_line_from_input(demuxstr,line,LINE_LEN)) {
343  if(i)
344  break; /* if something was read, transmit it */
345  return nullptr; /* if not, repport EOF */
346  }
347  for(temp_index=0,p=line;*p!='\0' && !end_sub && temp_index<SUB_BUFSIZE && i<SUB_MAX_TEXT;p++) {
348  switch(*p) {
349  case '\\':
350  if(*(p+1)=='N' || *(p+1)=='n') {
351  temp_line[temp_index++]='\0'; /* end of curent line */
352  p++;
353  } else
354  temp_line[temp_index++]=*p;
355  break;
356  case '{':
357 #if 0 /* italic not implemented in renderer, ignore them for now */
358  if(!strncmp(p,"{\\i1}",5) && temp_index+3<SUB_BUFSIZE) {
359  temp_line[temp_index++]='<';
360  temp_line[temp_index++]='i';
361  temp_line[temp_index++]='>';
362 #else
363  if(strncmp(p,"{\\i1}",5) == 0) { // NOLINT(bugprone-branch-clone)
364 #endif
365  p+=4;
366  }
367 #if 0 /* italic not implemented in renderer, ignore them for now */
368  else if(!strncmp(p,"{\\i0}",5) && temp_index+4<SUB_BUFSIZE) {
369  temp_line[temp_index++]='<';
370  temp_line[temp_index++]='/';
371  temp_line[temp_index++]='i';
372  temp_line[temp_index++]='>';
373 #else
374  else if(strncmp(p,"{\\i0}",5) == 0) {
375 #endif
376  p+=4;
377  }
378  else
379  temp_line[temp_index++]=*p;
380  break;
381  case '\r': /* just ignore '\r's */
382  break;
383  case '\n':
384  temp_line[temp_index++]='\0';
385  break;
386  default:
387  temp_line[temp_index++]=*p;
388  break;
389  }
390  if(temp_index>0) {
391  if(temp_index==SUB_BUFSIZE)
392  printf("Too many characters in a subtitle line\n");
393  if(temp_line[temp_index-1]=='\0' || temp_index==SUB_BUFSIZE) {
394  if(temp_index>1) { /* more than 1 char (including '\0') -> that is a valid one */
395  current->text[i]=(char *)malloc(temp_index);
396  if(!current->text[i])
397  return (subtitle_t *)ERR;
398  strncpy(current->text[i],temp_line,temp_index); /* temp_index<=SUB_BUFSIZE is always true here */
399  i++;
400  temp_index=0;
401  } else
402  end_sub=1;
403  }
404  }
405  }
406  } while(i<SUB_MAX_TEXT && !end_sub);
407  if(i>=SUB_MAX_TEXT)
408  printf("Too many lines in a subtitle\n");
409  current->lines=i;
410  return current;
411 }
412 
414  char line[LINE_LEN + 1];
415  int a1=0,a2=0,a3=0,b1=0,b2=0,b3=0; // NOLINT(readability-isolate-declaration)
416 
417  memset (current, 0, sizeof(subtitle_t));
418 
419  while (!current->text[0]) {
420  if( demuxstr->next_line[0] == '\0' ) { /* if the buffer is empty.... */
421  if( !read_line_from_input(demuxstr, line, LINE_LEN) ) return nullptr;
422  } else {
423  /* ... get the current line from buffer. */
424  strncpy( line, demuxstr->next_line, LINE_LEN);
425  line[LINE_LEN] = '\0'; /* I'm scared. This makes me feel better. */
426  demuxstr->next_line[0] = '\0'; /* mark the buffer as empty. */
427  }
428  /* Initialize buffer with next line */
429  if( ! read_line_from_input( demuxstr, demuxstr->next_line, LINE_LEN) ) {
430  demuxstr->next_line[0] = '\0';
431  return nullptr;
432  }
433  if( (sscanf( line, "%d:%d:%d:", &a1, &a2, &a3) < 3) ||
434  (sscanf( demuxstr->next_line, "%d:%d:%d:", &b1, &b2, &b3) < 3) )
435  continue;
436  current->start = a1*360000+a2*6000+a3*100;
437  current->end = b1*360000+b2*6000+b3*100;
438  if ((current->end - current->start) > LINE_LEN)
439  current->end = current->start + LINE_LEN; /* not too long though. */
440  /* teraz czas na wkopiowanie stringu */
441  char *p=line;
442  /* finds the body of the subtitle_t */
443  for (int i=0; i<3; i++){
444  char *p2=strchr( p, ':');
445  if( p2 == nullptr ) break;
446  p=p2+1;
447  }
448 
449  char *next=p;
450  int i=0;
451  while( (next = sub_readtext( next, &(current->text[i]))) ) {
452  if (next==ERR)
453  return (subtitle_t *)ERR;
454  i++;
455  if (i>=SUB_MAX_TEXT) {
456  printf("Too many lines in a subtitle\n");
457  current->lines=i;
458  return current;
459  }
460  }
461  current->lines=++i;
462  }
463  return current;
464 }
465 
467  /*
468  * TODO: This format uses quite rich (sub/super)set of xhtml
469  * I couldn't check it since DTD is not included.
470  * WARNING: full XML parses can be required for proper parsing
471  */
472  char line[LINE_LEN + 1];
473  int a1=0,a2=0,a3=0,a4=0,b1=0,b2=0,b3=0,b4=0; // NOLINT(readability-isolate-declaration)
474  int plen = 0;
475 
476  memset (current, 0, sizeof(subtitle_t));
477 
478  while (!current->text[0]) {
479  if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
480  /*
481  * TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0
482  * to describe the same moment in time. Maybe there are even more formats in use.
483  */
484  if (sscanf (line, "<Time Begin=\"%d:%d:%d.%d\" End=\"%d:%d:%d.%d\"",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8)
485 
486  plen=a1=a2=a3=a4=b1=b2=b3=b4=0;
487  if (
488  (sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&plen) < 4) &&
489  (sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&b4,&plen) < 5) &&
490  /* (sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&plen) < 5) && */
491  (sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&b4,&plen) < 6) &&
492  (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)
493  )
494  continue;
495  current->start = a1*360000+a2*6000+a3*100+a4/10;
496  current->end = b1*360000+b2*6000+b3*100+b4/10;
497  /* TODO: I don't know what kind of convention is here for marking multiline subs, maybe <br/> like in xml? */
498  char *next = strstr(line,"<clear/>")+8;
499  int i=0;
500  while ((next =sub_readtext (next, &(current->text[i])))) {
501  if (next==ERR)
502  return (subtitle_t *)ERR;
503  i++;
504  if (i>=SUB_MAX_TEXT) {
505  printf("Too many lines in a subtitle\n");
506  current->lines=i;
507  return current;
508  }
509  }
510  current->lines=i+1;
511  }
512  return current;
513 }
514 
516  int comma = 0;
517  static int s_maxComma = 32; /* let's use 32 for the case that the */
518  /* amount of commas increase with newer SSA versions */
519 
520  int hour1 = 0;
521  int min1 = 0;
522  int sec1 = 0;
523  int hunsec1 = 0;
524  int hour2 = 0;
525  int min2 = 0;
526  int sec2 = 0;
527  int hunsec2 = 0;
528  int nothing = 0;
529  char line[LINE_LEN + 1];
530  char line3[LINE_LEN + 1];
531  char *tmp = nullptr;
532 
533  do {
534  if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
535  } while (sscanf (line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,"
536  "%" LINE_LEN_QUOT "[^\n\r]", &nothing,
537  &hour1, &min1, &sec1, &hunsec1,
538  &hour2, &min2, &sec2, &hunsec2,
539  line3) < 9
540  &&
541  sscanf (line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,"
542  "%" LINE_LEN_QUOT "[^\n\r]", &nothing,
543  &hour1, &min1, &sec1, &hunsec1,
544  &hour2, &min2, &sec2, &hunsec2,
545  line3) < 9 );
546 
547  char *line2=strchr(line3, ',');
548  if (!line2)
549  return nullptr;
550 
551  for (comma = 4; comma < s_maxComma; comma ++)
552  {
553  tmp = line2;
554  if(!(tmp=strchr(++tmp, ','))) break;
555  if(*(++tmp) == ' ') break;
556  /* a space after a comma means we're already in a sentence */
557  line2 = tmp;
558  }
559 
560  if(comma < s_maxComma)s_maxComma = comma;
561  /* eliminate the trailing comma */
562  if(*line2 == ',') line2++;
563 
564  current->lines=0;
565  int num=0;
566  current->start = 360000*hour1 + 6000*min1 + 100*sec1 + hunsec1;
567  current->end = 360000*hour2 + 6000*min2 + 100*sec2 + hunsec2;
568 
569  while (((tmp=strstr(line2, "\\n")) != nullptr) || ((tmp=strstr(line2, "\\N")) != nullptr) ){
570  current->text[num]=(char *)malloc(tmp-line2+1);
571  strncpy (current->text[num], line2, tmp-line2);
572  current->text[num][tmp-line2]='\0';
573  line2=tmp+2;
574  num++;
575  current->lines++;
576  if (current->lines >= SUB_MAX_TEXT) return current;
577  }
578 
579  current->text[num]=strdup(line2);
580  current->lines++;
581 
582  return current;
583 }
584 
585 /* Sylvain "Skarsnik" Colinet <scolinet@gmail.com>
586  * From MPlayer subreader.c :
587  *
588  * PJS subtitles reader.
589  * That's the "Phoenix Japanimation Society" format.
590  * I found some of them in http://www.scriptsclub.org/ (used for anime).
591  * The time is in tenths of second.
592  *
593  * by set, based on code by szabi (dunnowhat sub format ;-)
594  */
595 
597  char line[LINE_LEN + 1];
598  char text[LINE_LEN + 1];
599  char *s = nullptr;
600  char *d = nullptr;
601 
602  memset (current, 0, sizeof(subtitle_t));
603 
604  if (!read_line_from_input(demuxstr, line, LINE_LEN))
605  return nullptr;
606  for (s = line; *s && isspace(*s); s++);
607  if (*s == 0)
608  return nullptr;
609  if (sscanf (line, "%ld,%ld,", &(current->start),
610  &(current->end)) <2)
611  return (subtitle_t *)ERR;
612  /* the files I have are in tenths of second */
613  current->start *= 10;
614  current->end *= 10;
615 
616  /* walk to the beggining of the string */
617  for (; *s; s++) if (*s==',') break;
618  if (*s) {
619  for (s++; *s; s++) if (*s==',') break;
620  if (*s) s++;
621  }
622  if (*s!='"') {
623  return (subtitle_t *)ERR;
624  }
625  /* copy the string to the text buffer */
626  for (s++, d=text; *s && *s!='"'; s++, d++)
627  *d=*s;
628  *d=0;
629  current->text[0] = strdup(text);
630  current->lines = 1;
631 
632  return current;
633 }
634 
636  char line[LINE_LEN + 1];
637  float a = NAN;
638  float b = NAN;
639  int num=0;
640 
641  do {
642  if (!read_line_from_input(demuxstr, line, LINE_LEN))
643  return nullptr;
644  } while (sscanf (line, "%f %f", &a, &b) !=2);
645 
646  demuxstr->mpsub_position += (a*100.0F);
647  current->start = (int) demuxstr->mpsub_position;
648  demuxstr->mpsub_position += (b*100.0F);
649  current->end = (int) demuxstr->mpsub_position;
650 
651  while (num < SUB_MAX_TEXT) {
652  if (!read_line_from_input(demuxstr, line, LINE_LEN))
653  return nullptr;
654 
655  char *p=line;
656  while (isspace(*p))
657  p++;
658 
659  if (eol(*p) && num > 0)
660  return current;
661 
662  if (eol(*p))
663  return nullptr;
664 
665  char *q = nullptr;
666  for (q=p; !eol(*q); q++);
667  *q='\0';
668  if (strlen(p)) {
669  current->text[num]=strdup(p);
670  printf(">%s<\n",p);
671  current->lines = ++num;
672  } else {
673  if (num)
674  return current;
675  return nullptr;
676  }
677  }
678 
679  return nullptr;
680 }
681 
683  char line[LINE_LEN + 1];
684 
685  memset (current, 0, sizeof(subtitle_t));
686 
687  while (true) {
688  /* try to locate next subtitle_t */
689  if (!read_line_from_input(demuxstr, line, LINE_LEN))
690  return nullptr;
691  if (!(sscanf (line, "-->> %ld", &(current->start)) <1))
692  break;
693  }
694 
695  if (!read_line_from_input(demuxstr, line, LINE_LEN))
696  return nullptr;
697 
698  sub_readtext((char *) &line,&current->text[0]);
699  current->lines = 1;
700  current->end = -1;
701 
702  if (!read_line_from_input(demuxstr, line, LINE_LEN))
703  return current;;
704 
705  sub_readtext((char *) &line,&current->text[1]);
706  current->lines = 2;
707 
708  if ((current->text[0][0]==0) && (current->text[1][0]==0)) {
709  return nullptr;
710  }
711 
712  return current;
713 }
714 
716  char line1[LINE_LEN+1];
717  char line2[LINE_LEN+1];
718  char directive[LINE_LEN+1];
719  char *p = nullptr;
720  char *q = nullptr;
721  unsigned a1=0, a2=0, a3=0, a4=0, b1=0, b2=0, b3=0, b4=0; // NOLINT(readability-isolate-declaration)
722  unsigned comment = 0;
723  static unsigned s_jacoTimeRes = 30;
724  static int s_jacoShift = 0;
725 
726  memset(current, 0, sizeof(subtitle_t));
727  memset(line1, 0, LINE_LEN+1);
728  memset(line2, 0, LINE_LEN+1);
729  memset(directive, 0, LINE_LEN+1);
730  while (!current->text[0]) {
731  if (!read_line_from_input(demuxstr, line1, LINE_LEN)) {
732  return nullptr;
733  }
734  if (sscanf
735  (line1, "%u:%u:%u.%u %u:%u:%u.%u %" LINE_LEN_QUOT "[^\n\r]", &a1, &a2, &a3, &a4,
736  &b1, &b2, &b3, &b4, line2) < 9) {
737  if (sscanf(line1, "@%u @%u %" LINE_LEN_QUOT "[^\n\r]", &a4, &b4, line2) < 3) {
738  if (line1[0] == '#') {
739  int hours = 0;
740  int minutes = 0;
741  int seconds = 0;
742  int delta = 0;
743  unsigned units = s_jacoShift;
744  int inverter = 1;
745  switch (toupper(line1[1])) {
746  case 'S':
747  if (isalpha(line1[2])) {
748  delta = 6;
749  } else {
750  delta = 2;
751  }
752  if (sscanf(&line1[delta], "%d", &hours)) {
753  if (hours < 0) {
754  hours *= -1;
755  inverter = -1;
756  }
757  if (sscanf(&line1[delta], "%*d:%d", &minutes)) {
758  if (sscanf
759  (&line1[delta], "%*d:%*d:%d",
760  &seconds)) {
761  sscanf(&line1[delta], "%*d:%*d:%*d.%u",
762  &units);
763  } else {
764  hours = 0;
765  sscanf(&line1[delta], "%d:%d.%u",
766  &minutes, &seconds, &units);
767  minutes *= inverter;
768  }
769  } else {
770  hours = minutes = 0;
771  sscanf(&line1[delta], "%d.%u", &seconds,
772  &units);
773  seconds *= inverter;
774  }
775  s_jacoShift =
776  ((hours * 3600 + minutes * 60 +
777  seconds) * s_jacoTimeRes +
778  units) * inverter;
779  }
780  break;
781  case 'T':
782  if (isalpha(line1[2])) {
783  delta = 8;
784  } else {
785  delta = 2;
786  }
787  sscanf(&line1[delta], "%u", &s_jacoTimeRes);
788  break;
789  }
790  }
791  continue;
792  }
793  current->start =
794  (unsigned long) ((a4 + s_jacoShift) * 100.0 /
795  s_jacoTimeRes);
796  current->end =
797  (unsigned long) ((b4 + s_jacoShift) * 100.0 /
798  s_jacoTimeRes);
799  } else {
800  current->start =
801  (unsigned
802  long) (((a1 * 3600 + a2 * 60 + a3) * s_jacoTimeRes + a4 +
803  s_jacoShift) * 100.0 / s_jacoTimeRes);
804  current->end =
805  (unsigned
806  long) (((b1 * 3600 + b2 * 60 + b3) * s_jacoTimeRes + b4 +
807  s_jacoShift) * 100.0 / s_jacoTimeRes);
808  }
809  current->lines = 0;
810  p = line2;
811  while ((*p == ' ') || (*p == '\t')) {
812  ++p;
813  }
814  if (isalpha(*p)||*p == '[') {
815  if (sscanf(p, "%" LINE_LEN_QUOT "s %" LINE_LEN_QUOT "[^\n\r]", directive, line1) < 2)
816  return (subtitle_t *)ERR;
817  int jLength = strlen(directive);
818  for (int cont = 0; cont < jLength; ++cont) {
819  if (isalpha(*(directive + cont)))
820  *(directive + cont) = toupper(*(directive + cont));
821  }
822  if ((strstr(directive, "RDB") != nullptr)
823  || (strstr(directive, "RDC") != nullptr)
824  || (strstr(directive, "RLB") != nullptr)
825  || (strstr(directive, "RLG") != nullptr)) {
826  continue;
827  }
828  /* no alignment */
829 #if 0
830  if (strstr(directive, "JL") != nullptr) {
831  current->alignment = SUB_ALIGNMENT_HLEFT;
832  } else if (strstr(directive, "JR") != nullptr) {
833  current->alignment = SUB_ALIGNMENT_HRIGHT;
834  } else {
835  current->alignment = SUB_ALIGNMENT_HCENTER;
836  }
837 #endif
838  strcpy(line2, line1);
839  p = line2;
840  }
841  for (q = line1; (!eol(*p)) && (current->lines < SUB_MAX_TEXT); ++p) {
842  switch (*p) {
843  case '{':
844  comment++;
845  break;
846  case '}':
847  if (comment) {
848  --comment;
849  /* the next line to get rid of a blank after the comment */
850  if ((*(p + 1)) == ' ')
851  p++;
852  }
853  break;
854  case '~':
855  if (!comment) {
856  *q = ' ';
857  ++q;
858  }
859  break;
860  case ' ':
861  case '\t':
862  if ((*(p + 1) == ' ') || (*(p + 1) == '\t'))
863  break;
864  if (!comment) {
865  *q = ' ';
866  ++q;
867  }
868  break;
869  case '\\':
870  if (*(p + 1) == 'n') {
871  *q = '\0';
872  q = line1;
873  current->text[current->lines++] = strdup(line1);
874  ++p;
875  break;
876  }
877  if ((toupper(*(p + 1)) == 'C')
878  || (toupper(*(p + 1)) == 'F')) {
879  ++p,++p;
880  break;
881  }
882  if ((*(p + 1) == 'B') || (*(p + 1) == 'b') ||
883  /* actually this means "insert current date here" */
884  (*(p + 1) == 'D') ||
885  (*(p + 1) == 'I') || (*(p + 1) == 'i') ||
886  (*(p + 1) == 'N') ||
887  /* actually this means "insert current time here" */
888  (*(p + 1) == 'T') ||
889  (*(p + 1) == 'U') || (*(p + 1) == 'u')) {
890  ++p;
891  break;
892  }
893  if ((*(p + 1) == '\\') ||
894  (*(p + 1) == '~') || (*(p + 1) == '{')) {
895  ++p;
896  } else if (eol(*(p + 1))) {
897  if (!read_line_from_input(demuxstr, directive, LINE_LEN))
898  return nullptr;
899  trail_space(directive);
900  strncat(line2, directive,
901  ((LINE_LEN > 511) ? LINE_LEN-1 : 511)
902  - strlen(line2));
903  break;
904  }
905  // Checked xine-lib-1.2.8. No fix there. Seems like it
906  // should be a break.
907  break;
908  default:
909  if (!comment) {
910  *q = *p;
911  ++q;
912  }
913  }
914  }
915  *q = '\0';
916  if (current->lines < SUB_MAX_TEXT)
917  current->text[current->lines] = strdup(line1);
918  else
919  printf ("Too many lines in a subtitle\n");
920  }
921  current->lines++;
922  return current;
923 }
924 
926  char line[LINE_LEN+1];
927  int a1=0,a2=0,a3=0,a4=0; // NOLINT(readability-isolate-declaration)
928  char *p=nullptr;
929  int i = 0;
930 
931  while (!current->text[0]) {
932  if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
933  if (line[0]!='{')
934  continue;
935  if (sscanf (line, "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4) < 4)
936  continue;
937  current->start = a1*360000+a2*6000+a3*100+a4/10;
938  for (i=0; i<SUB_MAX_TEXT;) {
939  if (!read_line_from_input(demuxstr, line, LINE_LEN)) break;
940  if (line[0]=='}') break;
941  int len=0;
942  for (p=line; *p!='\n' && *p!='\r' && *p; ++p,++len);
943  if (len) {
944  current->text[i]=(char *)malloc (len+1);
945  if (!current->text[i]) return (subtitle_t *)ERR;
946  strncpy (current->text[i], line, len); current->text[i][len]='\0';
947  ++i;
948  } else {
949  break;
950  }
951  }
952  current->lines=i;
953  }
954  return current;
955 }
956 
958  char line[LINE_LEN + 1];
959  int h = 0;
960  int m = 0;
961  int s = 0;
962 
963  memset (current, 0, sizeof(subtitle_t));
964 
965  do {
966  if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
967  } while (sscanf (line, "[%d:%d:%d]", &h, &m, &s) != 3);
968 
969  if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
970 
971  current->start = 360000 * h + 6000 * m + 100 * s;
972  current->end = -1;
973 
974  char *next=line;
975  int i=0;
976  while ((next = sub_readtext (next, &(current->text[i])))) {
977  if (next==ERR) return (subtitle_t *)ERR;
978  i++;
979  if (i>=SUB_MAX_TEXT) {
980  printf("Too many lines in a subtitle\n");
981  current->lines=i;
982  return current;
983  }
984  }
985  current->lines= ++i;
986 
987  return current;
988 }
989 
990 /* Code from subreader.c of MPlayer
991 ** Sylvain "Skarsnik" Colinet <scolinet@gmail.com>
992 */
993 
995  char line[LINE_LEN+1];
996  char line2[LINE_LEN+1];
997 
998  memset (current, 0, sizeof(subtitle_t));
999  do {
1000  if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
1001  } while ((sscanf (line,
1002  "[%ld][%ld]%" LINE_LEN_QUOT "[^\r\n]",
1003  &(current->start), &(current->end), line2) < 3));
1004  current->start *= 10;
1005  current->end *= 10;
1006 
1007  char *p=line2;
1008  char *next=p;
1009  int i=0;
1010  while ((next = sub_readtext (next, &(current->text[i])))) {
1011  if (next == ERR) {return (subtitle_t *)ERR;}
1012  i++;
1013  if (i >= SUB_MAX_TEXT) {
1014  printf("Too many lines in a subtitle\n");
1015  current->lines = i;
1016  return current;
1017  }
1018  }
1019  current->lines= ++i;
1020 
1021  return current;
1022 }
1023 
1024 
1025 static int sub_autodetect (demux_sputext_t *demuxstr) {
1026 
1027  char line[LINE_LEN + 1];
1028  int i = 0;
1029  int j = 0;
1030  char p = 0;
1031 
1032  while (j < 100) {
1033  j++;
1034  if (!read_line_from_input(demuxstr, line, LINE_LEN))
1035  return FORMAT_UNKNOWN;
1036 
1037  if ((sscanf (line, "{%d}{}", &i)==1) ||
1038  (sscanf (line, "{%d}{%d}", &i, &i)==2)) {
1039  demuxstr->uses_time=0;
1040  return FORMAT_MICRODVD;
1041  }
1042 
1043  if (sscanf (line, "%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d", &i, &i, &i, &i, &i, &i, &i, &i)==8) {
1044  demuxstr->uses_time=1;
1045  return FORMAT_SUBRIP;
1046  }
1047 
1048  if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){
1049  demuxstr->uses_time=1;
1050  return FORMAT_SUBVIEWER;
1051  }
1052 
1053  if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){
1054  demuxstr->uses_time=1;
1055  return FORMAT_SUBVIEWER;
1056  }
1057 
1058  if (strstr (line, "<SAMI>")) {
1059  demuxstr->uses_time=1;
1060  return FORMAT_SAMI;
1061  }
1062  if (sscanf (line, "%d:%d:%d:", &i, &i, &i )==3) {
1063  demuxstr->uses_time=1;
1064  return FORMAT_VPLAYER;
1065  }
1066  /*
1067  * A RealText format is a markup language, starts with <window> tag,
1068  * options (behaviour modifiers) are possible.
1069  */
1070  if ( strcasecmp(line, "<window") == 0 ) {
1071  demuxstr->uses_time=1;
1072  return FORMAT_RT;
1073  }
1074  if ((memcmp(line, "Dialogue: Marked", 16) == 0) || (memcmp(line, "Dialogue: ", 10) == 0)) {
1075  demuxstr->uses_time=1;
1076  return FORMAT_SSA;
1077  }
1078  if (sscanf (line, "%d,%d,\"%c", &i, &i, (char *) &i) == 3) {
1079  demuxstr->uses_time=0;
1080  return FORMAT_PJS;
1081  }
1082  if (sscanf (line, "FORMAT=%d", &i) == 1) {
1083  demuxstr->uses_time=0;
1084  return FORMAT_MPSUB;
1085  }
1086  if (sscanf (line, "FORMAT=TIM%c", &p)==1 && p=='E') {
1087  demuxstr->uses_time=1;
1088  return FORMAT_MPSUB;
1089  }
1090  if (strstr (line, "-->>")) {
1091  demuxstr->uses_time=0;
1092  return FORMAT_AQTITLE;
1093  }
1094  if (sscanf(line, "@%d @%d", &i, &i) == 2 ||
1095  sscanf(line, "%d:%d:%d.%d %d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) {
1096  demuxstr->uses_time = 1;
1097  return FORMAT_JACOBSUB;
1098  }
1099  if (sscanf(line, "{T %d:%d:%d:%d",&i, &i, &i, &i) == 4) {
1100  demuxstr->uses_time = 1;
1101  return FORMAT_SUBVIEWER2;
1102  }
1103  if (sscanf(line, "[%d:%d:%d]", &i, &i, &i) == 3) {
1104  demuxstr->uses_time = 1;
1105  return FORMAT_SUBRIP09;
1106  }
1107 
1108  if (sscanf (line, "[%d][%d]", &i, &i) == 2) {
1109  demuxstr->uses_time = 1;
1110  return FORMAT_MPL2;
1111  }
1112  }
1113  return FORMAT_UNKNOWN; /* too many bad lines */
1114 }
1115 
1117 
1118  // These functions all return either 1) nullptr, 2) (subtitle_t*)ERR,
1119  // or 3) a pointer to the dest parameter.
1120  subtitle_t * (*func[])(demux_sputext_t *demuxstr,subtitle_t *dest)=
1121  {
1136  };
1137 
1138  /* Rewind (sub_autodetect() needs to read input from the beginning) */
1139  demuxstr->rbuffer_cur = 0;
1140  demuxstr->buflen = 0;
1141  demuxstr->emptyReads = 0;
1142 
1143  demuxstr->format=sub_autodetect (demuxstr);
1144  if (demuxstr->format==FORMAT_UNKNOWN) {
1145  return nullptr;
1146  }
1147 
1148  /*printf("Detected subtitle file format: %d\n", demuxstr->format);*/
1149 
1150  /* Rewind */
1151  demuxstr->rbuffer_cur = 0;
1152  demuxstr->buflen = 0;
1153  demuxstr->emptyReads = 0;
1154 
1155  demuxstr->num=0;
1156  int n_max=32;
1157  auto *first = (subtitle_t *) malloc(n_max*sizeof(subtitle_t));
1158  if(!first) return nullptr;
1159  int timeout = MAX_TIMEOUT;
1160 
1161  if (demuxstr->uses_time) timeout *= 100;
1162  else timeout *= 10;
1163 
1164  while(true) {
1165  if(demuxstr->num>=n_max){
1166  n_max+=16;
1167  auto *new_first=(subtitle_t *)realloc(first,n_max*sizeof(subtitle_t));
1168  if (new_first == nullptr) {
1169  free(first);
1170  return nullptr;
1171  }
1172  first = new_first;
1173  }
1174 
1175  subtitle_t *sub = func[demuxstr->format] (demuxstr, &first[demuxstr->num]);
1176  if (!sub) {
1177  break; /* EOF */
1178  }
1179  demuxstr->emptyReads = 0;
1180 
1181  if (sub==ERR)
1182  ++demuxstr->errs;
1183  else {
1184  if (demuxstr->num > 0 && first[demuxstr->num-1].end == -1) {
1185  /* end time not defined in the subtitle */
1186  if (timeout > 0) {
1187  /* timeout */
1188  if (timeout > sub->start - first[demuxstr->num-1].start) {
1189  first[demuxstr->num-1].end = sub->start;
1190  } else
1191  first[demuxstr->num-1].end = first[demuxstr->num-1].start + timeout;
1192  } else {
1193  /* no timeout */
1194  first[demuxstr->num-1].end = sub->start;
1195  }
1196  }
1197  ++demuxstr->num; /* Error vs. Valid */
1198  }
1199  }
1200  /* timeout of last subtitle */
1201  if (demuxstr->num > 0 && first[demuxstr->num-1].end == -1)
1202  {
1203  if (timeout > 0) {
1204  first[demuxstr->num-1].end = first[demuxstr->num-1].start + timeout;
1205  }
1206  }
1207 
1208 #ifdef DEBUG_XINE_DEMUX_SPUTEXT
1209  {
1210  char buffer[1024];
1211 
1212  sprintf(buffer, "Read %i subtitles", demuxstr->num);
1213 
1214  if(demuxstr->errs)
1215  sprintf(buffer + strlen(buffer), ", %i bad line(s).\n", demuxstr->errs);
1216  else
1217  strcat(buffer, "\n");
1218 
1219  printf("%s", buffer);
1220  }
1221 #endif
1222 
1223  // No memory leak of 'sub' here. 'Sub' always points to an element in 'first'.
1224  // NOLINT(clang-analyzer-unix.Malloc)
1225  return first;
1226 }
#define MAX_TIMEOUT
static bool eol(char p)
#define LINE_LEN
#define SUB_MAX_TEXT
static subtitle_t * sub_read_line_subviewer2(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_AQTITLE
long end
Ending time in msec or starting frame.
static subtitle_t * sub_read_line_microdvd(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_MPSUB
subtitle_t * sub_read_file(demux_sputext_t *demuxstr)
#define FORMAT_SSA
#define SUB_BUFSIZE
#define FORMAT_SUBVIEWER2
static subtitle_t * sub_read_line_subrip(demux_sputext_t *demuxstr, subtitle_t *current)
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
static guint32 * tmp
Definition: goom_core.c:35
char buf[SUB_BUFSIZE]
#define off_t
static subtitle_t * sub_read_line_vplayer(demux_sputext_t *demuxstr, subtitle_t *current)
static subtitle_t * sub_read_line_sami(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_UNKNOWN
static subtitle_t * sub_read_line_jacobsub(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_SUBRIP09
static const uint16_t * d
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
static subtitle_t * sub_read_line_subrip09(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_SUBVIEWER
#define FORMAT_SAMI
static char * read_line_from_input(demux_sputext_t *demuxstr, char *line, off_t len)
#define FORMAT_VPLAYER
static int sub_autodetect(demux_sputext_t *demuxstr)
static subtitle_t * sub_read_line_rt(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_PJS
static subtitle_t * sub_read_line_mpl2(demux_sputext_t *demuxstr, subtitle_t *current)
PictureAttribute next(PictureAttributeSupported Supported, PictureAttribute Attribute)
static subtitle_t * sub_read_line_ssa(demux_sputext_t *demuxstr, subtitle_t *current)
static subtitle_t * sub_read_line_pjs(demux_sputext_t *demuxstr, subtitle_t *current)
static char * sub_readtext(char *source, char **dest)
Extract the next token from a string.
#define FORMAT_JACOBSUB
static guint32 * p2
Definition: goom_core.c:35
static subtitle_t * sub_read_line_subviewer(demux_sputext_t *demuxstr, subtitle_t *current)
static subtitle_t * sub_read_line_aqt(demux_sputext_t *demuxstr, subtitle_t *current)
static void trail_space(char *s)
static subtitle_t * sub_read_line_mpsub(demux_sputext_t *demuxstr, subtitle_t *current)
#define FORMAT_SUBRIP
#define ERR
long start
Starting time in msec or starting frame.
#define FORMAT_MPL2
#define FORMAT_RT
#define FORMAT_MICRODVD
char next_line[SUB_BUFSIZE]
#define LINE_LEN_QUOT