2 * Skeleton of function spudec_process_controll() is from xine sources.
4 * LGB,... (yeah, try to improve it and insert your name here! ;-)
7 * implement fragments reassembly, RLE decoding.
8 * read brightness from the IFO.
10 * For information on SPU format see <URL:http://sam.zoy.org/doc/dvd/subtitles/>
11 * and <URL:http://members.aol.com/mpucoder/DVD/spu.html>
13 * This file is part of MPlayer.
15 * MPlayer is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * MPlayer is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License along
26 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
40 #include "libvo/video_out.h"
43 #include "libavutil/avutil.h"
44 #include "ffmpeg_files/intreadwrite.h"
45 #include "libswscale/swscale.h"
47 /* Valid values for spu_aamode:
48 0: none (fastest, most ugly)
51 3: bilinear (similiar to vobsub, fast and not too bad)
52 4: uses swscaler gaussian (this is the only one that looks good)
56 int spu_alignment
= -1;
57 float spu_gaussvar
= 1.0;
60 typedef struct packet_t packet_t
;
62 unsigned char *packet
;
63 unsigned int palette
[4];
64 unsigned int alpha
[4];
65 unsigned int control_start
; /* index of start of control data */
66 unsigned int current_nibble
[2]; /* next data nibble (4 bits) to be
67 processed (for RLE decoding) for
69 int deinterlace_oddness
; /* 0 or 1, index into current_nibble */
70 unsigned int start_col
, end_col
;
71 unsigned int start_row
, end_row
;
72 unsigned int width
, height
, stride
;
73 unsigned int start_pts
, end_pts
;
80 unsigned int global_palette
[16];
81 unsigned int orig_frame_width
, orig_frame_height
;
82 unsigned char* packet
;
83 size_t packet_reserve
; /* size of the memory pointed to by packet */
84 unsigned int packet_offset
; /* end of the currently assembled fragment */
85 unsigned int packet_size
; /* size of the packet once all fragments are assembled */
86 int packet_pts
; /* PTS for this packet */
87 unsigned int palette
[4];
88 unsigned int alpha
[4];
89 unsigned int cuspal
[4];
92 unsigned int start_pts
, end_pts
;
93 unsigned int start_col
, end_col
;
94 unsigned int start_row
, end_row
;
95 unsigned int width
, height
, stride
;
96 size_t image_size
; /* Size of the image buffer */
97 unsigned char *image
; /* Grayscale value */
98 unsigned char *aimage
; /* Alpha value */
99 unsigned int scaled_frame_width
, scaled_frame_height
;
100 unsigned int scaled_start_col
, scaled_start_row
;
101 unsigned int scaled_width
, scaled_height
, scaled_stride
;
102 size_t scaled_image_size
;
103 unsigned char *scaled_image
;
104 unsigned char *scaled_aimage
;
105 int auto_palette
; /* 1 if we lack a palette and must use an heuristic. */
106 int font_start_level
; /* Darkest value used for the computed font */
109 unsigned int forced_subs_only
; /* flag: 0=display all subtitle, !0 display only forced subtitles */
110 unsigned int is_forced_sub
; /* true if current subtitle is a forced subtitle */
113 static void spudec_queue_packet(spudec_handle_t
*this, packet_t
*packet
)
115 if (this->queue_head
== NULL
)
116 this->queue_head
= packet
;
118 this->queue_tail
->next
= packet
;
119 this->queue_tail
= packet
;
122 static packet_t
*spudec_dequeue_packet(spudec_handle_t
*this)
124 packet_t
*retval
= this->queue_head
;
126 this->queue_head
= retval
->next
;
127 if (this->queue_head
== NULL
)
128 this->queue_tail
= NULL
;
133 static void spudec_free_packet(packet_t
*packet
)
135 if (packet
->packet
!= NULL
)
136 free(packet
->packet
);
140 static inline unsigned int get_be16(const unsigned char *p
)
142 return (p
[0] << 8) + p
[1];
145 static inline unsigned int get_be24(const unsigned char *p
)
147 return (get_be16(p
) << 8) + p
[2];
150 static void next_line(packet_t
*packet
)
152 if (packet
->current_nibble
[packet
->deinterlace_oddness
] % 2)
153 packet
->current_nibble
[packet
->deinterlace_oddness
]++;
154 packet
->deinterlace_oddness
= (packet
->deinterlace_oddness
+ 1) % 2;
157 static inline unsigned char get_nibble(packet_t
*packet
)
160 unsigned int *nibblep
= packet
->current_nibble
+ packet
->deinterlace_oddness
;
161 if (*nibblep
/ 2 >= packet
->control_start
) {
162 mp_msg(MSGT_SPUDEC
,MSGL_WARN
, "SPUdec: ERROR: get_nibble past end of packet\n");
165 nib
= packet
->packet
[*nibblep
/ 2];
174 static inline int mkalpha(int i
)
176 /* In mplayer's alpha planes, 0 is transparent, then 1 is nearly
177 opaque upto 255 which is fully opaque */
180 return (uint8_t)(-i
);
183 /* Cut the sub to visible part */
184 static inline void spudec_cut_image(spudec_handle_t
*this)
187 unsigned int first_y
, last_y
;
188 unsigned char *image
;
189 unsigned char *aimage
;
191 if (this->stride
== 0 || this->height
== 0) {
195 for (fy
= 0; fy
< this->image_size
&& !this->aimage
[fy
]; fy
++);
196 for (ly
= this->stride
* this->height
-1; ly
&& !this->aimage
[ly
]; ly
--);
197 first_y
= fy
/ this->stride
;
198 last_y
= ly
/ this->stride
;
199 //printf("first_y: %d, last_y: %d\n", first_y, last_y);
200 this->start_row
+= first_y
;
202 // Some subtitles trigger this condition
203 if (last_y
+ 1 > first_y
) {
204 this->height
= last_y
- first_y
+1;
207 this->image_size
= 0;
211 // printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride);
213 image
= malloc(2 * this->stride
* this->height
);
215 this->image_size
= this->stride
* this->height
;
216 aimage
= image
+ this->image_size
;
217 memcpy(image
, this->image
+ this->stride
* first_y
, this->image_size
);
218 memcpy(aimage
, this->aimage
+ this->stride
* first_y
, this->image_size
);
221 this->aimage
= aimage
;
223 mp_msg(MSGT_SPUDEC
, MSGL_FATAL
, "Fatal: update_spu: malloc requested %d bytes\n", 2 * this->stride
* this->height
);
228 static int spudec_alloc_image(spudec_handle_t
*this, int stride
, int height
)
230 if (this->width
> stride
) // just a safeguard
231 this->width
= stride
;
232 this->stride
= stride
;
233 this->height
= height
;
234 if (this->image_size
< this->stride
* this->height
) {
235 if (this->image
!= NULL
) {
237 this->image_size
= 0;
239 this->image
= malloc(2 * this->stride
* this->height
);
241 this->image_size
= this->stride
* this->height
;
242 this->aimage
= this->image
+ this->image_size
;
245 return this->image
!= NULL
;
248 static void spudec_process_data(spudec_handle_t
*this, packet_t
*packet
)
250 unsigned int cmap
[4], alpha
[4];
251 unsigned int i
, x
, y
;
253 this->scaled_frame_width
= 0;
254 this->scaled_frame_height
= 0;
255 this->start_col
= packet
->start_col
;
256 this->end_col
= packet
->end_col
;
257 this->start_row
= packet
->start_row
;
258 this->end_row
= packet
->end_row
;
259 this->height
= packet
->height
;
260 this->width
= packet
->width
;
261 this->stride
= packet
->stride
;
262 for (i
= 0; i
< 4; ++i
) {
263 alpha
[i
] = mkalpha(packet
->alpha
[i
]);
264 if (this->custom
&& (this->cuspal
[i
] >> 31) != 0)
268 else if (this->custom
){
269 cmap
[i
] = ((this->cuspal
[i
] >> 16) & 0xff);
270 if (cmap
[i
] + alpha
[i
] > 255)
271 cmap
[i
] = 256 - alpha
[i
];
274 cmap
[i
] = ((this->global_palette
[packet
->palette
[i
]] >> 16) & 0xff);
275 if (cmap
[i
] + alpha
[i
] > 255)
276 cmap
[i
] = 256 - alpha
[i
];
280 if (!spudec_alloc_image(this, this->stride
, this->height
))
283 /* Kludge: draw_alpha needs width multiple of 8. */
284 if (this->width
< this->stride
)
285 for (y
= 0; y
< this->height
; ++y
) {
286 memset(this->aimage
+ y
* this->stride
+ this->width
, 0, this->stride
- this->width
);
287 /* FIXME: Why is this one needed? */
288 memset(this->image
+ y
* this->stride
+ this->width
, 0, this->stride
- this->width
);
291 i
= packet
->current_nibble
[1];
294 while (packet
->current_nibble
[0] < i
295 && packet
->current_nibble
[1] / 2 < packet
->control_start
296 && y
< this->height
) {
297 unsigned int len
, color
;
298 unsigned int rle
= 0;
299 rle
= get_nibble(packet
);
301 rle
= (rle
<< 4) | get_nibble(packet
);
303 rle
= (rle
<< 4) | get_nibble(packet
);
305 rle
= (rle
<< 4) | get_nibble(packet
);
307 rle
|= ((this->width
- x
) << 2);
311 color
= 3 - (rle
& 0x3);
313 if (len
> this->width
- x
|| len
== 0)
314 len
= this->width
- x
;
315 /* FIXME have to use palette and alpha map*/
316 memset(this->image
+ y
* this->stride
+ x
, cmap
[color
], len
);
317 memset(this->aimage
+ y
* this->stride
+ x
, alpha
[color
], len
);
319 if (x
>= this->width
) {
325 spudec_cut_image(this);
330 This function tries to create a usable palette.
331 It determines how many non-transparent colors are used, and assigns different
332 gray scale values to each color.
333 I tested it with four streams and even got something readable. Half of the
334 times I got black characters with white around and half the reverse.
336 static void compute_palette(spudec_handle_t
*this, packet_t
*packet
)
338 int used
[16],i
,cused
,start
,step
,color
;
340 memset(used
, 0, sizeof(used
));
342 if (packet
->alpha
[i
]) /* !Transparent? */
343 used
[packet
->palette
[i
]] = 1;
344 for (cused
=0, i
=0; i
<16; i
++)
345 if (used
[i
]) cused
++;
351 start
= this->font_start_level
;
352 step
= (0xF0-this->font_start_level
)/(cused
-1);
354 memset(used
, 0, sizeof(used
));
355 for (i
=0; i
<4; i
++) {
356 color
= packet
->palette
[i
];
357 if (packet
->alpha
[i
] && !used
[color
]) { /* not assigned? */
359 this->global_palette
[color
] = start
<<16;
365 static void spudec_process_control(spudec_handle_t
*this, int pts100
)
367 int a
,b
,c
,d
; /* Temporary vars */
368 unsigned int date
, type
;
370 unsigned int start_off
= 0;
371 unsigned int next_off
;
372 unsigned int start_pts
= 0;
373 unsigned int end_pts
= 0;
374 unsigned int current_nibble
[2] = {0, 0};
375 unsigned int control_start
;
376 unsigned int display
= 0;
377 unsigned int start_col
= 0;
378 unsigned int end_col
= 0;
379 unsigned int start_row
= 0;
380 unsigned int end_row
= 0;
381 unsigned int width
= 0;
382 unsigned int height
= 0;
383 unsigned int stride
= 0;
385 control_start
= get_be16(this->packet
+ 2);
386 next_off
= control_start
;
387 while (start_off
!= next_off
) {
388 start_off
= next_off
;
389 date
= get_be16(this->packet
+ start_off
) * 1024;
390 next_off
= get_be16(this->packet
+ start_off
+ 2);
391 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
, "date=%d\n", date
);
393 for (type
= this->packet
[off
++]; type
!= 0xff; type
= this->packet
[off
++]) {
394 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
, "cmd=%d ",type
);
397 /* Menu ID, 1 byte */
398 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Menu ID\n");
399 /* shouldn't a Menu ID type force display start? */
400 start_pts
= pts100
< 0 && -pts100
>= date
? 0 : pts100
+ date
;
403 this->is_forced_sub
=~0; // current subtitle is forced
407 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Start display!\n");
408 start_pts
= pts100
< 0 && -pts100
>= date
? 0 : pts100
+ date
;
411 this->is_forced_sub
=0;
415 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Stop display!\n");
416 end_pts
= pts100
< 0 && -pts100
>= date
? 0 : pts100
+ date
;
420 this->palette
[0] = this->packet
[off
] >> 4;
421 this->palette
[1] = this->packet
[off
] & 0xf;
422 this->palette
[2] = this->packet
[off
+ 1] >> 4;
423 this->palette
[3] = this->packet
[off
+ 1] & 0xf;
424 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Palette %d, %d, %d, %d\n",
425 this->palette
[0], this->palette
[1], this->palette
[2], this->palette
[3]);
430 a
= this->packet
[off
] >> 4;
431 b
= this->packet
[off
] & 0xf;
432 c
= this->packet
[off
+ 1] >> 4;
433 d
= this->packet
[off
+ 1] & 0xf;
434 // Note: some DVDs change these values to create a fade-in/fade-out effect
435 // We can not handle this, so just keep the highest value during the display time.
437 a
= FFMAX(a
, this->alpha
[0]);
438 b
= FFMAX(b
, this->alpha
[1]);
439 c
= FFMAX(c
, this->alpha
[2]);
440 d
= FFMAX(d
, this->alpha
[3]);
446 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Alpha %d, %d, %d, %d\n",
447 this->alpha
[0], this->alpha
[1], this->alpha
[2], this->alpha
[3]);
452 a
= get_be24(this->packet
+ off
);
453 b
= get_be24(this->packet
+ off
+ 3);
456 width
= (end_col
< start_col
) ? 0 : end_col
- start_col
+ 1;
457 stride
= (width
+ 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */
460 height
= (end_row
< start_row
) ? 0 : end_row
- start_row
/* + 1 */;
461 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Coords col: %d - %d row: %d - %d (%dx%d)\n",
462 start_col
, end_col
, start_row
, end_row
,
468 current_nibble
[0] = 2 * get_be16(this->packet
+ off
);
469 current_nibble
[1] = 2 * get_be16(this->packet
+ off
+ 2);
470 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Graphic offset 1: %d offset 2: %d\n",
471 current_nibble
[0] / 2, current_nibble
[1] / 2);
475 /* All done, bye-bye */
476 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"Done!\n");
480 mp_msg(MSGT_SPUDEC
,MSGL_WARN
,"spudec: Error determining control type 0x%02x. Skipping %d bytes.\n",
481 type
, next_off
- off
);
488 if (end_pts
== UINT_MAX
&& start_off
!= next_off
) {
489 end_pts
= get_be16(this->packet
+ next_off
) * 1024;
490 end_pts
= 1 - pts100
>= end_pts
? 0 : pts100
+ end_pts
- 1;
493 packet_t
*packet
= calloc(1, sizeof(packet_t
));
495 packet
->start_pts
= start_pts
;
496 packet
->end_pts
= end_pts
;
497 packet
->current_nibble
[0] = current_nibble
[0];
498 packet
->current_nibble
[1] = current_nibble
[1];
499 packet
->start_row
= start_row
;
500 packet
->end_row
= end_row
;
501 packet
->start_col
= start_col
;
502 packet
->end_col
= end_col
;
503 packet
->width
= width
;
504 packet
->height
= height
;
505 packet
->stride
= stride
;
506 packet
->control_start
= control_start
;
507 for (i
=0; i
<4; i
++) {
508 packet
->alpha
[i
] = this->alpha
[i
];
509 packet
->palette
[i
] = this->palette
[i
];
511 packet
->packet
= malloc(this->packet_size
);
512 memcpy(packet
->packet
, this->packet
, this->packet_size
);
513 spudec_queue_packet(this, packet
);
518 static void spudec_decode(spudec_handle_t
*this, int pts100
)
521 spudec_process_control(this, pts100
);
522 else if (pts100
>= 0) {
523 static vo_mpegpes_t packet
= { NULL
, 0, 0x20, 0 };
524 static vo_mpegpes_t
*pkg
=&packet
;
525 packet
.data
= this->packet
;
526 packet
.size
= this->packet_size
;
527 packet
.timestamp
= pts100
;
528 vo_draw_frame(this->hw_spu
, (uint8_t**)&pkg
);
532 int spudec_changed(void * this)
534 spudec_handle_t
* spu
= (spudec_handle_t
*)this;
535 return spu
->spu_changed
|| spu
->now_pts
> spu
->end_pts
;
538 void spudec_assemble(void *this, unsigned char *packet
, unsigned int len
, int pts100
)
540 spudec_handle_t
*spu
= (spudec_handle_t
*)this;
541 // spudec_heartbeat(this, pts100);
543 mp_msg(MSGT_SPUDEC
,MSGL_WARN
,"SPUasm: packet too short\n");
546 spu
->packet_pts
= pts100
;
547 if (spu
->packet_offset
== 0) {
548 unsigned int len2
= get_be16(packet
);
549 // Start new fragment
550 if (spu
->packet_reserve
< len2
) {
551 if (spu
->packet
!= NULL
)
553 spu
->packet
= malloc(len2
);
554 spu
->packet_reserve
= spu
->packet
!= NULL
? len2
: 0;
556 if (spu
->packet
!= NULL
) {
557 spu
->packet_size
= len2
;
559 mp_msg(MSGT_SPUDEC
,MSGL_WARN
,"SPUasm: invalid frag len / len2: %d / %d \n", len
, len2
);
562 memcpy(spu
->packet
, packet
, len
);
563 spu
->packet_offset
= len
;
564 spu
->packet_pts
= pts100
;
567 // Continue current fragment
568 if (spu
->packet_size
< spu
->packet_offset
+ len
){
569 mp_msg(MSGT_SPUDEC
,MSGL_WARN
,"SPUasm: invalid fragment\n");
570 spu
->packet_size
= spu
->packet_offset
= 0;
573 memcpy(spu
->packet
+ spu
->packet_offset
, packet
, len
);
574 spu
->packet_offset
+= len
;
578 // check if we have a complete packet (unfortunatelly packet_size is bad
580 // [cb] packet_size is padded to be even -> may be one byte too long
581 if ((spu
->packet_offset
== spu
->packet_size
) ||
582 ((spu
->packet_offset
+ 1) == spu
->packet_size
)){
584 while(x
+4<=spu
->packet_offset
){
585 y
=get_be16(spu
->packet
+x
+2); // next control pointer
586 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"SPUtest: x=%d y=%d off=%d size=%d\n",x
,y
,spu
->packet_offset
,spu
->packet_size
);
587 if(x
>=4 && x
==y
){ // if it points to self - we're done!
589 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"SPUgot: off=%d size=%d \n",spu
->packet_offset
,spu
->packet_size
);
590 spudec_decode(spu
, pts100
);
591 spu
->packet_offset
= 0;
594 if(y
<=x
|| y
>=spu
->packet_size
){ // invalid?
595 mp_msg(MSGT_SPUDEC
,MSGL_WARN
,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y
,x
);
596 spu
->packet_size
= spu
->packet_offset
= 0;
601 // [cb] packet is done; start new packet
602 spu
->packet_offset
= 0;
605 if (spu
->packet_offset
== spu
->packet_size
) {
606 spudec_decode(spu
, pts100
);
607 spu
->packet_offset
= 0;
612 void spudec_reset(void *this) // called after seek
614 spudec_handle_t
*spu
= (spudec_handle_t
*)this;
615 while (spu
->queue_head
)
616 spudec_free_packet(spudec_dequeue_packet(spu
));
619 spu
->packet_size
= spu
->packet_offset
= 0;
622 void spudec_heartbeat(void *this, unsigned int pts100
)
624 spudec_handle_t
*spu
= (spudec_handle_t
*) this;
625 spu
->now_pts
= pts100
;
627 // TODO: detect and handle broken timestamps (e.g. due to wrapping)
628 while (spu
->queue_head
!= NULL
&& pts100
>= spu
->queue_head
->start_pts
) {
629 packet_t
*packet
= spudec_dequeue_packet(spu
);
630 spu
->start_pts
= packet
->start_pts
;
631 spu
->end_pts
= packet
->end_pts
;
632 if (spu
->auto_palette
)
633 compute_palette(spu
, packet
);
634 spudec_process_data(spu
, packet
);
635 spudec_free_packet(packet
);
636 spu
->spu_changed
= 1;
640 int spudec_visible(void *this){
641 spudec_handle_t
*spu
= (spudec_handle_t
*)this;
642 int ret
=(spu
->start_pts
<= spu
->now_pts
&&
643 spu
->now_pts
< spu
->end_pts
&&
645 // printf("spu visible: %d \n",ret);
649 void spudec_set_forced_subs_only(void * const this, const unsigned int flag
)
652 ((spudec_handle_t
*)this)->forced_subs_only
=flag
;
653 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"SPU: Display only forced subs now %s\n", flag
? "enabled": "disabled");
657 void spudec_draw(void *this, void (*draw_alpha
)(int x0
,int y0
, int w
,int h
, unsigned char* src
, unsigned char *srca
, int stride
))
659 spudec_handle_t
*spu
= (spudec_handle_t
*)this;
660 if (spudec_visible(spu
))
662 draw_alpha(spu
->start_col
, spu
->start_row
, spu
->width
, spu
->height
,
663 spu
->image
, spu
->aimage
, spu
->stride
);
664 spu
->spu_changed
= 0;
668 /* calc the bbox for spudec subs */
669 void spudec_calc_bbox(void *me
, unsigned int dxs
, unsigned int dys
, unsigned int* bbox
)
671 spudec_handle_t
*spu
;
672 spu
= (spudec_handle_t
*)me
;
673 if (spu
->orig_frame_width
== 0 || spu
->orig_frame_height
== 0
674 || (spu
->orig_frame_width
== dxs
&& spu
->orig_frame_height
== dys
)) {
675 bbox
[0] = spu
->start_col
;
676 bbox
[1] = spu
->start_col
+ spu
->width
;
677 bbox
[2] = spu
->start_row
;
678 bbox
[3] = spu
->start_row
+ spu
->height
;
680 else if (spu
->scaled_frame_width
!= dxs
|| spu
->scaled_frame_height
!= dys
) {
681 unsigned int scalex
= 0x100 * dxs
/ spu
->orig_frame_width
;
682 unsigned int scaley
= 0x100 * dys
/ spu
->orig_frame_height
;
683 bbox
[0] = spu
->start_col
* scalex
/ 0x100;
684 bbox
[1] = spu
->start_col
* scalex
/ 0x100 + spu
->width
* scalex
/ 0x100;
685 switch (spu_alignment
) {
687 bbox
[3] = dys
*sub_pos
/100 + spu
->height
* scaley
/ 0x100;
688 if (bbox
[3] > dys
) bbox
[3] = dys
;
689 bbox
[2] = bbox
[3] - spu
->height
* scaley
/ 0x100;
693 bbox
[2] = dys
*sub_pos
/100 - spu
->height
* scaley
/ 0x200;
694 bbox
[3] = bbox
[2] + spu
->height
;
696 bbox
[3] = dys
*sub_pos
/100 + spu
->height
* scaley
/ 0x200;
697 if (bbox
[3] > dys
) bbox
[3] = dys
;
698 bbox
[2] = bbox
[3] - spu
->height
* scaley
/ 0x100;
702 bbox
[2] = dys
*sub_pos
/100 - spu
->height
* scaley
/ 0x100;
703 bbox
[3] = bbox
[2] + spu
->height
;
706 bbox
[2] = spu
->start_row
* scaley
/ 0x100;
707 bbox
[3] = spu
->start_row
* scaley
/ 0x100 + spu
->height
* scaley
/ 0x100;
711 mp_msg(MSGT_SPUDEC
, MSGL_ERR
, "Bad values in spudec_calc_bbox\n");
712 bbox
[0] = bbox
[1] = bbox
[2] = bbox
[3] = 0;
715 /* transform mplayer's alpha value into an opacity value that is linear */
716 static inline int canon_alpha(int alpha
)
718 return alpha
? 256 - alpha
: 0;
728 static void scale_table(unsigned int start_src
, unsigned int start_tar
, unsigned int end_src
, unsigned int end_tar
, scale_pixel
* table
)
731 unsigned int delta_src
= end_src
- start_src
;
732 unsigned int delta_tar
= end_tar
- start_tar
;
735 if (delta_src
== 0 || delta_tar
== 0) {
738 src_step
= (delta_src
<< 16) / delta_tar
>>1;
739 for (t
= 0; t
<=delta_tar
; src
+= (src_step
<< 1), t
++){
740 table
[t
].position
= FFMIN(src
>> 16, end_src
- 1);
741 table
[t
].right_down
= src
& 0xffff;
742 table
[t
].left_up
= 0x10000 - table
[t
].right_down
;
746 /* bilinear scale, similar to vobsub's code */
747 static void scale_image(int x
, int y
, scale_pixel
* table_x
, scale_pixel
* table_y
, spudec_handle_t
* spu
)
751 unsigned int scale
[4];
752 int base
= table_y
[y
].position
* spu
->stride
+ table_x
[x
].position
;
753 int scaled
= y
* spu
->scaled_stride
+ x
;
754 alpha
[0] = canon_alpha(spu
->aimage
[base
]);
755 alpha
[1] = canon_alpha(spu
->aimage
[base
+ 1]);
756 alpha
[2] = canon_alpha(spu
->aimage
[base
+ spu
->stride
]);
757 alpha
[3] = canon_alpha(spu
->aimage
[base
+ spu
->stride
+ 1]);
758 color
[0] = spu
->image
[base
];
759 color
[1] = spu
->image
[base
+ 1];
760 color
[2] = spu
->image
[base
+ spu
->stride
];
761 color
[3] = spu
->image
[base
+ spu
->stride
+ 1];
762 scale
[0] = (table_x
[x
].left_up
* table_y
[y
].left_up
>> 16) * alpha
[0];
763 if (table_y
[y
].left_up
== 0x10000) // necessary to avoid overflow-case
764 scale
[0] = table_x
[x
].left_up
* alpha
[0];
765 scale
[1] = (table_x
[x
].right_down
* table_y
[y
].left_up
>>16) * alpha
[1];
766 scale
[2] = (table_x
[x
].left_up
* table_y
[y
].right_down
>> 16) * alpha
[2];
767 scale
[3] = (table_x
[x
].right_down
* table_y
[y
].right_down
>> 16) * alpha
[3];
768 spu
->scaled_image
[scaled
] = (color
[0] * scale
[0] + color
[1] * scale
[1] + color
[2] * scale
[2] + color
[3] * scale
[3])>>24;
769 spu
->scaled_aimage
[scaled
] = (scale
[0] + scale
[1] + scale
[2] + scale
[3]) >> 16;
770 if (spu
->scaled_aimage
[scaled
]){
771 // ensure that MPlayer's simplified alpha-blending can not overflow
772 spu
->scaled_image
[scaled
] = FFMIN(spu
->scaled_image
[scaled
], spu
->scaled_aimage
[scaled
]);
773 // convert to MPlayer-style alpha
774 spu
->scaled_aimage
[scaled
] = -spu
->scaled_aimage
[scaled
];
778 static void sws_spu_image(unsigned char *d1
, unsigned char *d2
, int dw
, int dh
,
779 int ds
, unsigned char *s1
, unsigned char *s2
, int sw
,
782 struct SwsContext
*ctx
;
783 static SwsFilter filter
;
784 static int firsttime
= 1;
788 if (!firsttime
&& oldvar
!= spu_gaussvar
) sws_freeVec(filter
.lumH
);
790 filter
.lumH
= filter
.lumV
=
791 filter
.chrH
= filter
.chrV
= sws_getGaussianVec(spu_gaussvar
, 3.0);
792 sws_normalizeVec(filter
.lumH
, 1.0);
794 oldvar
= spu_gaussvar
;
797 ctx
=sws_getContext(sw
, sh
, PIX_FMT_GRAY8
, dw
, dh
, PIX_FMT_GRAY8
, SWS_GAUSS
, &filter
, NULL
, NULL
);
798 sws_scale(ctx
,&s1
,&ss
,0,sh
,&d1
,&ds
);
799 for (i
=ss
*sh
-1; i
>=0; i
--) if (!s2
[i
]) s2
[i
] = 255; //else s2[i] = 1;
800 sws_scale(ctx
,&s2
,&ss
,0,sh
,&d2
,&ds
);
801 for (i
=ds
*dh
-1; i
>=0; i
--) if (d2
[i
]==0) d2
[i
] = 1; else if (d2
[i
]==255) d2
[i
] = 0;
802 sws_freeContext(ctx
);
805 void spudec_draw_scaled(void *me
, unsigned int dxs
, unsigned int dys
, void (*draw_alpha
)(void *ctx
, int x0
,int y0
, int w
,int h
, unsigned char* src
, unsigned char *srca
, int stride
), void *ctx
)
807 spudec_handle_t
*spu
= (spudec_handle_t
*)me
;
808 scale_pixel
*table_x
;
809 scale_pixel
*table_y
;
811 if (spudec_visible(spu
)) {
813 // check if only forced subtitles are requested
814 if( (spu
->forced_subs_only
) && !(spu
->is_forced_sub
) ){
818 if (!(spu_aamode
&16) && (spu
->orig_frame_width
== 0 || spu
->orig_frame_height
== 0
819 || (spu
->orig_frame_width
== dxs
&& spu
->orig_frame_height
== dys
))) {
822 draw_alpha(ctx
, spu
->start_col
, spu
->start_row
, spu
->width
, spu
->height
,
823 spu
->image
, spu
->aimage
, spu
->stride
);
824 spu
->spu_changed
= 0;
828 if (spu
->scaled_frame_width
!= dxs
|| spu
->scaled_frame_height
!= dys
) { /* Resizing is needed */
829 /* scaled_x = scalex * x / 0x100
830 scaled_y = scaley * y / 0x100
831 order of operations is important because of rounding. */
832 unsigned int scalex
= 0x100 * dxs
/ spu
->orig_frame_width
;
833 unsigned int scaley
= 0x100 * dys
/ spu
->orig_frame_height
;
834 spu
->scaled_start_col
= spu
->start_col
* scalex
/ 0x100;
835 spu
->scaled_start_row
= spu
->start_row
* scaley
/ 0x100;
836 spu
->scaled_width
= spu
->width
* scalex
/ 0x100;
837 spu
->scaled_height
= spu
->height
* scaley
/ 0x100;
838 /* Kludge: draw_alpha needs width multiple of 8 */
839 spu
->scaled_stride
= (spu
->scaled_width
+ 7) & ~7;
840 if (spu
->scaled_image_size
< spu
->scaled_stride
* spu
->scaled_height
) {
841 if (spu
->scaled_image
) {
842 free(spu
->scaled_image
);
843 spu
->scaled_image_size
= 0;
845 spu
->scaled_image
= malloc(2 * spu
->scaled_stride
* spu
->scaled_height
);
846 if (spu
->scaled_image
) {
847 spu
->scaled_image_size
= spu
->scaled_stride
* spu
->scaled_height
;
848 spu
->scaled_aimage
= spu
->scaled_image
+ spu
->scaled_image_size
;
851 if (spu
->scaled_image
) {
853 if (spu
->scaled_width
<= 1 || spu
->scaled_height
<= 1) {
856 switch(spu_aamode
&15) {
858 sws_spu_image(spu
->scaled_image
, spu
->scaled_aimage
,
859 spu
->scaled_width
, spu
->scaled_height
, spu
->scaled_stride
,
860 spu
->image
, spu
->aimage
, spu
->width
, spu
->height
, spu
->stride
);
863 table_x
= calloc(spu
->scaled_width
, sizeof(scale_pixel
));
864 table_y
= calloc(spu
->scaled_height
, sizeof(scale_pixel
));
865 if (!table_x
|| !table_y
) {
866 mp_msg(MSGT_SPUDEC
, MSGL_FATAL
, "Fatal: spudec_draw_scaled: calloc failed\n");
868 scale_table(0, 0, spu
->width
- 1, spu
->scaled_width
- 1, table_x
);
869 scale_table(0, 0, spu
->height
- 1, spu
->scaled_height
- 1, table_y
);
870 for (y
= 0; y
< spu
->scaled_height
; y
++)
871 for (x
= 0; x
< spu
->scaled_width
; x
++)
872 scale_image(x
, y
, table_x
, table_y
, spu
);
877 /* no antialiasing */
878 for (y
= 0; y
< spu
->scaled_height
; ++y
) {
879 int unscaled_y
= y
* 0x100 / scaley
;
880 int strides
= spu
->stride
* unscaled_y
;
881 int scaled_strides
= spu
->scaled_stride
* y
;
882 for (x
= 0; x
< spu
->scaled_width
; ++x
) {
883 int unscaled_x
= x
* 0x100 / scalex
;
884 spu
->scaled_image
[scaled_strides
+ x
] = spu
->image
[strides
+ unscaled_x
];
885 spu
->scaled_aimage
[scaled_strides
+ x
] = spu
->aimage
[strides
+ unscaled_x
];
891 /* Intermediate antialiasing. */
892 for (y
= 0; y
< spu
->scaled_height
; ++y
) {
893 const unsigned int unscaled_top
= y
* spu
->orig_frame_height
/ dys
;
894 unsigned int unscaled_bottom
= (y
+ 1) * spu
->orig_frame_height
/ dys
;
895 if (unscaled_bottom
>= spu
->height
)
896 unscaled_bottom
= spu
->height
- 1;
897 for (x
= 0; x
< spu
->scaled_width
; ++x
) {
898 const unsigned int unscaled_left
= x
* spu
->orig_frame_width
/ dxs
;
899 unsigned int unscaled_right
= (x
+ 1) * spu
->orig_frame_width
/ dxs
;
900 unsigned int color
= 0;
901 unsigned int alpha
= 0;
902 unsigned int walkx
, walky
;
903 unsigned int base
, tmp
;
904 if (unscaled_right
>= spu
->width
)
905 unscaled_right
= spu
->width
- 1;
906 for (walky
= unscaled_top
; walky
<= unscaled_bottom
; ++walky
)
907 for (walkx
= unscaled_left
; walkx
<= unscaled_right
; ++walkx
) {
908 base
= walky
* spu
->stride
+ walkx
;
909 tmp
= canon_alpha(spu
->aimage
[base
]);
911 color
+= tmp
* spu
->image
[base
];
913 base
= y
* spu
->scaled_stride
+ x
;
914 spu
->scaled_image
[base
] = alpha
? color
/ alpha
: 0;
915 spu
->scaled_aimage
[base
] =
916 alpha
* (1 + unscaled_bottom
- unscaled_top
) * (1 + unscaled_right
- unscaled_left
);
917 /* spu->scaled_aimage[base] =
918 alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */
919 if (spu
->scaled_aimage
[base
]) {
920 spu
->scaled_aimage
[base
] = 256 - spu
->scaled_aimage
[base
];
921 if (spu
->scaled_aimage
[base
] + spu
->scaled_image
[base
] > 255)
922 spu
->scaled_image
[base
] = 256 - spu
->scaled_aimage
[base
];
930 /* Best antialiasing. Very slow. */
931 /* Any pixel (x, y) represents pixels from the original
932 rectangular region comprised between the columns
933 unscaled_y and unscaled_y + 0x100 / scaley and the rows
934 unscaled_x and unscaled_x + 0x100 / scalex
936 The original rectangular region that the scaled pixel
937 represents is cut in 9 rectangular areas like this:
939 +---+-----------------+---+
941 +---+-----------------+---+
945 +---+-----------------+---+
947 +---+-----------------+---+
949 The width of the left column is at most one pixel and
950 it is never null and its right column is at a pixel
951 boundary. The height of the top row is at most one
952 pixel it is never null and its bottom row is at a
953 pixel boundary. The width and height of region 5 are
954 integral values. The width of the right column is
955 what remains and is less than one pixel. The height
956 of the bottom row is what remains and is less than
959 The row above 1, 2, 3 is unscaled_y. The row between
960 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4,
961 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom.
962 The row beneath 7, 8, 9 is unscaled_y_bottom.
964 The column left of 1, 4, 7 is unscaled_x. The column
965 between 1, 4, 7 and 2, 5, 8 is left_right_column. The
966 column between 2, 5, 8 and 3, 6, 9 is (unsigned
967 int)unscaled_x_right. The column right of 3, 6, 9 is
969 const double inv_scalex
= (double) 0x100 / scalex
;
970 const double inv_scaley
= (double) 0x100 / scaley
;
971 for (y
= 0; y
< spu
->scaled_height
; ++y
) {
972 const double unscaled_y
= y
* inv_scaley
;
973 const double unscaled_y_bottom
= unscaled_y
+ inv_scaley
;
974 const unsigned int top_low_row
= FFMIN(unscaled_y_bottom
, unscaled_y
+ 1.0);
975 const double top
= top_low_row
- unscaled_y
;
976 const unsigned int height
= unscaled_y_bottom
> top_low_row
977 ? (unsigned int) unscaled_y_bottom
- top_low_row
979 const double bottom
= unscaled_y_bottom
> top_low_row
980 ? unscaled_y_bottom
- floor(unscaled_y_bottom
)
982 for (x
= 0; x
< spu
->scaled_width
; ++x
) {
983 const double unscaled_x
= x
* inv_scalex
;
984 const double unscaled_x_right
= unscaled_x
+ inv_scalex
;
985 const unsigned int left_right_column
= FFMIN(unscaled_x_right
, unscaled_x
+ 1.0);
986 const double left
= left_right_column
- unscaled_x
;
987 const unsigned int width
= unscaled_x_right
> left_right_column
988 ? (unsigned int) unscaled_x_right
- left_right_column
990 const double right
= unscaled_x_right
> left_right_column
991 ? unscaled_x_right
- floor(unscaled_x_right
)
997 /* Now use these informations to compute a good alpha,
998 and lightness. The sum is on each of the 9
999 region's surface and alpha and lightness.
1001 transformed alpha = sum(surface * alpha) / sum(surface)
1002 transformed color = sum(surface * alpha * color) / sum(surface * alpha)
1004 /* 1: top left part */
1005 base
= spu
->stride
* (unsigned int) unscaled_y
;
1006 tmp
= left
* top
* canon_alpha(spu
->aimage
[base
+ (unsigned int) unscaled_x
]);
1008 color
+= tmp
* spu
->image
[base
+ (unsigned int) unscaled_x
];
1009 /* 2: top center part */
1012 for (walkx
= left_right_column
; walkx
< (unsigned int) unscaled_x_right
; ++walkx
) {
1013 base
= spu
->stride
* (unsigned int) unscaled_y
+ walkx
;
1014 tmp
= /* 1.0 * */ top
* canon_alpha(spu
->aimage
[base
]);
1016 color
+= tmp
* spu
->image
[base
];
1019 /* 3: top right part */
1021 base
= spu
->stride
* (unsigned int) unscaled_y
+ (unsigned int) unscaled_x_right
;
1022 tmp
= right
* top
* canon_alpha(spu
->aimage
[base
]);
1024 color
+= tmp
* spu
->image
[base
];
1026 /* 4: center left part */
1029 for (walky
= top_low_row
; walky
< (unsigned int) unscaled_y_bottom
; ++walky
) {
1030 base
= spu
->stride
* walky
+ (unsigned int) unscaled_x
;
1031 tmp
= left
/* * 1.0 */ * canon_alpha(spu
->aimage
[base
]);
1033 color
+= tmp
* spu
->image
[base
];
1036 /* 5: center part */
1037 if (width
> 0 && height
> 0) {
1039 for (walky
= top_low_row
; walky
< (unsigned int) unscaled_y_bottom
; ++walky
) {
1041 base
= spu
->stride
* walky
;
1042 for (walkx
= left_right_column
; walkx
< (unsigned int) unscaled_x_right
; ++walkx
) {
1043 tmp
= /* 1.0 * 1.0 * */ canon_alpha(spu
->aimage
[base
+ walkx
]);
1045 color
+= tmp
* spu
->image
[base
+ walkx
];
1049 /* 6: center right part */
1050 if (right
> 0.0 && height
> 0) {
1052 for (walky
= top_low_row
; walky
< (unsigned int) unscaled_y_bottom
; ++walky
) {
1053 base
= spu
->stride
* walky
+ (unsigned int) unscaled_x_right
;
1054 tmp
= right
/* * 1.0 */ * canon_alpha(spu
->aimage
[base
]);
1056 color
+= tmp
* spu
->image
[base
];
1059 /* 7: bottom left part */
1061 base
= spu
->stride
* (unsigned int) unscaled_y_bottom
+ (unsigned int) unscaled_x
;
1062 tmp
= left
* bottom
* canon_alpha(spu
->aimage
[base
]);
1064 color
+= tmp
* spu
->image
[base
];
1066 /* 8: bottom center part */
1067 if (width
> 0 && bottom
> 0.0) {
1069 base
= spu
->stride
* (unsigned int) unscaled_y_bottom
;
1070 for (walkx
= left_right_column
; walkx
< (unsigned int) unscaled_x_right
; ++walkx
) {
1071 tmp
= /* 1.0 * */ bottom
* canon_alpha(spu
->aimage
[base
+ walkx
]);
1073 color
+= tmp
* spu
->image
[base
+ walkx
];
1076 /* 9: bottom right part */
1077 if (right
> 0.0 && bottom
> 0.0) {
1078 base
= spu
->stride
* (unsigned int) unscaled_y_bottom
+ (unsigned int) unscaled_x_right
;
1079 tmp
= right
* bottom
* canon_alpha(spu
->aimage
[base
]);
1081 color
+= tmp
* spu
->image
[base
];
1083 /* Finally mix these transparency and brightness information suitably */
1084 base
= spu
->scaled_stride
* y
+ x
;
1085 spu
->scaled_image
[base
] = alpha
> 0 ? color
/ alpha
: 0;
1086 spu
->scaled_aimage
[base
] = alpha
* scalex
* scaley
/ 0x10000;
1087 if (spu
->scaled_aimage
[base
]) {
1088 spu
->scaled_aimage
[base
] = 256 - spu
->scaled_aimage
[base
];
1089 if (spu
->scaled_aimage
[base
] + spu
->scaled_image
[base
] > 255)
1090 spu
->scaled_image
[base
] = 256 - spu
->scaled_aimage
[base
];
1097 /* Kludge: draw_alpha needs width multiple of 8. */
1098 if (spu
->scaled_width
< spu
->scaled_stride
)
1099 for (y
= 0; y
< spu
->scaled_height
; ++y
) {
1100 memset(spu
->scaled_aimage
+ y
* spu
->scaled_stride
+ spu
->scaled_width
, 0,
1101 spu
->scaled_stride
- spu
->scaled_width
);
1103 spu
->scaled_frame_width
= dxs
;
1104 spu
->scaled_frame_height
= dys
;
1107 if (spu
->scaled_image
){
1108 switch (spu_alignment
) {
1110 spu
->scaled_start_row
= dys
*sub_pos
/100;
1111 if (spu
->scaled_start_row
+ spu
->scaled_height
> dys
)
1112 spu
->scaled_start_row
= dys
- spu
->scaled_height
;
1115 spu
->scaled_start_row
= dys
*sub_pos
/100 - spu
->scaled_height
/2;
1116 if (sub_pos
>= 50 && spu
->scaled_start_row
+ spu
->scaled_height
> dys
)
1117 spu
->scaled_start_row
= dys
- spu
->scaled_height
;
1120 spu
->scaled_start_row
= dys
*sub_pos
/100 - spu
->scaled_height
;
1123 draw_alpha(ctx
, spu
->scaled_start_col
, spu
->scaled_start_row
, spu
->scaled_width
, spu
->scaled_height
,
1124 spu
->scaled_image
, spu
->scaled_aimage
, spu
->scaled_stride
);
1125 spu
->spu_changed
= 0;
1131 mp_msg(MSGT_SPUDEC
,MSGL_DBG2
,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n",
1132 spu
->start_pts
, spu
->end_pts
, spu
->now_pts
);
1136 void spudec_update_palette(void * this, unsigned int *palette
)
1138 spudec_handle_t
*spu
= (spudec_handle_t
*) this;
1139 if (spu
&& palette
) {
1140 memcpy(spu
->global_palette
, palette
, sizeof(spu
->global_palette
));
1142 vo_control(spu
->hw_spu
, VOCTRL_SET_SPU_PALETTE
, spu
->global_palette
);
1146 void spudec_set_font_factor(void * this, double factor
)
1148 spudec_handle_t
*spu
= (spudec_handle_t
*) this;
1149 spu
->font_start_level
= (int)(0xF0-(0xE0*factor
));
1152 static void spudec_parse_extradata(spudec_handle_t
*this,
1153 uint8_t *extradata
, int extradata_len
)
1155 uint8_t *buffer
, *ptr
;
1156 unsigned int *pal
= this->global_palette
, *cuspal
= this->cuspal
;
1160 if (extradata_len
== 16*4) {
1161 for (i
=0; i
<16; i
++)
1162 pal
[i
] = AV_RB32(extradata
+ i
*4);
1163 this->auto_palette
= 0;
1167 if (!(ptr
= buffer
= malloc(extradata_len
+1)))
1169 memcpy(buffer
, extradata
, extradata_len
);
1170 buffer
[extradata_len
] = 0;
1175 if (!strncmp(ptr
, "size: ", 6))
1176 sscanf(ptr
+ 6, "%dx%d", &this->orig_frame_width
, &this->orig_frame_height
);
1177 if (!strncmp(ptr
, "palette: ", 9) &&
1178 sscanf(ptr
+ 9, "%x, %x, %x, %x, %x, %x, %x, %x, "
1179 "%x, %x, %x, %x, %x, %x, %x, %x",
1180 &pal
[ 0], &pal
[ 1], &pal
[ 2], &pal
[ 3],
1181 &pal
[ 4], &pal
[ 5], &pal
[ 6], &pal
[ 7],
1182 &pal
[ 8], &pal
[ 9], &pal
[10], &pal
[11],
1183 &pal
[12], &pal
[13], &pal
[14], &pal
[15]) == 16) {
1184 for (i
=0; i
<16; i
++)
1185 pal
[i
] = vobsub_palette_to_yuv(pal
[i
]);
1186 this->auto_palette
= 0;
1188 if (!strncasecmp(ptr
, "forced subs: on", 15))
1189 this->forced_subs_only
= 1;
1190 if (!strncmp(ptr
, "custom colors: ON, tridx: ", 26) &&
1191 sscanf(ptr
+ 26, "%x, colors: %x, %x, %x, %x",
1192 &tridx
, cuspal
+0, cuspal
+1, cuspal
+2, cuspal
+3) == 5) {
1193 for (i
=0; i
<4; i
++) {
1194 cuspal
[i
] = vobsub_rgb_to_yuv(cuspal
[i
]);
1195 if (tridx
& (1 << (12-4*i
)))
1196 cuspal
[i
] |= 1 << 31;
1200 } while ((ptr
=strchr(ptr
,'\n')) && *++ptr
);
1205 void *spudec_new_scaled(unsigned int *palette
, unsigned int frame_width
, unsigned int frame_height
, uint8_t *extradata
, int extradata_len
)
1207 spudec_handle_t
*this = calloc(1, sizeof(spudec_handle_t
));
1209 this->orig_frame_height
= frame_height
;
1212 memcpy(this->global_palette
, palette
, sizeof(this->global_palette
));
1214 this->auto_palette
= 1;
1216 spudec_parse_extradata(this, extradata
, extradata_len
);
1217 /* XXX Although the video frame is some size, the SPU frame is
1218 always maximum size i.e. 720 wide and 576 or 480 high */
1219 // For HD files in MKV the VobSub resolution can be higher though,
1220 // see largeres_vobsub.mkv
1221 if (this->orig_frame_width
<= 720 && this->orig_frame_height
<= 576) {
1222 this->orig_frame_width
= 720;
1223 if (this->orig_frame_height
== 480 || this->orig_frame_height
== 240)
1224 this->orig_frame_height
= 480;
1226 this->orig_frame_height
= 576;
1230 mp_msg(MSGT_SPUDEC
,MSGL_FATAL
, "FATAL: spudec_init: calloc");
1234 void *spudec_new(unsigned int *palette
)
1236 return spudec_new_scaled(palette
, 0, 0, NULL
, 0);
1239 void spudec_free(void *this)
1241 spudec_handle_t
*spu
= (spudec_handle_t
*)this;
1243 while (spu
->queue_head
)
1244 spudec_free_packet(spudec_dequeue_packet(spu
));
1247 if (spu
->scaled_image
)
1248 free(spu
->scaled_image
);
1255 void spudec_set_hw_spu(void *this, struct vo
*hw_spu
)
1257 spudec_handle_t
*spu
= (spudec_handle_t
*)this;
1260 spu
->hw_spu
= hw_spu
;
1261 vo_control(hw_spu
, VOCTRL_SET_SPU_PALETTE
, spu
->global_palette
);