494 changes: 238 additions & 256 deletions mythtv/libs/libmythtv/captions/xine_demux_sputext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fcntl.h>
#include <cctype>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "captions/xine_demux_sputext.h"

#include "mythlogging.h"

#define LOG_MODULE "demux_sputext"
#define LOG_VERBOSE
/*
Expand All @@ -61,33 +65,25 @@ static bool eol(char p) {
return (p=='\r' || p=='\n' || p=='\0');
}

static inline void trail_space(char *s) {
while (isspace(*s)) {
char *copy = s;
do {
// The clang-tidy warning is wrong. All callers have a null
// terminated string. If the null is the first character in the
// string, this loop is never called. If not, there's
// guaranteed to at least be a second character, even if that
// second character is the null.
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign)
copy[0] = copy[1];
copy++;
} while(*copy != 0);
}
int i = strlen(s) - 1;
while (i > 0 && isspace(s[i]))
s[i--] = '\0';
static inline void trail_space(std::string& str)
{
auto mark = str.find_last_of(" \t\r\n");
if (mark != std::string::npos)
str.erase(mark);
mark = str.find_first_not_of(" \t\r\n");
if (mark != std::string::npos)
str.erase(0, mark);
}

/*
* Reimplementation of fgets() using the input->read() method.
*
*/
static char *read_line_from_input(demux_sputext_t *demuxstr, char *line, off_t len) {
static char *read_line_from_input(demux_sputext_t *demuxstr, std::string& line) {
off_t nread = 0;

if ((len - demuxstr->buflen) > 512) {
nread = len - demuxstr->buflen;
line.reserve(LINE_LEN);
if ((line.capacity() - demuxstr->buflen) > 512) {
nread = line.capacity() - demuxstr->buflen;
nread = std::min(nread, demuxstr->rbuffer_len - demuxstr->rbuffer_cur);
if (nread < 0) {
printf("read failed.\n");
Expand All @@ -103,17 +99,18 @@ static char *read_line_from_input(demux_sputext_t *demuxstr, char *line, off_t l

char *s = strchr(demuxstr->buf, '\n');

if (line && (s || demuxstr->buflen)) {
if (s || (demuxstr->buflen > 0)) {

int linelen = s ? (s - demuxstr->buf) + 1 : demuxstr->buflen;
size_t linelen = s ? (s - demuxstr->buf) + 1 : demuxstr->buflen;
linelen = std::min(linelen, line.capacity());

memcpy(line, demuxstr->buf, linelen);
line[linelen] = '\0';
line.resize(linelen);
memcpy(line.data(), demuxstr->buf, linelen);

memmove(demuxstr->buf, &demuxstr->buf[linelen], SUB_BUFSIZE - linelen);
demuxstr->buflen -= linelen;

return line;
return line.data();
}

return nullptr;
Expand All @@ -122,18 +119,17 @@ static char *read_line_from_input(demux_sputext_t *demuxstr, char *line, off_t l

static subtitle_t *sub_read_line_sami(demux_sputext_t *demuxstr, subtitle_t *current) {

static std::array<char,LINE_LEN + 1> s_line;
static std::string s_line;
static char *s_s = nullptr;
char text[LINE_LEN + 1];
std::string text;

char *p = nullptr;
current->start = 0;
current->end = -1;
int state = 0;

/* read the first line */
if (!s_s)
if (!(s_s = read_line_from_input(demuxstr, s_line.data(), LINE_LEN))) return nullptr;
if (!(s_s = read_line_from_input(demuxstr, s_line))) return nullptr;

do {
switch (state) {
Expand All @@ -151,31 +147,32 @@ static subtitle_t *sub_read_line_sami(demux_sputext_t *demuxstr, subtitle_t *cur
break;

case 2: /* find ">" */
if ((s_s = strchr (s_s, '>'))) { s_s++; state = 3; p = text; continue; }
if ((s_s = strchr (s_s, '>'))) { s_s++; state = 3; text.clear(); continue; }
break;

case 3: /* get all text until '<' appears */
if (*s_s == '\0') { break; }
else if (strncasecmp (s_s, "&nbsp;", 6) == 0) { *p++ = ' '; s_s += 6; }
else if (strncasecmp (s_s, "&nbsp;", 6) == 0) { text += ' '; s_s += 6; }
else if (*s_s == '\r') { s_s++; }
else if (strncasecmp (s_s, "<br>", 4) == 0 || *s_s == '\n') {
*p = '\0'; p = text; trail_space (text);
if (text[0] != '\0')
current->text.push_back( strdup (text) );
trail_space (text);
if (!text.empty())
current->text.push_back(text);
text.clear();
if (*s_s == '\n') s_s++; else s_s += 4;
}
else if (*s_s == '<') { state = 4; }
else *p++ = *s_s++;
else text += *s_s++;
continue;

case 4: /* get current->end or skip <TAG> */
char *q = strcasestr (s_s, "start=");
if (q) {
current->end = strtol (q + 6, &q, 0) / 10 - 1;
*p = '\0'; trail_space (text);
if (text[0] != '\0')
current->text.push_back( strdup (text) );
if (current->text.size() > 0) { state = 99; break; }
trail_space (text);
if (!text.empty())
current->text.push_back(text);
if (!current->text.empty()) { state = 99; break; }
state = 0; continue;
}
s_s = strchr (s_s, '>');
Expand All @@ -184,7 +181,7 @@ static subtitle_t *sub_read_line_sami(demux_sputext_t *demuxstr, subtitle_t *cur
}

/* read next line */
if (state != 99 && !(s_s = read_line_from_input (demuxstr, s_line.data(), LINE_LEN)))
if (state != 99 && !(s_s = read_line_from_input (demuxstr, s_line)))
return nullptr;

} while (state != 99);
Expand All @@ -207,6 +204,9 @@ static subtitle_t *sub_read_line_sami(demux_sputext_t *demuxstr, subtitle_t *cur
* error.
*/
static char *sub_readtext(char *source, std::string& dest) {
if (source == nullptr)
return nullptr;

int len=0;
char *p=source;

Expand All @@ -225,18 +225,17 @@ static char *sub_readtext(char *source, std::string& dest) {

static subtitle_t *sub_read_line_microdvd(demux_sputext_t *demuxstr, subtitle_t *current) {

char line[LINE_LEN + 1];
char line2[LINE_LEN + 1];
std::string line; line.reserve(LINE_LEN + 1);
std::string line2; line2.reserve(LINE_LEN + 1);

current->end=-1;
do {
if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
} 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)
if (!read_line_from_input (demuxstr, line)) return nullptr;
} while ((sscanf (line.c_str(), "{%ld}{}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), line2.data()) !=2) &&
(sscanf (line.c_str(), "{%ld}{%ld}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), &(current->end),line2.data()) !=3)
);

char *p=line2;
char *next=p;
char *next=line2.data();
std::string out {};
while ((next = sub_readtext (next, out))) {
if (next==ERR) return (subtitle_t *)ERR;
Expand All @@ -249,28 +248,28 @@ static subtitle_t *sub_read_line_microdvd(demux_sputext_t *demuxstr, subtitle_t

static subtitle_t *sub_read_line_subviewer(demux_sputext_t *demuxstr, subtitle_t *current) {

char line[LINE_LEN + 1];
std::string line;
int a1=0,a2=0,a3=0,a4=0,b1=0,b2=0,b3=0,b4=0; // NOLINT(readability-isolate-declaration)

while (true) {
if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
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)
if (!read_line_from_input(demuxstr, line)) return nullptr;
if (sscanf (line.c_str(), "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) {
if (sscanf (line.c_str(), "%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(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return nullptr;

char *p=line;
char *p=line.data();
while (true) {
char *q=nullptr;
int len = 0;
for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' &&
(strncasecmp(p,"[br]",4) != 0); p++,len++);
current->text.push_back(std::string(q, len));
current->text.emplace_back(q, len);
if (!*p || *p=='\r' || *p=='\n') break;
if (*p=='[') while (*p++!=']');
if (*p=='|') p++;
Expand All @@ -281,111 +280,102 @@ static subtitle_t *sub_read_line_subviewer(demux_sputext_t *demuxstr, subtitle_t
}

static subtitle_t *sub_read_line_subrip(demux_sputext_t *demuxstr,subtitle_t *current) {
char line[LINE_LEN + 1];
std::string line;
int a1=0,a2=0,a3=0,a4=0,b1=0,b2=0,b3=0,b4=0; // NOLINT(readability-isolate-declaration)
int i = 0;

do {
if(!read_line_from_input(demuxstr,line,LINE_LEN))
if(!read_line_from_input(demuxstr,line))
return nullptr;
i = sscanf(line,"%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4);
i = sscanf(line.c_str(),"%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 end_sub=0;
bool end_sub = false;
do {
char *p = nullptr; /* pointer to the curently read char */
char temp_line[SUB_BUFSIZE]; /* subtitle line that will be transfered to current->text[i] */
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 */
temp_line[SUB_BUFSIZE-1]='\0'; /* just in case... */
if(!read_line_from_input(demuxstr,line,LINE_LEN))
return (current->text.size() > 0) ? current : nullptr;
for(temp_index=0,p=line;*p!='\0' && !end_sub && temp_index<SUB_BUFSIZE;p++) {
std::string temp_line; /* subtitle line that will be transfered to current->text[i] */
temp_line.reserve(SUB_BUFSIZE);
if(!read_line_from_input(demuxstr,line))
return (!current->text.empty()) ? current : nullptr;
for (p=line.data(); *p!='\0' && !end_sub; p++) {
bool eol = false;
switch(*p) {
case '\\':
if(*(p+1)=='N' || *(p+1)=='n') {
temp_line[temp_index++]='\0'; /* end of curent line */
eol = true;
p++;
} else
temp_line[temp_index++]=*p;
temp_line += *p;
break;
case '{':
// The different code for these if/else clauses is ifdef'd out.
// NOLINTNEXTLINE(bugprone-branch-clone)
if(strncmp(p,"{\\i1}",5) == 0) {
#if 0 /* italic not implemented in renderer, ignore them for now */
if(!strncmp(p,"{\\i1}",5) && temp_index+3<SUB_BUFSIZE) {
temp_line[temp_index++]='<';
temp_line[temp_index++]='i';
temp_line[temp_index++]='>';
#else
if(strncmp(p,"{\\i1}",5) == 0) { // NOLINT(bugprone-branch-clone)
temp_line.append("<i>");
#endif
p+=4;
}
#if 0 /* italic not implemented in renderer, ignore them for now */
else if(!strncmp(p,"{\\i0}",5) && temp_index+4<SUB_BUFSIZE) {
temp_line[temp_index++]='<';
temp_line[temp_index++]='/';
temp_line[temp_index++]='i';
temp_line[temp_index++]='>';
#else
else if(strncmp(p,"{\\i0}",5) == 0) {
#if 0 /* italic not implemented in renderer, ignore them for now */
temp_line.append("</i>");
#endif
p+=4;
}
else
temp_line[temp_index++]=*p;
temp_line += *p;
break;
case '\r': /* just ignore '\r's */
break;
case '\n':
temp_line[temp_index++]='\0';
eol = true;
break;
default:
temp_line[temp_index++]=*p;
temp_line += *p;
break;
}
if(temp_index>0) {
if(temp_index==SUB_BUFSIZE)
printf("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 */
current->text.push_back(std::string(temp_line,temp_index));
temp_index=0;
} else
end_sub=1;
if (eol) {
if (!temp_line.empty())
{
current->text.push_back(temp_line);
temp_line.clear();
} else {
end_sub = true;
}
}
}
} while(end_sub == 0);
} while (!end_sub);
return current;
}

static subtitle_t *sub_read_line_vplayer(demux_sputext_t *demuxstr,subtitle_t *current) {
char line[LINE_LEN + 1];
std::string line;
int a1=0,a2=0,a3=0,b1=0,b2=0,b3=0; // NOLINT(readability-isolate-declaration)

while (current->text.empty()) {
if( demuxstr->next_line[0] == '\0' ) { /* if the buffer is empty.... */
if( !read_line_from_input(demuxstr, line, LINE_LEN) ) return nullptr;
if( demuxstr->next_line.empty() ) {
/* if the buffer is empty.... */
if( !read_line_from_input(demuxstr, line) ) return nullptr;
} else {
/* ... get the current line from buffer. */
strncpy( line, demuxstr->next_line, LINE_LEN);
line[LINE_LEN] = '\0'; /* I'm scared. This makes me feel better. */
demuxstr->next_line[0] = '\0'; /* mark the buffer as empty. */
line = demuxstr->next_line;
demuxstr->next_line.clear();
}
/* Initialize buffer with next line */
if( ! read_line_from_input( demuxstr, demuxstr->next_line, LINE_LEN) ) {
demuxstr->next_line[0] = '\0';
if( ! read_line_from_input( demuxstr, demuxstr->next_line) ) {
demuxstr->next_line.clear();
return nullptr;
}
if( (sscanf( line, "%d:%d:%d:", &a1, &a2, &a3) < 3) ||
(sscanf( demuxstr->next_line, "%d:%d:%d:", &b1, &b2, &b3) < 3) )
if( (sscanf( line.c_str(), "%d:%d:%d:", &a1, &a2, &a3) < 3) ||
(sscanf( demuxstr->next_line.c_str(), "%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;
char *p=line.data();
/* finds the body of the subtitle_t */
for (int i=0; i<3; i++){
char *p2=strchr( p, ':');
Expand All @@ -411,31 +401,32 @@ static subtitle_t *sub_read_line_rt(demux_sputext_t *demuxstr,subtitle_t *curren
* I couldn't check it since DTD is not included.
* WARNING: full XML parses can be required for proper parsing
*/
char line[LINE_LEN + 1];
std::string line;
int a1=0,a2=0,a3=0,a4=0,b1=0,b2=0,b3=0,b4=0; // NOLINT(readability-isolate-declaration)
int plen = 0;

while (current->text.empty()) {
if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
if (!read_line_from_input(demuxstr, line)) return nullptr;
/*
* TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0
* to describe the same moment in time. Maybe there are even more formats in use.
*/
if (sscanf (line, R"(<Time Begin="%d:%d:%d.%d" End="%d:%d:%d.%d")",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8)
if (sscanf (line.c_str(), R"(<Time Begin="%d:%d:%d.%d" End="%d:%d:%d.%d")",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8)

plen=a1=a2=a3=a4=b1=b2=b3=b4=0;
if (
(sscanf (line, R"(<%*[tT]ime %*[bB]egin="%d:%d" %*[Ee]nd="%d:%d"%*[^<]<clear/>%n)",&a2,&a3,&b2,&b3,&plen) < 4) &&
(sscanf (line, R"(<%*[tT]ime %*[bB]egin="%d:%d" %*[Ee]nd="%d:%d.%d"%*[^<]<clear/>%n)",&a2,&a3,&b2,&b3,&b4,&plen) < 5) &&
(sscanf (line.c_str(), R"(<%*[tT]ime %*[bB]egin="%d:%d" %*[Ee]nd="%d:%d"%*[^<]<clear/>%n)",&a2,&a3,&b2,&b3,&plen) < 4) &&
(sscanf (line.c_str(), R"(<%*[tT]ime %*[bB]egin="%d:%d" %*[Ee]nd="%d:%d.%d"%*[^<]<clear/>%n)",&a2,&a3,&b2,&b3,&b4,&plen) < 5) &&
/* (sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&plen) < 5) && */
(sscanf (line, R"(<%*[tT]ime %*[bB]egin="%d:%d.%d" %*[Ee]nd="%d:%d.%d"%*[^<]<clear/>%n)",&a2,&a3,&a4,&b2,&b3,&b4,&plen) < 6) &&
(sscanf (line, R"(<%*[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)
(sscanf (line.c_str(), R"(<%*[tT]ime %*[bB]egin="%d:%d.%d" %*[Ee]nd="%d:%d.%d"%*[^<]<clear/>%n)",&a2,&a3,&a4,&b2,&b3,&b4,&plen) < 6) &&
(sscanf (line.c_str(), R"(<%*[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;
/* 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;
size_t index = line.find("<clear/>");
char *next = (index != std::string::npos) ? &line[index+8] : nullptr;
std::string out {};
while ((next = sub_readtext (next, out))) {
if (next==ERR)
Expand All @@ -461,27 +452,28 @@ static subtitle_t *sub_read_line_ssa(demux_sputext_t *demuxstr,subtitle_t *curre
int sec2 = 0;
int hunsec2 = 0;
int nothing = 0;
char line[LINE_LEN + 1];
char line3[LINE_LEN + 1];
std::string line;
std::string line3; line3.resize(LINE_LEN);
char *tmp = nullptr;

do {
if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
} while (sscanf (line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,"
if (!read_line_from_input(demuxstr, line)) return nullptr;
} while (sscanf (line.data(), "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,"
"%" LINE_LEN_QUOT "[^\n\r]", &nothing,
&hour1, &min1, &sec1, &hunsec1,
&hour2, &min2, &sec2, &hunsec2,
line3) < 9
line3.data()) < 9
&&
sscanf (line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,"
sscanf (line.data(), "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,"
"%" LINE_LEN_QUOT "[^\n\r]", &nothing,
&hour1, &min1, &sec1, &hunsec1,
&hour2, &min2, &sec2, &hunsec2,
line3) < 9 );
line3.data()) < 9 );

char *line2=strchr(line3, ',');
if (!line2)
size_t index = line3.find(',');
if (index == std::string::npos)
return nullptr;
char *line2 = &line3[index];

for (comma = 4; comma < s_maxComma; comma ++)
{
Expand All @@ -500,11 +492,11 @@ static subtitle_t *sub_read_line_ssa(demux_sputext_t *demuxstr,subtitle_t *curre
current->end = 360000*hour2 + 6000*min2 + 100*sec2 + hunsec2;

while (((tmp=strstr(line2, "\\n")) != nullptr) || ((tmp=strstr(line2, "\\N")) != nullptr) ){
current->text.push_back(std::string(line2, tmp-line2));
current->text.emplace_back(line2, tmp-line2);
line2=tmp+2;
}

current->text.push_back(line2);
current->text.emplace_back(line2);

return current;
}
Expand All @@ -521,78 +513,72 @@ static subtitle_t *sub_read_line_ssa(demux_sputext_t *demuxstr,subtitle_t *curre
*/

static subtitle_t *sub_read_line_pjs (demux_sputext_t *demuxstr, subtitle_t *current) {
char line[LINE_LEN + 1];
char text[LINE_LEN + 1];
char *s = nullptr;
char *d = nullptr;
std::string line;

if (!read_line_from_input(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return nullptr;
for (s = line; *s && isspace(*s); s++);
if (*s == 0)
size_t mark = line.find_first_not_of(" \t\r\n");
if (mark != std::string::npos)
line.erase(0, mark);
if (line.empty())
return nullptr;
if (sscanf (line, "%ld,%ld,", &(current->start),
if (sscanf (line.data(), "%ld,%ld,", &(current->start),
&(current->end)) <2)
return (subtitle_t *)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 (subtitle_t *)ERR;
}
/* copy the string to the text buffer */
for (s++, d=text; *s && *s!='"'; s++, d++)
*d=*s;
*d=0;
current->text.push_back(text);
auto start = line.find('\"');
if (start == std::string::npos)
return (subtitle_t *)ERR;
auto end = line.find('\"', start + 1);
if (end == std::string::npos)
return (subtitle_t *)ERR;
current->text.push_back(line.substr(start+1, end));

return current;
}

static subtitle_t *sub_read_line_mpsub (demux_sputext_t *demuxstr, subtitle_t *current) {
char line[LINE_LEN + 1];
std::string line;
float a = NAN;
float b = NAN;

do {
if (!read_line_from_input(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return nullptr;
} while (sscanf (line, "%f %f", &a, &b) !=2);
} while (sscanf (line.c_str(), "%f %f", &a, &b) !=2);

demuxstr->mpsub_position += (a*100.0F);
current->start = (int) demuxstr->mpsub_position;
demuxstr->mpsub_position += (b*100.0F);
current->end = (int) demuxstr->mpsub_position;

while (true) {
if (!read_line_from_input(demuxstr, line, LINE_LEN))
return (current->text.size() > 0) ? current : nullptr;
if (!read_line_from_input(demuxstr, line))
return (!current->text.empty()) ? current : nullptr;

char *p=line;
while (isspace(*p))
p++;
size_t mark = line.find_first_not_of(" \t\r\n");
if (mark != std::string::npos)
line.erase(0, mark);

if (eol(*p) && current->text.size() > 0)
if (eol(line[0]) && !current->text.empty())
return current;

if (eol(*p))
if (eol(line[0]))
return nullptr;

char *q = nullptr;
for (q=p; !eol(*q); q++);
for (q=line.data(); !eol(*q); q++);
*q='\0';
if (strlen(p)) {
current->text.push_back(p);
/* printf(">%s<\n",p); */
line.resize(strlen(line.c_str()));
if (!line.empty()) {
current->text.push_back(line);
/* printf(">%s<\n",line.data()); */
} else {
if (current->text.size())
if (!current->text.empty())
return current;
return nullptr;
}
Expand All @@ -602,28 +588,28 @@ static subtitle_t *sub_read_line_mpsub (demux_sputext_t *demuxstr, subtitle_t *c
}

static subtitle_t *sub_read_line_aqt (demux_sputext_t *demuxstr, subtitle_t *current) {
char line[LINE_LEN + 1];
std::string line;

while (true) {
/* try to locate next subtitle_t */
if (!read_line_from_input(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return nullptr;
if (!(sscanf (line, "-->> %ld", &(current->start)) <1))
if (!(sscanf (line.c_str(), "-->> %ld", &(current->start)) <1))
break;
}

if (!read_line_from_input(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return nullptr;

std::string out {};
sub_readtext((char *) &line,out);
sub_readtext(line.data(),out);
current->text.push_back(out);
current->end = -1;

if (!read_line_from_input(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return current;;

sub_readtext((char *) &line,out);
sub_readtext(line.data(),out);
current->text.push_back(out);

if ((current->text[0][0]==0) && (current->text[1][0]==0)) {
Expand All @@ -634,46 +620,44 @@ static subtitle_t *sub_read_line_aqt (demux_sputext_t *demuxstr, subtitle_t *cur
}

static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *demuxstr, subtitle_t *current) {
char line1[LINE_LEN+1];
char line2[LINE_LEN+1];
char directive[LINE_LEN+1];
std::string line1;
std::string line2;
std::string directive; directive.resize(LINE_LEN);
char *p = nullptr;
char *q = nullptr;
unsigned a1=0, a2=0, a3=0, a4=0, b1=0, b2=0, b3=0, b4=0; // NOLINT(readability-isolate-declaration)
unsigned comment = 0;
static unsigned s_jacoTimeRes = 30;
static int s_jacoShift = 0;
static uint32_t s_jacoTimeRes = 30;
static uint32_t s_jacoShift = 0;

memset(line1, 0, LINE_LEN+1);
memset(line2, 0, LINE_LEN+1);
memset(directive, 0, LINE_LEN+1);
while (current->text.empty()) {
if (!read_line_from_input(demuxstr, line1, LINE_LEN)) {
if (!read_line_from_input(demuxstr, line1)) {
return nullptr;
}
// Support continuation lines
if (strlen(line1) >= 2) {
while ((line1[strlen(line1)-2] == '\\') && (line1[strlen(line1)-1] == '\n')) {
int newlen = strlen(line1)-2;
if (!read_line_from_input(demuxstr, line2, LINE_LEN - newlen))
if (line1.size() >= 2) {
while ((line1[line1.size()-2] == '\\') && (line1[line1.size()-1] == '\n')) {
line1.resize(line1.size()-2);
if (!read_line_from_input(demuxstr, line2))
return nullptr;
p = line2;
while ((*p == ' ') || (*p == '\t')) {
++p;
}
strcpy(line1+newlen, p);
size_t index = line2.find_first_not_of(" \t\r\n");
if (index != std::string::npos)
line2.erase(0, index);
line1 += line2;
}
}
line2.resize(0);
line2.resize(LINE_LEN);
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) {
(line1.c_str(), "%u:%u:%u.%u %u:%u:%u.%u %" LINE_LEN_QUOT "[^\n\r]", &a1, &a2, &a3, &a4,
&b1, &b2, &b3, &b4, line2.data()) < 9) {
if (sscanf(line1.data(), "@%u @%u %" LINE_LEN_QUOT "[^\n\r]", &a4, &b4, line2.data()) < 3) {
if (line1[0] == '#') {
int hours = 0;
int minutes = 0;
int seconds = 0;
int delta = 0;
unsigned units = s_jacoShift;
uint32_t units = s_jacoShift;
int inverter = 1;
switch (toupper(line1[1])) {
case 'S':
Expand Down Expand Up @@ -739,38 +723,36 @@ static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *demuxstr, subtitle_t
long) (((b1 * 3600 + b2 * 60 + b3) * s_jacoTimeRes + b4 +
s_jacoShift) * 100.0 / s_jacoTimeRes);
}
p = line2;
p = line2.data();
while ((*p == ' ') || (*p == '\t')) {
++p;
}
if (isalpha(*p)||*p == '[') {
if (sscanf(p, "%" LINE_LEN_QUOT "s %" LINE_LEN_QUOT "[^\n\r]", directive, line1) < 2)
if (sscanf(p, "%" LINE_LEN_QUOT "s %" LINE_LEN_QUOT "[^\n\r]", directive.data(), line1.data()) < 2)
return (subtitle_t *)ERR;
int jLength = strlen(directive);
for (int cont = 0; cont < jLength; ++cont) {
if (isalpha(*(directive + cont)))
*(directive + cont) = toupper(*(directive + cont));
}
if ((strstr(directive, "RDB") != nullptr)
|| (strstr(directive, "RDC") != nullptr)
|| (strstr(directive, "RLB") != nullptr)
|| (strstr(directive, "RLG") != nullptr)) {
directive.resize(strlen(directive.c_str()));
std::transform(directive.begin(), directive.end(), directive.begin(),
[](unsigned char c){ return std::toupper(c);});
if ( (directive.find("RDB") != std::string::npos)
|| (directive.find("RDC") != std::string::npos)
|| (directive.find("RLB") != std::string::npos)
|| (directive.find("RLG") != std::string::npos)) {
continue;
}
/* no alignment */
#if 0
if (strstr(directive, "JL") != nullptr) {
if (directive.find("JL") != std::string::npos) {
current->alignment = SUB_ALIGNMENT_HLEFT;
} else if (strstr(directive, "JR") != nullptr) {
} else if (directive.find("JR") != std::string::npos) {
current->alignment = SUB_ALIGNMENT_HRIGHT;
} else {
current->alignment = SUB_ALIGNMENT_HCENTER;
}
#endif
strcpy(line2, line1);
p = line2;
line2 = line1;
p = line2.data();
}
for (q = line1; (!eol(*p)); ++p) {
for (q = line1.data(); (!eol(*p)); ++p) {
switch (*p) {
case '{':
comment++;
Expand Down Expand Up @@ -801,7 +783,7 @@ static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *demuxstr, subtitle_t
case '\\':
if (*(p + 1) == 'n') {
*q = '\0';
q = line1;
q = line1.data();
current->text.push_back(line1);
++p;
break;
Expand All @@ -826,12 +808,14 @@ static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *demuxstr, subtitle_t
(*(p + 1) == '~') || (*(p + 1) == '{')) {
++p;
} else if (eol(*(p + 1))) {
if (!read_line_from_input(demuxstr, directive, LINE_LEN))
std::string tmpstr {};
if (!read_line_from_input(demuxstr, tmpstr))
return nullptr;
trail_space(directive);
strncat(line2, directive,
((LINE_LEN > 511) ? LINE_LEN-1 : 511)
- strlen(line2));
trail_space(tmpstr);
// The std::string addition can reallocate...
size_t offset = p - line2.data();
line2 += tmpstr;
p = line2.data() + offset;
break;
}
// Checked xine-lib-1.2.8. No fix there. Seems like it
Expand All @@ -851,48 +835,44 @@ static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *demuxstr, subtitle_t
}

static subtitle_t *sub_read_line_subviewer2(demux_sputext_t *demuxstr, subtitle_t *current) {
char line[LINE_LEN+1];
std::string line;
int a1=0,a2=0,a3=0,a4=0; // NOLINT(readability-isolate-declaration)
char *p=nullptr;

while (current->text.empty()) {
if (!read_line_from_input(demuxstr, line, LINE_LEN)) return nullptr;
if (!read_line_from_input(demuxstr, line)) return nullptr;
if (line[0]!='{')
continue;
if (sscanf (line, "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4) < 4)
if (sscanf (line.data(), "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4) < 4)
continue;
current->start = a1*360000+a2*6000+a3*100+a4/10;
for (;;) {
if (!read_line_from_input(demuxstr, line, LINE_LEN)) break;
if (!read_line_from_input(demuxstr, line)) break;
if (line[0]=='}') break;
int len=0;
for (p=line; *p!='\n' && *p!='\r' && *p; ++p,++len);
if (len) {
current->text.push_back(std::string(line, len));
} else {
size_t len = line.find_first_of("\n\r");
if (len == 0)
break;
}
current->text.push_back(line.substr(0, len));
}
}
return current;
}

static subtitle_t *sub_read_line_subrip09 (demux_sputext_t *demuxstr, subtitle_t *current) {
char line[LINE_LEN + 1];
std::string line;
int h = 0;
int m = 0;
int s = 0;

do {
if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
} while (sscanf (line, "[%d:%d:%d]", &h, &m, &s) != 3);
if (!read_line_from_input (demuxstr, line)) return nullptr;
} while (sscanf (line.data(), "[%d:%d:%d]", &h, &m, &s) != 3);

if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
if (!read_line_from_input (demuxstr, line)) return nullptr;

current->start = 360000 * h + 6000 * m + 100 * s;
current->end = -1;

char *next=line;
char *next=line.data();
std::string out {};
while ((next = sub_readtext (next, out))) {
if (next==ERR) return (subtitle_t *)ERR;
Expand All @@ -908,18 +888,18 @@ static subtitle_t *sub_read_line_subrip09 (demux_sputext_t *demuxstr, subtitle_t
*/

static subtitle_t *sub_read_line_mpl2(demux_sputext_t *demuxstr, subtitle_t *current) {
char line[LINE_LEN+1];
char line2[LINE_LEN+1];
std::string line;
std::string line2; line2.resize(LINE_LEN);

do {
if (!read_line_from_input (demuxstr, line, LINE_LEN)) return nullptr;
} while ((sscanf (line,
if (!read_line_from_input (demuxstr, line)) return nullptr;
} while ((sscanf (line.data(),
"[%ld][%ld]%" LINE_LEN_QUOT "[^\r\n]",
&(current->start), &(current->end), line2) < 3));
&(current->start), &(current->end), line2.data()) < 3));
current->start *= 10;
current->end *= 10;

char *p=line2;
char *p=line2.data();
char *next=p;
std::string out {};
while ((next = sub_readtext (next, out))) {
Expand All @@ -934,39 +914,41 @@ static subtitle_t *sub_read_line_mpl2(demux_sputext_t *demuxstr, subtitle_t *cur

static int sub_autodetect (demux_sputext_t *demuxstr) {

char line[LINE_LEN + 1];
char line2[LINE_LEN + 1];
std::string line;
int i = 0;
int j = 0;
char p = 0;

while (j < 100) {
j++;
if (!read_line_from_input(demuxstr, line, LINE_LEN))
if (!read_line_from_input(demuxstr, line))
return FORMAT_UNKNOWN;

if ((sscanf (line, "{%d}{}", &i)==1) ||
(sscanf (line, "{%d}{%d}", &i, &i)==2)) {
std::transform(line.begin(), line.end(), line.begin(),
[](unsigned char c){ return std::tolower(c);});

if ((sscanf (line.data(), "{%d}{}", &i)==1) ||
(sscanf (line.data(), "{%d}{%d}", &i, &i)==2)) {
demuxstr->uses_time=0;
return FORMAT_MICRODVD;
}

if (sscanf (line, "%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d", &i, &i, &i, &i, &i, &i, &i, &i)==8) {
if (sscanf (line.data(), "%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d", &i, &i, &i, &i, &i, &i, &i, &i)==8) {
demuxstr->uses_time=1;
return FORMAT_SUBRIP;
}

if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){
if (sscanf (line.data(), "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){
demuxstr->uses_time=1;
return FORMAT_SUBVIEWER;
}

if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){
if (sscanf (line.data(), "%d:%d:%d,%d,%d:%d:%d,%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){
demuxstr->uses_time=1;
return FORMAT_SUBVIEWER;
}

if (strstr (line, "<SAMI>")) {
if (line.find("<sami>") != std::string::npos) {
demuxstr->uses_time=1;
return FORMAT_SAMI;
}
Expand All @@ -975,54 +957,56 @@ static int sub_autodetect (demux_sputext_t *demuxstr) {
// seconds. Add a final "the rest of the line" argument to get
// that validation, so that JACO subtitles can be distinguished
// from this format.
if (sscanf (line, "%d:%d:%d:%" LINE_LEN_QUOT "[^\n\r]",
&i, &i, &i, line2 )==4) {
std::string line2; line2.resize(LINE_LEN);
if (sscanf (line.data(), "%d:%d:%d:%" LINE_LEN_QUOT "[^\n\r]",
&i, &i, &i, line2.data() )==4) {
demuxstr->uses_time=1;
return FORMAT_VPLAYER;
}
/*
* A RealText format is a markup language, starts with <window> tag,
* options (behaviour modifiers) are possible.
*/
if ( strncasecmp(line, "<window", 7) == 0 ) {
if (line.find("<window") != std::string::npos) {
demuxstr->uses_time=1;
return FORMAT_RT;
}
if ((memcmp(line, "Dialogue: Marked", 16) == 0) || (memcmp(line, "Dialogue: ", 10) == 0)) {
if ((line.find("dialogue: marked") != std::string::npos) ||
(line.find("dialogue: ") != std::string::npos)) {
demuxstr->uses_time=1;
return FORMAT_SSA;
}
if (sscanf (line, "%d,%d,\"%c", &i, &i, (char *) &i) == 3) {
if (sscanf (line.data(), "%d,%d,\"%c", &i, &i, (char *) &i) == 3) {
demuxstr->uses_time=0;
return FORMAT_PJS;
}
if (sscanf (line, "FORMAT=%d", &i) == 1) {
if (sscanf (line.data(), "format=%d", &i) == 1) {
demuxstr->uses_time=0;
return FORMAT_MPSUB;
}
if (sscanf (line, "FORMAT=TIM%c", &p)==1 && p=='E') {
if (sscanf (line.data(), "format=tim%c", &p)==1 && p=='e') {
demuxstr->uses_time=1;
return FORMAT_MPSUB;
}
if (strstr (line, "-->>")) {
if (line.find("-->>") != std::string::npos) {
demuxstr->uses_time=0;
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) {
if (sscanf(line.data(), "@%d @%d", &i, &i) == 2 ||
sscanf(line.data(), "%d:%d:%d.%d %d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) {
demuxstr->uses_time = 1;
return FORMAT_JACOBSUB;
}
if (sscanf(line, "{T %d:%d:%d:%d",&i, &i, &i, &i) == 4) {
if (sscanf(line.data(), "{t %d:%d:%d:%d",&i, &i, &i, &i) == 4) {
demuxstr->uses_time = 1;
return FORMAT_SUBVIEWER2;
}
if (sscanf(line, "[%d:%d:%d]", &i, &i, &i) == 3) {
if (sscanf(line.data(), "[%d:%d:%d]", &i, &i, &i) == 3) {
demuxstr->uses_time = 1;
return FORMAT_SUBRIP09;
}

if (sscanf (line, "[%d][%d]", &i, &i) == 2) {
if (sscanf (line.data(), "[%d][%d]", &i, &i) == 2) {
demuxstr->uses_time = 1;
return FORMAT_MPL2;
}
Expand Down Expand Up @@ -1124,7 +1108,5 @@ bool sub_read_file (demux_sputext_t *demuxstr) {
}
#endif

// No memory leak of 'sub' here. 'Sub' always points to an element in 'first'.
// NOLINT(clang-analyzer-unix.Malloc)
return true;
}
8 changes: 4 additions & 4 deletions mythtv/libs/libmythtv/captions/xine_demux_sputext.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

struct subtitle_t {

long start; ///< Starting time in msec or starting frame
long end; ///< Ending time in msec or starting frame
int64_t start; ///< Starting time in msec or starting frame
int64_t end; ///< Ending time in msec or starting frame

std::vector<std::string> text; ///< The subtitle text lines.
};
Expand All @@ -53,8 +53,8 @@ struct demux_sputext_t {
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 */

std::string next_line; /* a buffer for next line read from file */
/* only used by vplayer */
};


Expand Down