2 * Some code freely inspired from VobSub <URL:http://vobsub.edensrising.com>,
3 * with kind permission from Gabest <gabest@freemail.hu>
15 #include <sys/types.h>
30 #define MIN(a, b) ((a)<(b)?(a):(b))
31 #define MAX(a, b) ((a)>(b)?(a):(b))
35 /**********************************************************************
37 * The RAR file must have the same basename as the file to open
38 * See <URL:http://www.unrarlib.org/>
39 **********************************************************************/
48 rar_open(const char *const filename
, const char *const mode
)
51 /* unrarlib can only read */
52 if (strcmp("r", mode
) && strcmp("rb", mode
)) {
56 stream
= malloc(sizeof(rar_stream_t
));
59 /* first try normal access */
60 stream
->file
= fopen(filename
, mode
);
61 if (stream
->file
== NULL
) {
65 /* Guess the RAR archive filename */
67 p
= strrchr(filename
, '.');
69 ptrdiff_t l
= p
- filename
;
70 rar_filename
= malloc(l
+ 5);
71 if (rar_filename
== NULL
) {
75 strncpy(rar_filename
, filename
, l
);
76 strcpy(rar_filename
+ l
, ".rar");
79 rar_filename
= malloc(strlen(filename
) + 5);
80 if (rar_filename
== 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
) {
93 rc
= urarlib_get(&stream
->data
, &stream
->size
, (char*) p
, rar_filename
, "");
105 rar_close(rar_stream_t
*stream
)
108 return fclose(stream
->file
);
114 rar_eof(rar_stream_t
*stream
)
117 return feof(stream
->file
);
118 return stream
->pos
>= stream
->size
;
122 rar_tell(rar_stream_t
*stream
)
125 return ftell(stream
->file
);
130 rar_seek(rar_stream_t
*stream
, long offset
, int whence
)
133 return fseek(stream
->file
, offset
, whence
);
140 stream
->pos
= offset
;
143 if (offset
< 0 && stream
->pos
< (unsigned long) -offset
) {
147 stream
->pos
+= offset
;
150 if (offset
< 0 && stream
->size
< (unsigned long) -offset
) {
154 stream
->pos
= stream
->size
+ offset
;
164 rar_getc(rar_stream_t
*stream
)
167 return getc(stream
->file
);
170 return stream
->data
[stream
->pos
++];
174 rar_read(void *ptr
, size_t size
, size_t nmemb
, rar_stream_t
*stream
)
177 unsigned long remain
;
179 return fread(ptr
, size
, nmemb
, stream
->file
);
183 remain
= stream
->size
- stream
->pos
;
185 res
= remain
/ size
* size
;
186 memcpy(ptr
, stream
->data
+ stream
->pos
, res
);
193 typedef FILE rar_stream_t
;
194 #define rar_open fopen
195 #define rar_close fclose
197 #define rar_tell ftell
198 #define rar_seek fseek
199 #define rar_getc getc
200 #define rar_read fread
203 /**********************************************************************/
206 getline(char **lineptr
, size_t *n
, rar_stream_t
*stream
)
210 if (*lineptr
== NULL
) {
211 *lineptr
= malloc(4096);
216 char *tmp
= realloc(*lineptr
, 4096);
222 if (*lineptr
== NULL
|| *n
== 0)
225 for (c
= rar_getc(stream
); c
!= EOF
; c
= rar_getc(stream
)) {
227 char *tmp
= realloc(*lineptr
, *n
* 2);
233 (*lineptr
)[res
++] = c
;
245 /**********************************************************************
247 **********************************************************************/
250 rar_stream_t
*stream
;
253 unsigned char *packet
;
254 unsigned int packet_reserve
;
255 unsigned int packet_size
;
259 mpeg_open(const char *filename
)
261 mpeg_t
*res
= malloc(sizeof(mpeg_t
));
262 int err
= res
== NULL
;
267 res
->packet_size
= 0;
268 res
->packet_reserve
= 0;
269 res
->stream
= rar_open(filename
, "r");
270 err
= res
->stream
== NULL
;
272 perror("fopen Vobsub file failed");
276 return err
? NULL
: res
;
280 mpeg_free(mpeg_t
*mpeg
)
285 rar_close(mpeg
->stream
);
290 mpeg_eof(mpeg_t
*mpeg
)
292 return rar_eof(mpeg
->stream
);
296 mpeg_tell(mpeg_t
*mpeg
)
298 return rar_tell(mpeg
->stream
);
302 mpeg_run(mpeg_t
*mpeg
)
304 unsigned int len
, idx
, version
;
306 /* Goto start of a packet, it starts with 0x000001?? */
307 const unsigned char wanted
[] = { 0, 0, 1 };
308 unsigned char buf
[5];
311 mpeg
->packet_size
= 0;
312 if (rar_read(buf
, 4, 1, mpeg
->stream
) != 1)
314 while (memcmp(buf
, wanted
, sizeof(wanted
)) != 0) {
315 c
= rar_getc(mpeg
->stream
);
318 memmove(buf
, buf
+ 1, 3);
322 case 0xb9: /* System End Code */
324 case 0xba: /* Packet start code */
325 c
= rar_getc(mpeg
->stream
);
328 if ((c
& 0xc0) == 0x40)
330 else if ((c
& 0xf0) == 0x20)
333 mp_msg(MSGT_VOBSUB
,MSGL_ERR
, "Unsupported MPEG version: 0x%02x", c
);
337 if (rar_seek(mpeg
->stream
, 9, SEEK_CUR
))
340 else if (version
== 2) {
341 if (rar_seek(mpeg
->stream
, 7, SEEK_CUR
))
347 case 0xbd: /* packet */
348 if (rar_read(buf
, 2, 1, mpeg
->stream
) != 1)
350 len
= buf
[0] << 8 | buf
[1];
351 idx
= mpeg_tell(mpeg
);
352 c
= rar_getc(mpeg
->stream
);
355 if ((c
& 0xC0) == 0x40) { /* skip STD scale & size */
356 if (rar_getc(mpeg
->stream
) < 0)
358 c
= rar_getc(mpeg
->stream
);
362 if ((c
& 0xf0) == 0x20) { /* System-1 stream timestamp */
363 /* Do we need this? */
366 else if ((c
& 0xf0) == 0x30) {
367 /* Do we need this? */
370 else if ((c
& 0xc0) == 0x80) { /* System-2 (.VOB) stream */
371 unsigned int pts_flags
, hdrlen
, dataidx
;
372 c
= rar_getc(mpeg
->stream
);
376 c
= rar_getc(mpeg
->stream
);
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
);
386 if ((pts_flags
& 0xc0) == 0x80) {
387 if (rar_read(buf
, 5, 1, mpeg
->stream
) != 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]);
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) */ {
402 rar_seek(mpeg
->stream
, dataidx
, SEEK_SET
);
403 mpeg
->aid
= rar_getc(mpeg
->stream
);
405 mp_msg(MSGT_VOBSUB
,MSGL_ERR
, "Bogus aid %d\n", mpeg
->aid
);
408 mpeg
->packet_size
= len
- ((unsigned int) mpeg_tell(mpeg
) - idx
);
409 if (mpeg
->packet_reserve
< mpeg
->packet_size
) {
412 mpeg
->packet
= malloc(mpeg
->packet_size
);
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;
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;
430 case 0xbe: /* Padding */
431 if (rar_read(buf
, 2, 1, mpeg
->stream
) != 1)
433 len
= buf
[0] << 8 | buf
[1];
434 if (len
> 0 && rar_seek(mpeg
->stream
, len
, SEEK_CUR
))
438 if (0xc0 <= buf
[3] && buf
[3] < 0xf0) {
439 /* MPEG audio or video */
440 if (rar_read(buf
, 2, 1, mpeg
->stream
) != 1)
442 len
= buf
[0] << 8 | buf
[1];
443 if (len
> 0 && rar_seek(mpeg
->stream
, len
, SEEK_CUR
))
448 mp_msg(MSGT_VOBSUB
,MSGL_ERR
,"unknown header 0x%02X%02X%02X%02X\n",
449 buf
[0], buf
[1], buf
[2], buf
[3]);
456 /**********************************************************************
458 **********************************************************************/
470 unsigned int packets_reserve
;
471 unsigned int packets_size
;
472 unsigned int current_index
;
476 packet_construct(packet_t
*pkt
)
485 packet_destroy(packet_t
*pkt
)
492 packet_queue_construct(packet_queue_t
*queue
)
495 queue
->packets
= NULL
;
496 queue
->packets_reserve
= 0;
497 queue
->packets_size
= 0;
498 queue
->current_index
= 0;
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
);
512 /* Make sure there is enough room for needed_size packets in the
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
));
521 mp_msg(MSGT_VOBSUB
,MSGL_FATAL
,"realloc failure");
524 queue
->packets
= tmp
;
525 queue
->packets_reserve
*= 2;
528 queue
->packets
= malloc(sizeof(packet_t
));
529 if (queue
->packets
== NULL
) {
530 mp_msg(MSGT_VOBSUB
,MSGL_FATAL
,"malloc failure");
533 queue
->packets_reserve
= 1;
539 /* add one more packet */
541 packet_queue_grow(packet_queue_t
*queue
)
543 if (packet_queue_ensure(queue
, queue
->packets_size
+ 1) < 0)
545 packet_construct(queue
->packets
+ queue
->packets_size
);
546 ++queue
->packets_size
;
550 /* insert a new packet, duplicating pts from the current one */
552 packet_queue_insert(packet_queue_t
*queue
)
555 if (packet_queue_ensure(queue
, queue
->packets_size
+ 1) < 0)
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
;
570 /**********************************************************************
572 **********************************************************************/
575 unsigned int palette
[16];
576 unsigned int cuspal
[4];
579 unsigned int have_palette
;
580 unsigned int orig_frame_width
, orig_frame_height
;
581 unsigned int origin_x
, origin_y
;
583 packet_queue_t
*spu_streams
;
584 unsigned int spu_streams_size
;
585 unsigned int spu_streams_current
;
588 /* Make sure that the spu stream idx exists. */
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
));
597 mp_msg(MSGT_VOBSUB
,MSGL_ERR
,"vobsub_ensure_spu_stream: realloc failure");
600 vob
->spu_streams
= tmp
;
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");
609 while (vob
->spu_streams_size
<= index
) {
610 packet_queue_construct(vob
->spu_streams
+ vob
->spu_streams_size
);
611 ++vob
->spu_streams_size
;
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)
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");
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
);
640 vobsub_add_timestamp(vobsub_t
*vob
, off_t filepos
, int ms
)
642 packet_queue_t
*queue
;
644 if (vob
->spu_streams
== 0) {
645 mp_msg(MSGT_VOBSUB
,MSGL_WARN
,"[vobsub] warning, binning some index entries. Check your index file\n");
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;
659 vobsub_parse_id(vobsub_t
*vob
, const char *line
)
676 if (strncmp("index:", q
, 6))
683 return vobsub_add_id(vob
, p
, idlen
, atoi(q
));
687 vobsub_parse_timestamp(vobsub_t
*vob
, const char *line
)
689 // timestamp: HH:MM:SS.mmm, filepos: 0nnnnnnnnn
693 while (isspace(*line
))
728 while (isspace(*line
))
730 if (strncmp("filepos:", line
, 8))
733 while (isspace(*line
))
735 if (! isxdigit(*line
))
737 filepos
= strtol(line
, NULL
, 16);
738 return vobsub_add_timestamp(vob
, filepos
, vob
->delay
+ ms
+ 1000 * (s
+ 60 * (m
+ 60 * h
)));
742 vobsub_parse_size(vobsub_t
*vob
, const char *line
)
746 while (isspace(*line
))
750 vob
->orig_frame_width
= strtoul(line
, &p
, 10);
754 vob
->orig_frame_height
= strtoul(p
, NULL
, 10);
759 vobsub_parse_origin(vobsub_t
*vob
, const char *line
)
763 while (isspace(*line
))
767 vob
->origin_x
= strtoul(line
, &p
, 10);
771 vob
->origin_y
= strtoul(p
, NULL
, 10);
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
783 int r
, g
, b
, y
, u
, v
, tmp
;
784 while (isspace(*line
))
791 tmp
= strtoul(line
, NULL
, 16);
792 r
= tmp
>> 16 & 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
;
805 vob
->have_palette
= 1;
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)
815 else if ((strncmp("OFF", line
+ 15, 3) == 0)||strncmp("0", line
+ 15, 1) == 0)
823 vobsub_parse_cuspal(vobsub_t
*vob
, const char *line
)
825 //colors: XXXXXX, XXXXXX, XXXXXX, XXXXXX
831 while (isspace(*line
))
838 vob
->cuspal
[n
++] = strtoul(line
, NULL
,16);
848 /* don't know how to use tridx */
850 vobsub_parse_tridx(const char *line
)
854 tridx
= strtoul((line
+ 26), NULL
, 16);
855 tridx
= ((tridx
&0x1000)>>12) | ((tridx
&0x100)>>7) | ((tridx
&0x10)>>2) | ((tridx
&1)<<3);
860 vobsub_parse_delay(vobsub_t
*vob
, const char *line
)
864 if (*(line
+ 7) == '+'){
868 else if (*(line
+ 7) == '-'){
872 mp_msg(MSGT_SPUDEC
,MSGL_V
, "forward=%d", forward
);
874 mp_msg(MSGT_VOBSUB
,MSGL_V
, "h=%d," ,h
);
876 mp_msg(MSGT_VOBSUB
,MSGL_V
, "m=%d,", m
);
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
;
886 vobsub_set_lang(const char *line
)
889 vobsub_id
= atoi(line
+ 8);
894 vobsub_parse_one_line(vobsub_t
*vob
, rar_stream_t
*fd
)
899 size_t line_reserve
= 0;
901 line_size
= getline(&line
, &line_reserve
, fd
);
907 if (*line
== 0 || *line
== '\r' || *line
== '\n' || *line
== '#')
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
);
927 mp_msg(MSGT_VOBSUB
,MSGL_V
, "vobsub: ignoring %s", line
);
931 mp_msg(MSGT_VOBSUB
,MSGL_ERR
, "ERROR in %s", line
);
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;
943 rar_stream_t
*fd
= rar_open(name
, "rb");
946 mp_msg(MSGT_VOBSUB
,MSGL_ERR
, "Can't open IFO file");
949 unsigned char block
[0x800];
950 const char *const ifo_magic
= "DVDVIDEO-VTS";
951 if (rar_read(block
, sizeof(block
), 1, fd
) != 1) {
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");
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;
963 switch (resolution
) {
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;
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");
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];
998 vob
->have_palette
= 1;
1008 vobsub_open(const char *const name
,const char *const ifo
,const int force
,void** spu
)
1010 vobsub_t
*vob
= malloc(sizeof(vobsub_t
));
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;
1023 buf
= malloc((strlen(name
) + 5) * sizeof(char));
1027 /* read in the info file */
1030 strcat(buf
, ".ifo");
1031 vobsub_parse_ifo(vob
,buf
, vob
->palette
, &vob
->orig_frame_width
, &vob
->orig_frame_height
, force
, -1, NULL
);
1033 vobsub_parse_ifo(vob
,ifo
, vob
->palette
, &vob
->orig_frame_width
, &vob
->orig_frame_height
, force
, -1, NULL
);
1034 /* read in the index */
1036 strcat(buf
, ".idx");
1037 fd
= rar_open(buf
, "rb");
1040 mp_msg(MSGT_VOBSUB
,MSGL_ERR
,"VobSub: Can't open IDX file");
1047 while (vobsub_parse_one_line(vob
, fd
) >= 0)
1051 /* if no palette in .idx then use custom colors */
1052 if ((vob
->custom
== 0)&&(vob
->have_palette
!=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 */
1059 strcat(buf
, ".sub");
1060 mpg
= mpeg_open(buf
);
1063 mp_msg(MSGT_VOBSUB
,MSGL_ERR
,"VobSub: Can't open SUB file");
1071 long last_pts_diff
= 0;
1072 while (!mpeg_eof(mpg
)) {
1073 off_t pos
= mpeg_tell(mpg
);
1074 if (mpeg_run(mpg
) < 0) {
1076 mp_msg(MSGT_VOBSUB
,MSGL_ERR
,"mpeg_run error");
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)
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
) {
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
;
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
;
1108 mpg
->packet_reserve
= 0;
1109 mpg
->packet_size
= 0;
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;
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
);
1142 vobsub_get_indexes_count(void *vobhandle
)
1144 vobsub_t
*vob
= (vobsub_t
*) vobhandle
;
1145 return vob
->spu_streams_size
;
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
)
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)){
1165 mp_msg(MSGT_VOBSUB
, MSGL_INFO
, "Selected VOBSUB language: %d language: %s\n", i
, vob
->spu_streams
[i
].id
);
1168 lang
+=2;while (lang
[0]==',' || lang
[0]==' ') ++lang
;
1170 mp_msg(MSGT_VOBSUB
, MSGL_WARN
, "No matching VOBSUB languge found!\n");
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
;
1186 *timestamp
= pkt
->pts100
;
1190 ++queue
->current_index
;
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
;
1206 *timestamp
= pkt
->pts100
;
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
;
1220 vob
->spu_streams
[n
].current_index
= 0;
1224 /**********************************************************************
1226 **********************************************************************/
1235 create_idx(vobsub_out_t
*me
, const unsigned int *palette
, unsigned int orig_width
, unsigned int orig_height
)
1239 "# VobSub index file, v7 (do not modify this line!)\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"
1246 orig_width
, orig_height
);
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;
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
);
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
;
1271 filename
= malloc(strlen(basename
) + 5);
1273 result
= malloc(sizeof(vobsub_out_t
));
1274 result
->fsub
= NULL
;
1275 result
->fidx
= NULL
;
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");
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
);
1298 perror("Error: vobsub_out_open index file open failed");
1306 vobsub_out_close(void *me
)
1308 vobsub_out_t
*vob
= (vobsub_out_t
*)me
;
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
;
1323 /* Windows' Vobsub require that every packet is exactly 2kB long */
1324 unsigned char buffer
[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
;
1338 ms
= (s
- (unsigned int) s
) * 1000;
1339 if (ms
>= 1000) /* prevent overfolws or bad float stuff */
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
));
1346 last_s
= (unsigned int) s
;
1353 /* Packet start code: Windows' Vobsub needs this */
1355 *p
++ = 0; /* 0x00 */
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
;
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 */
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... */
1386 /* Packet is too big. Let's try ommiting the PTS field */
1391 else if (pad_len
> 6)
1395 *p
++ = 0; /* 0x0e */
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
);
1407 memset(p
, 0, 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");
1415 remain
-= p
- buffer
+ len
;
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)
1440 "\nERROR: wrong thing happenned...\n"
1441 " I wrote a %i data bytes spu packet and that's too long\n", len
);