Real rstp:// streaming support, ported from xine
[mplayer/greg.git] / vobsub.c
blob0e76168181a08d0c2341da9a9db7ed78ac2656f4
1 /*
2 * Some code freely inspired from VobSub <URL:http://vobsub.edensrising.com>,
3 * with kind permission from Gabest <gabest@freemail.hu>
4 */
5 #include <ctype.h>
6 #include <errno.h>
7 #include <limits.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #ifdef __MINGW32__
17 typedef long ssize_t;
18 #endif
20 #include "config.h"
21 #include "version.h"
23 #include "vobsub.h"
24 #include "spudec.h"
25 #include "mp_msg.h"
26 #ifdef USE_UNRARLIB
27 #include "unrarlib.h"
28 #endif
30 #define MIN(a, b) ((a)<(b)?(a):(b))
31 #define MAX(a, b) ((a)>(b)?(a):(b))
33 extern int vobsub_id;
35 /**********************************************************************
36 * RAR stream handling
37 * The RAR file must have the same basename as the file to open
38 * See <URL:http://www.unrarlib.org/>
39 **********************************************************************/
40 #ifdef USE_UNRARLIB
41 typedef struct {
42 FILE *file;
43 unsigned char *data;
44 unsigned long size;
45 unsigned long pos;
46 } rar_stream_t;
47 static rar_stream_t *
48 rar_open(const char *const filename, const char *const mode)
50 rar_stream_t *stream;
51 /* unrarlib can only read */
52 if (strcmp("r", mode) && strcmp("rb", mode)) {
53 errno = EINVAL;
54 return NULL;
56 stream = malloc(sizeof(rar_stream_t));
57 if (stream == NULL)
58 return NULL;
59 /* first try normal access */
60 stream->file = fopen(filename, mode);
61 if (stream->file == NULL) {
62 char *rar_filename;
63 const char *p;
64 int rc;
65 /* Guess the RAR archive filename */
66 rar_filename = NULL;
67 p = strrchr(filename, '.');
68 if (p) {
69 ptrdiff_t l = p - filename;
70 rar_filename = malloc(l + 5);
71 if (rar_filename == NULL) {
72 free(stream);
73 return NULL;
75 strncpy(rar_filename, filename, l);
76 strcpy(rar_filename + l, ".rar");
78 else {
79 rar_filename = malloc(strlen(filename) + 5);
80 if (rar_filename == NULL) {
81 free(stream);
82 return NULL;
84 strcpy(rar_filename, filename);
85 strcat(rar_filename, ".rar");
87 /* get rid of the path if there is any */
88 if ((p = strrchr(filename, '/')) == NULL) {
89 p = filename;
90 } else {
91 p++;
93 rc = urarlib_get(&stream->data, &stream->size, (char*) p, rar_filename, "");
94 free(rar_filename);
95 if (!rc) {
96 free(stream);
97 return NULL;
99 stream->pos = 0;
101 return stream;
104 static int
105 rar_close(rar_stream_t *stream)
107 if (stream->file)
108 return fclose(stream->file);
109 free(stream->data);
110 return 0;
113 static int
114 rar_eof(rar_stream_t *stream)
116 if (stream->file)
117 return feof(stream->file);
118 return stream->pos >= stream->size;
121 static long
122 rar_tell(rar_stream_t *stream)
124 if (stream->file)
125 return ftell(stream->file);
126 return stream->pos;
129 static int
130 rar_seek(rar_stream_t *stream, long offset, int whence)
132 if (stream->file)
133 return fseek(stream->file, offset, whence);
134 switch (whence) {
135 case SEEK_SET:
136 if (offset < 0) {
137 errno = EINVAL;
138 return -1;
140 stream->pos = offset;
141 break;
142 case SEEK_CUR:
143 if (offset < 0 && stream->pos < (unsigned long) -offset) {
144 errno = EINVAL;
145 return -1;
147 stream->pos += offset;
148 break;
149 case SEEK_END:
150 if (offset < 0 && stream->size < (unsigned long) -offset) {
151 errno = EINVAL;
152 return -1;
154 stream->pos = stream->size + offset;
155 break;
156 default:
157 errno = EINVAL;
158 return -1;
160 return 0;
163 static int
164 rar_getc(rar_stream_t *stream)
166 if (stream->file)
167 return getc(stream->file);
168 if (rar_eof(stream))
169 return EOF;
170 return stream->data[stream->pos++];
173 static size_t
174 rar_read(void *ptr, size_t size, size_t nmemb, rar_stream_t *stream)
176 size_t res;
177 unsigned long remain;
178 if (stream->file)
179 return fread(ptr, size, nmemb, stream->file);
180 if (rar_eof(stream))
181 return 0;
182 res = size * nmemb;
183 remain = stream->size - stream->pos;
184 if (res > remain)
185 res = remain / size * size;
186 memcpy(ptr, stream->data + stream->pos, res);
187 stream->pos += res;
188 res /= size;
189 return res;
192 #else
193 typedef FILE rar_stream_t;
194 #define rar_open fopen
195 #define rar_close fclose
196 #define rar_eof feof
197 #define rar_tell ftell
198 #define rar_seek fseek
199 #define rar_getc getc
200 #define rar_read fread
201 #endif
203 /**********************************************************************/
205 static ssize_t
206 getline(char **lineptr, size_t *n, rar_stream_t *stream)
208 size_t res = 0;
209 int c;
210 if (*lineptr == NULL) {
211 *lineptr = malloc(4096);
212 if (*lineptr)
213 *n = 4096;
215 else if (*n == 0) {
216 char *tmp = realloc(*lineptr, 4096);
217 if (tmp) {
218 *lineptr = tmp;
219 *n = 4096;
222 if (*lineptr == NULL || *n == 0)
223 return -1;
225 for (c = rar_getc(stream); c != EOF; c = rar_getc(stream)) {
226 if (res + 1 >= *n) {
227 char *tmp = realloc(*lineptr, *n * 2);
228 if (tmp == NULL)
229 return -1;
230 *lineptr = tmp;
231 *n *= 2;
233 (*lineptr)[res++] = c;
234 if (c == '\n') {
235 (*lineptr)[res] = 0;
236 return res;
239 if (res == 0)
240 return -1;
241 (*lineptr)[res] = 0;
242 return res;
245 /**********************************************************************
246 * MPEG parsing
247 **********************************************************************/
249 typedef struct {
250 rar_stream_t *stream;
251 unsigned int pts;
252 int aid;
253 unsigned char *packet;
254 unsigned int packet_reserve;
255 unsigned int packet_size;
256 } mpeg_t;
258 static mpeg_t *
259 mpeg_open(const char *filename)
261 mpeg_t *res = malloc(sizeof(mpeg_t));
262 int err = res == NULL;
263 if (!err) {
264 res->pts = 0;
265 res->aid = -1;
266 res->packet = NULL;
267 res->packet_size = 0;
268 res->packet_reserve = 0;
269 res->stream = rar_open(filename, "r");
270 err = res->stream == NULL;
271 if (err)
272 perror("fopen Vobsub file failed");
273 if (err)
274 free(res);
276 return err ? NULL : res;
279 static void
280 mpeg_free(mpeg_t *mpeg)
282 if (mpeg->packet)
283 free(mpeg->packet);
284 if (mpeg->stream)
285 rar_close(mpeg->stream);
286 free(mpeg);
289 static int
290 mpeg_eof(mpeg_t *mpeg)
292 return rar_eof(mpeg->stream);
295 static off_t
296 mpeg_tell(mpeg_t *mpeg)
298 return rar_tell(mpeg->stream);
301 static int
302 mpeg_run(mpeg_t *mpeg)
304 unsigned int len, idx, version;
305 int c;
306 /* Goto start of a packet, it starts with 0x000001?? */
307 const unsigned char wanted[] = { 0, 0, 1 };
308 unsigned char buf[5];
310 mpeg->aid = -1;
311 mpeg->packet_size = 0;
312 if (rar_read(buf, 4, 1, mpeg->stream) != 1)
313 return -1;
314 while (memcmp(buf, wanted, sizeof(wanted)) != 0) {
315 c = rar_getc(mpeg->stream);
316 if (c < 0)
317 return -1;
318 memmove(buf, buf + 1, 3);
319 buf[3] = c;
321 switch (buf[3]) {
322 case 0xb9: /* System End Code */
323 break;
324 case 0xba: /* Packet start code */
325 c = rar_getc(mpeg->stream);
326 if (c < 0)
327 return -1;
328 if ((c & 0xc0) == 0x40)
329 version = 4;
330 else if ((c & 0xf0) == 0x20)
331 version = 2;
332 else {
333 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Unsupported MPEG version: 0x%02x", c);
334 return -1;
336 if (version == 4) {
337 if (rar_seek(mpeg->stream, 9, SEEK_CUR))
338 return -1;
340 else if (version == 2) {
341 if (rar_seek(mpeg->stream, 7, SEEK_CUR))
342 return -1;
344 else
345 abort();
346 break;
347 case 0xbd: /* packet */
348 if (rar_read(buf, 2, 1, mpeg->stream) != 1)
349 return -1;
350 len = buf[0] << 8 | buf[1];
351 idx = mpeg_tell(mpeg);
352 c = rar_getc(mpeg->stream);
353 if (c < 0)
354 return -1;
355 if ((c & 0xC0) == 0x40) { /* skip STD scale & size */
356 if (rar_getc(mpeg->stream) < 0)
357 return -1;
358 c = rar_getc(mpeg->stream);
359 if (c < 0)
360 return -1;
362 if ((c & 0xf0) == 0x20) { /* System-1 stream timestamp */
363 /* Do we need this? */
364 abort();
366 else if ((c & 0xf0) == 0x30) {
367 /* Do we need this? */
368 abort();
370 else if ((c & 0xc0) == 0x80) { /* System-2 (.VOB) stream */
371 unsigned int pts_flags, hdrlen, dataidx;
372 c = rar_getc(mpeg->stream);
373 if (c < 0)
374 return -1;
375 pts_flags = c;
376 c = rar_getc(mpeg->stream);
377 if (c < 0)
378 return -1;
379 hdrlen = c;
380 dataidx = mpeg_tell(mpeg) + hdrlen;
381 if (dataidx > idx + len) {
382 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Invalid header length: %d (total length: %d, idx: %d, dataidx: %d)\n",
383 hdrlen, len, idx, dataidx);
384 return -1;
386 if ((pts_flags & 0xc0) == 0x80) {
387 if (rar_read(buf, 5, 1, mpeg->stream) != 1)
388 return -1;
389 if (!(((buf[0] & 0xf0) == 0x20) && (buf[0] & 1) && (buf[2] & 1) && (buf[4] & 1))) {
390 mp_msg(MSGT_VOBSUB,MSGL_ERR, "vobsub PTS error: 0x%02x %02x%02x %02x%02x \n",
391 buf[0], buf[1], buf[2], buf[3], buf[4]);
392 mpeg->pts = 0;
394 else
395 mpeg->pts = ((buf[0] & 0x0e) << 29 | buf[1] << 22 | (buf[2] & 0xfe) << 14
396 | buf[3] << 7 | (buf[4] >> 1));
398 else /* if ((pts_flags & 0xc0) == 0xc0) */ {
399 /* what's this? */
400 /* abort(); */
402 rar_seek(mpeg->stream, dataidx, SEEK_SET);
403 mpeg->aid = rar_getc(mpeg->stream);
404 if (mpeg->aid < 0) {
405 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Bogus aid %d\n", mpeg->aid);
406 return -1;
408 mpeg->packet_size = len - ((unsigned int) mpeg_tell(mpeg) - idx);
409 if (mpeg->packet_reserve < mpeg->packet_size) {
410 if (mpeg->packet)
411 free(mpeg->packet);
412 mpeg->packet = malloc(mpeg->packet_size);
413 if (mpeg->packet)
414 mpeg->packet_reserve = mpeg->packet_size;
416 if (mpeg->packet == NULL) {
417 mp_msg(MSGT_VOBSUB,MSGL_FATAL,"malloc failure");
418 mpeg->packet_reserve = 0;
419 mpeg->packet_size = 0;
420 return -1;
422 if (rar_read(mpeg->packet, mpeg->packet_size, 1, mpeg->stream) != 1) {
423 mp_msg(MSGT_VOBSUB,MSGL_ERR,"fread failure");
424 mpeg->packet_size = 0;
425 return -1;
427 idx = len;
429 break;
430 case 0xbe: /* Padding */
431 if (rar_read(buf, 2, 1, mpeg->stream) != 1)
432 return -1;
433 len = buf[0] << 8 | buf[1];
434 if (len > 0 && rar_seek(mpeg->stream, len, SEEK_CUR))
435 return -1;
436 break;
437 default:
438 if (0xc0 <= buf[3] && buf[3] < 0xf0) {
439 /* MPEG audio or video */
440 if (rar_read(buf, 2, 1, mpeg->stream) != 1)
441 return -1;
442 len = buf[0] << 8 | buf[1];
443 if (len > 0 && rar_seek(mpeg->stream, len, SEEK_CUR))
444 return -1;
447 else {
448 mp_msg(MSGT_VOBSUB,MSGL_ERR,"unknown header 0x%02X%02X%02X%02X\n",
449 buf[0], buf[1], buf[2], buf[3]);
450 return -1;
453 return 0;
456 /**********************************************************************
457 * Packet queue
458 **********************************************************************/
460 typedef struct {
461 unsigned int pts100;
462 off_t filepos;
463 unsigned int size;
464 unsigned char *data;
465 } packet_t;
467 typedef struct {
468 char *id;
469 packet_t *packets;
470 unsigned int packets_reserve;
471 unsigned int packets_size;
472 unsigned int current_index;
473 } packet_queue_t;
475 static void
476 packet_construct(packet_t *pkt)
478 pkt->pts100 = 0;
479 pkt->filepos = 0;
480 pkt->size = 0;
481 pkt->data = NULL;
484 static void
485 packet_destroy(packet_t *pkt)
487 if (pkt->data)
488 free(pkt->data);
491 static void
492 packet_queue_construct(packet_queue_t *queue)
494 queue->id = NULL;
495 queue->packets = NULL;
496 queue->packets_reserve = 0;
497 queue->packets_size = 0;
498 queue->current_index = 0;
501 static void
502 packet_queue_destroy(packet_queue_t *queue)
504 if (queue->packets) {
505 while (queue->packets_size--)
506 packet_destroy(queue->packets + queue->packets_size);
507 free(queue->packets);
509 return;
512 /* Make sure there is enough room for needed_size packets in the
513 packet queue. */
514 static int
515 packet_queue_ensure(packet_queue_t *queue, unsigned int needed_size)
517 if (queue->packets_reserve < needed_size) {
518 if (queue->packets) {
519 packet_t *tmp = realloc(queue->packets, 2 * queue->packets_reserve * sizeof(packet_t));
520 if (tmp == NULL) {
521 mp_msg(MSGT_VOBSUB,MSGL_FATAL,"realloc failure");
522 return -1;
524 queue->packets = tmp;
525 queue->packets_reserve *= 2;
527 else {
528 queue->packets = malloc(sizeof(packet_t));
529 if (queue->packets == NULL) {
530 mp_msg(MSGT_VOBSUB,MSGL_FATAL,"malloc failure");
531 return -1;
533 queue->packets_reserve = 1;
536 return 0;
539 /* add one more packet */
540 static int
541 packet_queue_grow(packet_queue_t *queue)
543 if (packet_queue_ensure(queue, queue->packets_size + 1) < 0)
544 return -1;
545 packet_construct(queue->packets + queue->packets_size);
546 ++queue->packets_size;
547 return 0;
550 /* insert a new packet, duplicating pts from the current one */
551 static int
552 packet_queue_insert(packet_queue_t *queue)
554 packet_t *pkts;
555 if (packet_queue_ensure(queue, queue->packets_size + 1) < 0)
556 return -1;
557 /* XXX packet_size does not reflect the real thing here, it will be updated a bit later */
558 memmove(queue->packets + queue->current_index + 2,
559 queue->packets + queue->current_index + 1,
560 sizeof(packet_t) * (queue->packets_size - queue->current_index - 1));
561 pkts = queue->packets + queue->current_index;
562 ++queue->packets_size;
563 ++queue->current_index;
564 packet_construct(pkts + 1);
565 pkts[1].pts100 = pkts[0].pts100;
566 pkts[1].filepos = pkts[0].filepos;
567 return 0;
570 /**********************************************************************
571 * Vobsub
572 **********************************************************************/
574 typedef struct {
575 unsigned int palette[16];
576 unsigned int cuspal[4];
577 int delay;
578 unsigned int custom;
579 unsigned int have_palette;
580 unsigned int orig_frame_width, orig_frame_height;
581 unsigned int origin_x, origin_y;
582 /* index */
583 packet_queue_t *spu_streams;
584 unsigned int spu_streams_size;
585 unsigned int spu_streams_current;
586 } vobsub_t;
588 /* Make sure that the spu stream idx exists. */
589 static int
590 vobsub_ensure_spu_stream(vobsub_t *vob, unsigned int index)
592 if (index >= vob->spu_streams_size) {
593 /* This is a new stream */
594 if (vob->spu_streams) {
595 packet_queue_t *tmp = realloc(vob->spu_streams, (index + 1) * sizeof(packet_queue_t));
596 if (tmp == NULL) {
597 mp_msg(MSGT_VOBSUB,MSGL_ERR,"vobsub_ensure_spu_stream: realloc failure");
598 return -1;
600 vob->spu_streams = tmp;
602 else {
603 vob->spu_streams = malloc((index + 1) * sizeof(packet_queue_t));
604 if (vob->spu_streams == NULL) {
605 mp_msg(MSGT_VOBSUB,MSGL_ERR,"vobsub_ensure_spu_stream: malloc failure");
606 return -1;
609 while (vob->spu_streams_size <= index) {
610 packet_queue_construct(vob->spu_streams + vob->spu_streams_size);
611 ++vob->spu_streams_size;
614 return 0;
617 static int
618 vobsub_add_id(vobsub_t *vob, const char *id, size_t idlen, const unsigned int index)
620 if (vobsub_ensure_spu_stream(vob, index) < 0)
621 return -1;
622 if (id && idlen) {
623 if (vob->spu_streams[index].id)
624 free(vob->spu_streams[index].id);
625 vob->spu_streams[index].id = malloc(idlen + 1);
626 if (vob->spu_streams[index].id == NULL) {
627 mp_msg(MSGT_VOBSUB,MSGL_FATAL,"vobsub_add_id: malloc failure");
628 return -1;
630 vob->spu_streams[index].id[idlen] = 0;
631 memcpy(vob->spu_streams[index].id, id, idlen);
633 vob->spu_streams_current = index;
634 mp_msg(MSGT_VOBSUB,MSGL_V,"[vobsub] subtitle (vobsubid): %d language %s\n",
635 index, vob->spu_streams[index].id);
636 return 0;
639 static int
640 vobsub_add_timestamp(vobsub_t *vob, off_t filepos, int ms)
642 packet_queue_t *queue;
643 packet_t *pkt;
644 if (vob->spu_streams == 0) {
645 mp_msg(MSGT_VOBSUB,MSGL_WARN,"[vobsub] warning, binning some index entries. Check your index file\n");
646 return -1;
648 queue = vob->spu_streams + vob->spu_streams_current;
649 if (packet_queue_grow(queue) >= 0) {
650 pkt = queue->packets + (queue->packets_size - 1);
651 pkt->filepos = filepos;
652 pkt->pts100 = ms < 0 ? UINT_MAX : (unsigned int)ms * 90;
653 return 0;
655 return -1;
658 static int
659 vobsub_parse_id(vobsub_t *vob, const char *line)
661 // id: xx, index: n
662 size_t idlen;
663 const char *p, *q;
664 p = line;
665 while (isspace(*p))
666 ++p;
667 q = p;
668 while (isalpha(*q))
669 ++q;
670 idlen = q - p;
671 if (idlen == 0)
672 return -1;
673 ++q;
674 while (isspace(*q))
675 ++q;
676 if (strncmp("index:", q, 6))
677 return -1;
678 q += 6;
679 while (isspace(*q))
680 ++q;
681 if (!isdigit(*q))
682 return -1;
683 return vobsub_add_id(vob, p, idlen, atoi(q));
686 static int
687 vobsub_parse_timestamp(vobsub_t *vob, const char *line)
689 // timestamp: HH:MM:SS.mmm, filepos: 0nnnnnnnnn
690 const char *p;
691 int h, m, s, ms;
692 off_t filepos;
693 while (isspace(*line))
694 ++line;
695 p = line;
696 while (isdigit(*p))
697 ++p;
698 if (p - line != 2)
699 return -1;
700 h = atoi(line);
701 if (*p != ':')
702 return -1;
703 line = ++p;
704 while (isdigit(*p))
705 ++p;
706 if (p - line != 2)
707 return -1;
708 m = atoi(line);
709 if (*p != ':')
710 return -1;
711 line = ++p;
712 while (isdigit(*p))
713 ++p;
714 if (p - line != 2)
715 return -1;
716 s = atoi(line);
717 if (*p != ':')
718 return -1;
719 line = ++p;
720 while (isdigit(*p))
721 ++p;
722 if (p - line != 3)
723 return -1;
724 ms = atoi(line);
725 if (*p != ',')
726 return -1;
727 line = p + 1;
728 while (isspace(*line))
729 ++line;
730 if (strncmp("filepos:", line, 8))
731 return -1;
732 line += 8;
733 while (isspace(*line))
734 ++line;
735 if (! isxdigit(*line))
736 return -1;
737 filepos = strtol(line, NULL, 16);
738 return vobsub_add_timestamp(vob, filepos, vob->delay + ms + 1000 * (s + 60 * (m + 60 * h)));
741 static int
742 vobsub_parse_size(vobsub_t *vob, const char *line)
744 // size: WWWxHHH
745 char *p;
746 while (isspace(*line))
747 ++line;
748 if (!isdigit(*line))
749 return -1;
750 vob->orig_frame_width = strtoul(line, &p, 10);
751 if (*p != 'x')
752 return -1;
753 ++p;
754 vob->orig_frame_height = strtoul(p, NULL, 10);
755 return 0;
758 static int
759 vobsub_parse_origin(vobsub_t *vob, const char *line)
761 // org: X,Y
762 char *p;
763 while (isspace(*line))
764 ++line;
765 if (!isdigit(*line))
766 return -1;
767 vob->origin_x = strtoul(line, &p, 10);
768 if (*p != ',')
769 return -1;
770 ++p;
771 vob->origin_y = strtoul(p, NULL, 10);
772 return 0;
775 static int
776 vobsub_parse_palette(vobsub_t *vob, const char *line)
778 // palette: XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX, XXXXXX
779 unsigned int n;
780 n = 0;
781 while (1) {
782 const char *p;
783 int r, g, b, y, u, v, tmp;
784 while (isspace(*line))
785 ++line;
786 p = line;
787 while (isxdigit(*p))
788 ++p;
789 if (p - line != 6)
790 return -1;
791 tmp = strtoul(line, NULL, 16);
792 r = tmp >> 16 & 0xff;
793 g = tmp >> 8 & 0xff;
794 b = tmp & 0xff;
795 y = MIN(MAX((int)(0.1494 * r + 0.6061 * g + 0.2445 * b), 0), 0xff);
796 u = MIN(MAX((int)(0.6066 * r - 0.4322 * g - 0.1744 * b) + 128, 0), 0xff);
797 v = MIN(MAX((int)(-0.08435 * r - 0.3422 * g + 0.4266 * b) + 128, 0), 0xff);
798 vob->palette[n++] = y << 16 | u << 8 | v;
799 if (n == 16)
800 break;
801 if (*p == ',')
802 ++p;
803 line = p;
805 vob->have_palette = 1;
806 return 0;
809 static int
810 vobsub_parse_custom(vobsub_t *vob, const char *line)
812 //custom colors: OFF/ON(0/1)
813 if ((strncmp("ON", line + 15, 2) == 0)||strncmp("1", line + 15, 1) == 0)
814 vob->custom=1;
815 else if ((strncmp("OFF", line + 15, 3) == 0)||strncmp("0", line + 15, 1) == 0)
816 vob->custom=0;
817 else
818 return -1;
819 return 0;
822 static int
823 vobsub_parse_cuspal(vobsub_t *vob, const char *line)
825 //colors: XXXXXX, XXXXXX, XXXXXX, XXXXXX
826 unsigned int n;
827 n = 0;
828 line += 40;
829 while(1){
830 const char *p;
831 while (isspace(*line))
832 ++line;
833 p=line;
834 while (isxdigit(*p))
835 ++p;
836 if (p - line !=6)
837 return -1;
838 vob->cuspal[n++] = strtoul(line, NULL,16);
839 if (n==4)
840 break;
841 if(*p == ',')
842 ++p;
843 line = p;
845 return 0;
848 /* don't know how to use tridx */
849 static int
850 vobsub_parse_tridx(const char *line)
852 //tridx: XXXX
853 int tridx;
854 tridx = strtoul((line + 26), NULL, 16);
855 tridx = ((tridx&0x1000)>>12) | ((tridx&0x100)>>7) | ((tridx&0x10)>>2) | ((tridx&1)<<3);
856 return tridx;
859 static int
860 vobsub_parse_delay(vobsub_t *vob, const char *line)
862 int h, m, s, ms;
863 int forward = 1;
864 if (*(line + 7) == '+'){
865 forward = 1;
866 line++;
868 else if (*(line + 7) == '-'){
869 forward = -1;
870 line++;
872 mp_msg(MSGT_SPUDEC,MSGL_V, "forward=%d", forward);
873 h = atoi(line + 7);
874 mp_msg(MSGT_VOBSUB,MSGL_V, "h=%d," ,h);
875 m = atoi(line + 10);
876 mp_msg(MSGT_VOBSUB,MSGL_V, "m=%d,", m);
877 s = atoi(line + 13);
878 mp_msg(MSGT_VOBSUB,MSGL_V, "s=%d,", s);
879 ms = atoi(line + 16);
880 mp_msg(MSGT_VOBSUB,MSGL_V, "ms=%d", ms);
881 vob->delay = (ms + 1000 * (s + 60 * (m + 60 * h))) * forward;
882 return 0;
885 static int
886 vobsub_set_lang(const char *line)
888 if (vobsub_id == -1)
889 vobsub_id = atoi(line + 8);
890 return 0;
893 static int
894 vobsub_parse_one_line(vobsub_t *vob, rar_stream_t *fd)
896 ssize_t line_size;
897 int res = -1;
898 do {
899 size_t line_reserve = 0;
900 char *line = NULL;
901 line_size = getline(&line, &line_reserve, fd);
902 if (line_size < 0) {
903 if (line)
904 free(line);
905 break;
907 if (*line == 0 || *line == '\r' || *line == '\n' || *line == '#')
908 continue;
909 else if (strncmp("langidx:", line, 8) == 0)
910 res = vobsub_set_lang(line);
911 else if (strncmp("delay:", line, 6) == 0)
912 res = vobsub_parse_delay(vob, line);
913 else if (strncmp("id:", line, 3) == 0)
914 res = vobsub_parse_id(vob, line + 3);
915 else if (strncmp("palette:", line, 8) == 0)
916 res = vobsub_parse_palette(vob, line + 8);
917 else if (strncmp("size:", line, 5) == 0)
918 res = vobsub_parse_size(vob, line + 5);
919 else if (strncmp("org:", line, 4) == 0)
920 res = vobsub_parse_origin(vob, line + 4);
921 else if (strncmp("timestamp:", line, 10) == 0)
922 res = vobsub_parse_timestamp(vob, line + 10);
923 else if (strncmp("custom colors:", line, 14) == 0)
924 //custom colors: ON/OFF, tridx: XXXX, colors: XXXXXX, XXXXXX, XXXXXX,XXXXXX
925 res = vobsub_parse_cuspal(vob, line) + vobsub_parse_tridx(line) + vobsub_parse_custom(vob, line);
926 else {
927 mp_msg(MSGT_VOBSUB,MSGL_V, "vobsub: ignoring %s", line);
928 continue;
930 if (res < 0)
931 mp_msg(MSGT_VOBSUB,MSGL_ERR, "ERROR in %s", line);
932 break;
933 } while (1);
934 return res;
938 vobsub_parse_ifo(void* this, const char *const name, unsigned int *palette, unsigned int *width, unsigned int *height, int force,
939 int sid, char *langid)
941 vobsub_t *vob = (vobsub_t*)this;
942 int res = -1;
943 rar_stream_t *fd = rar_open(name, "rb");
944 if (fd == NULL) {
945 if (force)
946 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Can't open IFO file");
947 } else {
948 // parse IFO header
949 unsigned char block[0x800];
950 const char *const ifo_magic = "DVDVIDEO-VTS";
951 if (rar_read(block, sizeof(block), 1, fd) != 1) {
952 if (force)
953 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Can't read IFO header");
954 } else if (memcmp(block, ifo_magic, strlen(ifo_magic) + 1))
955 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Bad magic in IFO header\n");
956 else {
957 unsigned long pgci_sector = block[0xcc] << 24 | block[0xcd] << 16
958 | block[0xce] << 8 | block[0xcf];
959 int standard = (block[0x200] & 0x30) >> 4;
960 int resolution = (block[0x201] & 0x0c) >> 2;
961 *height = standard ? 576 : 480;
962 *width = 0;
963 switch (resolution) {
964 case 0x0:
965 *width = 720;
966 break;
967 case 0x1:
968 *width = 704;
969 break;
970 case 0x2:
971 *width = 352;
972 break;
973 case 0x3:
974 *width = 352;
975 *height /= 2;
976 break;
977 default:
978 mp_msg(MSGT_VOBSUB,MSGL_WARN,"Vobsub: Unknown resolution %d \n", resolution);
980 if (langid && 0 <= sid && sid < 32) {
981 unsigned char *tmp = block + 0x256 + sid * 6 + 2;
982 langid[0] = tmp[0];
983 langid[1] = tmp[1];
984 langid[2] = 0;
986 if (rar_seek(fd, pgci_sector * sizeof(block), SEEK_SET)
987 || rar_read(block, sizeof(block), 1, fd) != 1)
988 mp_msg(MSGT_VOBSUB,MSGL_ERR, "Can't read IFO PGCI");
989 else {
990 unsigned long idx;
991 unsigned long pgc_offset = block[0xc] << 24 | block[0xd] << 16
992 | block[0xe] << 8 | block[0xf];
993 for (idx = 0; idx < 16; ++idx) {
994 unsigned char *p = block + pgc_offset + 0xa4 + 4 * idx;
995 palette[idx] = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
997 if(vob)
998 vob->have_palette = 1;
999 res = 0;
1002 rar_close(fd);
1004 return res;
1007 void *
1008 vobsub_open(const char *const name,const char *const ifo,const int force,void** spu)
1010 vobsub_t *vob = malloc(sizeof(vobsub_t));
1011 if(spu)
1012 *spu = NULL;
1013 if (vob) {
1014 char *buf;
1015 vob->custom = 0;
1016 vob->have_palette = 0;
1017 vob->orig_frame_width = 0;
1018 vob->orig_frame_height = 0;
1019 vob->spu_streams = NULL;
1020 vob->spu_streams_size = 0;
1021 vob->spu_streams_current = 0;
1022 vob->delay = 0;
1023 buf = malloc((strlen(name) + 5) * sizeof(char));
1024 if (buf) {
1025 rar_stream_t *fd;
1026 mpeg_t *mpg;
1027 /* read in the info file */
1028 if(!ifo) {
1029 strcpy(buf, name);
1030 strcat(buf, ".ifo");
1031 vobsub_parse_ifo(vob,buf, vob->palette, &vob->orig_frame_width, &vob->orig_frame_height, force, -1, NULL);
1032 } else
1033 vobsub_parse_ifo(vob,ifo, vob->palette, &vob->orig_frame_width, &vob->orig_frame_height, force, -1, NULL);
1034 /* read in the index */
1035 strcpy(buf, name);
1036 strcat(buf, ".idx");
1037 fd = rar_open(buf, "rb");
1038 if (fd == NULL) {
1039 if(force)
1040 mp_msg(MSGT_VOBSUB,MSGL_ERR,"VobSub: Can't open IDX file");
1041 else {
1042 free(buf);
1043 free(vob);
1044 return NULL;
1046 } else {
1047 while (vobsub_parse_one_line(vob, fd) >= 0)
1048 /* NOOP */ ;
1049 rar_close(fd);
1051 /* if no palette in .idx then use custom colors */
1052 if ((vob->custom == 0)&&(vob->have_palette!=1))
1053 vob->custom = 1;
1054 if (spu && vob->orig_frame_width && vob->orig_frame_height)
1055 *spu = spudec_new_scaled_vobsub(vob->palette, vob->cuspal, vob->custom, vob->orig_frame_width, vob->orig_frame_height);
1057 /* read the indexed mpeg_stream */
1058 strcpy(buf, name);
1059 strcat(buf, ".sub");
1060 mpg = mpeg_open(buf);
1061 if (mpg == NULL) {
1062 if(force)
1063 mp_msg(MSGT_VOBSUB,MSGL_ERR,"VobSub: Can't open SUB file");
1064 else {
1066 free(buf);
1067 free(vob);
1068 return NULL;
1070 } else {
1071 long last_pts_diff = 0;
1072 while (!mpeg_eof(mpg)) {
1073 off_t pos = mpeg_tell(mpg);
1074 if (mpeg_run(mpg) < 0) {
1075 if (!mpeg_eof(mpg))
1076 mp_msg(MSGT_VOBSUB,MSGL_ERR,"mpeg_run error");
1077 break;
1079 if (mpg->packet_size) {
1080 if ((mpg->aid & 0xe0) == 0x20) {
1081 unsigned int sid = mpg->aid & 0x1f;
1082 if (vobsub_ensure_spu_stream(vob, sid) >= 0) {
1083 packet_queue_t *queue = vob->spu_streams + sid;
1084 /* get the packet to fill */
1085 if (queue->packets_size == 0 && packet_queue_grow(queue) < 0)
1086 abort();
1087 while (queue->current_index + 1 < queue->packets_size
1088 && queue->packets[queue->current_index + 1].filepos <= pos)
1089 ++queue->current_index;
1090 if (queue->current_index < queue->packets_size) {
1091 packet_t *pkt;
1092 if (queue->packets[queue->current_index].data) {
1093 /* insert a new packet and fix the PTS ! */
1094 packet_queue_insert(queue);
1095 queue->packets[queue->current_index].pts100 =
1096 mpg->pts + last_pts_diff;
1098 pkt = queue->packets + queue->current_index;
1099 if (pkt->pts100 != UINT_MAX) {
1100 if (queue->packets_size > 1)
1101 last_pts_diff = pkt->pts100 - mpg->pts;
1102 else
1103 pkt->pts100 = mpg->pts;
1104 /* FIXME: should not use mpg_sub internal informations, make a copy */
1105 pkt->data = mpg->packet;
1106 pkt->size = mpg->packet_size;
1107 mpg->packet = NULL;
1108 mpg->packet_reserve = 0;
1109 mpg->packet_size = 0;
1113 else
1114 mp_msg(MSGT_VOBSUB,MSGL_WARN, "don't know what to do with subtitle #%u\n", sid);
1118 vob->spu_streams_current = vob->spu_streams_size;
1119 while (vob->spu_streams_current-- > 0)
1120 vob->spu_streams[vob->spu_streams_current].current_index = 0;
1121 mpeg_free(mpg);
1123 free(buf);
1126 return vob;
1129 void
1130 vobsub_close(void *this)
1132 vobsub_t *vob = (vobsub_t *)this;
1133 if (vob->spu_streams) {
1134 while (vob->spu_streams_size--)
1135 packet_queue_destroy(vob->spu_streams + vob->spu_streams_size);
1136 free(vob->spu_streams);
1138 free(vob);
1141 unsigned int
1142 vobsub_get_indexes_count(void *vobhandle)
1144 vobsub_t *vob = (vobsub_t *) vobhandle;
1145 return vob->spu_streams_size;
1148 char *
1149 vobsub_get_id(void *vobhandle, unsigned int index)
1151 vobsub_t *vob = (vobsub_t *) vobhandle;
1152 return (index < vob->spu_streams_size) ? vob->spu_streams[index].id : NULL;
1156 vobsub_set_from_lang(void *vobhandle, unsigned char * lang)
1158 int i;
1159 vobsub_t *vob= (vobsub_t *) vobhandle;
1160 while(lang && strlen(lang) >= 2){
1161 for(i=0; i < vob->spu_streams_size; i++)
1162 if (vob->spu_streams[i].id)
1163 if ((strncmp(vob->spu_streams[i].id, lang, 2)==0)){
1164 vobsub_id=i;
1165 mp_msg(MSGT_VOBSUB, MSGL_INFO, "Selected VOBSUB language: %d language: %s\n", i, vob->spu_streams[i].id);
1166 return 0;
1168 lang+=2;while (lang[0]==',' || lang[0]==' ') ++lang;
1170 mp_msg(MSGT_VOBSUB, MSGL_WARN, "No matching VOBSUB languge found!\n");
1171 return -1;
1175 vobsub_get_packet(void *vobhandle, float pts,void** data, int* timestamp) {
1176 vobsub_t *vob = (vobsub_t *)vobhandle;
1177 unsigned int pts100 = 90000 * pts;
1178 if (vob->spu_streams && 0 <= vobsub_id && (unsigned) vobsub_id < vob->spu_streams_size) {
1179 packet_queue_t *queue = vob->spu_streams + vobsub_id;
1180 while (queue->current_index < queue->packets_size) {
1181 packet_t *pkt = queue->packets + queue->current_index;
1182 if (pkt->pts100 != UINT_MAX)
1183 if (pkt->pts100 <= pts100) {
1184 ++queue->current_index;
1185 *data = pkt->data;
1186 *timestamp = pkt->pts100;
1187 return pkt->size;
1188 } else break;
1189 else
1190 ++queue->current_index;
1193 return -1;
1197 vobsub_get_next_packet(void *vobhandle, void** data, int* timestamp)
1199 vobsub_t *vob = (vobsub_t *)vobhandle;
1200 if (vob->spu_streams && 0 <= vobsub_id && (unsigned) vobsub_id < vob->spu_streams_size) {
1201 packet_queue_t *queue = vob->spu_streams + vobsub_id;
1202 if (queue->current_index < queue->packets_size) {
1203 packet_t *pkt = queue->packets + queue->current_index;
1204 ++queue->current_index;
1205 *data = pkt->data;
1206 *timestamp = pkt->pts100;
1207 return pkt->size;
1210 return -1;
1213 void
1214 vobsub_reset(void *vobhandle)
1216 vobsub_t *vob = (vobsub_t *)vobhandle;
1217 if (vob->spu_streams) {
1218 unsigned int n = vob->spu_streams_size;
1219 while (n-- > 0)
1220 vob->spu_streams[n].current_index = 0;
1224 /**********************************************************************
1225 * Vobsub output
1226 **********************************************************************/
1228 typedef struct {
1229 FILE *fsub;
1230 FILE *fidx;
1231 unsigned int aid;
1232 } vobsub_out_t;
1234 static void
1235 create_idx(vobsub_out_t *me, const unsigned int *palette, unsigned int orig_width, unsigned int orig_height)
1237 int i;
1238 fprintf(me->fidx,
1239 "# VobSub index file, v7 (do not modify this line!)\n"
1240 "#\n"
1241 "# Generated by MPlayer " VERSION "\n"
1242 "# See <URL:http://www.mplayerhq.hu/> for more information about MPlayer\n"
1243 "# See <URL:http://vobsub.edensrising.com/> for more information about Vobsub\n"
1244 "#\n"
1245 "size: %ux%u\n",
1246 orig_width, orig_height);
1247 if (palette) {
1248 fputs("palette:", me->fidx);
1249 for (i = 0; i < 16; ++i) {
1250 const double y = palette[i] >> 16 & 0xff,
1251 u = (palette[i] >> 8 & 0xff) - 128.0,
1252 v = (palette[i] & 0xff) - 128.0;
1253 if (i)
1254 putc(',', me->fidx);
1255 fprintf(me->fidx, " %02x%02x%02x",
1256 MIN(MAX((int)(y + 1.4022 * u), 0), 0xff),
1257 MIN(MAX((int)(y - 0.3456 * u - 0.7145 * v), 0), 0xff),
1258 MIN(MAX((int)(y + 1.7710 * v), 0), 0xff));
1260 putc('\n', me->fidx);
1264 void *
1265 vobsub_out_open(const char *basename, const unsigned int *palette,
1266 unsigned int orig_width, unsigned int orig_height,
1267 const char *id, unsigned int index)
1269 vobsub_out_t *result = NULL;
1270 char *filename;
1271 filename = malloc(strlen(basename) + 5);
1272 if (filename) {
1273 result = malloc(sizeof(vobsub_out_t));
1274 result->fsub = NULL;
1275 result->fidx = NULL;
1276 result->aid = 0;
1277 if (result) {
1278 result->aid = index;
1279 strcpy(filename, basename);
1280 strcat(filename, ".sub");
1281 result->fsub = fopen(filename, "a");
1282 if (result->fsub == NULL)
1283 perror("Error: vobsub_out_open subtitle file open failed");
1284 strcpy(filename, basename);
1285 strcat(filename, ".idx");
1286 result->fidx = fopen(filename, "a");
1287 if (result->fidx) {
1288 if (ftell(result->fidx) == 0){
1289 create_idx(result, palette, orig_width, orig_height);
1290 /* Make the selected language the default language */
1291 fprintf(result->fidx, "\n# Language index in use\nlangidx: %u\n", index);
1293 fprintf(result->fidx, "\nid: %s, index: %u\n", id ? id : "xx", index);
1294 /* So that we can check the file now */
1295 fflush(result->fidx);
1297 else
1298 perror("Error: vobsub_out_open index file open failed");
1299 free(filename);
1302 return result;
1305 void
1306 vobsub_out_close(void *me)
1308 vobsub_out_t *vob = (vobsub_out_t*)me;
1309 if (vob->fidx)
1310 fclose(vob->fidx);
1311 if (vob->fsub)
1312 fclose(vob->fsub);
1313 free(vob);
1316 void
1317 vobsub_out_output(void *me, const unsigned char *packet, int len, double pts)
1319 static double last_pts;
1320 static int last_pts_set = 0;
1321 vobsub_out_t *vob = (vobsub_out_t*)me;
1322 if (vob->fsub) {
1323 /* Windows' Vobsub require that every packet is exactly 2kB long */
1324 unsigned char buffer[2048];
1325 unsigned char *p;
1326 int remain = 2048;
1327 /* Do not output twice a line with the same timestamp, this
1328 breaks Windows' Vobsub */
1329 if (vob->fidx && (!last_pts_set || last_pts != pts)) {
1330 static unsigned int last_h = 9999, last_m = 9999, last_s = 9999, last_ms = 9999;
1331 unsigned int h, m, ms;
1332 double s;
1333 s = pts;
1334 h = s / 3600;
1335 s -= h * 3600;
1336 m = s / 60;
1337 s -= m * 60;
1338 ms = (s - (unsigned int) s) * 1000;
1339 if (ms >= 1000) /* prevent overfolws or bad float stuff */
1340 ms = 0;
1341 if (h != last_h || m != last_m || (unsigned int) s != last_s || ms != last_ms) {
1342 fprintf(vob->fidx, "timestamp: %02u:%02u:%02u:%03u, filepos: %09lx\n",
1343 h, m, (unsigned int) s, ms, ftell(vob->fsub));
1344 last_h = h;
1345 last_m = m;
1346 last_s = (unsigned int) s;
1347 last_ms = ms;
1350 last_pts = pts;
1351 last_pts_set = 1;
1353 /* Packet start code: Windows' Vobsub needs this */
1354 p = buffer;
1355 *p++ = 0; /* 0x00 */
1356 *p++ = 0;
1357 *p++ = 1;
1358 *p++ = 0xba;
1359 *p++ = 0x40;
1360 memset(p, 0, 9);
1361 p += 9;
1362 { /* Packet */
1363 static unsigned char last_pts[5] = { 0, 0, 0, 0, 0};
1364 unsigned char now_pts[5];
1365 int pts_len, pad_len, datalen = len;
1366 pts *= 90000;
1367 now_pts[0] = 0x21 | (((unsigned long)pts >> 29) & 0x0e);
1368 now_pts[1] = ((unsigned long)pts >> 22) & 0xff;
1369 now_pts[2] = 0x01 | (((unsigned long)pts >> 14) & 0xfe);
1370 now_pts[3] = ((unsigned long)pts >> 7) & 0xff;
1371 now_pts[4] = 0x01 | (((unsigned long)pts << 1) & 0xfe);
1372 pts_len = memcmp(last_pts, now_pts, sizeof(now_pts)) ? sizeof(now_pts) : 0;
1373 memcpy(last_pts, now_pts, sizeof(now_pts));
1375 datalen += 3; /* Version, PTS_flags, pts_len */
1376 datalen += pts_len;
1377 datalen += 1; /* AID */
1378 pad_len = 2048 - (p - buffer) - 4 /* MPEG ID */ - 2 /* payload len */ - datalen;
1379 /* XXX - Go figure what should go here! In any case the
1380 packet has to be completly filled. If I can fill it
1381 with padding (0x000001be) latter I'll do that. But if
1382 there is only room for 6 bytes then I can not write a
1383 padding packet. So I add some padding in the PTS
1384 field. This looks like a dirty kludge. Oh well... */
1385 if (pad_len < 0) {
1386 /* Packet is too big. Let's try ommiting the PTS field */
1387 datalen -= pts_len;
1388 pts_len = 0;
1389 pad_len = 0;
1391 else if (pad_len > 6)
1392 pad_len = 0;
1393 datalen += pad_len;
1395 *p++ = 0; /* 0x0e */
1396 *p++ = 0;
1397 *p++ = 1;
1398 *p++ = 0xbd;
1400 *p++ = (datalen >> 8) & 0xff; /* length of payload */
1401 *p++ = datalen & 0xff;
1402 *p++ = 0x80; /* System-2 (.VOB) stream */
1403 *p++ = pts_len ? 0x80 : 0x00; /* pts_flags */
1404 *p++ = pts_len + pad_len;
1405 memcpy(p, now_pts, pts_len);
1406 p += pts_len;
1407 memset(p, 0, pad_len);
1408 p += pad_len;
1410 *p++ = 0x20 | vob->aid; /* aid */
1411 if (fwrite(buffer, p - buffer, 1, vob->fsub) != 1
1412 || fwrite(packet, len, 1, vob->fsub) != 1)
1413 perror("ERROR: vobsub write failed");
1414 else
1415 remain -= p - buffer + len;
1417 /* Padding */
1418 if (remain >= 6) {
1419 p = buffer;
1420 *p++ = 0x00;
1421 *p++ = 0x00;
1422 *p++ = 0x01;
1423 *p++ = 0xbe;
1424 *p++ = (remain - 6) >> 8;
1425 *p++ = (remain - 6) & 0xff;
1426 /* for better compression, blank this */
1427 memset(buffer + 6, 0, remain - (p - buffer));
1428 if (fwrite(buffer, remain, 1, vob->fsub) != 1)
1429 perror("ERROR: vobsub padding write failed");
1431 else if (remain > 0) {
1432 /* I don't know what to output. But anyway the block
1433 needs to be 2KB big */
1434 memset(buffer, 0, remain);
1435 if (fwrite(buffer, remain, 1, vob->fsub) != 1)
1436 perror("ERROR: vobsub blank padding write failed");
1438 else if (remain < 0)
1439 fprintf(stderr,
1440 "\nERROR: wrong thing happenned...\n"
1441 " I wrote a %i data bytes spu packet and that's too long\n", len);