2 * Some code freely inspired from VobSub <URL:http://vobsub.edensrising.com>,
3 * with kind permission from Gabest <gabest@freemail.hu>
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <sys/types.h>
40 #include "unrar_exec.h"
41 #include "libavutil/common.h"
44 // Record the original -vobsubid set by commandline, since vobsub_id will be
45 // overridden if slang match any of vobsub streams.
46 static int vobsubid
= -2;
48 /**********************************************************************
50 * The RAR file must have the same basename as the file to open
51 **********************************************************************/
52 #ifdef CONFIG_UNRAR_EXEC
60 static rar_stream_t
*rar_open(const char *const filename
,
61 const char *const mode
)
64 /* unrar_exec can only read */
65 if (strcmp("r", mode
) && strcmp("rb", mode
)) {
69 stream
= malloc(sizeof(rar_stream_t
));
72 /* first try normal access */
73 stream
->file
= fopen(filename
, mode
);
74 if (stream
->file
== NULL
) {
78 /* Guess the RAR archive filename */
80 p
= strrchr(filename
, '.');
82 ptrdiff_t l
= p
- filename
;
83 rar_filename
= malloc(l
+ 5);
84 if (rar_filename
== NULL
) {
88 strncpy(rar_filename
, filename
, l
);
89 strcpy(rar_filename
+ l
, ".rar");
91 rar_filename
= malloc(strlen(filename
) + 5);
92 if (rar_filename
== NULL
) {
96 strcpy(rar_filename
, filename
);
97 strcat(rar_filename
, ".rar");
99 /* get rid of the path if there is any */
100 p
= mp_basename(filename
);
101 rc
= unrar_exec_get(&stream
->data
, &stream
->size
, p
, rar_filename
);
103 /* There is no matching filename in the archive. However, sometimes
104 * the files we are looking for have been given arbitrary names in the archive.
105 * Let's look for a file with an exact match in the extension only. */
106 int i
, num_files
, name_len
;
107 ArchiveList_struct
*list
, *lp
;
108 num_files
= unrar_exec_list(rar_filename
, &list
);
111 demanded_ext
= strrchr (p
, '.');
113 int demanded_ext_len
= strlen (demanded_ext
);
114 for (i
= 0, lp
= list
; i
< num_files
; i
++, lp
= lp
->next
) {
115 name_len
= strlen (lp
->item
.Name
);
116 if (name_len
>= demanded_ext_len
&& !strcasecmp (lp
->item
.Name
+ name_len
- demanded_ext_len
, demanded_ext
)) {
117 rc
= unrar_exec_get(&stream
->data
, &stream
->size
,
118 lp
->item
.Name
, rar_filename
);
124 unrar_exec_freelist(list
);
139 static int rar_close(rar_stream_t
*stream
)
142 return fclose(stream
->file
);
147 static int rar_eof(rar_stream_t
*stream
)
150 return feof(stream
->file
);
151 return stream
->pos
>= stream
->size
;
154 static long rar_tell(rar_stream_t
*stream
)
157 return ftell(stream
->file
);
161 static int rar_seek(rar_stream_t
*stream
, long offset
, int whence
)
164 return fseek(stream
->file
, offset
, whence
);
171 stream
->pos
= offset
;
174 if (offset
< 0 && stream
->pos
< (unsigned long) -offset
) {
178 stream
->pos
+= offset
;
181 if (offset
< 0 && stream
->size
< (unsigned long) -offset
) {
185 stream
->pos
= stream
->size
+ offset
;
194 static int rar_getc(rar_stream_t
*stream
)
197 return getc(stream
->file
);
200 return stream
->data
[stream
->pos
++];
203 static size_t rar_read(void *ptr
, size_t size
, size_t nmemb
,
204 rar_stream_t
*stream
)
207 unsigned long remain
;
209 return fread(ptr
, size
, nmemb
, stream
->file
);
213 remain
= stream
->size
- stream
->pos
;
215 res
= remain
/ size
* size
;
216 memcpy(ptr
, stream
->data
+ stream
->pos
, res
);
223 typedef FILE rar_stream_t
;
224 #define rar_open fopen
225 #define rar_close fclose
227 #define rar_tell ftell
228 #define rar_seek fseek
229 #define rar_getc getc
230 #define rar_read fread
233 /**********************************************************************/
235 static ssize_t
vobsub_getline(char **lineptr
, size_t *n
, rar_stream_t
*stream
)
239 if (*lineptr
== NULL
) {
240 *lineptr
= malloc(4096);
243 } else if (*n
== 0) {
244 char *tmp
= realloc(*lineptr
, 4096);
250 if (*lineptr
== NULL
|| *n
== 0)
253 for (c
= rar_getc(stream
); c
!= EOF
; c
= rar_getc(stream
)) {
255 char *tmp
= realloc(*lineptr
, *n
* 2);
261 (*lineptr
)[res
++] = c
;
273 /**********************************************************************
275 **********************************************************************/
278 rar_stream_t
*stream
;
281 unsigned char *packet
;
282 unsigned int packet_reserve
;
283 unsigned int packet_size
;
284 int padding_was_here
;
288 static mpeg_t
*mpeg_open(const char *filename
)
290 mpeg_t
*res
= malloc(sizeof(mpeg_t
));
291 int err
= res
== NULL
;
296 res
->packet_size
= 0;
297 res
->packet_reserve
= 0;
298 res
->padding_was_here
= 1;
300 res
->stream
= rar_open(filename
, "rb");
301 err
= res
->stream
== NULL
;
303 perror("fopen Vobsub file failed");
307 return err
? NULL
: res
;
310 static void mpeg_free(mpeg_t
*mpeg
)
314 rar_close(mpeg
->stream
);
318 static int mpeg_eof(mpeg_t
*mpeg
)
320 return rar_eof(mpeg
->stream
);
323 static off_t
mpeg_tell(mpeg_t
*mpeg
)
325 return rar_tell(mpeg
->stream
);
328 static int mpeg_run(mpeg_t
*mpeg
)
330 unsigned int len
, idx
, version
;
332 /* Goto start of a packet, it starts with 0x000001?? */
333 const unsigned char wanted
[] = { 0, 0, 1 };
334 unsigned char buf
[5];
337 mpeg
->packet_size
= 0;
338 if (rar_read(buf
, 4, 1, mpeg
->stream
) != 1)
340 while (memcmp(buf
, wanted
, sizeof(wanted
)) != 0) {
341 c
= rar_getc(mpeg
->stream
);
344 memmove(buf
, buf
+ 1, 3);
348 case 0xb9: /* System End Code */
350 case 0xba: /* Packet start code */
351 c
= rar_getc(mpeg
->stream
);
354 if ((c
& 0xc0) == 0x40)
356 else if ((c
& 0xf0) == 0x20)
359 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: Unsupported MPEG version: 0x%02x\n", c
);
363 if (rar_seek(mpeg
->stream
, 9, SEEK_CUR
))
365 } else if (version
== 2) {
366 if (rar_seek(mpeg
->stream
, 7, SEEK_CUR
))
370 if (!mpeg
->padding_was_here
)
373 case 0xbd: /* packet */
374 if (rar_read(buf
, 2, 1, mpeg
->stream
) != 1)
376 mpeg
->padding_was_here
= 0;
377 len
= buf
[0] << 8 | buf
[1];
378 idx
= mpeg_tell(mpeg
);
379 c
= rar_getc(mpeg
->stream
);
382 if ((c
& 0xC0) == 0x40) { /* skip STD scale & size */
383 if (rar_getc(mpeg
->stream
) < 0)
385 c
= rar_getc(mpeg
->stream
);
389 if ((c
& 0xf0) == 0x20) { /* System-1 stream timestamp */
390 /* Do we need this? */
392 } else if ((c
& 0xf0) == 0x30) {
393 /* Do we need this? */
395 } else if ((c
& 0xc0) == 0x80) { /* System-2 (.VOB) stream */
396 unsigned int pts_flags
, hdrlen
, dataidx
;
397 c
= rar_getc(mpeg
->stream
);
401 c
= rar_getc(mpeg
->stream
);
405 dataidx
= mpeg_tell(mpeg
) + hdrlen
;
406 if (dataidx
> idx
+ len
) {
407 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "Invalid header length: %d (total length: %d, idx: %d, dataidx: %d)\n",
408 hdrlen
, len
, idx
, dataidx
);
411 if ((pts_flags
& 0xc0) == 0x80) {
412 if (rar_read(buf
, 5, 1, mpeg
->stream
) != 1)
414 if (!(((buf
[0] & 0xf0) == 0x20) && (buf
[0] & 1) && (buf
[2] & 1) && (buf
[4] & 1))) {
415 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "vobsub PTS error: 0x%02x %02x%02x %02x%02x \n",
416 buf
[0], buf
[1], buf
[2], buf
[3], buf
[4]);
419 mpeg
->pts
= ((buf
[0] & 0x0e) << 29 | buf
[1] << 22 | (buf
[2] & 0xfe) << 14
420 | buf
[3] << 7 | (buf
[4] >> 1));
421 } else /* if ((pts_flags & 0xc0) == 0xc0) */ {
425 rar_seek(mpeg
->stream
, dataidx
, SEEK_SET
);
426 mpeg
->aid
= rar_getc(mpeg
->stream
);
428 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "Bogus aid %d\n", mpeg
->aid
);
431 mpeg
->packet_size
= len
- ((unsigned int) mpeg_tell(mpeg
) - idx
);
432 if (mpeg
->packet_reserve
< mpeg
->packet_size
) {
434 mpeg
->packet
= malloc(mpeg
->packet_size
);
436 mpeg
->packet_reserve
= mpeg
->packet_size
;
438 if (mpeg
->packet
== NULL
) {
439 mp_msg(MSGT_VOBSUB
, MSGL_FATAL
, "malloc failure");
440 mpeg
->packet_reserve
= 0;
441 mpeg
->packet_size
= 0;
444 if (rar_read(mpeg
->packet
, mpeg
->packet_size
, 1, mpeg
->stream
) != 1) {
445 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "fread failure");
446 mpeg
->packet_size
= 0;
452 case 0xbe: /* Padding */
453 if (rar_read(buf
, 2, 1, mpeg
->stream
) != 1)
455 len
= buf
[0] << 8 | buf
[1];
456 if (len
> 0 && rar_seek(mpeg
->stream
, len
, SEEK_CUR
))
458 mpeg
->padding_was_here
= 1;
461 if (0xc0 <= buf
[3] && buf
[3] < 0xf0) {
462 /* MPEG audio or video */
463 if (rar_read(buf
, 2, 1, mpeg
->stream
) != 1)
465 len
= buf
[0] << 8 | buf
[1];
466 if (len
> 0 && rar_seek(mpeg
->stream
, len
, SEEK_CUR
))
469 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "unknown header 0x%02X%02X%02X%02X\n",
470 buf
[0], buf
[1], buf
[2], buf
[3]);
477 /**********************************************************************
479 **********************************************************************/
491 unsigned int packets_reserve
;
492 unsigned int packets_size
;
493 unsigned int current_index
;
496 static void packet_construct(packet_t
*pkt
)
504 static void packet_destroy(packet_t
*pkt
)
509 static void packet_queue_construct(packet_queue_t
*queue
)
512 queue
->packets
= NULL
;
513 queue
->packets_reserve
= 0;
514 queue
->packets_size
= 0;
515 queue
->current_index
= 0;
518 static void packet_queue_destroy(packet_queue_t
*queue
)
520 if (queue
->packets
) {
521 while (queue
->packets_size
--)
522 packet_destroy(queue
->packets
+ queue
->packets_size
);
523 free(queue
->packets
);
528 /* Make sure there is enough room for needed_size packets in the
530 static int packet_queue_ensure(packet_queue_t
*queue
, unsigned int needed_size
)
532 if (queue
->packets_reserve
< needed_size
) {
533 if (queue
->packets
) {
534 packet_t
*tmp
= realloc(queue
->packets
, 2 * queue
->packets_reserve
* sizeof(packet_t
));
536 mp_msg(MSGT_VOBSUB
, MSGL_FATAL
, "realloc failure");
539 queue
->packets
= tmp
;
540 queue
->packets_reserve
*= 2;
542 queue
->packets
= malloc(sizeof(packet_t
));
543 if (queue
->packets
== NULL
) {
544 mp_msg(MSGT_VOBSUB
, MSGL_FATAL
, "malloc failure");
547 queue
->packets_reserve
= 1;
553 /* add one more packet */
554 static int packet_queue_grow(packet_queue_t
*queue
)
556 if (packet_queue_ensure(queue
, queue
->packets_size
+ 1) < 0)
558 packet_construct(queue
->packets
+ queue
->packets_size
);
559 ++queue
->packets_size
;
563 /* insert a new packet, duplicating pts from the current one */
564 static int packet_queue_insert(packet_queue_t
*queue
)
567 if (packet_queue_ensure(queue
, queue
->packets_size
+ 1) < 0)
569 /* XXX packet_size does not reflect the real thing here, it will be updated a bit later */
570 memmove(queue
->packets
+ queue
->current_index
+ 2,
571 queue
->packets
+ queue
->current_index
+ 1,
572 sizeof(packet_t
) * (queue
->packets_size
- queue
->current_index
- 1));
573 pkts
= queue
->packets
+ queue
->current_index
;
574 ++queue
->packets_size
;
575 ++queue
->current_index
;
576 packet_construct(pkts
+ 1);
577 pkts
[1].pts100
= pkts
[0].pts100
;
578 pkts
[1].filepos
= pkts
[0].filepos
;
582 /**********************************************************************
584 **********************************************************************/
587 unsigned int palette
[16];
589 unsigned int have_palette
;
590 unsigned int orig_frame_width
, orig_frame_height
;
591 unsigned int origin_x
, origin_y
;
593 packet_queue_t
*spu_streams
;
594 unsigned int spu_streams_size
;
595 unsigned int spu_streams_current
;
596 unsigned int spu_valid_streams_size
;
599 /* Make sure that the spu stream idx exists. */
600 static int vobsub_ensure_spu_stream(vobsub_t
*vob
, unsigned int index
)
602 if (index
>= vob
->spu_streams_size
) {
603 /* This is a new stream */
604 if (vob
->spu_streams
) {
605 packet_queue_t
*tmp
= realloc(vob
->spu_streams
, (index
+ 1) * sizeof(packet_queue_t
));
607 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "vobsub_ensure_spu_stream: realloc failure");
610 vob
->spu_streams
= tmp
;
612 vob
->spu_streams
= malloc((index
+ 1) * sizeof(packet_queue_t
));
613 if (vob
->spu_streams
== NULL
) {
614 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "vobsub_ensure_spu_stream: malloc failure");
618 while (vob
->spu_streams_size
<= index
) {
619 packet_queue_construct(vob
->spu_streams
+ vob
->spu_streams_size
);
620 ++vob
->spu_streams_size
;
626 static int vobsub_add_id(vobsub_t
*vob
, const char *id
, size_t idlen
,
627 const unsigned int index
)
629 if (vobsub_ensure_spu_stream(vob
, index
) < 0)
632 free(vob
->spu_streams
[index
].id
);
633 vob
->spu_streams
[index
].id
= malloc(idlen
+ 1);
634 if (vob
->spu_streams
[index
].id
== NULL
) {
635 mp_msg(MSGT_VOBSUB
, MSGL_FATAL
, "vobsub_add_id: malloc failure");
638 vob
->spu_streams
[index
].id
[idlen
] = 0;
639 memcpy(vob
->spu_streams
[index
].id
, id
, idlen
);
641 vob
->spu_streams_current
= index
;
642 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_VOBSUB_ID=%d\n", index
);
644 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_VSID_%d_LANG=%s\n", index
, vob
->spu_streams
[index
].id
);
645 mp_msg(MSGT_VOBSUB
, MSGL_V
, "[vobsub] subtitle (vobsubid): %d language %s\n",
646 index
, vob
->spu_streams
[index
].id
);
650 static int vobsub_add_timestamp(vobsub_t
*vob
, off_t filepos
, int ms
)
652 packet_queue_t
*queue
;
654 if (vob
->spu_streams
== 0) {
655 mp_msg(MSGT_VOBSUB
, MSGL_WARN
, "[vobsub] warning, binning some index entries. Check your index file\n");
658 queue
= vob
->spu_streams
+ vob
->spu_streams_current
;
659 if (packet_queue_grow(queue
) >= 0) {
660 pkt
= queue
->packets
+ (queue
->packets_size
- 1);
661 pkt
->filepos
= filepos
;
662 pkt
->pts100
= ms
< 0 ? UINT_MAX
: (unsigned int)ms
* 90;
668 static int vobsub_parse_id(vobsub_t
*vob
, const char *line
)
685 if (strncmp("index:", q
, 6))
692 return vobsub_add_id(vob
, p
, idlen
, atoi(q
));
695 static int vobsub_parse_timestamp(vobsub_t
*vob
, const char *line
)
699 if (sscanf(line
, " %02d:%02d:%02d:%03d, filepos: %09"SCNx64
,
700 &h
, &m
, &s
, &ms
, &filepos
) != 5)
702 return vobsub_add_timestamp(vob
, filepos
, vob
->delay
+ ms
+ 1000 * (s
+ 60 * (m
+ 60 * h
)));
705 static int vobsub_parse_origin(vobsub_t
*vob
, const char *line
)
710 if (sscanf(line
, " %u,%u", &x
, &y
) == 2) {
718 unsigned int vobsub_palette_to_yuv(unsigned int pal
)
720 int r
, g
, b
, y
, u
, v
;
721 // Palette in idx file is not rgb value, it was calculated by wrong formula.
722 // Here's reversed formula of the one used to generate palette in idx file.
723 r
= pal
>> 16 & 0xff;
726 y
= av_clip_uint8( 0.1494 * r
+ 0.6061 * g
+ 0.2445 * b
);
727 u
= av_clip_uint8( 0.6066 * r
- 0.4322 * g
- 0.1744 * b
+ 128);
728 v
= av_clip_uint8(-0.08435 * r
- 0.3422 * g
+ 0.4266 * b
+ 128);
729 y
= y
* 219 / 255 + 16;
730 return y
<< 16 | u
<< 8 | v
;
733 unsigned int vobsub_rgb_to_yuv(unsigned int rgb
)
735 int r
, g
, b
, y
, u
, v
;
736 r
= rgb
>> 16 & 0xff;
739 y
= ( 0.299 * r
+ 0.587 * g
+ 0.114 * b
) * 219 / 255 + 16.5;
740 u
= (-0.16874 * r
- 0.33126 * g
+ 0.5 * b
) * 224 / 255 + 128.5;
741 v
= ( 0.5 * r
- 0.41869 * g
- 0.08131 * b
) * 224 / 255 + 128.5;
742 return y
<< 16 | u
<< 8 | v
;
745 static int vobsub_parse_delay(vobsub_t
*vob
, const char *line
)
749 if (*(line
+ 7) == '+') {
752 } else if (*(line
+ 7) == '-') {
756 mp_msg(MSGT_SPUDEC
, MSGL_V
, "forward=%d", forward
);
758 mp_msg(MSGT_VOBSUB
, MSGL_V
, "h=%d,", h
);
760 mp_msg(MSGT_VOBSUB
, MSGL_V
, "m=%d,", m
);
762 mp_msg(MSGT_VOBSUB
, MSGL_V
, "s=%d,", s
);
763 ms
= atoi(line
+ 16);
764 mp_msg(MSGT_VOBSUB
, MSGL_V
, "ms=%d", ms
);
765 vob
->delay
= (ms
+ 1000 * (s
+ 60 * (m
+ 60 * h
))) * forward
;
769 static int vobsub_set_lang(const char *line
)
772 vobsub_id
= atoi(line
+ 8);
776 static int vobsub_parse_one_line(vobsub_t
*vob
, rar_stream_t
*fd
,
777 unsigned char **extradata
,
778 unsigned int *extradata_len
)
782 size_t line_reserve
= 0;
785 line_size
= vobsub_getline(&line
, &line_reserve
, fd
);
786 if (line_size
< 0 || line_size
> 1000000 ||
787 *extradata_len
+line_size
> 10000000) {
791 *extradata
= realloc(*extradata
, *extradata_len
+line_size
+1);
792 memcpy(*extradata
+*extradata_len
, line
, line_size
);
793 *extradata_len
+= line_size
;
794 (*extradata
)[*extradata_len
] = 0;
796 if (*line
== 0 || *line
== '\r' || *line
== '\n' || *line
== '#')
798 else if (strncmp("langidx:", line
, 8) == 0)
799 res
= vobsub_set_lang(line
);
800 else if (strncmp("delay:", line
, 6) == 0)
801 res
= vobsub_parse_delay(vob
, line
);
802 else if (strncmp("id:", line
, 3) == 0)
803 res
= vobsub_parse_id(vob
, line
+ 3);
804 else if (strncmp("org:", line
, 4) == 0)
805 res
= vobsub_parse_origin(vob
, line
+ 4);
806 else if (strncmp("timestamp:", line
, 10) == 0)
807 res
= vobsub_parse_timestamp(vob
, line
+ 10);
809 mp_msg(MSGT_VOBSUB
, MSGL_V
, "vobsub: ignoring %s", line
);
813 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "ERROR in %s", line
);
820 int vobsub_parse_ifo(void* this, const char *const name
, unsigned int *palette
,
821 unsigned int *width
, unsigned int *height
, int force
,
822 int sid
, char *langid
)
824 vobsub_t
*vob
= this;
826 rar_stream_t
*fd
= rar_open(name
, "rb");
829 mp_msg(MSGT_VOBSUB
, MSGL_WARN
, "VobSub: Can't open IFO file\n");
832 unsigned char block
[0x800];
833 const char *const ifo_magic
= "DVDVIDEO-VTS";
834 if (rar_read(block
, sizeof(block
), 1, fd
) != 1) {
836 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: Can't read IFO header\n");
837 } else if (memcmp(block
, ifo_magic
, strlen(ifo_magic
) + 1))
838 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: Bad magic in IFO header\n");
840 unsigned long pgci_sector
= block
[0xcc] << 24 | block
[0xcd] << 16
841 | block
[0xce] << 8 | block
[0xcf];
842 int standard
= (block
[0x200] & 0x30) >> 4;
843 int resolution
= (block
[0x201] & 0x0c) >> 2;
844 *height
= standard
? 576 : 480;
846 switch (resolution
) {
861 mp_msg(MSGT_VOBSUB
, MSGL_WARN
, "Vobsub: Unknown resolution %d \n", resolution
);
863 if (langid
&& 0 <= sid
&& sid
< 32) {
864 unsigned char *tmp
= block
+ 0x256 + sid
* 6 + 2;
869 if (rar_seek(fd
, pgci_sector
* sizeof(block
), SEEK_SET
)
870 || rar_read(block
, sizeof(block
), 1, fd
) != 1)
871 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: Can't read IFO PGCI\n");
874 unsigned long pgc_offset
= block
[0xc] << 24 | block
[0xd] << 16
875 | block
[0xe] << 8 | block
[0xf];
876 for (idx
= 0; idx
< 16; ++idx
) {
877 unsigned char *p
= block
+ pgc_offset
+ 0xa4 + 4 * idx
;
878 palette
[idx
] = p
[0] << 24 | p
[1] << 16 | p
[2] << 8 | p
[3];
881 vob
->have_palette
= 1;
890 void *vobsub_open(const char *const name
, const char *const ifo
,
891 const int force
, void** spu
)
893 unsigned char *extradata
= NULL
;
894 unsigned int extradata_len
= 0;
895 vobsub_t
*vob
= calloc(1, sizeof(vobsub_t
));
899 vobsubid
= vobsub_id
;
902 buf
= malloc(strlen(name
) + 5);
906 /* read in the info file */
910 vobsub_parse_ifo(vob
, buf
, vob
->palette
, &vob
->orig_frame_width
, &vob
->orig_frame_height
, force
, -1, NULL
);
912 vobsub_parse_ifo(vob
, ifo
, vob
->palette
, &vob
->orig_frame_width
, &vob
->orig_frame_height
, force
, -1, NULL
);
913 /* read in the index */
916 fd
= rar_open(buf
, "rb");
919 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: Can't open IDX file\n");
926 while (vobsub_parse_one_line(vob
, fd
, &extradata
, &extradata_len
) >= 0)
931 *spu
= spudec_new_scaled(vob
->palette
, vob
->orig_frame_width
, vob
->orig_frame_height
, extradata
, extradata_len
);
934 /* read the indexed mpeg_stream */
937 mpg
= mpeg_open(buf
);
940 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: Can't open SUB file\n");
947 long last_pts_diff
= 0;
948 while (!mpeg_eof(mpg
)) {
949 off_t pos
= mpeg_tell(mpg
);
950 if (mpeg_run(mpg
) < 0) {
952 mp_msg(MSGT_VOBSUB
, MSGL_ERR
, "VobSub: mpeg_run error\n");
955 if (mpg
->packet_size
) {
956 if ((mpg
->aid
& 0xe0) == 0x20) {
957 unsigned int sid
= mpg
->aid
& 0x1f;
958 if (vobsub_ensure_spu_stream(vob
, sid
) >= 0) {
959 packet_queue_t
*queue
= vob
->spu_streams
+ sid
;
960 /* get the packet to fill */
961 if (queue
->packets_size
== 0 && packet_queue_grow(queue
) < 0)
963 while (queue
->current_index
+ 1 < queue
->packets_size
964 && queue
->packets
[queue
->current_index
+ 1].filepos
<= pos
)
965 ++queue
->current_index
;
966 if (queue
->current_index
< queue
->packets_size
) {
968 if (queue
->packets
[queue
->current_index
].data
) {
969 /* insert a new packet and fix the PTS ! */
970 packet_queue_insert(queue
);
971 queue
->packets
[queue
->current_index
].pts100
=
972 mpg
->pts
+ last_pts_diff
;
974 pkt
= queue
->packets
+ queue
->current_index
;
975 if (pkt
->pts100
!= UINT_MAX
) {
976 if (queue
->packets_size
> 1)
977 last_pts_diff
= pkt
->pts100
- mpg
->pts
;
979 pkt
->pts100
= mpg
->pts
;
980 if (mpg
->merge
&& queue
->current_index
> 0) {
981 packet_t
*last
= &queue
->packets
[queue
->current_index
- 1];
982 pkt
->pts100
= last
->pts100
;
985 /* FIXME: should not use mpg_sub internal informations, make a copy */
986 pkt
->data
= mpg
->packet
;
987 pkt
->size
= mpg
->packet_size
;
989 mpg
->packet_reserve
= 0;
990 mpg
->packet_size
= 0;
994 mp_msg(MSGT_VOBSUB
, MSGL_WARN
, "don't know what to do with subtitle #%u\n", sid
);
998 vob
->spu_streams_current
= vob
->spu_streams_size
;
999 while (vob
->spu_streams_current
-- > 0) {
1000 vob
->spu_streams
[vob
->spu_streams_current
].current_index
= 0;
1001 if (vobsubid
== vob
->spu_streams_current
||
1002 vob
->spu_streams
[vob
->spu_streams_current
].packets_size
> 0)
1003 ++vob
->spu_valid_streams_size
;
1013 void vobsub_close(void *this)
1015 vobsub_t
*vob
= this;
1016 if (vob
->spu_streams
) {
1017 while (vob
->spu_streams_size
--)
1018 packet_queue_destroy(vob
->spu_streams
+ vob
->spu_streams_size
);
1019 free(vob
->spu_streams
);
1024 unsigned int vobsub_get_indexes_count(void *vobhandle
)
1026 vobsub_t
*vob
= vobhandle
;
1027 return vob
->spu_valid_streams_size
;
1030 char *vobsub_get_id(void *vobhandle
, unsigned int index
)
1032 vobsub_t
*vob
= vobhandle
;
1033 return (index
< vob
->spu_streams_size
) ? vob
->spu_streams
[index
].id
: NULL
;
1036 int vobsub_get_id_by_index(void *vobhandle
, unsigned int index
)
1038 vobsub_t
*vob
= vobhandle
;
1042 for (i
= 0, j
= 0; i
< vob
->spu_streams_size
; ++i
)
1043 if (i
== vobsubid
|| vob
->spu_streams
[i
].packets_size
> 0) {
1051 int vobsub_get_index_by_id(void *vobhandle
, int id
)
1053 vobsub_t
*vob
= vobhandle
;
1055 if (vob
== NULL
|| id
< 0 || id
>= vob
->spu_streams_size
)
1057 if (id
!= vobsubid
&& !vob
->spu_streams
[id
].packets_size
)
1059 for (i
= 0, j
= 0; i
< id
; ++i
)
1060 if (i
== vobsubid
|| vob
->spu_streams
[i
].packets_size
> 0)
1065 int vobsub_set_from_lang(void *vobhandle
, char **lang
)
1068 vobsub_t
*vob
= vobhandle
;
1071 for (int n
= 0; lang
[n
]; n
++) {
1072 for (i
= 0; i
< vob
->spu_streams_size
; i
++)
1073 if (vob
->spu_streams
[i
].id
)
1074 if ((strncmp(vob
->spu_streams
[i
].id
, lang
[n
], 2) == 0)) {
1076 mp_msg(MSGT_VOBSUB
, MSGL_INFO
, "Selected VOBSUB language: %d language: %s\n", i
, vob
->spu_streams
[i
].id
);
1081 mp_msg(MSGT_VOBSUB
, MSGL_WARN
, "No matching VOBSUB language found!\n");
1085 /// make sure we seek to the first packet of packets having same pts values.
1086 static void vobsub_queue_reseek(packet_queue_t
*queue
, unsigned int pts100
)
1088 int reseek_count
= 0;
1089 unsigned int lastpts
= 0;
1091 if (queue
->current_index
> 0
1092 && (queue
->packets
[queue
->current_index
].pts100
== UINT_MAX
1093 || queue
->packets
[queue
->current_index
].pts100
> pts100
)) {
1094 // possible pts seek previous, try to check it.
1096 while (queue
->current_index
>= i
1097 && queue
->packets
[queue
->current_index
-i
].pts100
== UINT_MAX
)
1099 if (queue
->current_index
>= i
1100 && queue
->packets
[queue
->current_index
-i
].pts100
> pts100
)
1101 // pts seek previous confirmed, reseek from beginning
1102 queue
->current_index
= 0;
1104 while (queue
->current_index
< queue
->packets_size
1105 && queue
->packets
[queue
->current_index
].pts100
<= pts100
) {
1106 lastpts
= queue
->packets
[queue
->current_index
].pts100
;
1107 ++queue
->current_index
;
1110 while (reseek_count
-- && --queue
->current_index
) {
1111 if (queue
->packets
[queue
->current_index
-1].pts100
!= UINT_MAX
&&
1112 queue
->packets
[queue
->current_index
-1].pts100
!= lastpts
)
1117 int vobsub_get_packet(void *vobhandle
, float pts
, void** data
, int* timestamp
)
1119 vobsub_t
*vob
= vobhandle
;
1120 unsigned int pts100
= 90000 * pts
;
1121 if (vob
->spu_streams
&& 0 <= vobsub_id
&& (unsigned) vobsub_id
< vob
->spu_streams_size
) {
1122 packet_queue_t
*queue
= vob
->spu_streams
+ vobsub_id
;
1124 vobsub_queue_reseek(queue
, pts100
);
1126 while (queue
->current_index
< queue
->packets_size
) {
1127 packet_t
*pkt
= queue
->packets
+ queue
->current_index
;
1128 if (pkt
->pts100
!= UINT_MAX
)
1129 if (pkt
->pts100
<= pts100
) {
1130 ++queue
->current_index
;
1132 *timestamp
= pkt
->pts100
;
1137 ++queue
->current_index
;
1143 int vobsub_get_next_packet(void *vobhandle
, void** data
, int* timestamp
)
1145 vobsub_t
*vob
= vobhandle
;
1146 if (vob
->spu_streams
&& 0 <= vobsub_id
&& (unsigned) vobsub_id
< vob
->spu_streams_size
) {
1147 packet_queue_t
*queue
= vob
->spu_streams
+ vobsub_id
;
1148 if (queue
->current_index
< queue
->packets_size
) {
1149 packet_t
*pkt
= queue
->packets
+ queue
->current_index
;
1150 ++queue
->current_index
;
1152 *timestamp
= pkt
->pts100
;
1159 void vobsub_seek(void * vobhandle
, float pts
)
1161 vobsub_t
* vob
= vobhandle
;
1162 packet_queue_t
* queue
;
1163 int seek_pts100
= pts
* 90000;
1165 if (vob
->spu_streams
&& 0 <= vobsub_id
&& (unsigned) vobsub_id
< vob
->spu_streams_size
) {
1166 /* do not seek if we don't know the id */
1167 if (vobsub_get_id(vob
, vobsub_id
) == NULL
)
1169 queue
= vob
->spu_streams
+ vobsub_id
;
1170 queue
->current_index
= 0;
1171 vobsub_queue_reseek(queue
, seek_pts100
);
1175 void vobsub_reset(void *vobhandle
)
1177 vobsub_t
*vob
= vobhandle
;
1178 if (vob
->spu_streams
) {
1179 unsigned int n
= vob
->spu_streams_size
;
1181 vob
->spu_streams
[n
].current_index
= 0;
1185 /**********************************************************************
1187 **********************************************************************/
1195 static void create_idx(vobsub_out_t
*me
, const unsigned int *palette
,
1196 unsigned int orig_width
, unsigned int orig_height
)
1200 "# VobSub index file, v7 (do not modify this line!)\n"
1202 "# Generated by %s\n"
1203 "# See <URL:http://www.mplayerhq.hu/> for more information about MPlayer\n"
1204 "# See <URL:http://wiki.multimedia.cx/index.php?title=VOBsub> for more information about Vobsub\n"
1207 mplayer_version
, orig_width
, orig_height
);
1209 fputs("palette:", me
->fidx
);
1210 for (i
= 0; i
< 16; ++i
) {
1211 const double y
= palette
[i
] >> 16 & 0xff,
1212 u
= (palette
[i
] >> 8 & 0xff) - 128.0,
1213 v
= (palette
[i
] & 0xff) - 128.0;
1215 putc(',', me
->fidx
);
1216 fprintf(me
->fidx
, " %02x%02x%02x",
1217 av_clip_uint8(y
+ 1.4022 * u
),
1218 av_clip_uint8(y
- 0.3456 * u
- 0.7145 * v
),
1219 av_clip_uint8(y
+ 1.7710 * v
));
1221 putc('\n', me
->fidx
);
1224 fprintf(me
->fidx
, "# ON: displays only forced subtitles, OFF: shows everything\n"
1225 "forced subs: OFF\n");
1228 void *vobsub_out_open(const char *basename
, const unsigned int *palette
,
1229 unsigned int orig_width
, unsigned int orig_height
,
1230 const char *id
, unsigned int index
)
1232 vobsub_out_t
*result
= NULL
;
1234 filename
= malloc(strlen(basename
) + 5);
1236 result
= malloc(sizeof(vobsub_out_t
));
1238 result
->aid
= index
;
1239 strcpy(filename
, basename
);
1240 strcat(filename
, ".sub");
1241 result
->fsub
= fopen(filename
, "ab");
1242 if (result
->fsub
== NULL
)
1243 perror("Error: vobsub_out_open subtitle file open failed");
1244 strcpy(filename
, basename
);
1245 strcat(filename
, ".idx");
1246 result
->fidx
= fopen(filename
, "ab");
1248 if (ftell(result
->fidx
) == 0) {
1249 create_idx(result
, palette
, orig_width
, orig_height
);
1250 /* Make the selected language the default language */
1251 fprintf(result
->fidx
, "\n# Language index in use\nlangidx: %u\n", index
);
1253 fprintf(result
->fidx
, "\nid: %s, index: %u\n", id
? id
: "xx", index
);
1254 /* So that we can check the file now */
1255 fflush(result
->fidx
);
1257 perror("Error: vobsub_out_open index file open failed");
1264 void vobsub_out_close(void *me
)
1266 vobsub_out_t
*vob
= me
;
1274 void vobsub_out_output(void *me
, const unsigned char *packet
,
1275 int len
, double pts
)
1277 static double last_pts
;
1278 static int last_pts_set
= 0;
1279 vobsub_out_t
*vob
= me
;
1281 /* Windows' Vobsub require that every packet is exactly 2kB long */
1282 unsigned char buffer
[2048];
1285 /* Do not output twice a line with the same timestamp, this
1286 breaks Windows' Vobsub */
1287 if (vob
->fidx
&& (!last_pts_set
|| last_pts
!= pts
)) {
1288 static unsigned int last_h
= 9999, last_m
= 9999, last_s
= 9999, last_ms
= 9999;
1289 unsigned int h
, m
, ms
;
1296 ms
= (s
- (unsigned int) s
) * 1000;
1297 if (ms
>= 1000) /* prevent overflows or bad float stuff */
1299 if (h
!= last_h
|| m
!= last_m
|| (unsigned int) s
!= last_s
|| ms
!= last_ms
) {
1300 fprintf(vob
->fidx
, "timestamp: %02u:%02u:%02u:%03u, filepos: %09lx\n",
1301 h
, m
, (unsigned int) s
, ms
, ftell(vob
->fsub
));
1304 last_s
= (unsigned int) s
;
1311 /* Packet start code: Windows' Vobsub needs this */
1313 *p
++ = 0; /* 0x00 */
1321 static unsigned char last_pts
[5] = { 0, 0, 0, 0, 0};
1322 unsigned char now_pts
[5];
1323 int pts_len
, pad_len
, datalen
= len
;
1325 now_pts
[0] = 0x21 | (((unsigned long)pts
>> 29) & 0x0e);
1326 now_pts
[1] = ((unsigned long)pts
>> 22) & 0xff;
1327 now_pts
[2] = 0x01 | (((unsigned long)pts
>> 14) & 0xfe);
1328 now_pts
[3] = ((unsigned long)pts
>> 7) & 0xff;
1329 now_pts
[4] = 0x01 | (((unsigned long)pts
<< 1) & 0xfe);
1330 pts_len
= memcmp(last_pts
, now_pts
, sizeof(now_pts
)) ? sizeof(now_pts
) : 0;
1331 memcpy(last_pts
, now_pts
, sizeof(now_pts
));
1333 datalen
+= 3; /* Version, PTS_flags, pts_len */
1335 datalen
+= 1; /* AID */
1336 pad_len
= 2048 - (p
- buffer
) - 4 /* MPEG ID */ - 2 /* payload len */ - datalen
;
1337 /* XXX - Go figure what should go here! In any case the
1338 packet has to be completely filled. If I can fill it
1339 with padding (0x000001be) latter I'll do that. But if
1340 there is only room for 6 bytes then I can not write a
1341 padding packet. So I add some padding in the PTS
1342 field. This looks like a dirty kludge. Oh well... */
1344 /* Packet is too big. Let's try omitting the PTS field */
1348 } else if (pad_len
> 6)
1352 *p
++ = 0; /* 0x0e */
1357 *p
++ = (datalen
>> 8) & 0xff; /* length of payload */
1358 *p
++ = datalen
& 0xff;
1359 *p
++ = 0x80; /* System-2 (.VOB) stream */
1360 *p
++ = pts_len
? 0x80 : 0x00; /* pts_flags */
1361 *p
++ = pts_len
+ pad_len
;
1362 memcpy(p
, now_pts
, pts_len
);
1364 memset(p
, 0, pad_len
);
1367 *p
++ = 0x20 | vob
->aid
; /* aid */
1368 if (fwrite(buffer
, p
- buffer
, 1, vob
->fsub
) != 1
1369 || fwrite(packet
, len
, 1, vob
->fsub
) != 1)
1370 perror("ERROR: vobsub write failed");
1372 remain
-= p
- buffer
+ len
;
1381 *p
++ = (remain
- 6) >> 8;
1382 *p
++ = (remain
- 6) & 0xff;
1383 /* for better compression, blank this */
1384 memset(buffer
+ 6, 0, remain
- (p
- buffer
));
1385 if (fwrite(buffer
, remain
, 1, vob
->fsub
) != 1)
1386 perror("ERROR: vobsub padding write failed");
1387 } else if (remain
> 0) {
1388 /* I don't know what to output. But anyway the block
1389 needs to be 2KB big */
1390 memset(buffer
, 0, remain
);
1391 if (fwrite(buffer
, remain
, 1, vob
->fsub
) != 1)
1392 perror("ERROR: vobsub blank padding write failed");
1393 } else if (remain
< 0)
1395 "\nERROR: wrong thing happened...\n"
1396 " I wrote a %i data bytes spu packet and that's too long\n", len
);