Add callback to change mosaic mask at runtime.
[vlc.git] / modules / stream_out / mosaic_bridge.c
1 /*****************************************************************************
2  * mosaic_bridge.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea@videolan.org>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <stdlib.h>                                                /* free() */
30 #include <string.h>                                            /* strerror() */
31
32 #include <vlc/vlc.h>
33 #include <vlc_sout.h>
34 #include <vlc_block.h>
35 #include <vlc_codec.h>
36
37 #include "vlc_image.h"
38
39 #include "../video_filter/mosaic.h"
40
41 /*****************************************************************************
42  * Local structures
43  *****************************************************************************/
44 struct sout_stream_sys_t
45 {
46     bridged_es_t *p_es;
47     vlc_mutex_t *p_lock;
48
49     decoder_t       *p_decoder;
50     image_handler_t *p_image; /* filter for resizing */
51     int i_height, i_width;
52     unsigned int i_sar_num, i_sar_den;
53     char *psz_id;
54     vlc_bool_t b_inited;
55
56     picture_t *p_mask;
57     vlc_mutex_t mask_lock;
58 };
59
60 #define PICTURE_RING_SIZE 4
61 struct decoder_owner_sys_t
62 {
63     picture_t *pp_pics[PICTURE_RING_SIZE];
64
65     /* Current format in use by the output */
66     video_format_t video;
67 };
68
69 typedef void (* pf_release_t)( picture_t * );
70 static void ReleasePicture( picture_t *p_pic )
71 {
72     p_pic->i_refcount--;
73
74     if ( p_pic->i_refcount <= 0 )
75     {
76         if ( p_pic->p_sys != NULL )
77         {
78             pf_release_t pf_release = (pf_release_t)p_pic->p_sys;
79             p_pic->p_sys = NULL;
80             pf_release( p_pic );
81         }
82         else
83         {
84             if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
85             if( p_pic ) free( p_pic );
86         }
87     }
88 }
89
90 /* copied from video_filters/erase.c . Gruik ? */
91 static void LoadMask( sout_stream_t *p_stream, const char *psz_filename )
92 {
93     image_handler_t *p_image;
94     video_format_t fmt_in, fmt_out;
95     memset( &fmt_in, 0, sizeof( video_format_t ) );
96     memset( &fmt_out, 0, sizeof( video_format_t ) );
97     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
98     if( p_stream->p_sys->p_mask )
99         p_stream->p_sys->p_mask->pf_release( p_stream->p_sys->p_mask );
100     p_image = image_HandlerCreate( p_stream );
101     p_stream->p_sys->p_mask =
102         image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );
103     image_HandlerDelete( p_image );
104 }
105
106 /*****************************************************************************
107  * Local prototypes
108  *****************************************************************************/
109 static int  Open    ( vlc_object_t * );
110 static void Close   ( vlc_object_t * );
111 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
112 static int               Del ( sout_stream_t *, sout_stream_id_t * );
113 static int               Send( sout_stream_t *, sout_stream_id_t *, block_t * );
114
115 static void video_del_buffer( decoder_t *, picture_t * );
116 static picture_t *video_new_buffer( decoder_t * );
117 static void video_link_picture_decoder( decoder_t *, picture_t * );
118 static void video_unlink_picture_decoder( decoder_t *, picture_t * );
119 static int MosaicBridgeCallback( vlc_object_t *, char const *,
120                                  vlc_value_t, vlc_value_t, void * );
121
122 /*****************************************************************************
123  * Module descriptor
124  *****************************************************************************/
125 #define ID_TEXT N_("ID")
126 #define ID_LONGTEXT N_( \
127     "Specify an identifier string for this subpicture" )
128
129 #define WIDTH_TEXT N_("Video width")
130 #define WIDTH_LONGTEXT N_( \
131     "Output video width." )
132 #define HEIGHT_TEXT N_("Video height")
133 #define HEIGHT_LONGTEXT N_( \
134     "Output video height." )
135 #define RATIO_TEXT N_("Sample aspect ratio")
136 #define RATIO_LONGTEXT N_( \
137     "Sample aspect ratio of the destination (1:1, 3:4, 2:3)." )
138 #define MASK_TEXT N_("Transparency mask")
139 #define MASK_LONGTEXT N_( \
140     "Alpha blending transparency mask. Use's a png alpha channel.")
141
142 #define SOUT_CFG_PREFIX "sout-mosaic-bridge-"
143
144 vlc_module_begin();
145     set_shortname( _( "Mosaic bridge" ) );
146     set_description(_("Mosaic bridge stream output") );
147     set_capability( "sout stream", 0 );
148     add_shortcut( "mosaic-bridge" );
149
150     add_string( SOUT_CFG_PREFIX "id", "Id", NULL, ID_TEXT, ID_LONGTEXT,
151                 VLC_FALSE );
152     add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
153                  WIDTH_LONGTEXT, VLC_TRUE );
154     add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
155                  HEIGHT_LONGTEXT, VLC_TRUE );
156     add_string( SOUT_CFG_PREFIX "sar", "1:1", NULL, RATIO_TEXT,
157                 RATIO_LONGTEXT, VLC_FALSE );
158     add_string( SOUT_CFG_PREFIX "mask", NULL, NULL, MASK_TEXT,
159                 MASK_LONGTEXT, VLC_FALSE );
160
161     set_callbacks( Open, Close );
162
163     var_Create( p_module->p_libvlc_global, "mosaic-lock", VLC_VAR_MUTEX );
164 vlc_module_end();
165
166 static const char *ppsz_sout_options[] = {
167     "id", "width", "height", "sar", "mask", NULL
168 };
169
170 /*****************************************************************************
171  * Open
172  *****************************************************************************/
173 static int Open( vlc_object_t *p_this )
174 {
175     sout_stream_t     *p_stream = (sout_stream_t *)p_this;
176     sout_stream_sys_t *p_sys;
177     libvlc_global_data_t *p_libvlc_global = p_this->p_libvlc_global;
178     vlc_value_t val;
179
180     config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
181                    p_stream->p_cfg );
182
183     p_sys          = malloc( sizeof( sout_stream_sys_t ) );
184     p_stream->p_sys = p_sys;
185     p_sys->b_inited = VLC_FALSE;
186
187     var_Get( p_libvlc_global, "mosaic-lock", &val );
188     p_sys->p_lock = val.p_address;
189
190     var_Get( p_stream, SOUT_CFG_PREFIX "id", &val );
191     p_sys->psz_id = val.psz_string;
192
193     var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
194     p_sys->i_height = val.i_int;
195
196     var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
197     p_sys->i_width = val.i_int;
198
199     vlc_mutex_init( p_stream, &p_sys->mask_lock );
200     val.psz_string =
201     var_CreateGetStringCommand( p_stream, SOUT_CFG_PREFIX "mask" );
202     var_AddCallback( p_stream, SOUT_CFG_PREFIX "mask", MosaicBridgeCallback,
203                      p_stream );
204     if( val.psz_string && *val.psz_string )
205     {
206         p_sys->p_mask = NULL;
207         LoadMask( p_stream, val.psz_string );
208         if( !p_sys->p_mask )
209             msg_Err( p_stream, "Error while loading mask (%s).",
210                      val.psz_string );
211     }
212     else
213         p_sys->p_mask = NULL;
214     free( val.psz_string );
215
216     var_Get( p_stream, SOUT_CFG_PREFIX "sar", &val );
217     if ( val.psz_string )
218     {
219         char *psz_parser = strchr( val.psz_string, ':' );
220
221         if( psz_parser )
222         {
223             *psz_parser++ = '\0';
224             p_sys->i_sar_num = atoi( val.psz_string );
225             p_sys->i_sar_den = atoi( psz_parser );
226             vlc_ureduce( &p_sys->i_sar_num, &p_sys->i_sar_den,
227                          p_sys->i_sar_num, p_sys->i_sar_den, 0 );
228         }
229         else
230         {
231             msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
232             p_sys->i_sar_num = p_sys->i_sar_den = 1;
233         }
234
235         free( val.psz_string );
236     }
237     else
238     {
239         p_sys->i_sar_num = p_sys->i_sar_den = 1;
240     }
241
242     p_stream->pf_add    = Add;
243     p_stream->pf_del    = Del;
244     p_stream->pf_send   = Send;
245
246     p_stream->p_sout->i_out_pace_nocontrol++;
247
248     return VLC_SUCCESS;
249 }
250
251 /*****************************************************************************
252  * Close
253  *****************************************************************************/
254 static void Close( vlc_object_t * p_this )
255 {
256     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
257     sout_stream_sys_t *p_sys = p_stream->p_sys;
258
259     p_stream->p_sout->i_out_pace_nocontrol--;
260
261     if ( p_sys->psz_id )
262         free( p_sys->psz_id );
263
264     vlc_mutex_destroy( &p_sys->mask_lock );
265     if( p_stream->p_sys->p_mask )
266         p_stream->p_sys->p_mask->pf_release( p_stream->p_sys->p_mask );
267
268     free( p_sys );
269 }
270
271 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
272 {
273     sout_stream_sys_t *p_sys = p_stream->p_sys;
274     bridge_t *p_bridge;
275     bridged_es_t *p_es;
276     int i;
277
278     if ( p_sys->b_inited )
279     {
280         return NULL;
281     }
282
283     /* Create decoder object */
284     p_sys->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
285     vlc_object_attach( p_sys->p_decoder, p_stream );
286     p_sys->p_decoder->p_module = NULL;
287     p_sys->p_decoder->fmt_in = *p_fmt;
288     p_sys->p_decoder->b_pace_control = VLC_FALSE;
289     p_sys->p_decoder->fmt_out = p_sys->p_decoder->fmt_in;
290     p_sys->p_decoder->fmt_out.i_extra = 0;
291     p_sys->p_decoder->fmt_out.p_extra = 0;
292     p_sys->p_decoder->pf_decode_video = 0;
293     p_sys->p_decoder->pf_vout_buffer_new = video_new_buffer;
294     p_sys->p_decoder->pf_vout_buffer_del = video_del_buffer;
295     p_sys->p_decoder->pf_picture_link    = video_link_picture_decoder;
296     p_sys->p_decoder->pf_picture_unlink  = video_unlink_picture_decoder;
297     p_sys->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
298     for( i = 0; i < PICTURE_RING_SIZE; i++ )
299         p_sys->p_decoder->p_owner->pp_pics[i] = 0;
300     p_sys->p_decoder->p_owner->video = p_fmt->video;
301     //p_sys->p_decoder->p_cfg = p_sys->p_video_cfg;
302
303     p_sys->p_decoder->p_module =
304         module_Need( p_sys->p_decoder, "decoder", "$codec", 0 );
305
306     if( !p_sys->p_decoder->p_module )
307     {
308         msg_Err( p_stream, "cannot find decoder" );
309         vlc_object_detach( p_sys->p_decoder );
310         vlc_object_destroy( p_sys->p_decoder );
311         return NULL;
312     }
313
314     p_sys->b_inited = VLC_TRUE;
315     vlc_mutex_lock( p_sys->p_lock );
316
317     p_bridge = GetBridge( p_stream );
318     if ( p_bridge == NULL )
319     {
320         libvlc_global_data_t *p_libvlc_global = p_stream->p_libvlc_global;
321         vlc_value_t val;
322
323         p_bridge = malloc( sizeof( bridge_t ) );
324
325         var_Create( p_libvlc_global, "mosaic-struct", VLC_VAR_ADDRESS );
326         val.p_address = p_bridge;
327         var_Set( p_libvlc_global, "mosaic-struct", val );
328
329         p_bridge->i_es_num = 0;
330         p_bridge->pp_es = NULL;
331     }
332
333     for ( i = 0; i < p_bridge->i_es_num; i++ )
334     {
335         if ( p_bridge->pp_es[i]->b_empty )
336             break;
337     }
338
339     if ( i == p_bridge->i_es_num )
340     {
341         p_bridge->pp_es = realloc( p_bridge->pp_es,
342                                    (p_bridge->i_es_num + 1)
343                                      * sizeof(bridged_es_t *) );
344         p_bridge->i_es_num++;
345         p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
346     }
347
348     p_sys->p_es = p_es = p_bridge->pp_es[i];
349
350     //p_es->fmt = *p_fmt;
351     p_es->psz_id = p_sys->psz_id;
352     p_es->p_picture = NULL;
353     p_es->pp_last = &p_es->p_picture;
354     p_es->b_empty = VLC_FALSE;
355
356     vlc_mutex_unlock( p_sys->p_lock );
357
358     if ( p_sys->i_height || p_sys->i_width )
359     {
360         p_sys->p_image = image_HandlerCreate( p_stream );
361     }
362
363     msg_Dbg( p_stream, "mosaic bridge id=%s pos=%d", p_es->psz_id, i );
364
365     return (sout_stream_id_t *)p_sys;
366 }
367
368 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
369 {
370     sout_stream_sys_t *p_sys = p_stream->p_sys;
371     bridge_t *p_bridge;
372     bridged_es_t *p_es;
373     vlc_bool_t b_last_es = VLC_TRUE;
374     int i;
375
376     if ( !p_sys->b_inited )
377     {
378         return VLC_SUCCESS;
379     }
380
381     if ( p_sys->p_decoder != NULL )
382     {
383         picture_t **pp_ring = p_sys->p_decoder->p_owner->pp_pics;
384
385         if( p_sys->p_decoder->p_module )
386             module_Unneed( p_sys->p_decoder, p_sys->p_decoder->p_module );
387         vlc_object_detach( p_sys->p_decoder );
388         vlc_object_destroy( p_sys->p_decoder );
389
390         for( i = 0; i < PICTURE_RING_SIZE; i++ )
391         {
392             if ( pp_ring[i] != NULL )
393             {
394                 if ( pp_ring[i]->p_data_orig != NULL )
395                     free( pp_ring[i]->p_data_orig );
396                 free( pp_ring[i]->p_sys );
397                 free( pp_ring[i] );
398             }
399         }
400     }
401
402     vlc_mutex_lock( p_sys->p_lock );
403
404     p_bridge = GetBridge( p_stream );
405     p_es = p_sys->p_es;
406
407     p_es->b_empty = VLC_TRUE;
408     while ( p_es->p_picture )
409     {
410         picture_t *p_next = p_es->p_picture->p_next;
411         p_es->p_picture->pf_release( p_es->p_picture );
412         p_es->p_picture = p_next;
413     }
414
415     for ( i = 0; i < p_bridge->i_es_num; i++ )
416     {
417         if ( !p_bridge->pp_es[i]->b_empty )
418         {
419             b_last_es = VLC_FALSE;
420             break;
421         }
422     }
423
424     if ( b_last_es )
425     {
426         libvlc_global_data_t *p_libvlc_global = p_stream->p_libvlc_global;
427         for ( i = 0; i < p_bridge->i_es_num; i++ )
428             free( p_bridge->pp_es[i] );
429         free( p_bridge->pp_es );
430         free( p_bridge );
431         var_Destroy( p_libvlc_global, "mosaic-struct" );
432     }
433
434     vlc_mutex_unlock( p_sys->p_lock );
435
436     if ( p_sys->i_height || p_sys->i_width )
437     {
438         image_HandlerDelete( p_sys->p_image );
439     }
440
441     p_sys->b_inited = VLC_FALSE;
442
443     return VLC_SUCCESS;
444 }
445
446 /*****************************************************************************
447  * PushPicture : push a picture in the mosaic-struct structure
448  *****************************************************************************/
449 static void PushPicture( sout_stream_t *p_stream, picture_t *p_picture )
450 {
451     sout_stream_sys_t *p_sys = p_stream->p_sys;
452     bridged_es_t *p_es = p_sys->p_es;
453
454     vlc_mutex_lock( p_sys->p_lock );
455
456     *p_es->pp_last = p_picture;
457     p_picture->p_next = NULL;
458     p_es->pp_last = &p_picture->p_next;
459
460     vlc_mutex_unlock( p_sys->p_lock );
461 }
462
463 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
464                  block_t *p_buffer )
465 {
466     sout_stream_sys_t *p_sys = p_stream->p_sys;
467     picture_t *p_pic;
468
469     if ( (sout_stream_sys_t *)id != p_sys )
470     {
471         block_ChainRelease( p_buffer );
472         return VLC_SUCCESS;
473     }
474
475     while ( (p_pic = p_sys->p_decoder->pf_decode_video( p_sys->p_decoder,
476                                                         &p_buffer )) )
477     {
478         picture_t *p_new_pic;
479
480         if( p_sys->i_height || p_sys->i_width )
481         {
482             video_format_t fmt_out, fmt_in;
483
484             memset( &fmt_in, 0, sizeof(video_format_t) );
485             memset( &fmt_out, 0, sizeof(video_format_t) );
486             fmt_in = p_sys->p_decoder->fmt_out.video;
487
488             if( p_sys->p_mask )
489             {
490                 vlc_mutex_lock( &p_sys->mask_lock );
491                 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
492             }
493             else
494                 fmt_out.i_chroma = VLC_FOURCC('I','4','2','0');
495
496             if ( !p_sys->i_height )
497             {
498                 fmt_out.i_width = p_sys->i_width;
499                 fmt_out.i_height = (p_sys->i_width * VOUT_ASPECT_FACTOR
500                     * p_sys->i_sar_num / p_sys->i_sar_den / fmt_in.i_aspect)
501                       & ~0x1;
502             }
503             else if ( !p_sys->i_width )
504             {
505                 fmt_out.i_height = p_sys->i_height;
506                 fmt_out.i_width = (p_sys->i_height * fmt_in.i_aspect
507                     * p_sys->i_sar_den / p_sys->i_sar_num / VOUT_ASPECT_FACTOR)
508                       & ~0x1;
509             }
510             else
511             {
512                 fmt_out.i_width = p_sys->i_width;
513                 fmt_out.i_height = p_sys->i_height;
514             }
515             fmt_out.i_visible_width = fmt_out.i_width;
516             fmt_out.i_visible_height = fmt_out.i_height;
517
518             p_new_pic = image_Convert( p_sys->p_image,
519                                        p_pic, &fmt_in, &fmt_out );
520             if ( p_new_pic == NULL )
521             {
522                 msg_Err( p_stream, "image conversion failed" );
523                 continue;
524             }
525
526             if( p_sys->p_mask )
527             {
528                 plane_t *p_mask = p_sys->p_mask->p+A_PLANE;
529                 plane_t *p_apic = p_new_pic->p+A_PLANE;
530                 if(    p_mask->i_visible_pitch
531                     != p_apic->i_visible_pitch
532                     || p_mask->i_visible_lines
533                     != p_apic->i_visible_lines )
534                 {
535                     msg_Warn( p_stream,
536                               "Mask size (%d x %d) and image size (%d x %d) "
537                               "don't match. The mask will not be applied.",
538                               p_mask->i_visible_pitch,
539                               p_mask->i_visible_lines,
540                               p_apic->i_visible_pitch,
541                               p_apic->i_visible_lines );
542                 }
543                 else
544                 {
545                     if( p_mask->i_pitch != p_apic->i_pitch
546                     ||  p_mask->i_lines != p_apic->i_lines )
547                     {
548                         /* visible plane sizes match ... but not the undelying
549                          * buffer. I'm not sure that this can happen,
550                          * but better safe than sorry. */
551                         int i_line;
552                         int i_lines = p_mask->i_visible_lines;
553                         uint8_t *p_src = p_mask->p_pixels;
554                         uint8_t *p_dst = p_apic->p_pixels;
555                         int i_src_pitch = p_mask->i_pitch;
556                         int i_dst_pitch = p_apic->i_pitch;
557                         int i_visible_pitch = p_mask->i_visible_pitch;
558                         for( i_line = 0; i_line < i_lines; i_line++,
559                              p_src += i_src_pitch, p_dst += i_dst_pitch )
560                         {
561                             p_stream->p_libvlc->pf_memcpy(
562                                 p_dst, p_src, i_visible_pitch );
563                         }
564                     }
565                     else
566                     {
567                         /* plane sizes match */
568                         p_stream->p_libvlc->pf_memcpy(
569                             p_apic->p_pixels, p_mask->p_pixels,
570                             p_mask->i_pitch * p_mask->i_lines );
571                     }
572                 }
573                 vlc_mutex_unlock( &p_sys->mask_lock );
574             }
575         }
576         else
577         {
578             p_new_pic = (picture_t*)malloc( sizeof(picture_t) );
579             vout_AllocatePicture( p_stream, p_new_pic, p_pic->format.i_chroma,
580                                   p_pic->format.i_width, p_pic->format.i_height,
581                                   p_sys->p_decoder->fmt_out.video.i_aspect );
582
583             vout_CopyPicture( p_stream, p_new_pic, p_pic );
584         }
585
586         p_new_pic->i_refcount = 1;
587         p_new_pic->i_status = DESTROYED_PICTURE;
588         p_new_pic->i_type   = DIRECT_PICTURE;
589         p_new_pic->p_sys = (picture_sys_t *)p_new_pic->pf_release;
590         p_new_pic->pf_release = ReleasePicture;
591         p_new_pic->date = p_pic->date;
592
593         p_pic->pf_release( p_pic );
594         PushPicture( p_stream, p_new_pic );
595     }
596
597     return VLC_SUCCESS;
598 }
599
600 struct picture_sys_t
601 {
602     vlc_object_t *p_owner;
603     vlc_bool_t b_dead;
604 };
605
606 static void video_release_buffer( picture_t *p_pic )
607 {
608     if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
609     {
610         video_del_buffer( (decoder_t *)p_pic->p_sys->p_owner, p_pic );
611     }
612     else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
613 }
614
615 static picture_t *video_new_buffer( decoder_t *p_dec )
616 {
617     decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner;
618     picture_t **pp_ring = p_dec->p_owner->pp_pics;
619     picture_t *p_pic;
620     int i;
621
622     if( p_dec->fmt_out.video.i_width != p_sys->video.i_width ||
623         p_dec->fmt_out.video.i_height != p_sys->video.i_height ||
624         p_dec->fmt_out.video.i_chroma != p_sys->video.i_chroma ||
625         p_dec->fmt_out.video.i_aspect != p_sys->video.i_aspect )
626     {
627         if( !p_dec->fmt_out.video.i_sar_num ||
628             !p_dec->fmt_out.video.i_sar_den )
629         {
630             p_dec->fmt_out.video.i_sar_num =
631               p_dec->fmt_out.video.i_aspect * p_dec->fmt_out.video.i_height;
632
633             p_dec->fmt_out.video.i_sar_den = VOUT_ASPECT_FACTOR *
634               p_dec->fmt_out.video.i_width;
635         }
636
637         vlc_ureduce( &p_dec->fmt_out.video.i_sar_num,
638                      &p_dec->fmt_out.video.i_sar_den,
639                      p_dec->fmt_out.video.i_sar_num,
640                      p_dec->fmt_out.video.i_sar_den, 0 );
641
642         if( !p_dec->fmt_out.video.i_visible_width ||
643             !p_dec->fmt_out.video.i_visible_height )
644         {
645             p_dec->fmt_out.video.i_visible_width =
646                 p_dec->fmt_out.video.i_width;
647             p_dec->fmt_out.video.i_visible_height =
648                 p_dec->fmt_out.video.i_height;
649         }
650
651         p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
652         p_sys->video = p_dec->fmt_out.video;
653
654         for( i = 0; i < PICTURE_RING_SIZE; i++ )
655         {
656             if ( pp_ring[i] != NULL )
657             {
658                 if ( pp_ring[i]->i_status == DESTROYED_PICTURE )
659                 {
660                     if ( pp_ring[i]->p_data_orig != NULL )
661                         free( pp_ring[i]->p_data_orig );
662                     free( pp_ring[i]->p_sys );
663                     free( pp_ring[i] );
664                 }
665                 else
666                 {
667                     pp_ring[i]->p_sys->b_dead = VLC_TRUE;
668                 }
669                 pp_ring[i] = NULL;
670             }
671         }
672     }
673
674     /* Find an empty space in the picture ring buffer */
675     for( i = 0; i < PICTURE_RING_SIZE; i++ )
676     {
677         if( pp_ring[i] != NULL && pp_ring[i]->i_status == DESTROYED_PICTURE )
678         {
679             pp_ring[i]->i_status = RESERVED_PICTURE;
680             return pp_ring[i];
681         }
682     }
683     for( i = 0; i < PICTURE_RING_SIZE; i++ )
684     {
685         if( pp_ring[i] == NULL ) break;
686     }
687
688     if( i == PICTURE_RING_SIZE )
689     {
690         msg_Err( p_dec, "decoder/filter is leaking pictures, "
691                  "resetting its ring buffer" );
692
693         for( i = 0; i < PICTURE_RING_SIZE; i++ )
694         {
695             pp_ring[i]->pf_release( pp_ring[i] );
696         }
697
698         i = 0;
699     }
700
701     p_pic = malloc( sizeof(picture_t) );
702     p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
703     vout_AllocatePicture( VLC_OBJECT(p_dec), p_pic,
704                           p_dec->fmt_out.video.i_chroma,
705                           p_dec->fmt_out.video.i_width,
706                           p_dec->fmt_out.video.i_height,
707                           p_dec->fmt_out.video.i_aspect );
708
709     if( !p_pic->i_planes )
710     {
711         free( p_pic );
712         return NULL;
713     }
714
715     p_pic->pf_release = video_release_buffer;
716     p_pic->p_sys = malloc( sizeof(picture_sys_t) );
717     p_pic->p_sys->p_owner = VLC_OBJECT(p_dec);
718     p_pic->p_sys->b_dead = VLC_FALSE;
719     p_pic->i_status = RESERVED_PICTURE;
720
721     pp_ring[i] = p_pic;
722
723     return p_pic;
724 }
725
726 static void video_del_buffer( decoder_t *p_this, picture_t *p_pic )
727 {
728     p_pic->i_refcount = 0;
729     p_pic->i_status = DESTROYED_PICTURE;
730     if ( p_pic->p_sys->b_dead )
731     {
732         if ( p_pic->p_data_orig != NULL )
733             free( p_pic->p_data_orig );
734         free( p_pic->p_sys );
735         free( p_pic );
736     }
737 }
738
739 static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
740 {
741     p_pic->i_refcount++;
742 }
743
744 static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
745 {
746     video_release_buffer( p_pic );
747 }
748
749
750 /**********************************************************************
751  * Callback to update (some) params on the fly
752  **********************************************************************/
753 static int MosaicBridgeCallback( vlc_object_t *p_this, char const *psz_var,
754                                  vlc_value_t oldval, vlc_value_t newval,
755                                  void *p_data )
756 {
757     sout_stream_t *p_stream = (sout_stream_t *)p_data;
758     sout_stream_sys_t *p_sys = p_stream->p_sys;
759
760     if( !strcmp( psz_var, SOUT_CFG_PREFIX "mask" ) )
761     {
762         vlc_mutex_lock( &p_sys->mask_lock );
763         if( newval.psz_string && *newval.psz_string )
764         {
765             LoadMask( p_stream, newval.psz_string );
766             if( !p_sys->p_mask )
767                 msg_Err( p_stream, "Error while loading mask (%s).",
768                          newval.psz_string );
769         }
770         else if( p_sys->p_mask )
771         {
772             p_sys->p_mask->pf_release( p_sys->p_mask );
773             p_sys->p_mask = NULL;
774         }
775         vlc_mutex_unlock( &p_sys->mask_lock );
776     }
777
778     return VLC_SUCCESS;
779 }