mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
364 lines
12 KiB
C++
364 lines
12 KiB
C++
/*
|
|
ogmmerge -- utility for splicing together ogg bitstreams
|
|
from component media subtypes
|
|
|
|
r_vobsub.cpp
|
|
VobSub text subtitle reader module
|
|
|
|
Written by Moritz Bunkus <moritz@bunkus.org>
|
|
Based on Xiph.org's 'oggmerge' found in their CVS repository
|
|
See http://www.xiph.org
|
|
|
|
Distributed under the GPL
|
|
see the file COPYING for details
|
|
or visit http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <ogg/ogg.h>
|
|
|
|
#include "ogmmerge.h"
|
|
#include "ogmstreams.h"
|
|
#include "queue.h"
|
|
#include "r_vobsub.h"
|
|
#include "subtitles.h"
|
|
|
|
#ifdef DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif
|
|
|
|
#define istimestampstr(s) (!strncmp(s, "timestamp: ", 11))
|
|
#define iscommafileposstr(s) (!strncmp(s, ", filepos: ", 11))
|
|
#define iscolon(s) (*(s) == ':')
|
|
#define istwodigits(s) (isdigit(*(s)) && isdigit(*(s + 1)))
|
|
#define isthreedigits(s) (isdigit(*(s)) && isdigit(*(s + 1)) && \
|
|
isdigit(*(s + 2)))
|
|
#define istwodigitscolon(s) (istwodigits(s) && iscolon(s + 2))
|
|
#define istimestamp(s) (istwodigitscolon(s) && \
|
|
istwodigitscolon(s + 3) && \
|
|
istwodigitscolon(s + 6) && \
|
|
isthreedigits(s + 9))
|
|
#define ishexdigit(s) (isdigit(s) || \
|
|
(strchr("abcdefABCDEF", s) != NULL))
|
|
#define isfilepos(s) (ishexdigit(*(s)) && ishexdigit(*(s + 1)) && \
|
|
ishexdigit(*(s + 2)) && \
|
|
ishexdigit(*(s + 3)) && \
|
|
ishexdigit(*(s + 4)) && \
|
|
ishexdigit(*(s + 5)) && \
|
|
ishexdigit(*(s + 6)) && \
|
|
ishexdigit(*(s + 7)) && \
|
|
ishexdigit(*(s + 8)))
|
|
#define isvobsubline(s) (istimestampstr(s) && istimestamp(s + 11) && \
|
|
iscommafileposstr(s + 23) && \
|
|
isfilepos(s + 34))
|
|
|
|
int vobsub_reader_c::probe_file(FILE *file, u_int64_t size) {
|
|
char chunk[2048];
|
|
|
|
if (fseek(file, 0, SEEK_SET) != 0)
|
|
return 0;
|
|
if (fgets(chunk, 2047, file) == NULL)
|
|
return 0;
|
|
if (strncmp(chunk, "# VobSub index file, v7",
|
|
strlen("# VobSub index file, v7")))
|
|
return 0;
|
|
if (fseek(file, 0, SEEK_SET) != 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
vobsub_reader_c::vobsub_reader_c(char *fname, audio_sync_t *nasync,
|
|
range_t *nrange, char **ncomments)
|
|
throw (error_c) {
|
|
char *name;
|
|
|
|
if ((file = fopen(fname, "r")) == NULL)
|
|
throw error_c("vobsub_reader: Could not open source file.");
|
|
if (!vobsub_reader_c::probe_file(file, 0))
|
|
throw error_c("vobsub_reader: Source is not a valid VobSub index file.");
|
|
|
|
name = strdup(fname);
|
|
if (name == NULL)
|
|
die("strdup");
|
|
if ((strlen(name) > 4) && (name[strlen(name) - 4] == '.'))
|
|
name[strlen(name) - 4] = 0;
|
|
else {
|
|
name = (char *)realloc(name, strlen(name) + 5);
|
|
if (name == NULL)
|
|
die("realloc");
|
|
}
|
|
strcat(name, ".sub");
|
|
if ((subfile = fopen(name, "r")) == NULL)
|
|
throw error_c("vobsub_reader: Could not open the sub file.");
|
|
|
|
vobsub_packetizer = NULL;
|
|
all_packetizers = NULL;
|
|
num_packetizers = 0;
|
|
if (verbose)
|
|
fprintf(stderr, "Using VobSub subtitle reader for %s/%s.\n+-> Using " \
|
|
"VobSub subtitle output module for subtitles.\n", fname, name);
|
|
free(name);
|
|
memcpy(&async, nasync, sizeof(audio_sync_t));
|
|
memcpy(&range, nrange, sizeof(range_t));
|
|
if (ncomments == NULL)
|
|
comments = ncomments;
|
|
else
|
|
comments = dup_comments(ncomments);
|
|
}
|
|
|
|
vobsub_reader_c::~vobsub_reader_c() {
|
|
int i;
|
|
for (i = 0; i < num_packetizers; i++)
|
|
if (all_packetizers[i] != NULL)
|
|
delete all_packetizers[i];
|
|
if (comments != NULL)
|
|
free_comments(comments);
|
|
}
|
|
|
|
void vobsub_reader_c::add_vobsub_packetizer(int width, int height,
|
|
char *palette, int langidx,
|
|
char *id, int index) {
|
|
all_packetizers = (vobsub_packetizer_c **)realloc(all_packetizers,
|
|
(num_packetizers + 1) *
|
|
sizeof(void *));
|
|
if (all_packetizers == NULL)
|
|
die("realloc");
|
|
try {
|
|
vobsub_packetizer = new vobsub_packetizer_c(width, height, palette,
|
|
langidx, id, index,
|
|
&async, &range, comments);
|
|
} catch (error_c error) {
|
|
fprintf(stderr, "vobsub_reader: Could not create a new vobsub_packetizer: "
|
|
"%s\n", error.get_error());
|
|
exit(1);
|
|
}
|
|
all_packetizers[num_packetizers] = vobsub_packetizer;
|
|
num_packetizers++;
|
|
}
|
|
|
|
int vobsub_reader_c::read() {
|
|
ogg_int64_t start, filepos, last_start, last_filepos;
|
|
char *s, *s2;
|
|
int width = -1, height = -1;
|
|
char *palette = NULL;
|
|
int langidx = -1;
|
|
char *id = NULL;
|
|
int index = -1;
|
|
int lineno;
|
|
|
|
chunk[2047] = 0;
|
|
lineno = 0;
|
|
last_start = -1;
|
|
last_filepos = -1;
|
|
while (1) {
|
|
if (fgets(chunk, 2047, file) == NULL)
|
|
break;
|
|
lineno++;
|
|
if ((*chunk == 0) || (strchr("#\n\r", *chunk) != NULL))
|
|
continue;
|
|
if (!strncmp(chunk, "size: ", 6)) {
|
|
if (sscanf(&chunk[6], "%dx%d", &width, &height) != 2) {
|
|
width = -1;
|
|
height = -1;
|
|
fprintf(stdout, "vobsub_reader: Warning: Incorrect \"size:\" entry "
|
|
"on line %d. Ignored.\n", lineno);
|
|
}
|
|
} else if (!strncmp(chunk, "palette: ", 9)) {
|
|
if (strlen(chunk) < 10)
|
|
fprintf(stdout, "vobsub_reader: Warning: Incorrect \"palette:\" entry "
|
|
"on line %d. Ignored.\n", lineno);
|
|
else {
|
|
palette = strdup(&chunk[9]);
|
|
if (palette == NULL)
|
|
die("strdup");
|
|
}
|
|
} else if (!strncmp(chunk, "langidx: ", 9)) {
|
|
langidx = strtol(&chunk[9], NULL, 10);
|
|
if ((langidx < 0) || (errno != 0)) {
|
|
fprintf(stdout, "vobsub_reader: Warning: Incorrect \"langidx:\" entry "
|
|
"on line %d. Ignored.\n", lineno);
|
|
langidx = -1;
|
|
}
|
|
} else if (!strncmp(chunk, "id:", 3)) {
|
|
s = &chunk[3];
|
|
while (isspace(*s))
|
|
s++;
|
|
s2 = strchr(s, ',');
|
|
if (s2 == NULL) {
|
|
fprintf(stdout, "vobsub_reader: Warning: Incorrect \"id:\" entry "
|
|
"on line %d. Ignored.\n", lineno);
|
|
|
|
continue;
|
|
}
|
|
*s2 = 0;
|
|
id = strdup(s);
|
|
if (id == NULL)
|
|
die("strdup");
|
|
s = s2 + 1;
|
|
while (isspace(*s))
|
|
s++;
|
|
if (strncmp(s, "index:", 6)) {
|
|
fprintf(stdout, "vobsub_reader: Warning: Incorrect \"id:\" entry "
|
|
"on line %d. Ignored.\n", lineno);
|
|
|
|
continue;
|
|
}
|
|
s += 6;
|
|
while (isspace(*s))
|
|
s++;
|
|
index = strtol(s, NULL, 10);
|
|
if ((index < 0) || (errno != 0)) {
|
|
fprintf(stdout, "vobsub_reader: Warning: Incorrect \"id:\" entry "
|
|
"on line %d. Ignored.\n", lineno);
|
|
continue;
|
|
}
|
|
} else if (!isvobsubline(chunk))
|
|
fprintf(stdout, "vobsub_reader: Warning: Unknown line format on line "
|
|
"%d. Ignored.\n", lineno);
|
|
else if (vobsub_packetizer == NULL) {
|
|
if ((width == -1) || (height == -1)) {
|
|
fprintf(stdout, "vobsub_reader: No \"size:\" entry found. File seems "
|
|
"to be defect. Aborting.\n");
|
|
exit(1);
|
|
}
|
|
if (palette == NULL) {
|
|
fprintf(stdout, "vobsub_reader: No \"palette:\" entry found. File "
|
|
"seems to be defect. Aborting.\n");
|
|
exit(1);
|
|
}
|
|
if (langidx == -1) {
|
|
fprintf(stdout, "vobsub_reader: No \"langidx:\" entry found. File "
|
|
"seems to be defect. Aborting.\n");
|
|
exit(1);
|
|
}
|
|
if ((id == NULL) || (index == -1)) {
|
|
fprintf(stdout, "vobsub_reader: No \"id:\" entry found. File "
|
|
"seems to be defect. Aborting.\n");
|
|
exit(1);
|
|
}
|
|
add_vobsub_packetizer(width, height, palette, langidx, id, index);
|
|
width = -1;
|
|
height = -1;
|
|
if (palette != NULL) {
|
|
free(palette);
|
|
palette = NULL;
|
|
}
|
|
langidx = -1;
|
|
if (id != NULL) {
|
|
free(id);
|
|
id = NULL;
|
|
}
|
|
index = -1;
|
|
} else {
|
|
// timestamp: 00:00:03:440, filepos: 000000000
|
|
// 0123456789012345678901234567890123456789012
|
|
// 1 2 3 4
|
|
chunk[13] = 0;
|
|
chunk[16] = 0;
|
|
chunk[19] = 0;
|
|
chunk[23] = 0;
|
|
|
|
start = atol(&chunk[11]) * 3600000 + atol(&chunk[14]) * 60000 +
|
|
atol(&chunk[17]) * 1000 + atol(&chunk[20]);
|
|
filepos = strtoll(&chunk[34], NULL, 16);
|
|
|
|
if ((last_start != -1) && (last_filepos != -1)) {
|
|
if (fseek(subfile, last_filepos, SEEK_SET) != 0)
|
|
fprintf(stderr, "Warning: vobsub_reader: Could not seek to position "
|
|
"%lld. Ignoring this entry.\n", last_filepos);
|
|
else if (last_filepos == filepos)
|
|
fprintf(stderr, "Warning: vobsub_reader: This entry and the last "
|
|
"entry start at the same position in the file. Ignored.\n");
|
|
else {
|
|
s = (char *)malloc(filepos - last_filepos);
|
|
if (s == NULL)
|
|
die("malloc");
|
|
if (fread(s, 1, filepos - last_filepos, subfile) !=
|
|
(filepos - last_filepos))
|
|
fprintf(stderr, "Warning: vobsub_reader: Could not read entry "
|
|
"from the sub file. Ignored.\n");
|
|
else
|
|
vobsub_packetizer->process(last_start, start - last_start, s,
|
|
filepos - last_filepos, 0);
|
|
free(s);
|
|
}
|
|
}
|
|
last_start = start;
|
|
last_filepos = filepos;
|
|
|
|
fprintf(stdout, "line %d, start %lld, filepos %lld\n", lineno,
|
|
start, filepos);
|
|
}
|
|
}
|
|
if ((last_start != -1) && (last_filepos != -1) &&
|
|
(vobsub_packetizer != NULL)) {
|
|
if (fseek(subfile, 0, SEEK_END) != 0) {
|
|
fprintf(stderr, "Warning: vobsub_reader: Could not seek to end of "
|
|
"the sub file. Ignoring last entry.\n");
|
|
vobsub_packetizer->produce_eos_packet();
|
|
return 0;
|
|
}
|
|
filepos = ftell(subfile);
|
|
if (fseek(subfile, last_filepos, SEEK_SET) != 0)
|
|
fprintf(stderr, "Warning: vobsub_reader: Could not seek to position "
|
|
"%lld. Ignoring this entry.\n", last_filepos);
|
|
else if (last_filepos == filepos)
|
|
fprintf(stderr, "Warning: vobsub_reader: This entry and the last "
|
|
"entry start at the same position in the file. Ignored.\n");
|
|
else {
|
|
s = (char *)malloc(filepos - last_filepos);
|
|
if (s == NULL)
|
|
die("malloc");
|
|
if (fread(s, 1, filepos - last_filepos, subfile) !=
|
|
(filepos - last_filepos))
|
|
fprintf(stderr, "Warning: vobsub_reader: Could not read entry "
|
|
"from the sub file. Ignored.\n");
|
|
else
|
|
vobsub_packetizer->process(last_start, start - last_start, s,
|
|
filepos - last_filepos, 1);
|
|
free(s);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vobsub_reader_c::serial_in_use(int serial) {
|
|
// return vobsubpacketizer->serial_in_use(serial);
|
|
return 0;
|
|
}
|
|
|
|
ogmmerge_page_t *vobsub_reader_c::get_header_page(int header_type) {
|
|
// return vobsubpacketizer->get_header_page(header_type);
|
|
return NULL;
|
|
}
|
|
|
|
ogmmerge_page_t *vobsub_reader_c::get_page() {
|
|
// return vobsubpacketizer->get_page();
|
|
return NULL;
|
|
}
|
|
|
|
int vobsub_reader_c::display_priority() {
|
|
return DISPLAYPRIORITY_LOW;
|
|
}
|
|
|
|
void vobsub_reader_c::reset() {
|
|
// if (vobsubpacketizer != NULL)
|
|
// vobsubpacketizer->reset();
|
|
}
|
|
|
|
static char wchar[] = "-\\|/-\\|/-";
|
|
|
|
void vobsub_reader_c::display_progress() {
|
|
fprintf(stdout, "working... %c\r", wchar[act_wchar]);
|
|
act_wchar++;
|
|
if (act_wchar == strlen(wchar))
|
|
act_wchar = 0;
|
|
fflush(stdout);
|
|
}
|