📄 vobsub.c
字号:
/* * Some code freely inspired from VobSub <URL:http://vobsub.edensrising.com>, * with kind permission from Gabest <gabest@freemail.hu> */#include <ctype.h>#include <errno.h>#include <limits.h>#include <stddef.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <sys/stat.h>#include <sys/types.h>#include "config.h"#include "version.h"#include "vobsub.h"#include "spudec.h"#include "mp_msg.h"#ifdef USE_UNRARLIB#include "unrarlib.h"#endif#define MIN(a, b) ((a)<(b)?(a):(b))#define MAX(a, b) ((a)>(b)?(a):(b))extern int vobsub_id;/********************************************************************** * RAR stream handling * The RAR file must have the same basename as the file to open * See <URL:http://www.unrarlib.org/> **********************************************************************/#ifdef USE_UNRARLIBtypedef struct { FILE *file; unsigned char *data; unsigned long size; unsigned long pos;} rar_stream_t;static rar_stream_t *rar_open(const char *const filename, const char *const mode){ rar_stream_t *stream; /* unrarlib can only read */ if (strcmp("r", mode) && strcmp("rb", mode)) { errno = EINVAL; return NULL; } stream = malloc(sizeof(rar_stream_t)); if (stream == NULL) return NULL; /* first try normal access */ stream->file = fopen(filename, mode); if (stream->file == NULL) { char *rar_filename; const char *p; int rc; /* Guess the RAR archive filename */ rar_filename = NULL; p = strrchr(filename, '.'); if (p) { ptrdiff_t l = p - filename; rar_filename = malloc(l + 5); if (rar_filename == NULL) { free(stream); return NULL; } strncpy(rar_filename, filename, l); strcpy(rar_filename + l, ".rar"); } else { rar_filename = malloc(strlen(filename) + 5); if (rar_filename == NULL) { free(stream); return NULL; } strcpy(rar_filename, filename); strcat(rar_filename, ".rar"); } /* get rid of the path if there is any */ if ((p = strrchr(filename, '/')) == NULL) { p = filename; } else { p++; } rc = urarlib_get(&stream->data, &stream->size, (char*) p, rar_filename, ""); if (!rc) { /* There is no matching filename in the archive. However, sometimes * the files we are looking for have been given arbitrary names in the archive. * Let's look for a file with an exact match in the extension only. */ int i, num_files, name_len; ArchiveList_struct *list, *lp; /* the cast in the next line is a hack to overcome a design flaw (IMHO) in unrarlib */ num_files = urarlib_list (rar_filename, (ArchiveList_struct *)&list); if (num_files > 0) { char *demanded_ext; demanded_ext = strrchr (p, '.'); if (demanded_ext) { int demanded_ext_len = strlen (demanded_ext); for (i=0, lp=list; i<num_files; i++, lp=lp->next) { name_len = strlen (lp->item.Name); if (name_len >= demanded_ext_len && !strcasecmp (lp->item.Name + name_len - demanded_ext_len, demanded_ext)) { if ((rc = urarlib_get(&stream->data, &stream->size, lp->item.Name, rar_filename, ""))) { break; } } } } urarlib_freelist (list); } if (!rc) { free(rar_filename); free(stream); return NULL; } } free(rar_filename); stream->pos = 0; } return stream;}static intrar_close(rar_stream_t *stream){ if (stream->file) return fclose(stream->file); free(stream->data); return 0;}static intrar_eof(rar_stream_t *stream){ if (stream->file) return feof(stream->file); return stream->pos >= stream->size;}static longrar_tell(rar_stream_t *stream){ if (stream->file) return ftell(stream->file); return stream->pos;}static intrar_seek(rar_stream_t *stream, long offset, int whence){ if (stream->file) return fseek(stream->file, offset, whence); switch (whence) { case SEEK_SET: if (offset < 0) { errno = EINVAL; return -1; } stream->pos = offset; break; case SEEK_CUR: if (offset < 0 && stream->pos < (unsigned long) -offset) { errno = EINVAL; return -1; } stream->pos += offset; break; case SEEK_END: if (offset < 0 && stream->size < (unsigned long) -offset) { errno = EINVAL; return -1; } stream->pos = stream->size + offset; break; default: errno = EINVAL; return -1; } return 0;}static intrar_getc(rar_stream_t *stream){ if (stream->file) return getc(stream->file); if (rar_eof(stream)) return EOF; return stream->data[stream->pos++];}static size_trar_read(void *ptr, size_t size, size_t nmemb, rar_stream_t *stream){ size_t res; unsigned long remain; if (stream->file) return fread(ptr, size, nmemb, stream->file); if (rar_eof(stream)) return 0; res = size * nmemb; remain = stream->size - stream->pos; if (res > remain) res = remain / size * size; memcpy(ptr, stream->data + stream->pos, res); stream->pos += res; res /= size; return res;}#elsetypedef FILE rar_stream_t;#define rar_open fopen#define rar_close fclose#define rar_eof feof#define rar_tell ftell#define rar_seek fseek#define rar_getc getc#define rar_read fread#endif/**********************************************************************/static ssize_tgetline(char **lineptr, size_t *n, rar_stream_t *stream){ size_t res = 0; int c; if (*lineptr == NULL) { *lineptr = malloc(4096); if (*lineptr) *n = 4096; } else if (*n == 0) { char *tmp = realloc(*lineptr, 4096); if (tmp) { *lineptr = tmp; *n = 4096; } } if (*lineptr == NULL || *n == 0) return -1; for (c = rar_getc(stream); c != EOF; c = rar_getc(stream)) { if (res + 1 >= *n) { char *tmp = realloc(*lineptr, *n * 2); if (tmp == NULL) return -1; *lineptr = tmp; *n *= 2; } (*lineptr)[res++] = c; if (c == '\n') { (*lineptr)[res] = 0; return res; } } if (res == 0) return -1; (*lineptr)[res] = 0; return res;}/********************************************************************** * MPEG parsing **********************************************************************/typedef struct { rar_stream_t *stream; unsigned int pts; int aid; unsigned char *packet; unsigned int packet_reserve; unsigned int packet_size;} mpeg_t;static mpeg_t *mpeg_open(const char *filename){ mpeg_t *res = malloc(sizeof(mpeg_t)); int err = res == NULL; if (!err) { res->pts = 0; res->aid = -1; res->packet = NULL; res->packet_size = 0; res->packet_reserve = 0; res->stream = rar_open(filename, "rb"); err = res->stream == NULL; if (err) perror("fopen Vobsub file failed"); if (err) free(res); } return err ? NULL : res;}static voidmpeg_free(mpeg_t *mpeg){ if (mpeg->packet) free(mpeg->packet); if (mpeg->stream) rar_close(mpeg->stream); free(mpeg);}static intmpeg_eof(mpeg_t *mpeg){ return rar_eof(mpeg->stream);}static off_tmpeg_tell(mpeg_t *mpeg){ return rar_tell(mpeg->stream);}static intmpeg_run(mpeg_t *mpeg){ unsigned int len, idx, version; int c; /* Goto start of a packet, it starts with 0x000001?? */ const unsigned char wanted[] = { 0, 0, 1 }; unsigned char buf[5]; mpeg->aid = -1; mpeg->packet_size = 0; if (rar_read(buf, 4, 1, mpeg->stream) != 1) return -1; while (memcmp(buf, wanted, sizeof(wanted)) != 0) { c = rar_getc(mpeg->stream); if (c < 0) return -1; memmove(buf, buf + 1, 3); buf[3] = c; } switch (buf[3]) { case 0xb9: /* System End Code */ break; case 0xba: /* Packet start code */ c = rar_getc(mpeg->stream); if (c < 0) return -1; if ((c & 0xc0) == 0x40) version = 4; else if ((c & 0xf0) == 0x20) version = 2; else { mp_msg(MSGT_VOBSUB,MSGL_ERR, "VobSub: Unsupported MPEG version: 0x%02x\n", c); return -1; } if (version == 4) { if (rar_seek(mpeg->stream, 9, SEEK_CUR)) return -1; } else if (version == 2) { if (rar_seek(mpeg->stream, 7, SEEK_CUR)) return -1; } else abort(); break; case 0xbd: /* packet */ if (rar_read(buf, 2, 1, mpeg->stream) != 1) return -1; len = buf[0] << 8 | buf[1]; idx = mpeg_tell(mpeg); c = rar_getc(mpeg->stream); if (c < 0) return -1; if ((c & 0xC0) == 0x40) { /* skip STD scale & size */ if (rar_getc(mpeg->stream) < 0) return -1; c = rar_getc(mpeg->stream); if (c < 0) return -1; } if ((c & 0xf0) == 0x20) { /* System-1 stream timestamp */ /* Do we need this? */ abort(); } else if ((c & 0xf0) == 0x30) { /* Do we need this? */ abort(); } else if ((c & 0xc0) == 0x80) { /* System-2 (.VOB) stream */ unsigned int pts_flags, hdrlen, dataidx; c = rar_getc(mpeg->stream); if (c < 0) return -1; pts_flags = c; c = rar_getc(mpeg->stream); if (c < 0) return -1; hdrlen = c; dataidx = mpeg_tell(mpeg) + hdrlen; if (dataidx > idx + len) { mp_msg(MSGT_VOBSUB,MSGL_ERR, "Invalid header length: %d (total length: %d, idx: %d, dataidx: %d)\n", hdrlen, len, idx, dataidx); return -1; } if ((pts_flags & 0xc0) == 0x80) { if (rar_read(buf, 5, 1, mpeg->stream) != 1) return -1; if (!(((buf[0] & 0xf0) == 0x20) && (buf[0] & 1) && (buf[2] & 1) && (buf[4] & 1))) { mp_msg(MSGT_VOBSUB,MSGL_ERR, "vobsub PTS error: 0x%02x %02x%02x %02x%02x \n", buf[0], buf[1], buf[2], buf[3], buf[4]); mpeg->pts = 0; } else mpeg->pts = ((buf[0] & 0x0e) << 29 | buf[1] << 22 | (buf[2] & 0xfe) << 14 | buf[3] << 7 | (buf[4] >> 1)); } else /* if ((pts_flags & 0xc0) == 0xc0) */ { /* what's this? */ /* abort(); */ } rar_seek(mpeg->stream, dataidx, SEEK_SET); mpeg->aid = rar_getc(mpeg->stream); if (mpeg->aid < 0) { mp_msg(MSGT_VOBSUB,MSGL_ERR, "Bogus aid %d\n", mpeg->aid); return -1; } mpeg->packet_size = len - ((unsigned int) mpeg_tell(mpeg) - idx); if (mpeg->packet_reserve < mpeg->packet_size) { if (mpeg->packet) free(mpeg->packet); mpeg->packet = malloc(mpeg->packet_size); if (mpeg->packet) mpeg->packet_reserve = mpeg->packet_size; } if (mpeg->packet == NULL) { mp_msg(MSGT_VOBSUB,MSGL_FATAL,"malloc failure"); mpeg->packet_reserve = 0; mpeg->packet_size = 0; return -1; } if (rar_read(mpeg->packet, mpeg->packet_size, 1, mpeg->stream) != 1) { mp_msg(MSGT_VOBSUB,MSGL_ERR,"fread failure"); mpeg->packet_size = 0; return -1; } idx = len; } break; case 0xbe: /* Padding */ if (rar_read(buf, 2, 1, mpeg->stream) != 1) return -1; len = buf[0] << 8 | buf[1]; if (len > 0 && rar_seek(mpeg->stream, len, SEEK_CUR)) return -1; break; default: if (0xc0 <= buf[3] && buf[3] < 0xf0) { /* MPEG audio or video */ if (rar_read(buf, 2, 1, mpeg->stream) != 1) return -1; len = buf[0] << 8 | buf[1]; if (len > 0 && rar_seek(mpeg->stream, len, SEEK_CUR)) return -1; } else { mp_msg(MSGT_VOBSUB,MSGL_ERR,"unknown header 0x%02X%02X%02X%02X\n", buf[0], buf[1], buf[2], buf[3]); return -1; } } return 0;}/********************************************************************** * Packet queue **********************************************************************/typedef struct { unsigned int pts100; off_t filepos; unsigned int size; unsigned char *data;} packet_t;typedef struct { char *id; packet_t *packets; unsigned int packets_reserve; unsigned int packets_size; unsigned int current_index;} packet_queue_t;static voidpacket_construct(packet_t *pkt){ pkt->pts100 = 0; pkt->filepos = 0; pkt->size = 0; pkt->data = NULL;}static voidpacket_destroy(packet_t *pkt){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -