Refactor: rename "sub-filter" to "sub-source" this includes capability, functions...
[vlc.git] / src / video_output / vout_subpictures.c
blobc9e2f8c1ae1465c4b9bc6a059c36304f516497d8
1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2007 the VideoLAN team
5 * $Id$
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <assert.h>
34 #include <limits.h>
36 #include <vlc_common.h>
37 #include <vlc_modules.h>
38 #include <vlc_input.h>
39 #include <vlc_vout.h>
40 #include <vlc_filter.h>
41 #include <vlc_spu.h>
43 #include "../libvlc.h"
44 #include "vout_internal.h"
45 #include "../misc/subpicture.h"
47 /*****************************************************************************
48 * Local prototypes
49 *****************************************************************************/
51 /* Number of simultaneous subpictures */
52 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
54 /* */
55 typedef struct
57 subpicture_t *p_subpicture;
58 bool b_reject;
59 } spu_heap_entry_t;
61 typedef struct
63 spu_heap_entry_t p_entry[VOUT_MAX_SUBPICTURES];
65 } spu_heap_t;
67 struct spu_private_t
69 vlc_mutex_t lock; /* lock to protect all followings fields */
70 vlc_object_t *p_input;
72 spu_heap_t heap;
74 int i_channel; /**< number of subpicture channels registered */
75 filter_t *p_text; /**< text renderer module */
76 filter_t *p_scale_yuvp; /**< scaling module for YUVP */
77 filter_t *p_scale; /**< scaling module (all but YUVP) */
78 bool b_force_crop; /**< force cropping of subpicture */
79 int i_crop_x, i_crop_y, i_crop_width, i_crop_height; /**< cropping */
81 int i_margin; /**< force position of a subpicture */
82 bool b_force_palette; /**< force palette of subpicture */
83 uint8_t palette[4][4]; /**< forced palette */
85 /* Subpiture filters */
86 char *psz_chain_update;
87 vlc_mutex_t chain_lock;
88 filter_chain_t *p_chain;
90 /* */
91 mtime_t i_last_sort_date;
94 /*****************************************************************************
95 * heap managment
96 *****************************************************************************/
97 static void SpuHeapInit( spu_heap_t *p_heap )
99 for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
101 spu_heap_entry_t *e = &p_heap->p_entry[i];
103 e->p_subpicture = NULL;
104 e->b_reject = false;
108 static int SpuHeapPush( spu_heap_t *p_heap, subpicture_t *p_subpic )
110 for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
112 spu_heap_entry_t *e = &p_heap->p_entry[i];
114 if( e->p_subpicture )
115 continue;
117 e->p_subpicture = p_subpic;
118 e->b_reject = false;
119 return VLC_SUCCESS;
121 return VLC_EGENERIC;
124 static void SpuHeapDeleteAt( spu_heap_t *p_heap, int i_index )
126 spu_heap_entry_t *e = &p_heap->p_entry[i_index];
128 if( e->p_subpicture )
129 subpicture_Delete( e->p_subpicture );
131 e->p_subpicture = NULL;
134 static int SpuHeapDeleteSubpicture( spu_heap_t *p_heap, subpicture_t *p_subpic )
136 for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
138 spu_heap_entry_t *e = &p_heap->p_entry[i];
140 if( e->p_subpicture != p_subpic )
141 continue;
143 SpuHeapDeleteAt( p_heap, i );
144 return VLC_SUCCESS;
146 return VLC_EGENERIC;
149 static void SpuHeapClean( spu_heap_t *p_heap )
151 for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
153 spu_heap_entry_t *e = &p_heap->p_entry[i];
154 if( e->p_subpicture )
155 subpicture_Delete( e->p_subpicture );
159 struct filter_owner_sys_t
161 spu_t *p_spu;
162 int i_channel;
165 static void FilterRelease( filter_t *p_filter )
167 if( p_filter->p_module )
168 module_unneed( p_filter, p_filter->p_module );
169 if( p_filter->p_owner )
170 free( p_filter->p_owner );
172 vlc_object_release( p_filter );
175 static picture_t *spu_new_video_buffer( filter_t *p_filter )
177 const video_format_t *p_fmt = &p_filter->fmt_out.video;
179 VLC_UNUSED(p_filter);
180 return picture_NewFromFormat( p_fmt );
182 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_picture )
184 VLC_UNUSED(p_filter);
185 picture_Release( p_picture );
188 static int spu_get_attachments( filter_t *p_filter,
189 input_attachment_t ***ppp_attachment,
190 int *pi_attachment )
192 spu_t *p_spu = p_filter->p_owner->p_spu;
194 int i_ret = VLC_EGENERIC;
195 if( p_spu->p->p_input )
196 i_ret = input_Control( (input_thread_t*)p_spu->p->p_input,
197 INPUT_GET_ATTACHMENTS,
198 ppp_attachment, pi_attachment );
199 return i_ret;
202 static filter_t *SpuRenderCreateAndLoadText( spu_t *p_spu )
204 filter_t *p_text = vlc_custom_create( p_spu, sizeof(*p_text),
205 VLC_OBJECT_GENERIC, "spu text" );
206 if( !p_text )
207 return NULL;
209 p_text->p_owner = xmalloc( sizeof(*p_text->p_owner) );
210 p_text->p_owner->p_spu = p_spu;
212 es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );
214 es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
215 p_text->fmt_out.video.i_width =
216 p_text->fmt_out.video.i_visible_width = 32;
217 p_text->fmt_out.video.i_height =
218 p_text->fmt_out.video.i_visible_height = 32;
220 p_text->pf_get_attachments = spu_get_attachments;
222 vlc_object_attach( p_text, p_spu );
223 p_text->p_module = module_need( p_text, "text renderer", "$text-renderer", false );
225 /* Create a few variables used for enhanced text rendering */
226 var_Create( p_text, "spu-elapsed", VLC_VAR_TIME );
227 var_Create( p_text, "text-rerender", VLC_VAR_BOOL );
228 var_Create( p_text, "scale", VLC_VAR_INTEGER );
230 return p_text;
233 static filter_t *SpuRenderCreateAndLoadScale( vlc_object_t *p_obj,
234 vlc_fourcc_t i_src_chroma, vlc_fourcc_t i_dst_chroma,
235 bool b_resize )
237 filter_t *p_scale = vlc_custom_create( p_obj, sizeof(*p_scale),
238 VLC_OBJECT_GENERIC, "scale" );
239 if( !p_scale )
240 return NULL;
242 es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
243 p_scale->fmt_in.video.i_chroma = i_src_chroma;
244 p_scale->fmt_in.video.i_width =
245 p_scale->fmt_in.video.i_height = 32;
247 es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
248 p_scale->fmt_out.video.i_chroma = i_dst_chroma;
249 p_scale->fmt_out.video.i_width =
250 p_scale->fmt_out.video.i_height = b_resize ? 16 : 32;
252 p_scale->pf_video_buffer_new = spu_new_video_buffer;
253 p_scale->pf_video_buffer_del = spu_del_video_buffer;
255 vlc_object_attach( p_scale, p_obj );
256 p_scale->p_module = module_need( p_scale, "video filter2", NULL, false );
258 return p_scale;
261 static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
262 subpicture_region_t *p_region,
263 mtime_t render_date )
265 filter_t *p_text = p_spu->p->p_text;
267 assert( p_region->fmt.i_chroma == VLC_CODEC_TEXT );
269 if( !p_text || !p_text->p_module )
270 return;
272 /* Setup 3 variables which can be used to render
273 * time-dependent text (and effects). The first indicates
274 * the total amount of time the text will be on screen,
275 * the second the amount of time it has already been on
276 * screen (can be a negative value as text is layed out
277 * before it is rendered) and the third is a feedback
278 * variable from the renderer - if the renderer sets it
279 * then this particular text is time-dependent, eg. the
280 * visual progress bar inside the text in karaoke and the
281 * text needs to be rendered multiple times in order for
282 * the effect to work - we therefore need to return the
283 * region to its original state at the end of the loop,
284 * instead of leaving it in YUVA or YUVP.
285 * Any renderer which is unaware of how to render
286 * time-dependent text can happily ignore the variables
287 * and render the text the same as usual - it should at
288 * least show up on screen, but the effect won't change
289 * the text over time.
291 var_SetTime( p_text, "spu-elapsed", render_date );
292 var_SetBool( p_text, "text-rerender", false );
294 if( p_text->pf_render_html && p_region->psz_html )
296 p_text->pf_render_html( p_text, p_region, p_region );
298 else if( p_text->pf_render_text )
300 p_text->pf_render_text( p_text, p_region, p_region );
302 *pb_rerender_text = var_GetBool( p_text, "text-rerender" );
306 * A few scale functions helpers.
309 #define SCALE_UNIT (1000)
310 typedef struct
312 int w;
313 int h;
314 } spu_scale_t;
316 static spu_scale_t spu_scale_create( int w, int h )
318 spu_scale_t s = { .w = w, .h = h };
319 if( s.w <= 0 )
320 s.w = SCALE_UNIT;
321 if( s.h <= 0 )
322 s.h = SCALE_UNIT;
323 return s;
325 static spu_scale_t spu_scale_unit( void )
327 return spu_scale_create( SCALE_UNIT, SCALE_UNIT );
329 static spu_scale_t spu_scale_createq( int64_t wn, int64_t wd, int64_t hn, int64_t hd )
331 return spu_scale_create( wn * SCALE_UNIT / wd,
332 hn * SCALE_UNIT / hd );
334 static int spu_scale_w( int v, const spu_scale_t s )
336 return v * s.w / SCALE_UNIT;
338 static int spu_scale_h( int v, const spu_scale_t s )
340 return v * s.h / SCALE_UNIT;
342 static int spu_invscale_w( int v, const spu_scale_t s )
344 return v * SCALE_UNIT / s.w;
346 static int spu_invscale_h( int v, const spu_scale_t s )
348 return v * SCALE_UNIT / s.h;
352 * A few area functions helpers
354 typedef struct
356 int i_x;
357 int i_y;
358 int i_width;
359 int i_height;
361 spu_scale_t scale;
362 } spu_area_t;
364 static spu_area_t spu_area_create( int x, int y, int w, int h, spu_scale_t s )
366 spu_area_t a = { .i_x = x, .i_y = y, .i_width = w, .i_height = h, .scale = s };
367 return a;
369 static spu_area_t spu_area_scaled( spu_area_t a )
371 if( a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT )
372 return a;
374 a.i_x = spu_scale_w( a.i_x, a.scale );
375 a.i_y = spu_scale_h( a.i_y, a.scale );
377 a.i_width = spu_scale_w( a.i_width, a.scale );
378 a.i_height = spu_scale_h( a.i_height, a.scale );
380 a.scale = spu_scale_unit();
381 return a;
383 static spu_area_t spu_area_unscaled( spu_area_t a, spu_scale_t s )
385 if( a.scale.w == s.w && a.scale.h == s.h )
386 return a;
388 a = spu_area_scaled( a );
390 a.i_x = spu_invscale_w( a.i_x, s );
391 a.i_y = spu_invscale_h( a.i_y, s );
393 a.i_width = spu_invscale_w( a.i_width, s );
394 a.i_height = spu_invscale_h( a.i_height, s );
396 a.scale = s;
397 return a;
399 static bool spu_area_overlap( spu_area_t a, spu_area_t b )
401 const int i_dx = 0;
402 const int i_dy = 0;
404 a = spu_area_scaled( a );
405 b = spu_area_scaled( b );
407 return __MAX( a.i_x-i_dx, b.i_x ) < __MIN( a.i_x+a.i_width +i_dx, b.i_x+b.i_width ) &&
408 __MAX( a.i_y-i_dy, b.i_y ) < __MIN( a.i_y+a.i_height+i_dy, b.i_y+b.i_height );
412 * Avoid area overlapping
414 static void SpuAreaFixOverlap( spu_area_t *p_dst,
415 const spu_area_t *p_sub, int i_sub, int i_align )
417 spu_area_t a = spu_area_scaled( *p_dst );
418 bool b_moved = false;
419 bool b_ok;
421 /* Check for overlap
422 * XXX It is not fast O(n^2) but we should not have a lot of region */
425 b_ok = true;
426 for( int i = 0; i < i_sub; i++ )
428 spu_area_t sub = spu_area_scaled( p_sub[i] );
430 if( !spu_area_overlap( a, sub ) )
431 continue;
433 if( i_align & SUBPICTURE_ALIGN_TOP )
435 /* We go down */
436 int i_y = sub.i_y + sub.i_height;
437 a.i_y = i_y;
438 b_moved = true;
440 else if( i_align & SUBPICTURE_ALIGN_BOTTOM )
442 /* We go up */
443 int i_y = sub.i_y - a.i_height;
444 a.i_y = i_y;
445 b_moved = true;
447 else
449 /* TODO what to do in this case? */
450 //fprintf( stderr, "Overlap with unsupported alignment\n" );
451 break;
454 b_ok = false;
455 break;
457 } while( !b_ok );
459 if( b_moved )
460 *p_dst = spu_area_unscaled( a, p_dst->scale );
464 static void SpuAreaFitInside( spu_area_t *p_area, const spu_area_t *p_boundary )
466 spu_area_t a = spu_area_scaled( *p_area );
468 const int i_error_x = (a.i_x + a.i_width) - p_boundary->i_width;
469 if( i_error_x > 0 )
470 a.i_x -= i_error_x;
471 if( a.i_x < 0 )
472 a.i_x = 0;
474 const int i_error_y = (a.i_y + a.i_height) - p_boundary->i_height;
475 if( i_error_y > 0 )
476 a.i_y -= i_error_y;
477 if( a.i_y < 0 )
478 a.i_y = 0;
480 *p_area = spu_area_unscaled( a, p_area->scale );
484 * Place a region
486 static void SpuRegionPlace( int *pi_x, int *pi_y,
487 const subpicture_t *p_subpic,
488 const subpicture_region_t *p_region )
490 assert( p_region->i_x != INT_MAX && p_region->i_y != INT_MAX );
491 if( p_subpic->b_absolute )
493 *pi_x = p_region->i_x;
494 *pi_y = p_region->i_y;
496 else
498 if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
500 *pi_y = p_region->i_y;
502 else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
504 *pi_y = p_subpic->i_original_picture_height - p_region->fmt.i_height - p_region->i_y;
506 else
508 *pi_y = p_subpic->i_original_picture_height / 2 - p_region->fmt.i_height / 2;
511 if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
513 *pi_x = p_region->i_x;
515 else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
517 *pi_x = p_subpic->i_original_picture_width - p_region->fmt.i_width - p_region->i_x;
519 else
521 *pi_x = p_subpic->i_original_picture_width / 2 - p_region->fmt.i_width / 2;
527 * This function compares two 64 bits integers.
528 * It can be used by qsort.
530 static int IntegerCmp( int64_t i0, int64_t i1 )
532 return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
535 * This function compares 2 subpictures using the following properties
536 * (ordered by priority)
537 * 1. absolute positionning
538 * 2. start time
539 * 3. creation order (per channel)
541 * It can be used by qsort.
543 * XXX spu_RenderSubpictures depends heavily on this order.
545 static int SubpictureCmp( const void *s0, const void *s1 )
547 subpicture_t *p_subpic0 = *(subpicture_t**)s0;
548 subpicture_t *p_subpic1 = *(subpicture_t**)s1;
549 int r;
551 r = IntegerCmp( !p_subpic0->b_absolute, !p_subpic1->b_absolute );
552 if( !r )
553 r = IntegerCmp( p_subpic0->i_start, p_subpic1->i_start );
554 if( !r )
555 r = IntegerCmp( p_subpic0->i_channel, p_subpic1->i_channel );
556 if( !r )
557 r = IntegerCmp( p_subpic0->i_order, p_subpic1->i_order );
558 return r;
561 /*****************************************************************************
562 * SpuSelectSubpictures: find the subpictures to display
563 *****************************************************************************
564 * This function parses all subpictures and decides which ones need to be
565 * displayed. If no picture has been selected, display_date will depend on
566 * the subpicture.
567 * We also check for ephemer DVD subpictures (subpictures that have
568 * to be removed if a newer one is available), which makes it a lot
569 * more difficult to guess if a subpicture has to be rendered or not.
570 *****************************************************************************/
571 static void SpuSelectSubpictures( spu_t *p_spu,
572 unsigned int *pi_subpicture,
573 subpicture_t **pp_subpicture,
574 mtime_t render_subtitle_date,
575 mtime_t render_osd_date,
576 bool b_subtitle_only )
578 spu_private_t *p_sys = p_spu->p;
580 /* */
581 *pi_subpicture = 0;
583 /* Create a list of channels */
584 int pi_channel[VOUT_MAX_SUBPICTURES];
585 int i_channel_count = 0;
587 for( int i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
589 spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
590 if( !p_entry->p_subpicture || p_entry->b_reject )
591 continue;
592 const int i_channel = p_entry->p_subpicture->i_channel;
593 int i;
594 for( i = 0; i < i_channel_count; i++ )
596 if( pi_channel[i] == i_channel )
597 break;
599 if( i_channel_count <= i )
600 pi_channel[i_channel_count++] = i_channel;
603 /* Fill up the pp_subpicture arrays with relevent pictures */
604 for( int i = 0; i < i_channel_count; i++ )
606 const int i_channel = pi_channel[i];
607 subpicture_t *p_available_subpic[VOUT_MAX_SUBPICTURES];
608 bool pb_available_late[VOUT_MAX_SUBPICTURES];
609 int i_available = 0;
611 mtime_t start_date = render_subtitle_date;
612 mtime_t ephemer_subtitle_date = 0;
613 mtime_t ephemer_osd_date = 0;
614 int64_t i_ephemer_subtitle_order = INT64_MIN;
615 int64_t i_ephemer_system_order = INT64_MIN;
616 int i_index;
618 /* Select available pictures */
619 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
621 spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
622 subpicture_t *p_current = p_entry->p_subpicture;
623 bool b_stop_valid;
624 bool b_late;
626 if( !p_current || p_entry->b_reject )
628 if( p_entry->b_reject )
629 SpuHeapDeleteAt( &p_sys->heap, i_index );
630 continue;
633 if( p_current->i_channel != i_channel ||
634 ( b_subtitle_only && !p_current->b_subtitle ) )
636 continue;
638 const mtime_t render_date = p_current->b_subtitle ? render_subtitle_date : render_osd_date;
639 if( render_date &&
640 render_date < p_current->i_start )
642 /* Too early, come back next monday */
643 continue;
646 mtime_t *pi_ephemer_date = p_current->b_subtitle ? &ephemer_subtitle_date : &ephemer_osd_date;
647 int64_t *pi_ephemer_order = p_current->b_subtitle ? &i_ephemer_subtitle_order : &i_ephemer_system_order;
648 if( p_current->i_start >= *pi_ephemer_date )
650 *pi_ephemer_date = p_current->i_start;
651 if( p_current->i_order > *pi_ephemer_order )
652 *pi_ephemer_order = p_current->i_order;
655 b_stop_valid = !p_current->b_ephemer || p_current->i_stop > p_current->i_start;
657 b_late = b_stop_valid && p_current->i_stop <= render_date;
659 /* start_date will be used for correct automatic overlap support
660 * in case picture that should not be displayed anymore (display_time)
661 * overlap with a picture to be displayed (p_current->i_start) */
662 if( p_current->b_subtitle && !b_late && !p_current->b_ephemer )
663 start_date = p_current->i_start;
665 /* */
666 p_available_subpic[i_available] = p_current;
667 pb_available_late[i_available] = b_late;
668 i_available++;
671 /* Only forced old picture display at the transition */
672 if( start_date < p_sys->i_last_sort_date )
673 start_date = p_sys->i_last_sort_date;
674 if( start_date <= 0 )
675 start_date = INT64_MAX;
677 /* Select pictures to be displayed */
678 for( i_index = 0; i_index < i_available; i_index++ )
680 subpicture_t *p_current = p_available_subpic[i_index];
681 bool b_late = pb_available_late[i_index];
683 const mtime_t stop_date = p_current->b_subtitle ? __MAX( start_date, p_sys->i_last_sort_date ) : render_osd_date;
684 const mtime_t ephemer_date = p_current->b_subtitle ? ephemer_subtitle_date : ephemer_osd_date;
685 const int64_t i_ephemer_order = p_current->b_subtitle ? i_ephemer_subtitle_order : i_ephemer_system_order;
687 /* Destroy late and obsolete ephemer subpictures */
688 bool b_rejet = b_late && p_current->i_stop <= stop_date;
689 if( p_current->b_ephemer )
691 if( p_current->i_start < ephemer_date )
692 b_rejet = true;
693 else if( p_current->i_start == ephemer_date &&
694 p_current->i_order < i_ephemer_order )
695 b_rejet = true;
698 if( b_rejet )
699 SpuHeapDeleteSubpicture( &p_sys->heap, p_current );
700 else
701 pp_subpicture[(*pi_subpicture)++] = p_current;
705 p_sys->i_last_sort_date = render_subtitle_date;
711 * It will transform the provided region into another region suitable for rendering.
714 static void SpuRenderRegion( spu_t *p_spu,
715 subpicture_region_t **pp_dst, spu_area_t *p_dst_area,
716 subpicture_t *p_subpic, subpicture_region_t *p_region,
717 const spu_scale_t scale_size,
718 const vlc_fourcc_t *p_chroma_list,
719 const video_format_t *p_fmt,
720 const spu_area_t *p_subtitle_area, int i_subtitle_area,
721 mtime_t render_date )
723 spu_private_t *p_sys = p_spu->p;
725 video_format_t fmt_original = p_region->fmt;
726 bool b_restore_text = false;
727 int i_x_offset;
728 int i_y_offset;
730 video_format_t region_fmt;
731 picture_t *p_region_picture;
733 /* Invalidate area by default */
734 *p_dst_area = spu_area_create( 0,0, 0,0, scale_size );
735 *pp_dst = NULL;
737 /* Render text region */
738 if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
740 SpuRenderText( p_spu, &b_restore_text, p_region,
741 render_date );
743 /* Check if the rendering has failed ... */
744 if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
745 goto exit;
748 /* Force palette if requested
749 * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
750 * instead of only the right one (being the dvd spu).
752 const bool b_using_palette = p_region->fmt.i_chroma == VLC_CODEC_YUVP;
753 const bool b_force_palette = b_using_palette && p_sys->b_force_palette;
754 const bool b_force_crop = b_force_palette && p_sys->b_force_crop;
755 bool b_changed_palette = false;
757 /* Compute the margin which is expressed in destination pixel unit
758 * The margin is applied only to subtitle and when no forced crop is
759 * requested (dvd menu) */
760 int i_margin_y = 0;
761 if( !b_force_crop && p_subpic->b_subtitle )
762 i_margin_y = spu_invscale_h( p_sys->i_margin, scale_size );
764 /* Place the picture
765 * We compute the position in the rendered size */
766 SpuRegionPlace( &i_x_offset, &i_y_offset,
767 p_subpic, p_region );
769 /* Save this position for subtitle overlap support
770 * it is really important that there are given without scale_size applied */
771 *p_dst_area = spu_area_create( i_x_offset, i_y_offset,
772 p_region->fmt.i_width, p_region->fmt.i_height,
773 scale_size );
775 /* Handle overlapping subtitles when possible */
776 if( p_subpic->b_subtitle && !p_subpic->b_absolute )
778 SpuAreaFixOverlap( p_dst_area, p_subtitle_area, i_subtitle_area,
779 p_region->i_align );
782 /* we copy the area: for the subtitle overlap support we want
783 * to only save the area without margin applied */
784 spu_area_t restrained = *p_dst_area;
786 /* apply margin to subtitles and correct if they go over the picture edge */
787 if( p_subpic->b_subtitle )
788 restrained.i_y -= i_margin_y;
790 spu_area_t display = spu_area_create( 0, 0, p_fmt->i_width, p_fmt->i_height,
791 spu_scale_unit() );
792 SpuAreaFitInside( &restrained, &display );
794 /* Fix the position for the current scale_size */
795 i_x_offset = spu_scale_w( restrained.i_x, restrained.scale );
796 i_y_offset = spu_scale_h( restrained.i_y, restrained.scale );
798 /* */
799 if( b_force_palette )
801 video_palette_t *p_palette = p_region->fmt.p_palette;
802 video_palette_t palette;
804 /* We suppose DVD palette here */
805 palette.i_entries = 4;
806 for( int i = 0; i < 4; i++ )
807 for( int j = 0; j < 4; j++ )
808 palette.palette[i][j] = p_sys->palette[i][j];
810 if( p_palette->i_entries == palette.i_entries )
812 for( int i = 0; i < p_palette->i_entries; i++ )
813 for( int j = 0; j < 4; j++ )
814 b_changed_palette |= p_palette->palette[i][j] != palette.palette[i][j];
816 else
818 b_changed_palette = true;
820 *p_palette = palette;
823 /* */
824 region_fmt = p_region->fmt;
825 p_region_picture = p_region->p_picture;
827 bool b_convert_chroma = true;
828 for( int i = 0; p_chroma_list[i] && b_convert_chroma; i++ )
830 if( region_fmt.i_chroma == p_chroma_list[i] )
831 b_convert_chroma = false;
834 /* Scale from rendered size to destination size */
835 if( p_sys->p_scale && p_sys->p_scale->p_module &&
836 ( !b_using_palette || ( p_sys->p_scale_yuvp && p_sys->p_scale_yuvp->p_module ) ) &&
837 ( scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT ||
838 b_using_palette || b_convert_chroma) )
840 const unsigned i_dst_width = spu_scale_w( p_region->fmt.i_width, scale_size );
841 const unsigned i_dst_height = spu_scale_h( p_region->fmt.i_height, scale_size );
843 /* Destroy the cache if unusable */
844 if( p_region->p_private )
846 subpicture_region_private_t *p_private = p_region->p_private;
847 bool b_changed = false;
849 /* Check resize changes */
850 if( i_dst_width != p_private->fmt.i_width ||
851 i_dst_height != p_private->fmt.i_height )
852 b_changed = true;
854 /* Check forced palette changes */
855 if( b_changed_palette )
856 b_changed = true;
858 if( b_convert_chroma && p_private->fmt.i_chroma != p_chroma_list[0] )
859 b_changed = true;
861 if( b_changed )
863 subpicture_region_private_Delete( p_private );
864 p_region->p_private = NULL;
868 /* Scale if needed into cache */
869 if( !p_region->p_private && i_dst_width > 0 && i_dst_height > 0 )
871 filter_t *p_scale = p_sys->p_scale;
873 picture_t *p_picture = p_region->p_picture;
874 picture_Hold( p_picture );
876 /* Convert YUVP to YUVA/RGBA first for better scaling quality */
877 if( b_using_palette )
879 filter_t *p_scale_yuvp = p_sys->p_scale_yuvp;
881 p_scale_yuvp->fmt_in.video = p_region->fmt;
883 p_scale_yuvp->fmt_out.video = p_region->fmt;
884 p_scale_yuvp->fmt_out.video.i_chroma = p_chroma_list[0];
886 p_picture = p_scale_yuvp->pf_video_filter( p_scale_yuvp, p_picture );
887 if( !p_picture )
889 /* Well we will try conversion+scaling */
890 msg_Warn( p_spu, "%4.4s to %4.4s conversion failed",
891 (const char*)&p_scale_yuvp->fmt_in.video.i_chroma,
892 (const char*)&p_scale_yuvp->fmt_out.video.i_chroma );
896 /* Conversion(except from YUVP)/Scaling */
897 if( p_picture &&
898 ( p_picture->format.i_width != i_dst_width ||
899 p_picture->format.i_height != i_dst_height ||
900 ( b_convert_chroma && !b_using_palette ) ) )
902 p_scale->fmt_in.video = p_picture->format;
903 p_scale->fmt_out.video = p_picture->format;
904 if( b_convert_chroma )
905 p_scale->fmt_out.i_codec =
906 p_scale->fmt_out.video.i_chroma = p_chroma_list[0];
908 p_scale->fmt_out.video.i_width = i_dst_width;
909 p_scale->fmt_out.video.i_height = i_dst_height;
911 p_scale->fmt_out.video.i_visible_width =
912 spu_scale_w( p_region->fmt.i_visible_width, scale_size );
913 p_scale->fmt_out.video.i_visible_height =
914 spu_scale_h( p_region->fmt.i_visible_height, scale_size );
916 p_picture = p_scale->pf_video_filter( p_scale, p_picture );
917 if( !p_picture )
918 msg_Err( p_spu, "scaling failed" );
921 /* */
922 if( p_picture )
924 p_region->p_private = subpicture_region_private_New( &p_picture->format );
925 if( p_region->p_private )
927 p_region->p_private->p_picture = p_picture;
928 if( !p_region->p_private->p_picture )
930 subpicture_region_private_Delete( p_region->p_private );
931 p_region->p_private = NULL;
934 else
936 picture_Release( p_picture );
941 /* And use the scaled picture */
942 if( p_region->p_private )
944 region_fmt = p_region->p_private->fmt;
945 p_region_picture = p_region->p_private->p_picture;
949 /* Force cropping if requested */
950 if( b_force_crop )
952 int i_crop_x = spu_scale_w( p_sys->i_crop_x, scale_size );
953 int i_crop_y = spu_scale_h( p_sys->i_crop_y, scale_size );
954 int i_crop_width = spu_scale_w( p_sys->i_crop_width, scale_size );
955 int i_crop_height= spu_scale_h( p_sys->i_crop_height,scale_size );
957 /* Find the intersection */
958 if( i_crop_x + i_crop_width <= i_x_offset ||
959 i_x_offset + (int)region_fmt.i_visible_width < i_crop_x ||
960 i_crop_y + i_crop_height <= i_y_offset ||
961 i_y_offset + (int)region_fmt.i_visible_height < i_crop_y )
963 /* No intersection */
964 region_fmt.i_visible_width =
965 region_fmt.i_visible_height = 0;
967 else
969 int i_x, i_y, i_x_end, i_y_end;
970 i_x = __MAX( i_crop_x, i_x_offset );
971 i_y = __MAX( i_crop_y, i_y_offset );
972 i_x_end = __MIN( i_crop_x + i_crop_width,
973 i_x_offset + (int)region_fmt.i_visible_width );
974 i_y_end = __MIN( i_crop_y + i_crop_height,
975 i_y_offset + (int)region_fmt.i_visible_height );
977 region_fmt.i_x_offset = i_x - i_x_offset;
978 region_fmt.i_y_offset = i_y - i_y_offset;
979 region_fmt.i_visible_width = i_x_end - i_x;
980 region_fmt.i_visible_height = i_y_end - i_y;
982 i_x_offset = __MAX( i_x, 0 );
983 i_y_offset = __MAX( i_y, 0 );
987 subpicture_region_t *p_dst = *pp_dst = subpicture_region_New( &region_fmt );
988 if( p_dst )
990 p_dst->i_x = i_x_offset;
991 p_dst->i_y = i_y_offset;
992 p_dst->i_align = 0;
993 if( p_dst->p_picture )
994 picture_Release( p_dst->p_picture );
995 p_dst->p_picture = picture_Hold( p_region_picture );
996 int i_fade_alpha = 255;
997 if( p_subpic->b_fade )
999 mtime_t fade_start = ( p_subpic->i_stop +
1000 p_subpic->i_start ) / 2;
1002 if( fade_start <= render_date && fade_start < p_subpic->i_stop )
1003 i_fade_alpha = 255 * ( p_subpic->i_stop - render_date ) /
1004 ( p_subpic->i_stop - fade_start );
1006 p_dst->i_alpha = i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025;
1009 exit:
1010 if( b_restore_text )
1012 /* Some forms of subtitles need to be re-rendered more than
1013 * once, eg. karaoke. We therefore restore the region to its
1014 * pre-rendered state, so the next time through everything is
1015 * calculated again.
1017 if( p_region->p_picture )
1019 picture_Release( p_region->p_picture );
1020 p_region->p_picture = NULL;
1022 if( p_region->p_private )
1024 subpicture_region_private_Delete( p_region->p_private );
1025 p_region->p_private = NULL;
1027 p_region->fmt = fmt_original;
1032 * This function renders all sub picture units in the list.
1034 static subpicture_t *SpuRenderSubpictures( spu_t *p_spu,
1035 unsigned int i_subpicture,
1036 subpicture_t **pp_subpicture,
1037 const vlc_fourcc_t *p_chroma_list,
1038 const video_format_t *p_fmt_dst,
1039 const video_format_t *p_fmt_src,
1040 mtime_t render_subtitle_date,
1041 mtime_t render_osd_date )
1043 spu_private_t *p_sys = p_spu->p;
1045 /* Count the number of regions and subtitle regions */
1046 unsigned int i_subtitle_region_count = 0;
1047 unsigned int i_region_count = 0;
1048 for( unsigned i = 0; i < i_subpicture; i++ )
1050 const subpicture_t *p_subpic = pp_subpicture[i];
1052 unsigned i_count = 0;
1053 for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
1054 i_count++;
1056 if( p_subpic->b_subtitle )
1057 i_subtitle_region_count += i_count;
1058 i_region_count += i_count;
1060 if( i_region_count <= 0 )
1061 return NULL;
1063 /* Create the output subpicture */
1064 subpicture_t *p_output = subpicture_New( NULL );
1065 if( !p_output )
1066 return NULL;
1067 p_output->i_original_picture_width = p_fmt_dst->i_width;
1068 p_output->i_original_picture_height = p_fmt_dst->i_height;
1069 subpicture_region_t **pp_output_last = &p_output->p_region;
1071 /* Allocate area array for subtitle overlap */
1072 spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
1073 spu_area_t *p_subtitle_area;
1074 int i_subtitle_area;
1076 i_subtitle_area = 0;
1077 p_subtitle_area = p_subtitle_area_buffer;
1078 if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
1079 p_subtitle_area = calloc( i_subtitle_region_count, sizeof(*p_subtitle_area) );
1081 /* Process all subpictures and regions (in the right order) */
1082 for( unsigned int i_index = 0; i_index < i_subpicture; i_index++ )
1084 subpicture_t *p_subpic = pp_subpicture[i_index];
1085 subpicture_region_t *p_region;
1087 if( !p_subpic->p_region )
1088 continue;
1090 if( p_subpic->i_original_picture_width <= 0 ||
1091 p_subpic->i_original_picture_height <= 0 )
1093 if( p_subpic->i_original_picture_width > 0 ||
1094 p_subpic->i_original_picture_height > 0 )
1095 msg_Err( p_spu, "original picture size %dx%d is unsupported",
1096 p_subpic->i_original_picture_width,
1097 p_subpic->i_original_picture_height );
1098 else
1099 msg_Warn( p_spu, "original picture size is undefined" );
1101 p_subpic->i_original_picture_width = p_fmt_src->i_width;
1102 p_subpic->i_original_picture_height = p_fmt_src->i_height;
1105 if( p_sys->p_text )
1107 /* FIXME aspect ratio ? */
1108 p_sys->p_text->fmt_out.video.i_width =
1109 p_sys->p_text->fmt_out.video.i_visible_width = p_subpic->i_original_picture_width;
1111 p_sys->p_text->fmt_out.video.i_height =
1112 p_sys->p_text->fmt_out.video.i_visible_height = p_subpic->i_original_picture_height;
1114 var_SetInteger( p_sys->p_text, "scale", SCALE_UNIT );
1117 /* Render all regions
1118 * We always transform non absolute subtitle into absolute one on the
1119 * first rendering to allow good subtitle overlap support.
1121 for( p_region = p_subpic->p_region; p_region != NULL; p_region = p_region->p_next )
1123 spu_area_t area;
1125 /* Compute region scale AR */
1126 video_format_t region_fmt =p_region->fmt;
1127 if( region_fmt.i_sar_num <= 0 || region_fmt.i_sar_den <= 0 )
1129 region_fmt.i_sar_num = p_fmt_src->i_sar_num;
1130 region_fmt.i_sar_den = p_fmt_src->i_sar_den;
1133 /* Compute scaling from original size to destination size
1134 * FIXME The current scaling ensure that the heights match, the width being
1135 * cropped.
1137 spu_scale_t scale= spu_scale_createq( (int64_t)p_fmt_dst->i_height * p_fmt_dst->i_sar_den * region_fmt.i_sar_num,
1138 (int64_t)p_subpic->i_original_picture_height * p_fmt_dst->i_sar_num * region_fmt.i_sar_den,
1139 p_fmt_dst->i_height,
1140 p_subpic->i_original_picture_height );
1142 /* Check scale validity */
1143 if( scale.w <= 0 || scale.h <= 0 )
1144 continue;
1146 /* */
1147 SpuRenderRegion( p_spu, pp_output_last, &area,
1148 p_subpic, p_region, scale,
1149 p_chroma_list, p_fmt_dst,
1150 p_subtitle_area, i_subtitle_area,
1151 p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
1152 if( *pp_output_last )
1153 pp_output_last = &(*pp_output_last)->p_next;
1155 if( p_subpic->b_subtitle )
1157 area = spu_area_unscaled( area, scale );
1158 if( !p_subpic->b_absolute && area.i_width > 0 && area.i_height > 0 )
1160 p_region->i_x = area.i_x;
1161 p_region->i_y = area.i_y;
1163 if( p_subtitle_area )
1164 p_subtitle_area[i_subtitle_area++] = area;
1167 if( p_subpic->b_subtitle && p_subpic->p_region )
1168 p_subpic->b_absolute = true;
1171 /* */
1172 if( p_subtitle_area != p_subtitle_area_buffer )
1173 free( p_subtitle_area );
1175 return p_output;
1178 /*****************************************************************************
1179 * Object variables callbacks
1180 *****************************************************************************/
1182 /*****************************************************************************
1183 * UpdateSPU: update subpicture settings
1184 *****************************************************************************
1185 * This function is called from CropCallback and at initialization time, to
1186 * retrieve crop information from the input.
1187 *****************************************************************************/
1188 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1190 spu_private_t *p_sys = p_spu->p;
1191 vlc_value_t val;
1193 vlc_mutex_lock( &p_sys->lock );
1195 p_sys->b_force_palette = false;
1196 p_sys->b_force_crop = false;
1198 if( var_Get( p_object, "highlight", &val ) || !val.b_bool )
1200 vlc_mutex_unlock( &p_sys->lock );
1201 return;
1204 p_sys->b_force_crop = true;
1205 p_sys->i_crop_x = var_GetInteger( p_object, "x-start" );
1206 p_sys->i_crop_y = var_GetInteger( p_object, "y-start" );
1207 p_sys->i_crop_width = var_GetInteger( p_object, "x-end" ) - p_sys->i_crop_x;
1208 p_sys->i_crop_height = var_GetInteger( p_object, "y-end" ) - p_sys->i_crop_y;
1210 if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1212 memcpy( p_sys->palette, val.p_address, 16 );
1213 p_sys->b_force_palette = true;
1215 vlc_mutex_unlock( &p_sys->lock );
1217 msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1218 p_sys->i_crop_x, p_sys->i_crop_y,
1219 p_sys->i_crop_width, p_sys->i_crop_height,
1220 p_sys->b_force_palette );
1223 /*****************************************************************************
1224 * CropCallback: called when the highlight properties are changed
1225 *****************************************************************************
1226 * This callback is called from the input thread when we need cropping
1227 *****************************************************************************/
1228 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1229 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1231 VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1233 UpdateSPU( (spu_t *)p_data, p_object );
1234 return VLC_SUCCESS;
1237 /*****************************************************************************
1238 * Buffers allocation callbacks for the filters
1239 *****************************************************************************/
1241 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1243 filter_owner_sys_t *p_sys = p_filter->p_owner;
1245 subpicture_t *p_subpicture = subpicture_New( NULL );
1246 if( p_subpicture )
1247 p_subpicture->i_channel = p_sys->i_channel;
1248 return p_subpicture;
1250 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1252 VLC_UNUSED( p_filter );
1253 subpicture_Delete( p_subpic );
1256 static int SubSourceAllocationInit( filter_t *p_filter, void *p_data )
1258 spu_t *p_spu = p_data;
1260 filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1261 if( !p_sys )
1262 return VLC_EGENERIC;
1264 p_filter->pf_sub_buffer_new = sub_new_buffer;
1265 p_filter->pf_sub_buffer_del = sub_del_buffer;
1267 p_filter->p_owner = p_sys;
1268 p_sys->i_channel = spu_RegisterChannel( p_spu );
1269 p_sys->p_spu = p_spu;
1271 return VLC_SUCCESS;
1274 static void SubSourceAllocationClean( filter_t *p_filter )
1276 filter_owner_sys_t *p_sys = p_filter->p_owner;
1278 spu_ClearChannel( p_sys->p_spu, p_sys->i_channel );
1279 free( p_filter->p_owner );
1282 /*****************************************************************************
1283 * Public API
1284 *****************************************************************************/
1286 #undef spu_Create
1288 * Creates the subpicture unit
1290 * \param p_this the parent object which creates the subpicture unit
1292 spu_t *spu_Create( vlc_object_t *p_this )
1294 spu_t *p_spu;
1295 spu_private_t *p_sys;
1297 p_spu = vlc_custom_create( p_this, sizeof(spu_t) + sizeof(spu_private_t),
1298 VLC_OBJECT_GENERIC, "subpicture" );
1299 if( !p_spu )
1300 return NULL;
1301 vlc_object_attach( p_spu, p_this );
1303 /* Initialize spu fields */
1304 p_spu->p = p_sys = (spu_private_t*)&p_spu[1];
1306 /* Initialize private fields */
1307 vlc_mutex_init( &p_sys->lock );
1309 SpuHeapInit( &p_sys->heap );
1311 p_sys->p_text = NULL;
1312 p_sys->p_scale = NULL;
1313 p_sys->p_scale_yuvp = NULL;
1315 p_sys->i_margin = var_InheritInteger( p_spu, "sub-margin" );
1317 /* Register the default subpicture channel */
1318 p_sys->i_channel = SPU_DEFAULT_CHANNEL + 1;
1320 p_sys->psz_chain_update = NULL;
1321 vlc_mutex_init( &p_sys->chain_lock );
1322 p_sys->p_chain = filter_chain_New( p_spu, "sub source", false,
1323 SubSourceAllocationInit,
1324 SubSourceAllocationClean,
1325 p_spu );
1327 /* Load text and scale module */
1328 p_sys->p_text = SpuRenderCreateAndLoadText( p_spu );
1330 /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
1331 * yuva/rgba */
1332 p_sys->p_scale = SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu),
1333 VLC_CODEC_YUVA, VLC_CODEC_RGBA, true );
1335 /* This one is used for YUVP to YUVA/RGBA without scaling
1336 * FIXME rename it */
1337 p_sys->p_scale_yuvp = SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu),
1338 VLC_CODEC_YUVP, VLC_CODEC_YUVA, false );
1340 /* */
1341 p_sys->i_last_sort_date = -1;
1343 return p_spu;
1347 * Destroy the subpicture unit
1349 * \param p_this the parent object which destroys the subpicture unit
1351 void spu_Destroy( spu_t *p_spu )
1353 spu_private_t *p_sys = p_spu->p;
1355 if( p_sys->p_text )
1356 FilterRelease( p_sys->p_text );
1358 if( p_sys->p_scale_yuvp )
1359 FilterRelease( p_sys->p_scale_yuvp );
1361 if( p_sys->p_scale )
1362 FilterRelease( p_sys->p_scale );
1364 filter_chain_Delete( p_sys->p_chain );
1365 vlc_mutex_destroy( &p_sys->chain_lock );
1366 free( p_sys->psz_chain_update );
1368 /* Destroy all remaining subpictures */
1369 SpuHeapClean( &p_sys->heap );
1371 vlc_mutex_destroy( &p_sys->lock );
1373 vlc_object_release( p_spu );
1377 * Attach/Detach the SPU from any input
1379 * \param p_this the object in which to destroy the subpicture unit
1380 * \param b_attach to select attach or detach
1382 void spu_Attach( spu_t *p_spu, vlc_object_t *p_input, bool b_attach )
1384 if( b_attach )
1386 UpdateSPU( p_spu, p_input );
1387 var_Create( p_input, "highlight", VLC_VAR_BOOL );
1388 var_AddCallback( p_input, "highlight", CropCallback, p_spu );
1390 vlc_mutex_lock( &p_spu->p->lock );
1391 p_spu->p->p_input = p_input;
1393 if( p_spu->p->p_text )
1394 FilterRelease( p_spu->p->p_text );
1395 p_spu->p->p_text = SpuRenderCreateAndLoadText( p_spu );
1397 vlc_mutex_unlock( &p_spu->p->lock );
1399 else
1401 vlc_mutex_lock( &p_spu->p->lock );
1402 p_spu->p->p_input = NULL;
1403 vlc_mutex_unlock( &p_spu->p->lock );
1405 /* Delete callbacks */
1406 var_DelCallback( p_input, "highlight", CropCallback, p_spu );
1407 var_Destroy( p_input, "highlight" );
1412 * Inform the SPU filters of mouse event
1414 int spu_ProcessMouse( spu_t *p_spu,
1415 const vlc_mouse_t *p_mouse,
1416 const video_format_t *p_fmt )
1418 spu_private_t *p_sys = p_spu->p;
1420 vlc_mutex_lock( &p_sys->chain_lock );
1421 filter_chain_MouseEvent( p_sys->p_chain, p_mouse, p_fmt );
1422 vlc_mutex_unlock( &p_sys->chain_lock );
1424 return VLC_SUCCESS;
1428 * Display a subpicture
1430 * Remove the reservation flag of a subpicture, which will cause it to be
1431 * ready for display.
1432 * \param p_spu the subpicture unit object
1433 * \param p_subpic the subpicture to display
1435 void spu_PutSubpicture( spu_t *p_spu, subpicture_t *p_subpic )
1437 spu_private_t *p_sys = p_spu->p;
1439 /* SPU_DEFAULT_CHANNEL always reset itself */
1440 if( p_subpic->i_channel == SPU_DEFAULT_CHANNEL )
1441 spu_ClearChannel( p_spu, SPU_DEFAULT_CHANNEL );
1443 /* p_private is for spu only and cannot be non NULL here */
1444 for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
1445 assert( r->p_private == NULL );
1447 /* */
1448 vlc_mutex_lock( &p_sys->lock );
1449 if( SpuHeapPush( &p_sys->heap, p_subpic ) )
1451 vlc_mutex_unlock( &p_sys->lock );
1452 msg_Err( p_spu, "subpicture heap full" );
1453 subpicture_Delete( p_subpic );
1454 return;
1456 vlc_mutex_unlock( &p_sys->lock );
1459 subpicture_t *spu_Render( spu_t *p_spu,
1460 const vlc_fourcc_t *p_chroma_list,
1461 const video_format_t *p_fmt_dst,
1462 const video_format_t *p_fmt_src,
1463 mtime_t render_subtitle_date,
1464 mtime_t render_osd_date,
1465 bool b_subtitle_only )
1467 spu_private_t *p_sys = p_spu->p;
1469 /* Update sub-source chain */
1470 vlc_mutex_lock( &p_sys->lock );
1471 char *psz_chain_update = p_sys->psz_chain_update;
1472 p_sys->psz_chain_update = NULL;
1473 vlc_mutex_unlock( &p_sys->lock );
1475 vlc_mutex_lock( &p_sys->chain_lock );
1476 if( psz_chain_update )
1478 filter_chain_Reset( p_sys->p_chain, NULL, NULL );
1480 filter_chain_AppendFromString( p_spu->p->p_chain, psz_chain_update );
1482 free( psz_chain_update );
1484 /* Run subpicture sources */
1485 filter_chain_SubSource( p_sys->p_chain, render_osd_date );
1486 vlc_mutex_unlock( &p_sys->chain_lock );
1488 static const vlc_fourcc_t p_chroma_list_default_yuv[] = {
1489 VLC_CODEC_YUVA,
1490 VLC_CODEC_RGBA,
1491 VLC_CODEC_YUVP,
1494 static const vlc_fourcc_t p_chroma_list_default_rgb[] = {
1495 VLC_CODEC_RGBA,
1496 VLC_CODEC_YUVA,
1497 VLC_CODEC_YUVP,
1501 if( !p_chroma_list || *p_chroma_list == 0 )
1502 p_chroma_list = vlc_fourcc_IsYUV(p_fmt_dst->i_chroma) ? p_chroma_list_default_yuv
1503 : p_chroma_list_default_rgb;
1505 vlc_mutex_lock( &p_sys->lock );
1507 unsigned int i_subpicture;
1508 subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
1510 /* Get an array of subpictures to render */
1511 SpuSelectSubpictures( p_spu, &i_subpicture, pp_subpicture,
1512 render_subtitle_date, render_osd_date, b_subtitle_only );
1513 if( i_subpicture <= 0 )
1515 vlc_mutex_unlock( &p_sys->lock );
1516 return NULL;
1519 /* Updates the subpictures */
1520 for( unsigned i = 0; i < i_subpicture; i++ )
1522 subpicture_t *p_subpic = pp_subpicture[i];
1523 subpicture_Update( p_subpic,
1524 p_fmt_src, p_fmt_dst,
1525 p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
1528 /* Now order the subpicture array
1529 * XXX The order is *really* important for overlap subtitles positionning */
1530 qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
1532 /* Render the subpictures */
1533 subpicture_t *p_render = SpuRenderSubpictures( p_spu,
1534 i_subpicture, pp_subpicture,
1535 p_chroma_list,
1536 p_fmt_dst,
1537 p_fmt_src,
1538 render_subtitle_date,
1539 render_osd_date );
1540 vlc_mutex_unlock( &p_sys->lock );
1542 return p_render;
1545 void spu_OffsetSubtitleDate( spu_t *p_spu, mtime_t i_duration )
1547 spu_private_t *p_sys = p_spu->p;
1549 vlc_mutex_lock( &p_sys->lock );
1550 for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
1552 spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i];
1553 subpicture_t *p_current = p_entry->p_subpicture;
1555 if( p_current && p_current->b_subtitle )
1557 if( p_current->i_start > 0 )
1558 p_current->i_start += i_duration;
1559 if( p_current->i_stop > 0 )
1560 p_current->i_stop += i_duration;
1563 vlc_mutex_unlock( &p_sys->lock );
1566 int spu_RegisterChannel( spu_t *p_spu )
1568 spu_private_t *p_sys = p_spu->p;
1570 vlc_mutex_lock( &p_sys->lock );
1571 int i_channel = p_sys->i_channel++;
1572 vlc_mutex_unlock( &p_sys->lock );
1574 return i_channel;
1577 void spu_ClearChannel( spu_t *p_spu, int i_channel )
1579 spu_private_t *p_sys = p_spu->p;
1581 vlc_mutex_lock( &p_sys->lock );
1583 for( int i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1585 spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_subpic];
1586 subpicture_t *p_subpic = p_entry->p_subpicture;
1588 if( !p_subpic )
1589 continue;
1590 if( p_subpic->i_channel != i_channel && ( i_channel != -1 || p_subpic->i_channel == SPU_DEFAULT_CHANNEL ) )
1591 continue;
1593 /* You cannot delete subpicture outside of spu_SortSubpictures */
1594 p_entry->b_reject = true;
1597 vlc_mutex_unlock( &p_sys->lock );
1600 void spu_ChangeSources( spu_t *p_spu, const char *psz_filters )
1602 spu_private_t *p_sys = p_spu->p;
1604 vlc_mutex_lock( &p_sys->lock );
1606 free( p_sys->psz_chain_update );
1607 p_sys->psz_chain_update = strdup( psz_filters );
1609 vlc_mutex_unlock( &p_sys->lock );
1612 void spu_ChangeMargin( spu_t *p_spu, int i_margin )
1614 spu_private_t *p_sys = p_spu->p;
1616 vlc_mutex_lock( &p_sys->lock );
1617 p_sys->i_margin = i_margin;
1618 vlc_mutex_unlock( &p_sys->lock );