/*
* Subtitle reader with format autodetection
*
* Copyright (c) 2001 laaz
* Some code cleanup & realloc() by A'rpi/ESP-team
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <libavutil/common.h>
#include <libavutil/avstring.h>
#include "config.h"
#include "core/mp_msg.h"
#include "core/mp_common.h"
#include "core/options.h"
#include "stream/stream.h"
#include "demux/demux.h"
#ifdef CONFIG_ENCA
#include <enca.h>
#endif
#define ERR ((void *) -1)
#ifdef CONFIG_ICONV
#include <iconv.h>
#endif
// subtitle formats
#define SUB_INVALID -1
#define SUB_MICRODVD 0
#define SUB_SUBRIP 1
#define SUB_SUBVIEWER 2
#define SUB_SAMI 3
#define SUB_VPLAYER 4
#define SUB_RT 5
#define SUB_SSA 6
#define SUB_PJS 7
#define SUB_MPSUB 8
#define SUB_AQTITLE 9
#define SUB_SUBVIEWER2 10
#define SUB_SUBRIP09 11
#define SUB_JACOSUB 12
#define SUB_MPL2 13
#define SUB_MAX_TEXT 12
#define SUB_ALIGNMENT_BOTTOMLEFT 1
#define SUB_ALIGNMENT_BOTTOMCENTER 2
#define SUB_ALIGNMENT_BOTTOMRIGHT 3
#define SUB_ALIGNMENT_MIDDLELEFT 4
#define SUB_ALIGNMENT_MIDDLECENTER 5
#define SUB_ALIGNMENT_MIDDLERIGHT 6
#define SUB_ALIGNMENT_TOPLEFT 7
#define SUB_ALIGNMENT_TOPCENTER 8
#define SUB_ALIGNMENT_TOPRIGHT 9
typedef struct subtitle {
int lines;
unsigned long start;
unsigned long end;
char *text[SUB_MAX_TEXT];
unsigned char alignment;
} subtitle;
typedef struct sub_data {
const char *codec;
subtitle *subtitles;
int sub_uses_time;
int sub_num; // number of subtitle structs
int sub_errs;
double fallback_fps;
} sub_data;
// Parameter struct for the format-specific readline functions
struct readline_args {
int utf16;
struct MPOpts *opts;
// subtitle reader state used by some formats
float mpsub_multiplier;
float mpsub_position;
int sub_slacktime;
int uses_time;
/*
Some subtitling formats, namely AQT and Subrip09, define the end of a
subtitle as the beginning of the following. Since currently we read one
subtitle at time, for these format we keep two global *subtitle,
previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle,
so we can change its end when we read current subtitle starting time.
We use a single global unsigned long,
previous_sub_end, for both (and even future) formats, to store the end of
the previous sub: it is initialized to 0 in sub_read_file and eventually
modified by sub_read_aqt_line or sub_read_subrip09_line.
*/
unsigned long previous_sub_end;
};
/* Maximal length of line of a subtitle */
#define LINE_LEN 1000
static int eol(char p) {
return p=='\r' || p=='\n' || p=='\0';
}
/* Remove leading and trailing space */
static void trail_space(char *s) {
int i = 0;
while (isspace(s[i])) ++i;
if (i) strcpy(s, s + i);
i = strlen(s) - 1;
while (i > 0 && isspace(s[i])) s[i--] = '\0';
}
static char *stristr(const char *haystack, const char *needle) {
int len = 0;
const char *p = haystack;
if (!(haystack && needle)) return NULL;
len=strlen(needle);
while (*p != '\0') {
if (strncasecmp(p, needle, len) == 0) return (char*)p;
p++;
}
return NULL;
}
static void sami_add_line(subtitle *current, char *buffer, char **pos) {
char *p = *pos;
*p = 0;
trail_space(buffer);
if (*buffer && current->lines < SUB_MAX_TEXT)
current->text[current->lines++] = strdup(buffer);
*pos = buffer;
}
static subtitle *sub_read_line_sami(stream_t* st, subtitle *current,
struct readline_args *args)
{
int utf16 = args->utf16;
static char line[LINE_LEN+1];
static char *s = NULL, *slacktime_s;
char text[LINE_LEN+1], *p=NULL, *q;
int state;
current->lines = current->start = current->end = 0;
current->alignment = SUB_ALIGNMENT_BOTTOMCENTER;
state = 0;
/* read the first line */
if (!s)
if (!(s = stream_read_line(st, line, LINE_LEN, utf16))) return 0;
do {
switch (state) {
case 0: /* find "START=" or "Slacktime:" */
slacktime_s = stristr (s, "Slacktime:");
if (slacktime_s)
args->sub_slacktime = strtol (slacktime_s+10, NULL, 0) / 10;
s = stristr (s, "Start=");
if (s) {
current->start = strtol (s + 6, &s, 0) / 10;
/* eat '>' */
for (; *s != '>' && *s != '\0'; s++);
s++;
state = 1; continue;
}
break;
case 1: /* find (optional) "<P", skip other TAGs */
for (; *s == ' ' || *s == '\t'; s++); /* strip blanks, if any */
if (*s == '\0') break;
if (*s != '<') { state = 3; p = text; continue; } /* not a TAG */
s++;
if (*s == 'P' || *s == 'p') { s++; state = 2; continue; } /* found '<P' */
for (; *s != '>' && *s != '\0'; s++); /* skip remains of non-<P> TAG */
if (*s == '\0')
break;
s++;
continue;
case 2: /* find ">" */
if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue; }
break;
case 3: /* get all text until '
|