1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2007 VLC authors and VideoLAN
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 it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_modules.h>
38 #include <vlc_input.h>
40 #include <vlc_filter.h>
43 #include "../libvlc.h"
44 #include "vout_internal.h"
45 #include "../misc/subpicture.h"
47 /*****************************************************************************
49 *****************************************************************************/
51 /* Number of simultaneous subpictures */
52 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
56 subpicture_t
*subpicture
;
61 spu_heap_entry_t entry
[VOUT_MAX_SUBPICTURES
];
64 struct spu_private_t
{
65 vlc_mutex_t lock
; /* lock to protect all followings fields */
70 int channel
; /**< number of subpicture channels registered */
71 filter_t
*text
; /**< text renderer module */
72 filter_t
*scale_yuvp
; /**< scaling module for YUVP */
73 filter_t
*scale
; /**< scaling module (all but YUVP) */
74 bool force_crop
; /**< force cropping of subpicture */
80 } crop
; /**< cropping */
82 int margin
; /**< force position of a subpicture */
83 bool force_palette
; /**< force palette of subpicture */
84 uint8_t palette
[4][4]; /**< forced palette */
86 /* Subpiture filters */
87 char *source_chain_current
;
88 char *source_chain_update
;
89 vlc_mutex_t source_chain_lock
;
90 filter_chain_t
*source_chain
;
91 char *filter_chain_current
;
92 char *filter_chain_update
;
93 vlc_mutex_t filter_chain_lock
;
94 filter_chain_t
*filter_chain
;
97 mtime_t last_sort_date
;
101 /*****************************************************************************
103 *****************************************************************************/
104 static void SpuHeapInit(spu_heap_t
*heap
)
106 for (int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++) {
107 spu_heap_entry_t
*e
= &heap
->entry
[i
];
109 e
->subpicture
= NULL
;
114 static int SpuHeapPush(spu_heap_t
*heap
, subpicture_t
*subpic
)
116 for (int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++) {
117 spu_heap_entry_t
*e
= &heap
->entry
[i
];
122 e
->subpicture
= subpic
;
129 static void SpuHeapDeleteAt(spu_heap_t
*heap
, int index
)
131 spu_heap_entry_t
*e
= &heap
->entry
[index
];
134 subpicture_Delete(e
->subpicture
);
136 e
->subpicture
= NULL
;
139 static int SpuHeapDeleteSubpicture(spu_heap_t
*heap
, subpicture_t
*subpic
)
141 for (int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++) {
142 spu_heap_entry_t
*e
= &heap
->entry
[i
];
144 if (e
->subpicture
!= subpic
)
147 SpuHeapDeleteAt(heap
, i
);
153 static void SpuHeapClean(spu_heap_t
*heap
)
155 for (int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++) {
156 spu_heap_entry_t
*e
= &heap
->entry
[i
];
158 subpicture_Delete(e
->subpicture
);
162 static void FilterRelease(filter_t
*filter
)
164 if (filter
->p_module
)
165 module_unneed(filter
, filter
->p_module
);
166 vlc_object_release(filter
);
169 static picture_t
*spu_new_video_buffer(filter_t
*filter
)
171 const video_format_t
*fmt
= &filter
->fmt_out
.video
;
173 return picture_NewFromFormat(fmt
);
176 static int spu_get_attachments(filter_t
*filter
,
177 input_attachment_t
***attachment_ptr
,
178 int *attachment_count
)
180 spu_t
*spu
= filter
->owner
.sys
;
182 int ret
= VLC_EGENERIC
;
184 ret
= input_Control((input_thread_t
*)spu
->p
->input
,
185 INPUT_GET_ATTACHMENTS
,
186 attachment_ptr
, attachment_count
);
190 static filter_t
*SpuRenderCreateAndLoadText(spu_t
*spu
)
192 filter_t
*text
= vlc_custom_create(spu
, sizeof(*text
), "spu text");
196 text
->owner
.sys
= spu
;
198 es_format_Init(&text
->fmt_in
, VIDEO_ES
, 0);
200 es_format_Init(&text
->fmt_out
, VIDEO_ES
, 0);
201 text
->fmt_out
.video
.i_width
=
202 text
->fmt_out
.video
.i_visible_width
= 32;
203 text
->fmt_out
.video
.i_height
=
204 text
->fmt_out
.video
.i_visible_height
= 32;
206 text
->pf_get_attachments
= spu_get_attachments
;
208 text
->p_module
= module_need(text
, "text renderer", "$text-renderer", false);
210 /* Create a few variables used for enhanced text rendering */
211 var_Create(text
, "spu-elapsed", VLC_VAR_INTEGER
);
212 var_Create(text
, "text-rerender", VLC_VAR_BOOL
);
217 static filter_t
*SpuRenderCreateAndLoadScale(vlc_object_t
*object
,
218 vlc_fourcc_t src_chroma
,
219 vlc_fourcc_t dst_chroma
,
222 filter_t
*scale
= vlc_custom_create(object
, sizeof(*scale
), "scale");
226 es_format_Init(&scale
->fmt_in
, VIDEO_ES
, 0);
227 scale
->fmt_in
.video
.i_chroma
= src_chroma
;
228 scale
->fmt_in
.video
.i_width
=
229 scale
->fmt_in
.video
.i_visible_width
=
230 scale
->fmt_in
.video
.i_height
=
231 scale
->fmt_in
.video
.i_visible_height
= 32;
233 es_format_Init(&scale
->fmt_out
, VIDEO_ES
, 0);
234 scale
->fmt_out
.video
.i_chroma
= dst_chroma
;
235 scale
->fmt_out
.video
.i_width
=
236 scale
->fmt_out
.video
.i_visible_width
=
237 scale
->fmt_out
.video
.i_height
=
238 scale
->fmt_out
.video
.i_visible_height
= require_resize
? 16 : 32;
240 scale
->owner
.video
.buffer_new
= spu_new_video_buffer
;
242 scale
->p_module
= module_need(scale
, "video converter", NULL
, false);
247 static void SpuRenderText(spu_t
*spu
, bool *rerender_text
,
248 subpicture_region_t
*region
,
249 const vlc_fourcc_t
*chroma_list
,
250 mtime_t elapsed_time
)
252 filter_t
*text
= spu
->p
->text
;
254 assert(region
->fmt
.i_chroma
== VLC_CODEC_TEXT
);
256 if (!text
|| !text
->p_module
)
259 /* Setup 3 variables which can be used to render
260 * time-dependent text (and effects). The first indicates
261 * the total amount of time the text will be on screen,
262 * the second the amount of time it has already been on
263 * screen (can be a negative value as text is laid out
264 * before it is rendered) and the third is a feedback
265 * variable from the renderer - if the renderer sets it
266 * then this particular text is time-dependent, eg. the
267 * visual progress bar inside the text in karaoke and the
268 * text needs to be rendered multiple times in order for
269 * the effect to work - we therefore need to return the
270 * region to its original state at the end of the loop,
271 * instead of leaving it in YUVA or YUVP.
272 * Any renderer which is unaware of how to render
273 * time-dependent text can happily ignore the variables
274 * and render the text the same as usual - it should at
275 * least show up on screen, but the effect won't change
276 * the text over time.
278 var_SetInteger(text
, "spu-elapsed", elapsed_time
);
279 var_SetBool(text
, "text-rerender", false);
281 if ( region
->p_text
)
282 text
->pf_render(text
, region
, region
, chroma_list
);
283 *rerender_text
= var_GetBool(text
, "text-rerender");
287 * A few scale functions helpers.
290 #define SCALE_UNIT (10000)
296 static spu_scale_t
spu_scale_create(unsigned w
, unsigned h
)
298 spu_scale_t s
= { .w
= w
, .h
= h
};
305 static spu_scale_t
spu_scale_unit(void)
307 return spu_scale_create(SCALE_UNIT
, SCALE_UNIT
);
309 static spu_scale_t
spu_scale_createq(uint64_t wn
, uint64_t wd
, uint64_t hn
, uint64_t hd
)
311 return spu_scale_create(wn
* SCALE_UNIT
/ wd
,
312 hn
* SCALE_UNIT
/ hd
);
314 static int spu_scale_w(unsigned v
, const spu_scale_t s
)
316 return v
* s
.w
/ SCALE_UNIT
;
318 static int spu_scale_h(unsigned v
, const spu_scale_t s
)
320 return v
* s
.h
/ SCALE_UNIT
;
322 static int spu_invscale_w(unsigned v
, const spu_scale_t s
)
324 return v
* SCALE_UNIT
/ s
.w
;
326 static int spu_invscale_h(unsigned v
, const spu_scale_t s
)
328 return v
* SCALE_UNIT
/ s
.h
;
332 * A few area functions helpers
343 static spu_area_t
spu_area_create(unsigned x
, unsigned y
, unsigned w
, unsigned h
, spu_scale_t s
)
345 spu_area_t a
= { .x
= x
, .y
= y
, .width
= w
, .height
= h
, .scale
= s
};
348 static spu_area_t
spu_area_scaled(spu_area_t a
)
350 if (a
.scale
.w
== SCALE_UNIT
&& a
.scale
.h
== SCALE_UNIT
)
353 a
.x
= spu_scale_w(a
.x
, a
.scale
);
354 a
.y
= spu_scale_h(a
.y
, a
.scale
);
355 a
.width
= spu_scale_w(a
.width
, a
.scale
);
356 a
.height
= spu_scale_h(a
.height
, a
.scale
);
358 a
.scale
= spu_scale_unit();
361 static spu_area_t
spu_area_unscaled(spu_area_t a
, spu_scale_t s
)
363 if (a
.scale
.w
== s
.w
&& a
.scale
.h
== s
.h
)
366 a
= spu_area_scaled(a
);
368 a
.x
= spu_invscale_w(a
.x
, s
);
369 a
.y
= spu_invscale_h(a
.y
, s
);
370 a
.width
= spu_invscale_w(a
.width
, s
);
371 a
.height
= spu_invscale_h(a
.height
, s
);
376 static bool spu_area_overlap(spu_area_t a
, spu_area_t b
)
378 a
= spu_area_scaled(a
);
379 b
= spu_area_scaled(b
);
381 return __MAX(a
.x
, b
.x
) < __MIN(a
.x
+ a
.width
, b
.x
+ b
.width
) &&
382 __MAX(a
.y
, b
.y
) < __MIN(a
.y
+ a
.height
, b
.y
+ b
.height
);
386 * Avoid area overlapping
388 static void SpuAreaFixOverlap(spu_area_t
*dst
,
389 const spu_area_t
*sub_array
, int sub_count
, int align
)
391 spu_area_t a
= spu_area_scaled(*dst
);
392 bool is_moved
= false;
396 * XXX It is not fast O(n^2) but we should not have a lot of region */
399 for (int i
= 0; i
< sub_count
; i
++) {
400 spu_area_t sub
= spu_area_scaled(sub_array
[i
]);
402 if (!spu_area_overlap(a
, sub
))
405 if (align
& SUBPICTURE_ALIGN_TOP
) {
407 int i_y
= sub
.y
+ sub
.height
;
410 } else if (align
& SUBPICTURE_ALIGN_BOTTOM
) {
412 int i_y
= sub
.y
- a
.height
;
416 /* TODO what to do in this case? */
417 //fprintf(stderr, "Overlap with unsupported alignment\n");
427 *dst
= spu_area_unscaled(a
, dst
->scale
);
431 static void SpuAreaFitInside(spu_area_t
*area
, const spu_area_t
*boundary
)
433 spu_area_t a
= spu_area_scaled(*area
);
435 if((a
.x
+ a
.width
) > boundary
->width
)
437 if(boundary
->width
> a
.width
)
438 a
.x
= boundary
->width
- a
.width
;
443 if((a
.y
+ a
.height
) > boundary
->height
)
445 if(boundary
->height
> a
.height
)
446 a
.y
= boundary
->height
- a
.height
;
451 *area
= spu_area_unscaled(a
, area
->scale
);
457 static void SpuRegionPlace(int *x
, int *y
,
458 const subpicture_t
*subpic
,
459 const subpicture_region_t
*region
)
461 assert(region
->i_x
!= INT_MAX
&& region
->i_y
!= INT_MAX
);
462 if (subpic
->b_absolute
) {
466 if (region
->i_align
& SUBPICTURE_ALIGN_TOP
)
468 else if (region
->i_align
& SUBPICTURE_ALIGN_BOTTOM
)
469 *y
= subpic
->i_original_picture_height
- region
->fmt
.i_visible_height
- region
->i_y
;
471 *y
= subpic
->i_original_picture_height
/ 2 - region
->fmt
.i_visible_height
/ 2;
473 if (region
->i_align
& SUBPICTURE_ALIGN_LEFT
)
475 else if (region
->i_align
& SUBPICTURE_ALIGN_RIGHT
)
476 *x
= subpic
->i_original_picture_width
- region
->fmt
.i_visible_width
- region
->i_x
;
478 *x
= subpic
->i_original_picture_width
/ 2 - region
->fmt
.i_visible_width
/ 2;
483 * This function compares two 64 bits integers.
484 * It can be used by qsort.
486 static int IntegerCmp(int64_t i0
, int64_t i1
)
488 return i0
< i1
? -1 : i0
> i1
? 1 : 0;
491 * This function compares 2 subpictures using the following properties
492 * (ordered by priority)
493 * 1. absolute positionning
495 * 3. creation order (per channel)
497 * It can be used by qsort.
499 * XXX spu_RenderSubpictures depends heavily on this order.
501 static int SubpictureCmp(const void *s0
, const void *s1
)
503 subpicture_t
*subpic0
= *(subpicture_t
**)s0
;
504 subpicture_t
*subpic1
= *(subpicture_t
**)s1
;
507 r
= IntegerCmp(!subpic0
->b_absolute
, !subpic1
->b_absolute
);
509 r
= IntegerCmp(subpic0
->i_start
, subpic1
->i_start
);
511 r
= IntegerCmp(subpic0
->i_channel
, subpic1
->i_channel
);
513 r
= IntegerCmp(subpic0
->i_order
, subpic1
->i_order
);
517 /*****************************************************************************
518 * SpuSelectSubpictures: find the subpictures to display
519 *****************************************************************************
520 * This function parses all subpictures and decides which ones need to be
521 * displayed. If no picture has been selected, display_date will depend on
523 * We also check for ephemer DVD subpictures (subpictures that have
524 * to be removed if a newer one is available), which makes it a lot
525 * more difficult to guess if a subpicture has to be rendered or not.
526 *****************************************************************************/
527 static void SpuSelectSubpictures(spu_t
*spu
,
528 unsigned int *subpicture_count
,
529 subpicture_t
**subpicture_array
,
530 mtime_t render_subtitle_date
,
531 mtime_t render_osd_date
,
534 spu_private_t
*sys
= spu
->p
;
537 *subpicture_count
= 0;
539 /* Create a list of channels */
540 int channel
[VOUT_MAX_SUBPICTURES
];
541 int channel_count
= 0;
543 for (int index
= 0; index
< VOUT_MAX_SUBPICTURES
; index
++) {
544 spu_heap_entry_t
*entry
= &sys
->heap
.entry
[index
];
545 if (!entry
->subpicture
|| entry
->reject
)
547 const int i_channel
= entry
->subpicture
->i_channel
;
549 for (i
= 0; i
< channel_count
; i
++) {
550 if (channel
[i
] == i_channel
)
553 if (channel_count
<= i
)
554 channel
[channel_count
++] = i_channel
;
557 /* Fill up the subpicture_array arrays with relevant pictures */
558 for (int i
= 0; i
< channel_count
; i
++) {
559 subpicture_t
*available_subpic
[VOUT_MAX_SUBPICTURES
];
560 bool is_available_late
[VOUT_MAX_SUBPICTURES
];
561 int available_count
= 0;
563 mtime_t start_date
= render_subtitle_date
;
564 mtime_t ephemer_subtitle_date
= 0;
565 mtime_t ephemer_osd_date
= 0;
566 int64_t ephemer_subtitle_order
= INT64_MIN
;
567 int64_t ephemer_system_order
= INT64_MIN
;
569 /* Select available pictures */
570 for (int index
= 0; index
< VOUT_MAX_SUBPICTURES
; index
++) {
571 spu_heap_entry_t
*entry
= &sys
->heap
.entry
[index
];
572 subpicture_t
*current
= entry
->subpicture
;
576 if (!current
|| entry
->reject
) {
578 SpuHeapDeleteAt(&sys
->heap
, index
);
582 if (current
->i_channel
!= channel
[i
] ||
583 (ignore_osd
&& !current
->b_subtitle
))
586 const mtime_t render_date
= current
->b_subtitle
? render_subtitle_date
: render_osd_date
;
588 render_date
< current
->i_start
) {
589 /* Too early, come back next monday */
593 mtime_t
*ephemer_date_ptr
= current
->b_subtitle
? &ephemer_subtitle_date
: &ephemer_osd_date
;
594 int64_t *ephemer_order_ptr
= current
->b_subtitle
? &ephemer_subtitle_order
: &ephemer_system_order
;
595 if (current
->i_start
>= *ephemer_date_ptr
) {
596 *ephemer_date_ptr
= current
->i_start
;
597 if (current
->i_order
> *ephemer_order_ptr
)
598 *ephemer_order_ptr
= current
->i_order
;
601 is_stop_valid
= !current
->b_ephemer
|| current
->i_stop
> current
->i_start
;
603 is_late
= is_stop_valid
&& current
->i_stop
<= render_date
;
605 /* start_date will be used for correct automatic overlap support
606 * in case picture that should not be displayed anymore (display_time)
607 * overlap with a picture to be displayed (current->i_start) */
608 if (current
->b_subtitle
&& !is_late
&& !current
->b_ephemer
)
609 start_date
= current
->i_start
;
612 available_subpic
[available_count
] = current
;
613 is_available_late
[available_count
] = is_late
;
617 /* Only forced old picture display at the transition */
618 if (start_date
< sys
->last_sort_date
)
619 start_date
= sys
->last_sort_date
;
621 start_date
= INT64_MAX
;
623 /* Select pictures to be displayed */
624 for (int index
= 0; index
< available_count
; index
++) {
625 subpicture_t
*current
= available_subpic
[index
];
626 bool is_late
= is_available_late
[index
];
628 const mtime_t stop_date
= current
->b_subtitle
? __MAX(start_date
, sys
->last_sort_date
) : render_osd_date
;
629 const mtime_t ephemer_date
= current
->b_subtitle
? ephemer_subtitle_date
: ephemer_osd_date
;
630 const int64_t ephemer_order
= current
->b_subtitle
? ephemer_subtitle_order
: ephemer_system_order
;
632 /* Destroy late and obsolete ephemer subpictures */
633 bool is_rejeted
= is_late
&& current
->i_stop
<= stop_date
;
634 if (current
->b_ephemer
) {
635 if (current
->i_start
< ephemer_date
)
637 else if (current
->i_start
== ephemer_date
&&
638 current
->i_order
< ephemer_order
)
643 SpuHeapDeleteSubpicture(&sys
->heap
, current
);
645 subpicture_array
[(*subpicture_count
)++] = current
;
649 sys
->last_sort_date
= render_subtitle_date
;
655 * It will transform the provided region into another region suitable for rendering.
657 static void SpuRenderRegion(spu_t
*spu
,
658 subpicture_region_t
**dst_ptr
, spu_area_t
*dst_area
,
659 subpicture_t
*subpic
, subpicture_region_t
*region
,
660 const spu_scale_t scale_size
,
661 const vlc_fourcc_t
*chroma_list
,
662 const video_format_t
*fmt
,
663 const spu_area_t
*subtitle_area
, int subtitle_area_count
,
666 spu_private_t
*sys
= spu
->p
;
668 video_format_t fmt_original
= region
->fmt
;
669 bool restore_text
= false;
673 video_format_t region_fmt
;
674 picture_t
*region_picture
;
676 /* Invalidate area by default */
677 *dst_area
= spu_area_create(0,0, 0,0, scale_size
);
680 /* Render text region */
681 if (region
->fmt
.i_chroma
== VLC_CODEC_TEXT
) {
682 SpuRenderText(spu
, &restore_text
, region
,
684 render_date
- subpic
->i_start
);
686 /* Check if the rendering has failed ... */
687 if (region
->fmt
.i_chroma
== VLC_CODEC_TEXT
)
691 /* Force palette if requested
692 * FIXME b_force_palette and force_crop are applied to all subpictures using palette
693 * instead of only the right one (being the dvd spu).
695 const bool using_palette
= region
->fmt
.i_chroma
== VLC_CODEC_YUVP
;
696 const bool force_palette
= using_palette
&& sys
->force_palette
;
697 const bool crop_requested
= (force_palette
&& sys
->force_crop
) ||
698 region
->i_max_width
|| region
->i_max_height
;
699 bool changed_palette
= false;
701 /* Compute the margin which is expressed in destination pixel unit
702 * The margin is applied only to subtitle and when no forced crop is
703 * requested (dvd menu) */
705 if (!crop_requested
&& subpic
->b_subtitle
)
706 y_margin
= spu_invscale_h(sys
->margin
, scale_size
);
709 * We compute the position in the rendered size */
710 SpuRegionPlace(&x_offset
, &y_offset
,
713 /* Save this position for subtitle overlap support
714 * it is really important that there are given without scale_size applied */
715 *dst_area
= spu_area_create(x_offset
, y_offset
,
716 region
->fmt
.i_visible_width
,
717 region
->fmt
.i_visible_height
,
720 /* Handle overlapping subtitles when possible */
721 if (subpic
->b_subtitle
&& !subpic
->b_absolute
)
722 SpuAreaFixOverlap(dst_area
, subtitle_area
, subtitle_area_count
,
725 /* we copy the area: for the subtitle overlap support we want
726 * to only save the area without margin applied */
727 spu_area_t restrained
= *dst_area
;
729 /* apply margin to subtitles and correct if they go over the picture edge */
730 if (subpic
->b_subtitle
)
731 restrained
.y
-= y_margin
;
733 spu_area_t display
= spu_area_create(0, 0, fmt
->i_visible_width
,
734 fmt
->i_visible_height
,
737 SpuAreaFitInside(&restrained
, &display
);
739 /* Fix the position for the current scale_size */
740 x_offset
= spu_scale_w(restrained
.x
, restrained
.scale
);
741 y_offset
= spu_scale_h(restrained
.y
, restrained
.scale
);
745 video_palette_t
*old_palette
= region
->fmt
.p_palette
;
746 video_palette_t new_palette
;
748 /* We suppose DVD palette here */
749 new_palette
.i_entries
= 4;
750 for (int i
= 0; i
< 4; i
++)
751 for (int j
= 0; j
< 4; j
++)
752 new_palette
.palette
[i
][j
] = sys
->palette
[i
][j
];
754 if (old_palette
->i_entries
== new_palette
.i_entries
) {
755 for (int i
= 0; i
< old_palette
->i_entries
; i
++)
756 for (int j
= 0; j
< 4; j
++)
757 changed_palette
|= old_palette
->palette
[i
][j
] != new_palette
.palette
[i
][j
];
759 changed_palette
= true;
761 *old_palette
= new_palette
;
765 region_fmt
= region
->fmt
;
766 region_picture
= region
->p_picture
;
768 bool convert_chroma
= true;
769 for (int i
= 0; chroma_list
[i
] && convert_chroma
; i
++) {
770 if (region_fmt
.i_chroma
== chroma_list
[i
])
771 convert_chroma
= false;
774 /* Scale from rendered size to destination size */
775 if (sys
->scale
&& sys
->scale
->p_module
&&
776 (!using_palette
|| (sys
->scale_yuvp
&& sys
->scale_yuvp
->p_module
)) &&
777 (scale_size
.w
!= SCALE_UNIT
|| scale_size
.h
!= SCALE_UNIT
||
778 using_palette
|| convert_chroma
)) {
779 const unsigned dst_width
= spu_scale_w(region
->fmt
.i_visible_width
, scale_size
);
780 const unsigned dst_height
= spu_scale_h(region
->fmt
.i_visible_height
, scale_size
);
782 /* Destroy the cache if unusable */
783 if (region
->p_private
) {
784 subpicture_region_private_t
*private = region
->p_private
;
785 bool is_changed
= false;
787 /* Check resize changes */
788 if (dst_width
!= private->fmt
.i_visible_width
||
789 dst_height
!= private->fmt
.i_visible_height
)
792 /* Check forced palette changes */
796 if (convert_chroma
&& private->fmt
.i_chroma
!= chroma_list
[0])
800 subpicture_region_private_Delete(private);
801 region
->p_private
= NULL
;
805 /* Scale if needed into cache */
806 if (!region
->p_private
&& dst_width
> 0 && dst_height
> 0) {
807 filter_t
*scale
= sys
->scale
;
809 picture_t
*picture
= region
->p_picture
;
810 picture_Hold(picture
);
812 /* Convert YUVP to YUVA/RGBA first for better scaling quality */
814 filter_t
*scale_yuvp
= sys
->scale_yuvp
;
816 scale_yuvp
->fmt_in
.video
= region
->fmt
;
818 scale_yuvp
->fmt_out
.video
= region
->fmt
;
819 scale_yuvp
->fmt_out
.video
.i_chroma
= chroma_list
[0];
821 picture
= scale_yuvp
->pf_video_filter(scale_yuvp
, picture
);
823 /* Well we will try conversion+scaling */
824 msg_Warn(spu
, "%4.4s to %4.4s conversion failed",
825 (const char*)&scale_yuvp
->fmt_in
.video
.i_chroma
,
826 (const char*)&scale_yuvp
->fmt_out
.video
.i_chroma
);
830 /* Conversion(except from YUVP)/Scaling */
832 (picture
->format
.i_visible_width
!= dst_width
||
833 picture
->format
.i_visible_height
!= dst_height
||
834 (convert_chroma
&& !using_palette
)))
836 scale
->fmt_in
.video
= picture
->format
;
837 scale
->fmt_out
.video
= picture
->format
;
839 scale
->fmt_in
.video
.i_chroma
= chroma_list
[0];
841 scale
->fmt_out
.i_codec
=
842 scale
->fmt_out
.video
.i_chroma
= chroma_list
[0];
844 scale
->fmt_out
.video
.i_width
= dst_width
;
845 scale
->fmt_out
.video
.i_height
= dst_height
;
847 scale
->fmt_out
.video
.i_visible_width
=
848 spu_scale_w(region
->fmt
.i_visible_width
, scale_size
);
849 scale
->fmt_out
.video
.i_visible_height
=
850 spu_scale_h(region
->fmt
.i_visible_height
, scale_size
);
852 picture
= scale
->pf_video_filter(scale
, picture
);
854 msg_Err(spu
, "scaling failed");
859 region
->p_private
= subpicture_region_private_New(&picture
->format
);
860 if (region
->p_private
) {
861 region
->p_private
->p_picture
= picture
;
862 if (!region
->p_private
->p_picture
) {
863 subpicture_region_private_Delete(region
->p_private
);
864 region
->p_private
= NULL
;
867 picture_Release(picture
);
872 /* And use the scaled picture */
873 if (region
->p_private
) {
874 region_fmt
= region
->p_private
->fmt
;
875 region_picture
= region
->p_private
->p_picture
;
879 /* Force cropping if requested */
880 if (crop_requested
) {
881 int crop_x
, crop_y
, crop_width
, crop_height
;
883 crop_x
= sys
->crop
.x
;
884 crop_y
= sys
->crop
.y
;
885 crop_width
= sys
->crop
.width
;
886 crop_height
= sys
->crop
.height
;
892 crop_width
= region_fmt
.i_visible_width
;
893 crop_height
= region_fmt
.i_visible_height
;
896 if(region
->i_max_width
&& region
->i_max_width
< crop_width
)
897 crop_width
= region
->i_max_width
;
899 if(region
->i_max_height
&& region
->i_max_height
< crop_height
)
900 crop_height
= region
->i_max_height
;
902 crop_x
= spu_scale_w(crop_x
, scale_size
);
903 crop_y
= spu_scale_h(crop_y
, scale_size
);
904 crop_width
= spu_scale_w(crop_width
, scale_size
);
905 crop_height
= spu_scale_h(crop_height
,scale_size
);
907 /* Find the intersection */
908 if (crop_x
+ crop_width
<= x_offset
||
909 x_offset
+ (int)region_fmt
.i_visible_width
< crop_x
||
910 crop_y
+ crop_height
<= y_offset
||
911 y_offset
+ (int)region_fmt
.i_visible_height
< crop_y
) {
912 /* No intersection */
913 region_fmt
.i_visible_width
=
914 region_fmt
.i_visible_height
= 0;
916 int x
, y
, x_end
, y_end
;
917 x
= __MAX(crop_x
, x_offset
);
918 y
= __MAX(crop_y
, y_offset
);
919 x_end
= __MIN(crop_x
+ crop_width
,
920 x_offset
+ (int)region_fmt
.i_visible_width
);
921 y_end
= __MIN(crop_y
+ crop_height
,
922 y_offset
+ (int)region_fmt
.i_visible_height
);
924 region_fmt
.i_x_offset
= x
- x_offset
;
925 region_fmt
.i_y_offset
= y
- y_offset
;
926 region_fmt
.i_visible_width
= x_end
- x
;
927 region_fmt
.i_visible_height
= y_end
- y
;
929 x_offset
= __MAX(x
, 0);
930 y_offset
= __MAX(y
, 0);
934 subpicture_region_t
*dst
= *dst_ptr
= subpicture_region_New(®ion_fmt
);
940 picture_Release(dst
->p_picture
);
941 dst
->p_picture
= picture_Hold(region_picture
);
942 int fade_alpha
= 255;
943 if (subpic
->b_fade
) {
944 mtime_t fade_start
= subpic
->i_start
+ 3 * (subpic
->i_stop
- subpic
->i_start
) / 4;
946 if (fade_start
<= render_date
&& fade_start
< subpic
->i_stop
)
947 fade_alpha
= 255 * (subpic
->i_stop
- render_date
) /
948 (subpic
->i_stop
- fade_start
);
950 dst
->i_alpha
= fade_alpha
* subpic
->i_alpha
* region
->i_alpha
/ 65025;
955 /* Some forms of subtitles need to be re-rendered more than
956 * once, eg. karaoke. We therefore restore the region to its
957 * pre-rendered state, so the next time through everything is
960 if (region
->p_picture
) {
961 picture_Release(region
->p_picture
);
962 region
->p_picture
= NULL
;
964 if (region
->p_private
) {
965 subpicture_region_private_Delete(region
->p_private
);
966 region
->p_private
= NULL
;
968 region
->fmt
= fmt_original
;
973 * This function renders all sub picture units in the list.
975 static subpicture_t
*SpuRenderSubpictures(spu_t
*spu
,
976 unsigned int i_subpicture
,
977 subpicture_t
**pp_subpicture
,
978 const vlc_fourcc_t
*chroma_list
,
979 const video_format_t
*fmt_dst
,
980 const video_format_t
*fmt_src
,
981 mtime_t render_subtitle_date
,
982 mtime_t render_osd_date
)
984 spu_private_t
*sys
= spu
->p
;
986 /* Count the number of regions and subtitle regions */
987 unsigned int subtitle_region_count
= 0;
988 unsigned int region_count
= 0;
989 for (unsigned i
= 0; i
< i_subpicture
; i
++) {
990 const subpicture_t
*subpic
= pp_subpicture
[i
];
993 for (subpicture_region_t
*r
= subpic
->p_region
; r
!= NULL
; r
= r
->p_next
)
996 if (subpic
->b_subtitle
)
997 subtitle_region_count
+= count
;
998 region_count
+= count
;
1000 if (region_count
<= 0)
1003 /* Create the output subpicture */
1004 subpicture_t
*output
= subpicture_New(NULL
);
1007 output
->i_order
= pp_subpicture
[i_subpicture
- 1]->i_order
;
1008 output
->i_original_picture_width
= fmt_dst
->i_visible_width
;
1009 output
->i_original_picture_height
= fmt_dst
->i_visible_height
;
1010 subpicture_region_t
**output_last_ptr
= &output
->p_region
;
1012 /* Allocate area array for subtitle overlap */
1013 spu_area_t subtitle_area_buffer
[VOUT_MAX_SUBPICTURES
];
1014 spu_area_t
*subtitle_area
;
1015 int subtitle_area_count
;
1017 subtitle_area_count
= 0;
1018 subtitle_area
= subtitle_area_buffer
;
1019 if (subtitle_region_count
> sizeof(subtitle_area_buffer
)/sizeof(*subtitle_area_buffer
))
1020 subtitle_area
= calloc(subtitle_region_count
, sizeof(*subtitle_area
));
1022 /* Process all subpictures and regions (in the right order) */
1023 for (unsigned int index
= 0; index
< i_subpicture
; index
++) {
1024 subpicture_t
*subpic
= pp_subpicture
[index
];
1025 subpicture_region_t
*region
;
1027 if (!subpic
->p_region
)
1030 if (subpic
->i_original_picture_width
<= 0 ||
1031 subpic
->i_original_picture_height
<= 0) {
1032 if (subpic
->i_original_picture_width
> 0 ||
1033 subpic
->i_original_picture_height
> 0)
1034 msg_Err(spu
, "original picture size %dx%d is unsupported",
1035 subpic
->i_original_picture_width
,
1036 subpic
->i_original_picture_height
);
1038 msg_Warn(spu
, "original picture size is undefined");
1040 subpic
->i_original_picture_width
= fmt_src
->i_visible_width
;
1041 subpic
->i_original_picture_height
= fmt_src
->i_visible_height
;
1045 /* FIXME aspect ratio ? */
1046 sys
->text
->fmt_out
.video
.i_width
=
1047 sys
->text
->fmt_out
.video
.i_visible_width
= subpic
->i_original_picture_width
;
1049 sys
->text
->fmt_out
.video
.i_height
=
1050 sys
->text
->fmt_out
.video
.i_visible_height
= subpic
->i_original_picture_height
;
1053 /* Render all regions
1054 * We always transform non absolute subtitle into absolute one on the
1055 * first rendering to allow good subtitle overlap support.
1057 for (region
= subpic
->p_region
; region
!= NULL
; region
= region
->p_next
) {
1060 /* Compute region scale AR */
1061 video_format_t region_fmt
= region
->fmt
;
1062 if (region_fmt
.i_sar_num
<= 0 || region_fmt
.i_sar_den
<= 0) {
1063 region_fmt
.i_sar_num
= (int64_t)fmt_dst
->i_visible_width
* fmt_dst
->i_sar_num
* subpic
->i_original_picture_height
;
1064 region_fmt
.i_sar_den
= (int64_t)fmt_dst
->i_visible_height
* fmt_dst
->i_sar_den
* subpic
->i_original_picture_width
;
1065 vlc_ureduce(®ion_fmt
.i_sar_num
, ®ion_fmt
.i_sar_den
,
1066 region_fmt
.i_sar_num
, region_fmt
.i_sar_den
, 65536);
1069 /* Compute scaling from original size to destination size
1070 * FIXME The current scaling ensure that the heights match, the width being
1073 spu_scale_t scale
= spu_scale_createq((uint64_t)fmt_dst
->i_visible_height
* fmt_dst
->i_sar_den
* region_fmt
.i_sar_num
,
1074 (uint64_t)subpic
->i_original_picture_height
* fmt_dst
->i_sar_num
* region_fmt
.i_sar_den
,
1075 fmt_dst
->i_visible_height
,
1076 subpic
->i_original_picture_height
);
1078 /* Check scale validity */
1079 if (scale
.w
<= 0 || scale
.h
<= 0)
1083 SpuRenderRegion(spu
, output_last_ptr
, &area
,
1084 subpic
, region
, scale
,
1085 chroma_list
, fmt_dst
,
1086 subtitle_area
, subtitle_area_count
,
1087 subpic
->b_subtitle
? render_subtitle_date
: render_osd_date
);
1088 if (*output_last_ptr
)
1089 output_last_ptr
= &(*output_last_ptr
)->p_next
;
1091 if (subpic
->b_subtitle
) {
1092 area
= spu_area_unscaled(area
, scale
);
1093 if (!subpic
->b_absolute
&& area
.width
> 0 && area
.height
> 0) {
1094 region
->i_x
= area
.x
;
1095 region
->i_y
= area
.y
;
1098 subtitle_area
[subtitle_area_count
++] = area
;
1101 if (subpic
->b_subtitle
&& subpic
->p_region
)
1102 subpic
->b_absolute
= true;
1106 if (subtitle_area
!= subtitle_area_buffer
)
1107 free(subtitle_area
);
1112 /*****************************************************************************
1113 * Object variables callbacks
1114 *****************************************************************************/
1116 /*****************************************************************************
1117 * UpdateSPU: update subpicture settings
1118 *****************************************************************************
1119 * This function is called from CropCallback and at initialization time, to
1120 * retrieve crop information from the input.
1121 *****************************************************************************/
1122 static void UpdateSPU(spu_t
*spu
, vlc_object_t
*object
)
1124 spu_private_t
*sys
= spu
->p
;
1127 vlc_mutex_lock(&sys
->lock
);
1129 sys
->force_palette
= false;
1130 sys
->force_crop
= false;
1132 if (var_Get(object
, "highlight", &val
) || !val
.b_bool
) {
1133 vlc_mutex_unlock(&sys
->lock
);
1137 sys
->force_crop
= true;
1138 sys
->crop
.x
= var_GetInteger(object
, "x-start");
1139 sys
->crop
.y
= var_GetInteger(object
, "y-start");
1140 sys
->crop
.width
= var_GetInteger(object
, "x-end") - sys
->crop
.x
;
1141 sys
->crop
.height
= var_GetInteger(object
, "y-end") - sys
->crop
.y
;
1143 if (var_Get(object
, "menu-palette", &val
) == VLC_SUCCESS
) {
1144 memcpy(sys
->palette
, val
.p_address
, 16);
1145 sys
->force_palette
= true;
1147 vlc_mutex_unlock(&sys
->lock
);
1149 msg_Dbg(object
, "crop: %i,%i,%i,%i, palette forced: %i",
1150 sys
->crop
.x
, sys
->crop
.y
,
1151 sys
->crop
.width
, sys
->crop
.height
,
1152 sys
->force_palette
);
1155 /*****************************************************************************
1156 * CropCallback: called when the highlight properties are changed
1157 *****************************************************************************
1158 * This callback is called from the input thread when we need cropping
1159 *****************************************************************************/
1160 static int CropCallback(vlc_object_t
*object
, char const *var
,
1161 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
1163 VLC_UNUSED(oldval
); VLC_UNUSED(newval
); VLC_UNUSED(var
);
1165 UpdateSPU((spu_t
*)data
, object
);
1169 /*****************************************************************************
1170 * Buffers allocation callbacks for the filters
1171 *****************************************************************************/
1173 static subpicture_t
*sub_new_buffer(filter_t
*filter
)
1175 int channel
= (intptr_t)filter
->owner
.sys
;
1177 subpicture_t
*subpicture
= subpicture_New(NULL
);
1179 subpicture
->i_channel
= channel
;
1183 static int SubSourceInit(filter_t
*filter
, void *data
)
1186 int channel
= spu_RegisterChannel(spu
);
1188 filter
->owner
.sys
= (void *)(intptr_t)channel
;
1189 filter
->owner
.sub
.buffer_new
= sub_new_buffer
;
1193 static int SubSourceClean(filter_t
*filter
, void *data
)
1196 int channel
= (intptr_t)filter
->owner
.sys
;
1198 spu_ClearChannel(spu
, channel
);
1202 /*****************************************************************************
1204 *****************************************************************************/
1206 static int RestartSubFilterCallback(vlc_object_t
*obj
, char const *psz_var
,
1207 vlc_value_t oldval
, vlc_value_t newval
,
1209 { VLC_UNUSED(obj
); VLC_UNUSED(psz_var
); VLC_UNUSED(oldval
); VLC_UNUSED(newval
);
1210 vout_ControlChangeSubFilters((vout_thread_t
*)p_data
, NULL
);
1214 static int SubFilterAddProxyCallbacks(filter_t
*filter
, void *opaque
)
1216 filter_AddProxyCallbacks((vlc_object_t
*)opaque
, filter
,
1217 RestartSubFilterCallback
);
1221 static int SubFilterDelProxyCallbacks(filter_t
*filter
, void *opaque
)
1223 filter_DelProxyCallbacks((vlc_object_t
*)opaque
, filter
,
1224 RestartSubFilterCallback
);
1228 static int RestartSubSourceCallback(vlc_object_t
*obj
, char const *psz_var
,
1229 vlc_value_t oldval
, vlc_value_t newval
,
1231 { VLC_UNUSED(obj
); VLC_UNUSED(psz_var
); VLC_UNUSED(oldval
); VLC_UNUSED(newval
);
1232 vout_ControlChangeSubSources((vout_thread_t
*)p_data
, NULL
);
1236 static int SubSourceAddProxyCallbacks(filter_t
*filter
, void *opaque
)
1238 filter_AddProxyCallbacks((vlc_object_t
*)opaque
, filter
,
1239 RestartSubSourceCallback
);
1243 static int SubSourceDelProxyCallbacks(filter_t
*filter
, void *opaque
)
1245 filter_DelProxyCallbacks((vlc_object_t
*)opaque
, filter
,
1246 RestartSubSourceCallback
);
1250 /*****************************************************************************
1252 *****************************************************************************/
1256 * Creates the subpicture unit
1258 * \param p_this the parent object which creates the subpicture unit
1260 spu_t
*spu_Create(vlc_object_t
*object
, vout_thread_t
*vout
)
1262 spu_t
*spu
= vlc_custom_create(object
,
1263 sizeof(spu_t
) + sizeof(spu_private_t
),
1268 /* Initialize spu fields */
1269 spu_private_t
*sys
= spu
->p
= (spu_private_t
*)&spu
[1];
1271 /* Initialize private fields */
1272 vlc_mutex_init(&sys
->lock
);
1274 SpuHeapInit(&sys
->heap
);
1278 sys
->scale_yuvp
= NULL
;
1280 sys
->margin
= var_InheritInteger(spu
, "sub-margin");
1282 /* Register the default subpicture channel */
1283 sys
->channel
= VOUT_SPU_CHANNEL_AVAIL_FIRST
;
1285 sys
->source_chain_update
= NULL
;
1286 sys
->filter_chain_update
= NULL
;
1287 vlc_mutex_init(&sys
->source_chain_lock
);
1288 vlc_mutex_init(&sys
->filter_chain_lock
);
1289 sys
->source_chain
= filter_chain_New(spu
, "sub source");
1290 sys
->filter_chain
= filter_chain_New(spu
, "sub filter");
1292 /* Load text and scale module */
1293 sys
->text
= SpuRenderCreateAndLoadText(spu
);
1295 /* XXX spu->p_scale is used for all conversion/scaling except yuvp to
1297 sys
->scale
= SpuRenderCreateAndLoadScale(VLC_OBJECT(spu
),
1298 VLC_CODEC_YUVA
, VLC_CODEC_RGBA
, true);
1300 /* This one is used for YUVP to YUVA/RGBA without scaling
1301 * FIXME rename it */
1302 sys
->scale_yuvp
= SpuRenderCreateAndLoadScale(VLC_OBJECT(spu
),
1303 VLC_CODEC_YUVP
, VLC_CODEC_YUVA
, false);
1306 sys
->last_sort_date
= -1;
1313 * Destroy the subpicture unit
1315 * \param p_this the parent object which destroys the subpicture unit
1317 void spu_Destroy(spu_t
*spu
)
1319 spu_private_t
*sys
= spu
->p
;
1322 FilterRelease(sys
->text
);
1324 if (sys
->scale_yuvp
)
1325 FilterRelease(sys
->scale_yuvp
);
1328 FilterRelease(sys
->scale
);
1330 filter_chain_ForEach(sys
->source_chain
, SubSourceClean
, spu
);
1332 filter_chain_ForEach(sys
->source_chain
,
1333 SubSourceDelProxyCallbacks
, sys
->vout
);
1334 filter_chain_Delete(sys
->source_chain
);
1335 free(sys
->source_chain_current
);
1337 filter_chain_ForEach(sys
->filter_chain
,
1338 SubFilterDelProxyCallbacks
, sys
->vout
);
1339 filter_chain_Delete(sys
->filter_chain
);
1340 free(sys
->filter_chain_current
);
1341 vlc_mutex_destroy(&sys
->source_chain_lock
);
1342 vlc_mutex_destroy(&sys
->filter_chain_lock
);
1343 free(sys
->source_chain_update
);
1344 free(sys
->filter_chain_update
);
1346 /* Destroy all remaining subpictures */
1347 SpuHeapClean(&sys
->heap
);
1349 vlc_mutex_destroy(&sys
->lock
);
1351 vlc_object_release(spu
);
1355 * Attach/Detach the SPU from any input
1357 * \param p_this the object in which to destroy the subpicture unit
1358 * \param b_attach to select attach or detach
1360 void spu_Attach(spu_t
*spu
, vlc_object_t
*input
, bool attach
)
1363 UpdateSPU(spu
, input
);
1364 var_Create(input
, "highlight", VLC_VAR_BOOL
);
1365 var_AddCallback(input
, "highlight", CropCallback
, spu
);
1367 vlc_mutex_lock(&spu
->p
->lock
);
1368 spu
->p
->input
= input
;
1371 FilterRelease(spu
->p
->text
);
1372 spu
->p
->text
= SpuRenderCreateAndLoadText(spu
);
1374 vlc_mutex_unlock(&spu
->p
->lock
);
1376 vlc_mutex_lock(&spu
->p
->lock
);
1377 spu
->p
->input
= NULL
;
1378 vlc_mutex_unlock(&spu
->p
->lock
);
1380 /* Delete callbacks */
1381 var_DelCallback(input
, "highlight", CropCallback
, spu
);
1382 var_Destroy(input
, "highlight");
1387 * Inform the SPU filters of mouse event
1389 int spu_ProcessMouse(spu_t
*spu
,
1390 const vlc_mouse_t
*mouse
,
1391 const video_format_t
*fmt
)
1393 spu_private_t
*sys
= spu
->p
;
1395 vlc_mutex_lock(&sys
->source_chain_lock
);
1396 filter_chain_MouseEvent(sys
->source_chain
, mouse
, fmt
);
1397 vlc_mutex_unlock(&sys
->source_chain_lock
);
1403 * Display a subpicture
1405 * Remove the reservation flag of a subpicture, which will cause it to be
1406 * ready for display.
1407 * \param spu the subpicture unit object
1408 * \param subpic the subpicture to display
1410 void spu_PutSubpicture(spu_t
*spu
, subpicture_t
*subpic
)
1412 spu_private_t
*sys
= spu
->p
;
1414 /* Update sub-filter chain */
1415 vlc_mutex_lock(&sys
->lock
);
1416 char *chain_update
= sys
->filter_chain_update
;
1417 sys
->filter_chain_update
= NULL
;
1418 vlc_mutex_unlock(&sys
->lock
);
1420 bool is_left_empty
= false;
1422 vlc_mutex_lock(&sys
->filter_chain_lock
);
1424 if (*chain_update
) {
1426 filter_chain_ForEach(sys
->filter_chain
,
1427 SubFilterDelProxyCallbacks
,
1429 filter_chain_Reset(sys
->filter_chain
, NULL
, NULL
);
1431 filter_chain_AppendFromString(spu
->p
->filter_chain
, chain_update
);
1433 filter_chain_ForEach(sys
->filter_chain
,
1434 SubFilterAddProxyCallbacks
,
1437 else if (filter_chain_GetLength(spu
->p
->filter_chain
) > 0)
1438 filter_chain_Reset(sys
->filter_chain
, NULL
, NULL
);
1440 /* "sub-source" was formerly "sub-filter", so now the "sub-filter"
1441 configuration may contain sub-filters or sub-sources configurations.
1442 if the filters chain was left empty it may indicate that it's a sub-source configuration */
1443 is_left_empty
= (filter_chain_GetLength(spu
->p
->filter_chain
) == 0);
1445 vlc_mutex_unlock(&sys
->filter_chain_lock
);
1447 if (is_left_empty
) {
1448 /* try to use the configuration as a sub-source configuration,
1449 but only if there is no 'source_chain_update' value and
1450 if only if 'chain_update' has a value */
1451 if (chain_update
&& *chain_update
) {
1452 vlc_mutex_lock(&sys
->lock
);
1453 if (!sys
->source_chain_update
|| !*sys
->source_chain_update
) {
1454 if (sys
->source_chain_update
)
1455 free(sys
->source_chain_update
);
1456 sys
->source_chain_update
= chain_update
;
1457 sys
->source_chain_current
= strdup(chain_update
);
1458 chain_update
= NULL
;
1460 vlc_mutex_unlock(&sys
->lock
);
1466 /* Run filter chain on the new subpicture */
1467 vlc_mutex_lock(&sys
->filter_chain_lock
);
1468 subpic
= filter_chain_SubFilter(spu
->p
->filter_chain
, subpic
);
1469 vlc_mutex_unlock(&sys
->filter_chain_lock
);
1473 /* SPU_DEFAULT_CHANNEL always reset itself */
1474 if (subpic
->i_channel
== VOUT_SPU_CHANNEL_OSD
)
1475 spu_ClearChannel(spu
, VOUT_SPU_CHANNEL_OSD
);
1477 /* p_private is for spu only and cannot be non NULL here */
1478 for (subpicture_region_t
*r
= subpic
->p_region
; r
!= NULL
; r
= r
->p_next
)
1479 assert(r
->p_private
== NULL
);
1482 vlc_mutex_lock(&sys
->lock
);
1483 if (SpuHeapPush(&sys
->heap
, subpic
)) {
1484 vlc_mutex_unlock(&sys
->lock
);
1485 msg_Err(spu
, "subpicture heap full");
1486 subpicture_Delete(subpic
);
1489 vlc_mutex_unlock(&sys
->lock
);
1492 subpicture_t
*spu_Render(spu_t
*spu
,
1493 const vlc_fourcc_t
*chroma_list
,
1494 const video_format_t
*fmt_dst
,
1495 const video_format_t
*fmt_src
,
1496 mtime_t render_subtitle_date
,
1497 mtime_t render_osd_date
,
1500 spu_private_t
*sys
= spu
->p
;
1502 /* Update sub-source chain */
1503 vlc_mutex_lock(&sys
->lock
);
1504 char *chain_update
= sys
->source_chain_update
;
1505 sys
->source_chain_update
= NULL
;
1506 vlc_mutex_unlock(&sys
->lock
);
1508 vlc_mutex_lock(&sys
->source_chain_lock
);
1510 filter_chain_ForEach(sys
->source_chain
, SubSourceClean
, spu
);
1512 filter_chain_ForEach(sys
->source_chain
,
1513 SubSourceDelProxyCallbacks
,
1515 filter_chain_Reset(sys
->source_chain
, NULL
, NULL
);
1517 filter_chain_AppendFromString(spu
->p
->source_chain
, chain_update
);
1519 filter_chain_ForEach(sys
->source_chain
,
1520 SubSourceAddProxyCallbacks
, sys
->vout
);
1521 filter_chain_ForEach(sys
->source_chain
, SubSourceInit
, spu
);
1525 /* Run subpicture sources */
1526 filter_chain_SubSource(sys
->source_chain
, spu
, render_osd_date
);
1527 vlc_mutex_unlock(&sys
->source_chain_lock
);
1529 static const vlc_fourcc_t chroma_list_default_yuv
[] = {
1537 static const vlc_fourcc_t chroma_list_default_rgb
[] = {
1546 if (!chroma_list
|| *chroma_list
== 0)
1547 chroma_list
= vlc_fourcc_IsYUV(fmt_dst
->i_chroma
) ? chroma_list_default_yuv
1548 : chroma_list_default_rgb
;
1550 vlc_mutex_lock(&sys
->lock
);
1552 unsigned int subpicture_count
;
1553 subpicture_t
*subpicture_array
[VOUT_MAX_SUBPICTURES
];
1555 /* Get an array of subpictures to render */
1556 SpuSelectSubpictures(spu
, &subpicture_count
, subpicture_array
,
1557 render_subtitle_date
, render_osd_date
, ignore_osd
);
1558 if (subpicture_count
<= 0) {
1559 vlc_mutex_unlock(&sys
->lock
);
1563 /* Updates the subpictures */
1564 for (unsigned i
= 0; i
< subpicture_count
; i
++) {
1565 subpicture_t
*subpic
= subpicture_array
[i
];
1566 subpicture_Update(subpic
,
1568 subpic
->b_subtitle
? render_subtitle_date
: render_osd_date
);
1571 /* Now order the subpicture array
1572 * XXX The order is *really* important for overlap subtitles positionning */
1573 qsort(subpicture_array
, subpicture_count
, sizeof(*subpicture_array
), SubpictureCmp
);
1575 /* Render the subpictures */
1576 subpicture_t
*render
= SpuRenderSubpictures(spu
,
1577 subpicture_count
, subpicture_array
,
1581 render_subtitle_date
,
1583 vlc_mutex_unlock(&sys
->lock
);
1588 void spu_OffsetSubtitleDate(spu_t
*spu
, mtime_t duration
)
1590 spu_private_t
*sys
= spu
->p
;
1592 vlc_mutex_lock(&sys
->lock
);
1593 for (int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++) {
1594 spu_heap_entry_t
*entry
= &sys
->heap
.entry
[i
];
1595 subpicture_t
*current
= entry
->subpicture
;
1597 if (current
&& current
->b_subtitle
) {
1598 if (current
->i_start
> 0)
1599 current
->i_start
+= duration
;
1600 if (current
->i_stop
> 0)
1601 current
->i_stop
+= duration
;
1604 vlc_mutex_unlock(&sys
->lock
);
1607 int spu_RegisterChannel(spu_t
*spu
)
1609 spu_private_t
*sys
= spu
->p
;
1611 vlc_mutex_lock(&sys
->lock
);
1612 int channel
= sys
->channel
++;
1613 vlc_mutex_unlock(&sys
->lock
);
1618 void spu_ClearChannel(spu_t
*spu
, int channel
)
1620 spu_private_t
*sys
= spu
->p
;
1622 vlc_mutex_lock(&sys
->lock
);
1624 for (int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++) {
1625 spu_heap_entry_t
*entry
= &sys
->heap
.entry
[i
];
1626 subpicture_t
*subpic
= entry
->subpicture
;
1630 if (subpic
->i_channel
!= channel
&& (channel
!= -1 || subpic
->i_channel
== VOUT_SPU_CHANNEL_OSD
))
1633 /* You cannot delete subpicture outside of spu_SortSubpictures */
1634 entry
->reject
= true;
1637 vlc_mutex_unlock(&sys
->lock
);
1640 void spu_ChangeSources(spu_t
*spu
, const char *filters
)
1642 spu_private_t
*sys
= spu
->p
;
1644 vlc_mutex_lock(&sys
->lock
);
1646 free(sys
->source_chain_update
);
1649 sys
->source_chain_update
= strdup(filters
);
1650 free(sys
->source_chain_current
);
1651 sys
->source_chain_current
= strdup(filters
);
1653 else if (sys
->source_chain_current
)
1654 sys
->source_chain_update
= strdup(sys
->source_chain_current
);
1656 vlc_mutex_unlock(&sys
->lock
);
1659 void spu_ChangeFilters(spu_t
*spu
, const char *filters
)
1661 spu_private_t
*sys
= spu
->p
;
1663 vlc_mutex_lock(&sys
->lock
);
1665 free(sys
->filter_chain_update
);
1668 sys
->filter_chain_update
= strdup(filters
);
1669 free(sys
->filter_chain_current
);
1670 sys
->filter_chain_current
= strdup(filters
);
1672 else if (sys
->filter_chain_current
)
1673 sys
->filter_chain_update
= strdup(sys
->filter_chain_current
);
1675 vlc_mutex_unlock(&sys
->lock
);
1678 void spu_ChangeMargin(spu_t
*spu
, int margin
)
1680 spu_private_t
*sys
= spu
->p
;
1682 vlc_mutex_lock(&sys
->lock
);
1683 sys
->margin
= margin
;
1684 vlc_mutex_unlock(&sys
->lock
);