]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
spu: fix ugly cast in filter chain (and unexport function)
[vlc] / src / video_output / vout_subpictures.c
1 /*****************************************************************************
2  * vout_subpictures.c : subpicture management functions
3  *****************************************************************************
4  * Copyright (C) 2000-2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <assert.h>
34 #include <limits.h>
35
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>
42
43 #include "../libvlc.h"
44 #include "vout_internal.h"
45 #include "../misc/subpicture.h"
46
47 /*****************************************************************************
48  * Local prototypes
49  *****************************************************************************/
50
51 /* Number of simultaneous subpictures */
52 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
53
54 /* */
55 typedef struct {
56     subpicture_t *subpicture;
57     bool          reject;
58 } spu_heap_entry_t;
59
60 typedef struct {
61     spu_heap_entry_t entry[VOUT_MAX_SUBPICTURES];
62 } spu_heap_t;
63
64 struct spu_private_t {
65     vlc_mutex_t  lock;            /* lock to protect all followings fields */
66     vlc_object_t *input;
67
68     spu_heap_t   heap;
69
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 */
75     struct {
76         int x;
77         int y;
78         int width;
79         int height;
80     } crop;                                                  /**< cropping */
81
82     int     margin;                    /**< force position of a subpicture */
83     bool    force_palette;                /**< force palette of subpicture */
84     uint8_t palette[4][4];                             /**< forced palette */
85
86     /* Subpiture filters */
87     char           *source_chain_update;
88     vlc_mutex_t    source_chain_lock;
89     filter_chain_t *source_chain;
90     char           *filter_chain_update;
91     vlc_mutex_t    filter_chain_lock;
92     filter_chain_t *filter_chain;
93
94     /* */
95     mtime_t last_sort_date;
96 };
97
98 /*****************************************************************************
99  * heap managment
100  *****************************************************************************/
101 static void SpuHeapInit(spu_heap_t *heap)
102 {
103     for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
104         spu_heap_entry_t *e = &heap->entry[i];
105
106         e->subpicture = NULL;
107         e->reject     = false;
108     }
109 }
110
111 static int SpuHeapPush(spu_heap_t *heap, subpicture_t *subpic)
112 {
113     for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
114         spu_heap_entry_t *e = &heap->entry[i];
115
116         if (e->subpicture)
117             continue;
118
119         e->subpicture = subpic;
120         e->reject     = false;
121         return VLC_SUCCESS;
122     }
123     return VLC_EGENERIC;
124 }
125
126 static void SpuHeapDeleteAt(spu_heap_t *heap, int index)
127 {
128     spu_heap_entry_t *e = &heap->entry[index];
129
130     if (e->subpicture)
131         subpicture_Delete(e->subpicture);
132
133     e->subpicture = NULL;
134 }
135
136 static int SpuHeapDeleteSubpicture(spu_heap_t *heap, subpicture_t *subpic)
137 {
138     for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
139         spu_heap_entry_t *e = &heap->entry[i];
140
141         if (e->subpicture != subpic)
142             continue;
143
144         SpuHeapDeleteAt(heap, i);
145         return VLC_SUCCESS;
146     }
147     return VLC_EGENERIC;
148 }
149
150 static void SpuHeapClean(spu_heap_t *heap)
151 {
152     for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
153         spu_heap_entry_t *e = &heap->entry[i];
154         if (e->subpicture)
155             subpicture_Delete(e->subpicture);
156     }
157 }
158
159 static void FilterRelease(filter_t *filter)
160 {
161     if (filter->p_module)
162         module_unneed(filter, filter->p_module);
163     vlc_object_release(filter);
164 }
165
166 static picture_t *spu_new_video_buffer(filter_t *filter)
167 {
168     const video_format_t *fmt = &filter->fmt_out.video;
169
170     return picture_NewFromFormat(fmt);
171 }
172
173 static void spu_del_video_buffer(filter_t *filter, picture_t *picture)
174 {
175     VLC_UNUSED(filter);
176     picture_Release(picture);
177 }
178
179 static int spu_get_attachments(filter_t *filter,
180                                input_attachment_t ***attachment_ptr,
181                                int *attachment_count)
182 {
183     spu_t *spu = filter->owner.sys;
184
185     int ret = VLC_EGENERIC;
186     if (spu->p->input)
187         ret = input_Control((input_thread_t*)spu->p->input,
188                             INPUT_GET_ATTACHMENTS,
189                             attachment_ptr, attachment_count);
190     return ret;
191 }
192
193 static filter_t *SpuRenderCreateAndLoadText(spu_t *spu)
194 {
195     filter_t *text = vlc_custom_create(spu, sizeof(*text), "spu text");
196     if (!text)
197         return NULL;
198
199     text->owner.sys = spu;
200
201     es_format_Init(&text->fmt_in, VIDEO_ES, 0);
202
203     es_format_Init(&text->fmt_out, VIDEO_ES, 0);
204     text->fmt_out.video.i_width          =
205     text->fmt_out.video.i_visible_width  = 32;
206     text->fmt_out.video.i_height         =
207     text->fmt_out.video.i_visible_height = 32;
208
209     text->pf_get_attachments = spu_get_attachments;
210
211     text->p_module = module_need(text, "text renderer", "$text-renderer", false);
212
213     /* Create a few variables used for enhanced text rendering */
214     var_Create(text, "spu-elapsed",   VLC_VAR_TIME);
215     var_Create(text, "text-rerender", VLC_VAR_BOOL);
216
217     return text;
218 }
219
220 static filter_t *SpuRenderCreateAndLoadScale(vlc_object_t *object,
221                                              vlc_fourcc_t src_chroma,
222                                              vlc_fourcc_t dst_chroma,
223                                              bool require_resize)
224 {
225     filter_t *scale = vlc_custom_create(object, sizeof(*scale), "scale");
226     if (!scale)
227         return NULL;
228
229     es_format_Init(&scale->fmt_in, VIDEO_ES, 0);
230     scale->fmt_in.video.i_chroma = src_chroma;
231     scale->fmt_in.video.i_width =
232     scale->fmt_in.video.i_visible_width =
233     scale->fmt_in.video.i_height =
234     scale->fmt_in.video.i_visible_height = 32;
235
236     es_format_Init(&scale->fmt_out, VIDEO_ES, 0);
237     scale->fmt_out.video.i_chroma = dst_chroma;
238     scale->fmt_out.video.i_width =
239     scale->fmt_out.video.i_visible_width =
240     scale->fmt_out.video.i_height =
241     scale->fmt_out.video.i_visible_height = require_resize ? 16 : 32;
242
243     scale->owner.video.buffer_new = spu_new_video_buffer;
244     scale->owner.video.buffer_del = spu_del_video_buffer;
245
246     scale->p_module = module_need(scale, "video filter2", NULL, false);
247
248     return scale;
249 }
250
251 static void SpuRenderText(spu_t *spu, bool *rerender_text,
252                           subpicture_region_t *region,
253                           const vlc_fourcc_t *chroma_list,
254                           mtime_t elapsed_time)
255 {
256     filter_t *text = spu->p->text;
257
258     assert(region->fmt.i_chroma == VLC_CODEC_TEXT);
259
260     if (!text || !text->p_module)
261         return;
262
263     /* Setup 3 variables which can be used to render
264      * time-dependent text (and effects). The first indicates
265      * the total amount of time the text will be on screen,
266      * the second the amount of time it has already been on
267      * screen (can be a negative value as text is layed out
268      * before it is rendered) and the third is a feedback
269      * variable from the renderer - if the renderer sets it
270      * then this particular text is time-dependent, eg. the
271      * visual progress bar inside the text in karaoke and the
272      * text needs to be rendered multiple times in order for
273      * the effect to work - we therefore need to return the
274      * region to its original state at the end of the loop,
275      * instead of leaving it in YUVA or YUVP.
276      * Any renderer which is unaware of how to render
277      * time-dependent text can happily ignore the variables
278      * and render the text the same as usual - it should at
279      * least show up on screen, but the effect won't change
280      * the text over time.
281      */
282     var_SetTime(text, "spu-elapsed", elapsed_time);
283     var_SetBool(text, "text-rerender", false);
284
285     if (text->pf_render_html && region->psz_html)
286         text->pf_render_html(text, region, region, chroma_list);
287     else if (text->pf_render_text)
288         text->pf_render_text(text, region, region, chroma_list);
289     *rerender_text = var_GetBool(text, "text-rerender");
290 }
291
292 /**
293  * A few scale functions helpers.
294  */
295
296 #define SCALE_UNIT (1000)
297 typedef struct {
298     int w;
299     int h;
300 } spu_scale_t;
301
302 static spu_scale_t spu_scale_create(int w, int h)
303 {
304     spu_scale_t s = { .w = w, .h = h };
305     if (s.w <= 0)
306         s.w = SCALE_UNIT;
307     if (s.h <= 0)
308         s.h = SCALE_UNIT;
309     return s;
310 }
311 static spu_scale_t spu_scale_unit(void)
312 {
313     return spu_scale_create(SCALE_UNIT, SCALE_UNIT);
314 }
315 static spu_scale_t spu_scale_createq(int64_t wn, int64_t wd, int64_t hn, int64_t hd)
316 {
317     return spu_scale_create(wn * SCALE_UNIT / wd,
318                             hn * SCALE_UNIT / hd);
319 }
320 static int spu_scale_w(int v, const spu_scale_t s)
321 {
322     return v * s.w / SCALE_UNIT;
323 }
324 static int spu_scale_h(int v, const spu_scale_t s)
325 {
326     return v * s.h / SCALE_UNIT;
327 }
328 static int spu_invscale_w(int v, const spu_scale_t s)
329 {
330     return v * SCALE_UNIT / s.w;
331 }
332 static int spu_invscale_h(int v, const spu_scale_t s)
333 {
334     return v * SCALE_UNIT / s.h;
335 }
336
337 /**
338  * A few area functions helpers
339  */
340 typedef struct {
341     int x;
342     int y;
343     int width;
344     int height;
345
346     spu_scale_t scale;
347 } spu_area_t;
348
349 static spu_area_t spu_area_create(int x, int y, int w, int h, spu_scale_t s)
350 {
351     spu_area_t a = { .x = x, .y = y, .width = w, .height = h, .scale = s };
352     return a;
353 }
354 static spu_area_t spu_area_scaled(spu_area_t a)
355 {
356     if (a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT)
357         return a;
358
359     a.x      = spu_scale_w(a.x,      a.scale);
360     a.y      = spu_scale_h(a.y,      a.scale);
361     a.width  = spu_scale_w(a.width,  a.scale);
362     a.height = spu_scale_h(a.height, a.scale);
363
364     a.scale = spu_scale_unit();
365     return a;
366 }
367 static spu_area_t spu_area_unscaled(spu_area_t a, spu_scale_t s)
368 {
369     if (a.scale.w == s.w && a.scale.h == s.h)
370         return a;
371
372     a = spu_area_scaled(a);
373
374     a.x      = spu_invscale_w(a.x,      s);
375     a.y      = spu_invscale_h(a.y,      s);
376     a.width  = spu_invscale_w(a.width,  s);
377     a.height = spu_invscale_h(a.height, s);
378
379     a.scale = s;
380     return a;
381 }
382 static bool spu_area_overlap(spu_area_t a, spu_area_t b)
383 {
384     const int dx = 0;
385     const int dy = 0;
386
387     a = spu_area_scaled(a);
388     b = spu_area_scaled(b);
389
390     return __MAX(a.x - dx, b.x) < __MIN(a.x + a.width  + dx, b.x + b.width ) &&
391            __MAX(a.y - dy, b.y) < __MIN(a.y + a.height + dy, b.y + b.height);
392 }
393
394 /**
395  * Avoid area overlapping
396  */
397 static void SpuAreaFixOverlap(spu_area_t *dst,
398                               const spu_area_t *sub_array, int sub_count, int align)
399 {
400     spu_area_t a = spu_area_scaled(*dst);
401     bool is_moved = false;
402     bool is_ok;
403
404     /* Check for overlap
405      * XXX It is not fast O(n^2) but we should not have a lot of region */
406     do {
407         is_ok = true;
408         for (int i = 0; i < sub_count; i++) {
409             spu_area_t sub = spu_area_scaled(sub_array[i]);
410
411             if (!spu_area_overlap(a, sub))
412                 continue;
413
414             if (align & SUBPICTURE_ALIGN_TOP) {
415                 /* We go down */
416                 int i_y = sub.y + sub.height;
417                 a.y = i_y;
418                 is_moved = true;
419             } else if (align & SUBPICTURE_ALIGN_BOTTOM) {
420                 /* We go up */
421                 int i_y = sub.y - a.height;
422                 a.y = i_y;
423                 is_moved = true;
424             } else {
425                 /* TODO what to do in this case? */
426                 //fprintf(stderr, "Overlap with unsupported alignment\n");
427                 break;
428             }
429
430             is_ok = false;
431             break;
432         }
433     } while (!is_ok);
434
435     if (is_moved)
436         *dst = spu_area_unscaled(a, dst->scale);
437 }
438
439
440 static void SpuAreaFitInside(spu_area_t *area, const spu_area_t *boundary)
441 {
442     spu_area_t a = spu_area_scaled(*area);
443
444     const int i_error_x = (a.x + a.width) - boundary->width;
445     if (i_error_x > 0)
446         a.x -= i_error_x;
447     if (a.x < 0)
448         a.x = 0;
449
450     const int i_error_y = (a.y + a.height) - boundary->height;
451     if (i_error_y > 0)
452         a.y -= i_error_y;
453     if (a.y < 0)
454         a.y = 0;
455
456     *area = spu_area_unscaled(a, area->scale);
457 }
458
459 /**
460  * Place a region
461  */
462 static void SpuRegionPlace(int *x, int *y,
463                            const subpicture_t *subpic,
464                            const subpicture_region_t *region)
465 {
466     assert(region->i_x != INT_MAX && region->i_y != INT_MAX);
467     if (subpic->b_absolute) {
468         *x = region->i_x;
469         *y = region->i_y;
470     } else {
471         if (region->i_align & SUBPICTURE_ALIGN_TOP)
472             *y = region->i_y;
473         else if (region->i_align & SUBPICTURE_ALIGN_BOTTOM)
474             *y = subpic->i_original_picture_height - region->fmt.i_visible_height - region->i_y;
475         else
476             *y = subpic->i_original_picture_height / 2 - region->fmt.i_visible_height / 2;
477
478         if (region->i_align & SUBPICTURE_ALIGN_LEFT)
479             *x = region->i_x;
480         else if (region->i_align & SUBPICTURE_ALIGN_RIGHT)
481             *x = subpic->i_original_picture_width - region->fmt.i_visible_width - region->i_x;
482         else
483             *x = subpic->i_original_picture_width / 2 - region->fmt.i_visible_width / 2;
484     }
485 }
486
487 /**
488  * This function compares two 64 bits integers.
489  * It can be used by qsort.
490  */
491 static int IntegerCmp(int64_t i0, int64_t i1)
492 {
493     return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
494 }
495 /**
496  * This function compares 2 subpictures using the following properties
497  * (ordered by priority)
498  * 1. absolute positionning
499  * 2. start time
500  * 3. creation order (per channel)
501  *
502  * It can be used by qsort.
503  *
504  * XXX spu_RenderSubpictures depends heavily on this order.
505  */
506 static int SubpictureCmp(const void *s0, const void *s1)
507 {
508     subpicture_t *subpic0 = *(subpicture_t**)s0;
509     subpicture_t *subpic1 = *(subpicture_t**)s1;
510     int r;
511
512     r = IntegerCmp(!subpic0->b_absolute, !subpic1->b_absolute);
513     if (!r)
514         r = IntegerCmp(subpic0->i_start, subpic1->i_start);
515     if (!r)
516         r = IntegerCmp(subpic0->i_channel, subpic1->i_channel);
517     if (!r)
518         r = IntegerCmp(subpic0->i_order, subpic1->i_order);
519     return r;
520 }
521
522 /*****************************************************************************
523  * SpuSelectSubpictures: find the subpictures to display
524  *****************************************************************************
525  * This function parses all subpictures and decides which ones need to be
526  * displayed. If no picture has been selected, display_date will depend on
527  * the subpicture.
528  * We also check for ephemer DVD subpictures (subpictures that have
529  * to be removed if a newer one is available), which makes it a lot
530  * more difficult to guess if a subpicture has to be rendered or not.
531  *****************************************************************************/
532 static void SpuSelectSubpictures(spu_t *spu,
533                                  unsigned int *subpicture_count,
534                                  subpicture_t **subpicture_array,
535                                  mtime_t render_subtitle_date,
536                                  mtime_t render_osd_date,
537                                  bool ignore_osd)
538 {
539     spu_private_t *sys = spu->p;
540
541     /* */
542     *subpicture_count = 0;
543
544     /* Create a list of channels */
545     int channel[VOUT_MAX_SUBPICTURES];
546     int channel_count = 0;
547
548     for (int index = 0; index < VOUT_MAX_SUBPICTURES; index++) {
549         spu_heap_entry_t *entry = &sys->heap.entry[index];
550         if (!entry->subpicture || entry->reject)
551             continue;
552         const int i_channel = entry->subpicture->i_channel;
553         int i;
554         for (i = 0; i < channel_count; i++) {
555             if (channel[i] == i_channel)
556                 break;
557         }
558         if (channel_count <= i)
559             channel[channel_count++] = i_channel;
560     }
561
562     /* Fill up the subpicture_array arrays with relevent pictures */
563     for (int i = 0; i < channel_count; i++) {
564         subpicture_t *available_subpic[VOUT_MAX_SUBPICTURES];
565         bool         is_available_late[VOUT_MAX_SUBPICTURES];
566         int          available_count = 0;
567
568         mtime_t      start_date = render_subtitle_date;
569         mtime_t      ephemer_subtitle_date = 0;
570         mtime_t      ephemer_osd_date = 0;
571         int64_t      ephemer_subtitle_order = INT64_MIN;
572         int64_t      ephemer_system_order = INT64_MIN;
573
574         /* Select available pictures */
575         for (int index = 0; index < VOUT_MAX_SUBPICTURES; index++) {
576             spu_heap_entry_t *entry = &sys->heap.entry[index];
577             subpicture_t *current = entry->subpicture;
578             bool is_stop_valid;
579             bool is_late;
580
581             if (!current || entry->reject) {
582                 if (entry->reject)
583                     SpuHeapDeleteAt(&sys->heap, index);
584                 continue;
585             }
586
587             if (current->i_channel != channel[i] ||
588                (ignore_osd && !current->b_subtitle))
589                 continue;
590
591             const mtime_t render_date = current->b_subtitle ? render_subtitle_date : render_osd_date;
592             if (render_date &&
593                 render_date < current->i_start) {
594                 /* Too early, come back next monday */
595                 continue;
596             }
597
598             mtime_t *ephemer_date_ptr  = current->b_subtitle ? &ephemer_subtitle_date  : &ephemer_osd_date;
599             int64_t *ephemer_order_ptr = current->b_subtitle ? &ephemer_subtitle_order : &ephemer_system_order;
600             if (current->i_start >= *ephemer_date_ptr) {
601                 *ephemer_date_ptr = current->i_start;
602                 if (current->i_order > *ephemer_order_ptr)
603                     *ephemer_order_ptr = current->i_order;
604             }
605
606             is_stop_valid = !current->b_ephemer || current->i_stop > current->i_start;
607
608             is_late = is_stop_valid && current->i_stop <= render_date;
609
610             /* start_date will be used for correct automatic overlap support
611              * in case picture that should not be displayed anymore (display_time)
612              * overlap with a picture to be displayed (current->i_start)  */
613             if (current->b_subtitle && !is_late && !current->b_ephemer)
614                 start_date = current->i_start;
615
616             /* */
617             available_subpic[available_count] = current;
618             is_available_late[available_count] = is_late;
619             available_count++;
620         }
621
622         /* Only forced old picture display at the transition */
623         if (start_date < sys->last_sort_date)
624             start_date = sys->last_sort_date;
625         if (start_date <= 0)
626             start_date = INT64_MAX;
627
628         /* Select pictures to be displayed */
629         for (int index = 0; index < available_count; index++) {
630             subpicture_t *current = available_subpic[index];
631             bool is_late = is_available_late[index];
632
633             const mtime_t stop_date = current->b_subtitle ? __MAX(start_date, sys->last_sort_date) : render_osd_date;
634             const mtime_t ephemer_date  = current->b_subtitle ? ephemer_subtitle_date  : ephemer_osd_date;
635             const int64_t ephemer_order = current->b_subtitle ? ephemer_subtitle_order : ephemer_system_order;
636
637             /* Destroy late and obsolete ephemer subpictures */
638             bool is_rejeted = is_late && current->i_stop <= stop_date;
639             if (current->b_ephemer) {
640                 if (current->i_start < ephemer_date)
641                     is_rejeted = true;
642                 else if (current->i_start == ephemer_date &&
643                          current->i_order < ephemer_order)
644                     is_rejeted = true;
645             }
646
647             if (is_rejeted)
648                 SpuHeapDeleteSubpicture(&sys->heap, current);
649             else
650                 subpicture_array[(*subpicture_count)++] = current;
651         }
652     }
653
654     sys->last_sort_date = render_subtitle_date;
655 }
656
657
658
659 /**
660  * It will transform the provided region into another region suitable for rendering.
661  */
662 static void SpuRenderRegion(spu_t *spu,
663                             subpicture_region_t **dst_ptr, spu_area_t *dst_area,
664                             subpicture_t *subpic, subpicture_region_t *region,
665                             const spu_scale_t scale_size,
666                             const vlc_fourcc_t *chroma_list,
667                             const video_format_t *fmt,
668                             const spu_area_t *subtitle_area, int subtitle_area_count,
669                             mtime_t render_date)
670 {
671     spu_private_t *sys = spu->p;
672
673     video_format_t fmt_original = region->fmt;
674     bool restore_text = false;
675     int x_offset;
676     int y_offset;
677
678     video_format_t region_fmt;
679     picture_t *region_picture;
680
681     /* Invalidate area by default */
682     *dst_area = spu_area_create(0,0, 0,0, scale_size);
683     *dst_ptr  = NULL;
684
685     /* Render text region */
686     if (region->fmt.i_chroma == VLC_CODEC_TEXT) {
687         SpuRenderText(spu, &restore_text, region,
688                       chroma_list,
689                       render_date - subpic->i_start);
690
691         /* Check if the rendering has failed ... */
692         if (region->fmt.i_chroma == VLC_CODEC_TEXT)
693             goto exit;
694     }
695
696     /* Force palette if requested
697      * FIXME b_force_palette and force_crop are applied to all subpictures using palette
698      * instead of only the right one (being the dvd spu).
699      */
700     const bool using_palette = region->fmt.i_chroma == VLC_CODEC_YUVP;
701     const bool force_palette = using_palette && sys->force_palette;
702     const bool force_crop    = force_palette && sys->force_crop;
703     bool changed_palette     = false;
704
705     /* Compute the margin which is expressed in destination pixel unit
706      * The margin is applied only to subtitle and when no forced crop is
707      * requested (dvd menu) */
708     int y_margin = 0;
709     if (!force_crop && subpic->b_subtitle)
710         y_margin = spu_invscale_h(sys->margin, scale_size);
711
712     /* Place the picture
713      * We compute the position in the rendered size */
714     SpuRegionPlace(&x_offset, &y_offset,
715                    subpic, region);
716
717     /* Save this position for subtitle overlap support
718      * it is really important that there are given without scale_size applied */
719     *dst_area = spu_area_create(x_offset, y_offset,
720                                 region->fmt.i_visible_width,
721                                 region->fmt.i_visible_height,
722                                 scale_size);
723
724     /* Handle overlapping subtitles when possible */
725     if (subpic->b_subtitle && !subpic->b_absolute)
726         SpuAreaFixOverlap(dst_area, subtitle_area, subtitle_area_count,
727                           region->i_align);
728
729     /* we copy the area: for the subtitle overlap support we want
730      * to only save the area without margin applied */
731     spu_area_t restrained = *dst_area;
732
733     /* apply margin to subtitles and correct if they go over the picture edge */
734     if (subpic->b_subtitle)
735         restrained.y -= y_margin;
736
737     spu_area_t display = spu_area_create(0, 0, fmt->i_visible_width,
738                                          fmt->i_visible_height,
739                                          spu_scale_unit());
740     //fprintf("
741     SpuAreaFitInside(&restrained, &display);
742
743     /* Fix the position for the current scale_size */
744     x_offset = spu_scale_w(restrained.x, restrained.scale);
745     y_offset = spu_scale_h(restrained.y, restrained.scale);
746
747     /* */
748     if (force_palette) {
749         video_palette_t *old_palette = region->fmt.p_palette;
750         video_palette_t new_palette;
751
752         /* We suppose DVD palette here */
753         new_palette.i_entries = 4;
754         for (int i = 0; i < 4; i++)
755             for (int j = 0; j < 4; j++)
756                 new_palette.palette[i][j] = sys->palette[i][j];
757
758         if (old_palette->i_entries == new_palette.i_entries) {
759             for (int i = 0; i < old_palette->i_entries; i++)
760                 for (int j = 0; j < 4; j++)
761                     changed_palette |= old_palette->palette[i][j] != new_palette.palette[i][j];
762         } else {
763             changed_palette = true;
764         }
765         *old_palette = new_palette;
766     }
767
768     /* */
769     region_fmt = region->fmt;
770     region_picture = region->p_picture;
771
772     bool convert_chroma = true;
773     for (int i = 0; chroma_list[i] && convert_chroma; i++) {
774         if (region_fmt.i_chroma == chroma_list[i])
775             convert_chroma = false;
776     }
777
778     /* Scale from rendered size to destination size */
779     if (sys->scale && sys->scale->p_module &&
780         (!using_palette || (sys->scale_yuvp && sys->scale_yuvp->p_module)) &&
781         (scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT ||
782         using_palette || convert_chroma)) {
783         const unsigned dst_width  = spu_scale_w(region->fmt.i_visible_width,  scale_size);
784         const unsigned dst_height = spu_scale_h(region->fmt.i_visible_height, scale_size);
785
786         /* Destroy the cache if unusable */
787         if (region->p_private) {
788             subpicture_region_private_t *private = region->p_private;
789             bool is_changed = false;
790
791             /* Check resize changes */
792             if (dst_width  != private->fmt.i_visible_width ||
793                 dst_height != private->fmt.i_visible_height)
794                 is_changed = true;
795
796             /* Check forced palette changes */
797             if (changed_palette)
798                 is_changed = true;
799
800             if (convert_chroma && private->fmt.i_chroma != chroma_list[0])
801                 is_changed = true;
802
803             if (is_changed) {
804                 subpicture_region_private_Delete(private);
805                 region->p_private = NULL;
806             }
807         }
808
809         /* Scale if needed into cache */
810         if (!region->p_private && dst_width > 0 && dst_height > 0) {
811             filter_t *scale = sys->scale;
812
813             picture_t *picture = region->p_picture;
814             picture_Hold(picture);
815
816             /* Convert YUVP to YUVA/RGBA first for better scaling quality */
817             if (using_palette) {
818                 filter_t *scale_yuvp = sys->scale_yuvp;
819
820                 scale_yuvp->fmt_in.video = region->fmt;
821
822                 scale_yuvp->fmt_out.video = region->fmt;
823                 scale_yuvp->fmt_out.video.i_chroma = chroma_list[0];
824
825                 picture = scale_yuvp->pf_video_filter(scale_yuvp, picture);
826                 if (!picture) {
827                     /* Well we will try conversion+scaling */
828                     msg_Warn(spu, "%4.4s to %4.4s conversion failed",
829                              (const char*)&scale_yuvp->fmt_in.video.i_chroma,
830                              (const char*)&scale_yuvp->fmt_out.video.i_chroma);
831                 }
832             }
833
834             /* Conversion(except from YUVP)/Scaling */
835             if (picture &&
836                 (picture->format.i_visible_width  != dst_width ||
837                  picture->format.i_visible_height != dst_height ||
838                  (convert_chroma && !using_palette)))
839             {
840                 scale->fmt_in.video  = picture->format;
841                 scale->fmt_out.video = picture->format;
842                 if (using_palette)
843                     scale->fmt_in.video.i_chroma = chroma_list[0];
844                 if (convert_chroma)
845                     scale->fmt_out.i_codec        =
846                     scale->fmt_out.video.i_chroma = chroma_list[0];
847
848                 scale->fmt_out.video.i_width  = dst_width;
849                 scale->fmt_out.video.i_height = dst_height;
850
851                 scale->fmt_out.video.i_visible_width =
852                     spu_scale_w(region->fmt.i_visible_width, scale_size);
853                 scale->fmt_out.video.i_visible_height =
854                     spu_scale_h(region->fmt.i_visible_height, scale_size);
855
856                 picture = scale->pf_video_filter(scale, picture);
857                 if (!picture)
858                     msg_Err(spu, "scaling failed");
859             }
860
861             /* */
862             if (picture) {
863                 region->p_private = subpicture_region_private_New(&picture->format);
864                 if (region->p_private) {
865                     region->p_private->p_picture = picture;
866                     if (!region->p_private->p_picture) {
867                         subpicture_region_private_Delete(region->p_private);
868                         region->p_private = NULL;
869                     }
870                 } else {
871                     picture_Release(picture);
872                 }
873             }
874         }
875
876         /* And use the scaled picture */
877         if (region->p_private) {
878             region_fmt     = region->p_private->fmt;
879             region_picture = region->p_private->p_picture;
880         }
881     }
882
883     /* Force cropping if requested */
884     if (force_crop) {
885         int crop_x     = spu_scale_w(sys->crop.x,     scale_size);
886         int crop_y     = spu_scale_h(sys->crop.y,     scale_size);
887         int crop_width = spu_scale_w(sys->crop.width, scale_size);
888         int crop_height= spu_scale_h(sys->crop.height,scale_size);
889
890         /* Find the intersection */
891         if (crop_x + crop_width <= x_offset ||
892             x_offset + (int)region_fmt.i_visible_width  < crop_x ||
893             crop_y + crop_height <= y_offset ||
894             y_offset + (int)region_fmt.i_visible_height < crop_y) {
895             /* No intersection */
896             region_fmt.i_visible_width  =
897             region_fmt.i_visible_height = 0;
898         } else {
899             int x, y, x_end, y_end;
900             x = __MAX(crop_x, x_offset);
901             y = __MAX(crop_y, y_offset);
902             x_end = __MIN(crop_x + crop_width,
903                           x_offset + (int)region_fmt.i_visible_width);
904             y_end = __MIN(crop_y + crop_height,
905                           y_offset + (int)region_fmt.i_visible_height);
906
907             region_fmt.i_x_offset       = x - x_offset;
908             region_fmt.i_y_offset       = y - y_offset;
909             region_fmt.i_visible_width  = x_end - x;
910             region_fmt.i_visible_height = y_end - y;
911
912             x_offset = __MAX(x, 0);
913             y_offset = __MAX(y, 0);
914         }
915     }
916
917     subpicture_region_t *dst = *dst_ptr = subpicture_region_New(&region_fmt);
918     if (dst) {
919         dst->i_x       = x_offset;
920         dst->i_y       = y_offset;
921         dst->i_align   = 0;
922         if (dst->p_picture)
923             picture_Release(dst->p_picture);
924         dst->p_picture = picture_Hold(region_picture);
925         int fade_alpha = 255;
926         if (subpic->b_fade) {
927             mtime_t fade_start = subpic->i_start + 3 * (subpic->i_stop - subpic->i_start) / 4;
928
929             if (fade_start <= render_date && fade_start < subpic->i_stop)
930                 fade_alpha = 255 * (subpic->i_stop - render_date) /
931                                    (subpic->i_stop - fade_start);
932         }
933         dst->i_alpha   = fade_alpha * subpic->i_alpha * region->i_alpha / 65025;
934     }
935
936 exit:
937     if (restore_text) {
938         /* Some forms of subtitles need to be re-rendered more than
939          * once, eg. karaoke. We therefore restore the region to its
940          * pre-rendered state, so the next time through everything is
941          * calculated again.
942          */
943         if (region->p_picture) {
944             picture_Release(region->p_picture);
945             region->p_picture = NULL;
946         }
947         if (region->p_private) {
948             subpicture_region_private_Delete(region->p_private);
949             region->p_private = NULL;
950         }
951         region->fmt = fmt_original;
952     }
953 }
954
955 /**
956  * This function renders all sub picture units in the list.
957  */
958 static subpicture_t *SpuRenderSubpictures(spu_t *spu,
959                                           unsigned int i_subpicture,
960                                           subpicture_t **pp_subpicture,
961                                           const vlc_fourcc_t *chroma_list,
962                                           const video_format_t *fmt_dst,
963                                           const video_format_t *fmt_src,
964                                           mtime_t render_subtitle_date,
965                                           mtime_t render_osd_date)
966 {
967     spu_private_t *sys = spu->p;
968
969     /* Count the number of regions and subtitle regions */
970     unsigned int subtitle_region_count = 0;
971     unsigned int region_count          = 0;
972     for (unsigned i = 0; i < i_subpicture; i++) {
973         const subpicture_t *subpic = pp_subpicture[i];
974
975         unsigned count = 0;
976         for (subpicture_region_t *r = subpic->p_region; r != NULL; r = r->p_next)
977             count++;
978
979         if (subpic->b_subtitle)
980             subtitle_region_count += count;
981         region_count += count;
982     }
983     if (region_count <= 0)
984         return NULL;
985
986     /* Create the output subpicture */
987     subpicture_t *output = subpicture_New(NULL);
988     if (!output)
989         return NULL;
990     output->i_original_picture_width  = fmt_dst->i_visible_width;
991     output->i_original_picture_height = fmt_dst->i_visible_height;
992     subpicture_region_t **output_last_ptr = &output->p_region;
993
994     /* Allocate area array for subtitle overlap */
995     spu_area_t subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
996     spu_area_t *subtitle_area;
997     int subtitle_area_count;
998
999     subtitle_area_count = 0;
1000     subtitle_area = subtitle_area_buffer;
1001     if (subtitle_region_count > sizeof(subtitle_area_buffer)/sizeof(*subtitle_area_buffer))
1002         subtitle_area = calloc(subtitle_region_count, sizeof(*subtitle_area));
1003
1004     /* Process all subpictures and regions (in the right order) */
1005     for (unsigned int index = 0; index < i_subpicture; index++) {
1006         subpicture_t        *subpic = pp_subpicture[index];
1007         subpicture_region_t *region;
1008
1009         if (!subpic->p_region)
1010             continue;
1011
1012         if (subpic->i_original_picture_width  <= 0 ||
1013             subpic->i_original_picture_height <= 0) {
1014             if (subpic->i_original_picture_width  > 0 ||
1015                 subpic->i_original_picture_height > 0)
1016                 msg_Err(spu, "original picture size %dx%d is unsupported",
1017                          subpic->i_original_picture_width,
1018                          subpic->i_original_picture_height);
1019             else
1020                 msg_Warn(spu, "original picture size is undefined");
1021
1022             subpic->i_original_picture_width  = fmt_src->i_visible_width;
1023             subpic->i_original_picture_height = fmt_src->i_visible_height;
1024         }
1025
1026         if (sys->text) {
1027             /* FIXME aspect ratio ? */
1028             sys->text->fmt_out.video.i_width          =
1029             sys->text->fmt_out.video.i_visible_width  = subpic->i_original_picture_width;
1030
1031             sys->text->fmt_out.video.i_height         =
1032             sys->text->fmt_out.video.i_visible_height = subpic->i_original_picture_height;
1033         }
1034
1035         /* Render all regions
1036          * We always transform non absolute subtitle into absolute one on the
1037          * first rendering to allow good subtitle overlap support.
1038          */
1039         for (region = subpic->p_region; region != NULL; region = region->p_next) {
1040             spu_area_t area;
1041
1042             /* Compute region scale AR */
1043             video_format_t region_fmt = region->fmt;
1044             if (region_fmt.i_sar_num <= 0 || region_fmt.i_sar_den <= 0) {
1045                 region_fmt.i_sar_num = (int64_t)fmt_dst->i_visible_width  * fmt_dst->i_sar_num * subpic->i_original_picture_height;
1046                 region_fmt.i_sar_den = (int64_t)fmt_dst->i_visible_height * fmt_dst->i_sar_den * subpic->i_original_picture_width;
1047                 vlc_ureduce(&region_fmt.i_sar_num, &region_fmt.i_sar_den,
1048                             region_fmt.i_sar_num, region_fmt.i_sar_den, 65536);
1049             }
1050
1051             /* Compute scaling from original size to destination size
1052              * FIXME The current scaling ensure that the heights match, the width being
1053              * cropped.
1054              */
1055             spu_scale_t scale = spu_scale_createq((int64_t)fmt_dst->i_visible_height                 * fmt_dst->i_sar_den * region_fmt.i_sar_num,
1056                                                   (int64_t)subpic->i_original_picture_height * fmt_dst->i_sar_num * region_fmt.i_sar_den,
1057                                                   fmt_dst->i_visible_height,
1058                                                   subpic->i_original_picture_height);
1059
1060             /* Check scale validity */
1061             if (scale.w <= 0 || scale.h <= 0)
1062                 continue;
1063
1064             /* */
1065             SpuRenderRegion(spu, output_last_ptr, &area,
1066                             subpic, region, scale,
1067                             chroma_list, fmt_dst,
1068                             subtitle_area, subtitle_area_count,
1069                             subpic->b_subtitle ? render_subtitle_date : render_osd_date);
1070             if (*output_last_ptr)
1071                 output_last_ptr = &(*output_last_ptr)->p_next;
1072
1073             if (subpic->b_subtitle) {
1074                 area = spu_area_unscaled(area, scale);
1075                 if (!subpic->b_absolute && area.width > 0 && area.height > 0) {
1076                     region->i_x = area.x;
1077                     region->i_y = area.y;
1078                 }
1079                 if (subtitle_area)
1080                     subtitle_area[subtitle_area_count++] = area;
1081             }
1082         }
1083         if (subpic->b_subtitle && subpic->p_region)
1084             subpic->b_absolute = true;
1085     }
1086
1087     /* */
1088     if (subtitle_area != subtitle_area_buffer)
1089         free(subtitle_area);
1090
1091     return output;
1092 }
1093
1094 /*****************************************************************************
1095  * Object variables callbacks
1096  *****************************************************************************/
1097
1098 /*****************************************************************************
1099  * UpdateSPU: update subpicture settings
1100  *****************************************************************************
1101  * This function is called from CropCallback and at initialization time, to
1102  * retrieve crop information from the input.
1103  *****************************************************************************/
1104 static void UpdateSPU(spu_t *spu, vlc_object_t *object)
1105 {
1106     spu_private_t *sys = spu->p;
1107     vlc_value_t val;
1108
1109     vlc_mutex_lock(&sys->lock);
1110
1111     sys->force_palette = false;
1112     sys->force_crop = false;
1113
1114     if (var_Get(object, "highlight", &val) || !val.b_bool) {
1115         vlc_mutex_unlock(&sys->lock);
1116         return;
1117     }
1118
1119     sys->force_crop = true;
1120     sys->crop.x      = var_GetInteger(object, "x-start");
1121     sys->crop.y      = var_GetInteger(object, "y-start");
1122     sys->crop.width  = var_GetInteger(object, "x-end") - sys->crop.x;
1123     sys->crop.height = var_GetInteger(object, "y-end") - sys->crop.y;
1124
1125     if (var_Get(object, "menu-palette", &val) == VLC_SUCCESS) {
1126         memcpy(sys->palette, val.p_address, 16);
1127         sys->force_palette = true;
1128     }
1129     vlc_mutex_unlock(&sys->lock);
1130
1131     msg_Dbg(object, "crop: %i,%i,%i,%i, palette forced: %i",
1132             sys->crop.x, sys->crop.y,
1133             sys->crop.width, sys->crop.height,
1134             sys->force_palette);
1135 }
1136
1137 /*****************************************************************************
1138  * CropCallback: called when the highlight properties are changed
1139  *****************************************************************************
1140  * This callback is called from the input thread when we need cropping
1141  *****************************************************************************/
1142 static int CropCallback(vlc_object_t *object, char const *var,
1143                         vlc_value_t oldval, vlc_value_t newval, void *data)
1144 {
1145     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(var);
1146
1147     UpdateSPU((spu_t *)data, object);
1148     return VLC_SUCCESS;
1149 }
1150
1151 /*****************************************************************************
1152  * Buffers allocation callbacks for the filters
1153  *****************************************************************************/
1154
1155 static subpicture_t *sub_new_buffer(filter_t *filter)
1156 {
1157     int channel = (intptr_t)filter->owner.sys;
1158
1159     subpicture_t *subpicture = subpicture_New(NULL);
1160     if (subpicture)
1161         subpicture->i_channel = channel;
1162     return subpicture;
1163 }
1164
1165 static void sub_del_buffer(filter_t *filter, subpicture_t *subpic)
1166 {
1167     VLC_UNUSED(filter);
1168     subpicture_Delete(subpic);
1169 }
1170
1171 static int SubSourceInit(filter_t *filter, void *data)
1172 {
1173     spu_t *spu = data;
1174     int channel = spu_RegisterChannel(spu);
1175
1176     filter->owner.sys = (void *)(intptr_t)channel;
1177     filter->owner.sub.buffer_new = sub_new_buffer;
1178     filter->owner.sub.buffer_del = sub_del_buffer;
1179     return VLC_SUCCESS;
1180 }
1181
1182 static int SubSourceClean(filter_t *filter, void *data)
1183 {
1184     spu_t *spu = data;
1185     int channel = (intptr_t)filter->owner.sys;
1186
1187     spu_ClearChannel(spu, channel);
1188     return VLC_SUCCESS;
1189 }
1190
1191 /*****************************************************************************
1192  * Public API
1193  *****************************************************************************/
1194
1195 #undef spu_Create
1196 /**
1197  * Creates the subpicture unit
1198  *
1199  * \param p_this the parent object which creates the subpicture unit
1200  */
1201 spu_t *spu_Create(vlc_object_t *object)
1202 {
1203     spu_t *spu = vlc_custom_create(object,
1204                                    sizeof(spu_t) + sizeof(spu_private_t),
1205                                    "subpicture");
1206     if (!spu)
1207         return NULL;
1208
1209     /* Initialize spu fields */
1210     spu_private_t *sys = spu->p = (spu_private_t*)&spu[1];
1211
1212     /* Initialize private fields */
1213     vlc_mutex_init(&sys->lock);
1214
1215     SpuHeapInit(&sys->heap);
1216
1217     sys->text = NULL;
1218     sys->scale = NULL;
1219     sys->scale_yuvp = NULL;
1220
1221     sys->margin = var_InheritInteger(spu, "sub-margin");
1222
1223     /* Register the default subpicture channel */
1224     sys->channel = SPU_DEFAULT_CHANNEL + 1;
1225
1226     sys->source_chain_update = NULL;
1227     sys->filter_chain_update = NULL;
1228     vlc_mutex_init(&sys->source_chain_lock);
1229     vlc_mutex_init(&sys->filter_chain_lock);
1230     sys->source_chain = filter_chain_New(spu, "sub source", false,
1231                                          NULL, NULL, NULL);
1232     sys->filter_chain = filter_chain_New(spu, "sub filter", false,
1233                                          NULL, NULL, NULL);
1234
1235     /* Load text and scale module */
1236     sys->text = SpuRenderCreateAndLoadText(spu);
1237
1238     /* XXX spu->p_scale is used for all conversion/scaling except yuvp to
1239      * yuva/rgba */
1240     sys->scale = SpuRenderCreateAndLoadScale(VLC_OBJECT(spu),
1241                                              VLC_CODEC_YUVA, VLC_CODEC_RGBA, true);
1242
1243     /* This one is used for YUVP to YUVA/RGBA without scaling
1244      * FIXME rename it */
1245     sys->scale_yuvp = SpuRenderCreateAndLoadScale(VLC_OBJECT(spu),
1246                                                   VLC_CODEC_YUVP, VLC_CODEC_YUVA, false);
1247
1248     /* */
1249     sys->last_sort_date = -1;
1250
1251     return spu;
1252 }
1253
1254 /**
1255  * Destroy the subpicture unit
1256  *
1257  * \param p_this the parent object which destroys the subpicture unit
1258  */
1259 void spu_Destroy(spu_t *spu)
1260 {
1261     spu_private_t *sys = spu->p;
1262
1263     if (sys->text)
1264         FilterRelease(sys->text);
1265
1266     if (sys->scale_yuvp)
1267         FilterRelease(sys->scale_yuvp);
1268
1269     if (sys->scale)
1270         FilterRelease(sys->scale);
1271
1272     filter_chain_ForEach(sys->source_chain, SubSourceClean, spu);
1273     filter_chain_Delete(sys->source_chain);
1274     filter_chain_Delete(sys->filter_chain);
1275     vlc_mutex_destroy(&sys->source_chain_lock);
1276     vlc_mutex_destroy(&sys->filter_chain_lock);
1277     free(sys->source_chain_update);
1278     free(sys->filter_chain_update);
1279
1280     /* Destroy all remaining subpictures */
1281     SpuHeapClean(&sys->heap);
1282
1283     vlc_mutex_destroy(&sys->lock);
1284
1285     vlc_object_release(spu);
1286 }
1287
1288 /**
1289  * Attach/Detach the SPU from any input
1290  *
1291  * \param p_this the object in which to destroy the subpicture unit
1292  * \param b_attach to select attach or detach
1293  */
1294 void spu_Attach(spu_t *spu, vlc_object_t *input, bool attach)
1295 {
1296     if (attach) {
1297         UpdateSPU(spu, input);
1298         var_Create(input, "highlight", VLC_VAR_BOOL);
1299         var_AddCallback(input, "highlight", CropCallback, spu);
1300
1301         vlc_mutex_lock(&spu->p->lock);
1302         spu->p->input = input;
1303
1304         if (spu->p->text)
1305             FilterRelease(spu->p->text);
1306         spu->p->text = SpuRenderCreateAndLoadText(spu);
1307
1308         vlc_mutex_unlock(&spu->p->lock);
1309     } else {
1310         vlc_mutex_lock(&spu->p->lock);
1311         spu->p->input = NULL;
1312         vlc_mutex_unlock(&spu->p->lock);
1313
1314         /* Delete callbacks */
1315         var_DelCallback(input, "highlight", CropCallback, spu);
1316         var_Destroy(input, "highlight");
1317     }
1318 }
1319
1320 /**
1321  * Inform the SPU filters of mouse event
1322  */
1323 int spu_ProcessMouse(spu_t *spu,
1324                      const vlc_mouse_t *mouse,
1325                      const video_format_t *fmt)
1326 {
1327     spu_private_t *sys = spu->p;
1328
1329     vlc_mutex_lock(&sys->source_chain_lock);
1330     filter_chain_MouseEvent(sys->source_chain, mouse, fmt);
1331     vlc_mutex_unlock(&sys->source_chain_lock);
1332
1333     return VLC_SUCCESS;
1334 }
1335
1336 /**
1337  * Display a subpicture
1338  *
1339  * Remove the reservation flag of a subpicture, which will cause it to be
1340  * ready for display.
1341  * \param spu the subpicture unit object
1342  * \param subpic the subpicture to display
1343  */
1344 void spu_PutSubpicture(spu_t *spu, subpicture_t *subpic)
1345 {
1346     spu_private_t *sys = spu->p;
1347
1348     /* Update sub-filter chain */
1349     vlc_mutex_lock(&sys->lock);
1350     char *chain_update = sys->filter_chain_update;
1351     sys->filter_chain_update = NULL;
1352     vlc_mutex_unlock(&sys->lock);
1353
1354     bool is_left_empty = false;
1355
1356     vlc_mutex_lock(&sys->filter_chain_lock);
1357     if (chain_update) {
1358         if (*chain_update) {
1359             filter_chain_Reset(sys->filter_chain, NULL, NULL);
1360
1361             filter_chain_AppendFromString(spu->p->filter_chain, chain_update);
1362         }
1363         else if (filter_chain_GetLength(spu->p->filter_chain) > 0)
1364             filter_chain_Reset(sys->filter_chain, NULL, NULL);
1365
1366         /* "sub-source"  was formerly "sub-filter", so now the "sub-filter"
1367         configuration may contain sub-filters or sub-sources configurations.
1368         if the filters chain was left empty it may indicate that it's a sub-source configuration */
1369         is_left_empty = (filter_chain_GetLength(spu->p->filter_chain) == 0);
1370     }
1371     vlc_mutex_unlock(&sys->filter_chain_lock);
1372
1373     if (is_left_empty) {
1374         /* try to use the configuration as a sub-source configuration,
1375            but only if there is no 'source_chain_update' value and
1376            if only if 'chain_update' has a value */
1377         if (chain_update && *chain_update) {
1378             vlc_mutex_lock(&sys->lock);
1379             if (!sys->source_chain_update || !*sys->source_chain_update) {
1380                 if (sys->source_chain_update)
1381                     free(sys->source_chain_update);
1382                 sys->source_chain_update = chain_update;
1383                 chain_update = NULL;
1384             }
1385             vlc_mutex_unlock(&sys->lock);
1386         }
1387     }
1388
1389     free(chain_update);
1390
1391     /* Run filter chain on the new subpicture */
1392     vlc_mutex_lock(&sys->filter_chain_lock);
1393     subpic = filter_chain_SubFilter(spu->p->filter_chain, subpic);
1394     vlc_mutex_unlock(&sys->filter_chain_lock);
1395     if (!subpic)
1396         return;
1397
1398     /* SPU_DEFAULT_CHANNEL always reset itself */
1399     if (subpic->i_channel == SPU_DEFAULT_CHANNEL)
1400         spu_ClearChannel(spu, SPU_DEFAULT_CHANNEL);
1401
1402     /* p_private is for spu only and cannot be non NULL here */
1403     for (subpicture_region_t *r = subpic->p_region; r != NULL; r = r->p_next)
1404         assert(r->p_private == NULL);
1405
1406     /* */
1407     vlc_mutex_lock(&sys->lock);
1408     if (SpuHeapPush(&sys->heap, subpic)) {
1409         vlc_mutex_unlock(&sys->lock);
1410         msg_Err(spu, "subpicture heap full");
1411         subpicture_Delete(subpic);
1412         return;
1413     }
1414     vlc_mutex_unlock(&sys->lock);
1415 }
1416
1417 subpicture_t *spu_Render(spu_t *spu,
1418                          const vlc_fourcc_t *chroma_list,
1419                          const video_format_t *fmt_dst,
1420                          const video_format_t *fmt_src,
1421                          mtime_t render_subtitle_date,
1422                          mtime_t render_osd_date,
1423                          bool ignore_osd)
1424 {
1425     spu_private_t *sys = spu->p;
1426
1427     /* Update sub-source chain */
1428     vlc_mutex_lock(&sys->lock);
1429     char *chain_update = sys->source_chain_update;
1430     sys->source_chain_update = NULL;
1431     vlc_mutex_unlock(&sys->lock);
1432
1433     vlc_mutex_lock(&sys->source_chain_lock);
1434     if (chain_update) {
1435         filter_chain_ForEach(sys->source_chain, SubSourceClean, spu);
1436         filter_chain_Reset(sys->source_chain, NULL, NULL);
1437
1438         filter_chain_AppendFromString(spu->p->source_chain, chain_update);
1439         filter_chain_ForEach(sys->source_chain, SubSourceInit, spu);
1440
1441         free(chain_update);
1442     }
1443     /* Run subpicture sources */
1444     filter_chain_SubSource(sys->source_chain, spu, render_osd_date);
1445     vlc_mutex_unlock(&sys->source_chain_lock);
1446
1447     static const vlc_fourcc_t chroma_list_default_yuv[] = {
1448         VLC_CODEC_YUVA,
1449         VLC_CODEC_RGBA,
1450         VLC_CODEC_ARGB,
1451         VLC_CODEC_YUVP,
1452         0,
1453     };
1454     static const vlc_fourcc_t chroma_list_default_rgb[] = {
1455         VLC_CODEC_RGBA,
1456         VLC_CODEC_ARGB,
1457         VLC_CODEC_YUVA,
1458         VLC_CODEC_YUVP,
1459         0,
1460     };
1461
1462     if (!chroma_list || *chroma_list == 0)
1463         chroma_list = vlc_fourcc_IsYUV(fmt_dst->i_chroma) ? chroma_list_default_yuv
1464                                                           : chroma_list_default_rgb;
1465
1466     vlc_mutex_lock(&sys->lock);
1467
1468     unsigned int subpicture_count;
1469     subpicture_t *subpicture_array[VOUT_MAX_SUBPICTURES];
1470
1471     /* Get an array of subpictures to render */
1472     SpuSelectSubpictures(spu, &subpicture_count, subpicture_array,
1473                          render_subtitle_date, render_osd_date, ignore_osd);
1474     if (subpicture_count <= 0) {
1475         vlc_mutex_unlock(&sys->lock);
1476         return NULL;
1477     }
1478
1479     /* Updates the subpictures */
1480     for (unsigned i = 0; i < subpicture_count; i++) {
1481         subpicture_t *subpic = subpicture_array[i];
1482         subpicture_Update(subpic,
1483                           fmt_src, fmt_dst,
1484                           subpic->b_subtitle ? render_subtitle_date : render_osd_date);
1485     }
1486
1487     /* Now order the subpicture array
1488      * XXX The order is *really* important for overlap subtitles positionning */
1489     qsort(subpicture_array, subpicture_count, sizeof(*subpicture_array), SubpictureCmp);
1490
1491     /* Render the subpictures */
1492     subpicture_t *render = SpuRenderSubpictures(spu,
1493                                                 subpicture_count, subpicture_array,
1494                                                 chroma_list,
1495                                                 fmt_dst,
1496                                                 fmt_src,
1497                                                 render_subtitle_date,
1498                                                 render_osd_date);
1499     vlc_mutex_unlock(&sys->lock);
1500
1501     return render;
1502 }
1503
1504 void spu_OffsetSubtitleDate(spu_t *spu, mtime_t duration)
1505 {
1506     spu_private_t *sys = spu->p;
1507
1508     vlc_mutex_lock(&sys->lock);
1509     for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
1510         spu_heap_entry_t *entry = &sys->heap.entry[i];
1511         subpicture_t *current = entry->subpicture;
1512
1513         if (current && current->b_subtitle) {
1514             if (current->i_start > 0)
1515                 current->i_start += duration;
1516             if (current->i_stop > 0)
1517                 current->i_stop  += duration;
1518         }
1519     }
1520     vlc_mutex_unlock(&sys->lock);
1521 }
1522
1523 int spu_RegisterChannel(spu_t *spu)
1524 {
1525     spu_private_t *sys = spu->p;
1526
1527     vlc_mutex_lock(&sys->lock);
1528     int channel = sys->channel++;
1529     vlc_mutex_unlock(&sys->lock);
1530
1531     return channel;
1532 }
1533
1534 void spu_ClearChannel(spu_t *spu, int channel)
1535 {
1536     spu_private_t *sys = spu->p;
1537
1538     vlc_mutex_lock(&sys->lock);
1539
1540     for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
1541         spu_heap_entry_t *entry = &sys->heap.entry[i];
1542         subpicture_t *subpic = entry->subpicture;
1543
1544         if (!subpic)
1545             continue;
1546         if (subpic->i_channel != channel && (channel != -1 || subpic->i_channel == SPU_DEFAULT_CHANNEL))
1547             continue;
1548
1549         /* You cannot delete subpicture outside of spu_SortSubpictures */
1550         entry->reject = true;
1551     }
1552
1553     vlc_mutex_unlock(&sys->lock);
1554 }
1555
1556 void spu_ChangeSources(spu_t *spu, const char *filters)
1557 {
1558     spu_private_t *sys = spu->p;
1559
1560     vlc_mutex_lock(&sys->lock);
1561
1562     free(sys->source_chain_update);
1563     sys->source_chain_update = strdup(filters);
1564
1565     vlc_mutex_unlock(&sys->lock);
1566 }
1567
1568 void spu_ChangeFilters(spu_t *spu, const char *filters)
1569 {
1570     spu_private_t *sys = spu->p;
1571
1572     vlc_mutex_lock(&sys->lock);
1573
1574     free(sys->filter_chain_update);
1575     sys->filter_chain_update = strdup(filters);
1576
1577     vlc_mutex_unlock(&sys->lock);
1578 }
1579
1580 void spu_ChangeMargin(spu_t *spu, int margin)
1581 {
1582     spu_private_t *sys = spu->p;
1583
1584     vlc_mutex_lock(&sys->lock);
1585     sys->margin = margin;
1586     vlc_mutex_unlock(&sys->lock);
1587 }
1588