]> git.sesse.net Git - vlc/blob - modules/stream_out/mosaic_bridge.c
17c396ec4db114be80f1b376e5deee827fc9ae23
[vlc] / modules / stream_out / mosaic_bridge.c
1 /*****************************************************************************
2  * mosaic_bridge.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2007 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
30 #include <vlc/vlc.h>
31 #include <vlc_sout.h>
32 #include <vlc_block.h>
33 #include <vlc_codec.h>
34
35 #include <vlc_image.h>
36 #include <vlc_filter.h>
37
38 #include "../video_filter/mosaic.h"
39
40 /*****************************************************************************
41  * Local structures
42  *****************************************************************************/
43 struct sout_stream_sys_t
44 {
45     bridged_es_t *p_es;
46     vlc_mutex_t *p_lock;
47
48     decoder_t       *p_decoder;
49     image_handler_t *p_image; /* filter for resizing */
50     int i_height, i_width;
51     unsigned int i_sar_num, i_sar_den;
52     char *psz_id;
53     vlc_bool_t b_inited;
54
55     int i_chroma; /* force image format chroma */
56
57     filter_t **pp_vfilters;
58     int i_vfilters;
59 };
60
61 #define PICTURE_RING_SIZE 4
62 struct decoder_owner_sys_t
63 {
64     picture_t *pp_pics[PICTURE_RING_SIZE];
65
66     /* Current format in use by the output */
67     video_format_t video;
68 };
69
70 typedef void (* pf_release_t)( picture_t * );
71 static void ReleasePicture( picture_t *p_pic )
72 {
73     p_pic->i_refcount--;
74
75     if ( p_pic->i_refcount <= 0 )
76     {
77         if ( p_pic->p_sys != NULL )
78         {
79             pf_release_t pf_release = (pf_release_t)p_pic->p_sys;
80             p_pic->p_sys = NULL;
81             pf_release( p_pic );
82         }
83         else
84         {
85             if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
86             if( p_pic ) free( p_pic );
87         }
88     }
89 }
90
91 /*****************************************************************************
92  * Local prototypes
93  *****************************************************************************/
94 static int  Open    ( vlc_object_t * );
95 static void Close   ( vlc_object_t * );
96 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
97 static int               Del ( sout_stream_t *, sout_stream_id_t * );
98 static int               Send( sout_stream_t *, sout_stream_id_t *, block_t * );
99
100 inline static void video_del_buffer_decoder( decoder_t *, picture_t * );
101 inline static void video_del_buffer_filter( filter_t *, picture_t * );
102 static void video_del_buffer( vlc_object_t *, picture_t * );
103
104 inline static picture_t *video_new_buffer_decoder( decoder_t * );
105 inline static picture_t *video_new_buffer_filter( filter_t * );
106 static picture_t *video_new_buffer( vlc_object_t *, decoder_owner_sys_t *,
107                                     es_format_t *, void (*)( picture_t * ) );
108
109 static void video_link_picture_decoder( decoder_t *, picture_t * );
110 static void video_unlink_picture_decoder( decoder_t *, picture_t * );
111 static int MosaicBridgeCallback( vlc_object_t *, char const *,
112                                  vlc_value_t, vlc_value_t, void * );
113
114 /*****************************************************************************
115  * Module descriptor
116  *****************************************************************************/
117 #define ID_TEXT N_("ID")
118 #define ID_LONGTEXT N_( \
119     "Specify an identifier string for this subpicture" )
120
121 #define WIDTH_TEXT N_("Video width")
122 #define WIDTH_LONGTEXT N_( \
123     "Output video width." )
124 #define HEIGHT_TEXT N_("Video height")
125 #define HEIGHT_LONGTEXT N_( \
126     "Output video height." )
127 #define RATIO_TEXT N_("Sample aspect ratio")
128 #define RATIO_LONGTEXT N_( \
129     "Sample aspect ratio of the destination (1:1, 3:4, 2:3)." )
130
131 #define VFILTER_TEXT N_("Video filter")
132 #define VFILTER_LONGTEXT N_( \
133     "Video filters will be applied to the video stream." )
134
135 #define CHROMA_TEXT N_("Image chroma")
136 #define CHROMA_LONGTEXT N_( \
137     "Force the use of a specific chroma. Use YUVA if you're planning " \
138     "to use the Alphamask or Bluescreen video filter." )
139
140 #define CFG_PREFIX "sout-mosaic-bridge-"
141
142 vlc_module_begin();
143     set_shortname( _( "Mosaic bridge" ) );
144     set_description(_("Mosaic bridge stream output") );
145     set_capability( "sout stream", 0 );
146     add_shortcut( "mosaic-bridge" );
147
148     add_string( CFG_PREFIX "id", "Id", NULL, ID_TEXT, ID_LONGTEXT,
149                 VLC_FALSE );
150     add_integer( CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
151                  WIDTH_LONGTEXT, VLC_TRUE );
152     add_integer( CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
153                  HEIGHT_LONGTEXT, VLC_TRUE );
154     add_string( CFG_PREFIX "sar", "1:1", NULL, RATIO_TEXT,
155                 RATIO_LONGTEXT, VLC_FALSE );
156     add_string( CFG_PREFIX "chroma", 0, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
157                 VLC_FALSE );
158
159     add_module_list( CFG_PREFIX "vfilter", "video filter2",
160                      NULL, NULL, VFILTER_TEXT, VFILTER_LONGTEXT, VLC_FALSE );
161
162     set_callbacks( Open, Close );
163 vlc_module_end();
164
165 static const char *ppsz_sout_options[] = {
166     "id", "width", "height", "sar", "vfilter", "chroma", NULL
167 };
168
169 /*****************************************************************************
170  * Open
171  *****************************************************************************/
172 static int Open( vlc_object_t *p_this )
173 {
174     sout_stream_t        *p_stream = (sout_stream_t *)p_this;
175     sout_stream_sys_t    *p_sys;
176     vlc_object_t         *p_libvlc = VLC_OBJECT( p_this->p_libvlc );
177     vlc_value_t           val;
178
179     config_ChainParse( p_stream, CFG_PREFIX, ppsz_sout_options,
180                        p_stream->p_cfg );
181
182     p_sys = malloc( sizeof( sout_stream_sys_t ) );
183     if( !p_sys )
184     {
185         return VLC_ENOMEM;
186     }
187
188     p_stream->p_sys = p_sys;
189     p_sys->b_inited = VLC_FALSE;
190
191     var_Create( p_libvlc, "mosaic-lock", VLC_VAR_MUTEX );
192     var_Get( p_libvlc, "mosaic-lock", &val );
193     p_sys->p_lock = val.p_address;
194
195     var_Get( p_stream, CFG_PREFIX "id", &val );
196     p_sys->psz_id = val.psz_string;
197
198     p_sys->i_height =
199         var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "height" );
200     var_AddCallback( p_stream, CFG_PREFIX "height", MosaicBridgeCallback,
201                      p_stream );
202
203     p_sys->i_width =
204         var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "width" );
205     var_AddCallback( p_stream, CFG_PREFIX "width", MosaicBridgeCallback,
206                      p_stream );
207
208     var_Get( p_stream, CFG_PREFIX "sar", &val );
209     if ( val.psz_string )
210     {
211         char *psz_parser = strchr( val.psz_string, ':' );
212
213         if( psz_parser )
214         {
215             *psz_parser++ = '\0';
216             p_sys->i_sar_num = atoi( val.psz_string );
217             p_sys->i_sar_den = atoi( psz_parser );
218             vlc_ureduce( &p_sys->i_sar_num, &p_sys->i_sar_den,
219                          p_sys->i_sar_num, p_sys->i_sar_den, 0 );
220         }
221         else
222         {
223             msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
224             p_sys->i_sar_num = p_sys->i_sar_den = 1;
225         }
226
227         free( val.psz_string );
228     }
229     else
230     {
231         p_sys->i_sar_num = p_sys->i_sar_den = 1;
232     }
233
234     p_sys->i_chroma = 0;
235     val.psz_string = var_GetNonEmptyString( p_stream, CFG_PREFIX "chroma" );
236     if( val.psz_string && strlen( val.psz_string ) >= 4 )
237     {
238         memcpy( &p_sys->i_chroma, val.psz_string, 4 );
239         msg_Dbg( p_stream, "Forcing image chroma to 0x%.8x (%4.4s)", p_sys->i_chroma, (char*)&p_sys->i_chroma );
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     free( p_sys );
265 }
266
267 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
268 {
269     sout_stream_sys_t *p_sys = p_stream->p_sys;
270     bridge_t *p_bridge;
271     bridged_es_t *p_es;
272     char *psz_chain, *psz_parser;
273     int i;
274
275     if ( p_sys->b_inited )
276     {
277         return NULL;
278     }
279
280     /* Create decoder object */
281     p_sys->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
282     vlc_object_attach( p_sys->p_decoder, p_stream );
283     p_sys->p_decoder->p_module = NULL;
284     p_sys->p_decoder->fmt_in = *p_fmt;
285     p_sys->p_decoder->b_pace_control = VLC_FALSE;
286     p_sys->p_decoder->fmt_out = p_sys->p_decoder->fmt_in;
287     p_sys->p_decoder->fmt_out.i_extra = 0;
288     p_sys->p_decoder->fmt_out.p_extra = 0;
289     p_sys->p_decoder->pf_decode_video = 0;
290     p_sys->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder;
291     p_sys->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder;
292     p_sys->p_decoder->pf_picture_link    = video_link_picture_decoder;
293     p_sys->p_decoder->pf_picture_unlink  = video_unlink_picture_decoder;
294     p_sys->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
295     for( i = 0; i < PICTURE_RING_SIZE; i++ )
296         p_sys->p_decoder->p_owner->pp_pics[i] = 0;
297     p_sys->p_decoder->p_owner->video = p_fmt->video;
298     //p_sys->p_decoder->p_cfg = p_sys->p_video_cfg;
299
300     p_sys->p_decoder->p_module =
301         module_Need( p_sys->p_decoder, "decoder", "$codec", 0 );
302
303     if( !p_sys->p_decoder->p_module )
304     {
305         msg_Err( p_stream, "cannot find decoder" );
306         vlc_object_detach( p_sys->p_decoder );
307         vlc_object_destroy( p_sys->p_decoder );
308         return NULL;
309     }
310
311     p_sys->b_inited = VLC_TRUE;
312     vlc_mutex_lock( p_sys->p_lock );
313
314     p_bridge = GetBridge( p_stream );
315     if ( p_bridge == NULL )
316     {
317         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
318         vlc_value_t val;
319
320         p_bridge = malloc( sizeof( bridge_t ) );
321
322         var_Create( p_libvlc, "mosaic-struct", VLC_VAR_ADDRESS );
323         val.p_address = p_bridge;
324         var_Set( p_libvlc, "mosaic-struct", val );
325
326         p_bridge->i_es_num = 0;
327         p_bridge->pp_es = NULL;
328     }
329
330     for ( i = 0; i < p_bridge->i_es_num; i++ )
331     {
332         if ( p_bridge->pp_es[i]->b_empty )
333             break;
334     }
335
336     if ( i == p_bridge->i_es_num )
337     {
338         p_bridge->pp_es = realloc( p_bridge->pp_es,
339                                    (p_bridge->i_es_num + 1)
340                                      * sizeof(bridged_es_t *) );
341         p_bridge->i_es_num++;
342         p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
343     }
344
345     p_sys->p_es = p_es = p_bridge->pp_es[i];
346
347     //p_es->fmt = *p_fmt;
348     p_es->psz_id = p_sys->psz_id;
349     p_es->p_picture = NULL;
350     p_es->pp_last = &p_es->p_picture;
351     p_es->b_empty = VLC_FALSE;
352
353     vlc_mutex_unlock( p_sys->p_lock );
354
355     if ( p_sys->i_height || p_sys->i_width )
356     {
357         p_sys->p_image = image_HandlerCreate( p_stream );
358     }
359     else
360     {
361         p_sys->p_image = NULL;
362     }
363
364     msg_Dbg( p_stream, "mosaic bridge id=%s pos=%d", p_es->psz_id, i );
365
366     /* Create user specified video filters */
367     psz_chain = var_GetNonEmptyString( p_stream, CFG_PREFIX "vfilter" );
368     msg_Dbg( p_stream, "psz_chain: %s\n", psz_chain );
369     {
370         config_chain_t *p_cfg;
371         for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
372         {
373             msg_Dbg( p_stream, " - %s\n", p_cfg->psz_value );
374         }
375     }
376     p_sys->i_vfilters = 0;
377     p_sys->pp_vfilters = NULL;
378     psz_parser = psz_chain;
379     while( psz_parser && *psz_parser )
380     {
381         config_chain_t *p_cfg;
382         char *psz_name;
383         filter_t **pp_vfilter;
384         psz_parser = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
385         p_sys->i_vfilters++;
386         p_sys->pp_vfilters =
387             (filter_t **)realloc( p_sys->pp_vfilters,
388                                   p_sys->i_vfilters * sizeof(filter_t *) );
389         pp_vfilter = p_sys->pp_vfilters+(p_sys->i_vfilters - 1);
390         *pp_vfilter = vlc_object_create( p_stream, VLC_OBJECT_FILTER );
391         vlc_object_attach( *pp_vfilter, p_stream );
392         (*pp_vfilter)->pf_vout_buffer_new = video_new_buffer_filter;
393         (*pp_vfilter)->pf_vout_buffer_del = video_del_buffer_filter;
394         (*pp_vfilter)->fmt_in = p_sys->p_decoder->fmt_out;
395         if( p_sys->i_chroma )
396             (*pp_vfilter)->fmt_in.video.i_chroma = p_sys->i_chroma;
397         (*pp_vfilter)->fmt_out = (*pp_vfilter)->fmt_in;
398         (*pp_vfilter)->p_cfg = p_cfg;
399         (*pp_vfilter)->p_module =
400             module_Need( *pp_vfilter, "video filter2", psz_name, VLC_TRUE );
401         if( (*pp_vfilter)->p_module )
402         {
403             /* It worked! */
404             (*pp_vfilter)->p_owner = (filter_owner_sys_t *)
405                 p_sys->p_decoder->p_owner;
406             msg_Err( p_stream, "Added video filter %s to the chain",
407                      psz_name );
408         }
409         else
410         {
411             /* Crap ... we didn't find a filter */
412             msg_Warn( p_stream,
413                       "no video filter matching name \"%s\" found",
414                       psz_name );
415             vlc_object_detach( *pp_vfilter );
416             vlc_object_destroy( *pp_vfilter );
417             p_sys->i_vfilters--;
418         }
419     }
420     free( psz_chain );
421
422     return (sout_stream_id_t *)p_sys;
423 }
424
425 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
426 {
427     sout_stream_sys_t *p_sys = p_stream->p_sys;
428     bridge_t *p_bridge;
429     bridged_es_t *p_es;
430     vlc_bool_t b_last_es = VLC_TRUE;
431     filter_t **pp_vfilter, **pp_end;
432     int i;
433
434     if ( !p_sys->b_inited )
435     {
436         return VLC_SUCCESS;
437     }
438
439     if ( p_sys->p_decoder != NULL )
440     {
441         picture_t **pp_ring = p_sys->p_decoder->p_owner->pp_pics;
442
443         if( p_sys->p_decoder->p_module )
444             module_Unneed( p_sys->p_decoder, p_sys->p_decoder->p_module );
445         vlc_object_detach( p_sys->p_decoder );
446         vlc_object_destroy( p_sys->p_decoder );
447
448         for( i = 0; i < PICTURE_RING_SIZE; i++ )
449         {
450             if ( pp_ring[i] != NULL )
451             {
452                 if ( pp_ring[i]->p_data_orig != NULL )
453                     free( pp_ring[i]->p_data_orig );
454                 free( pp_ring[i]->p_sys );
455                 free( pp_ring[i] );
456             }
457         }
458     }
459
460     /* Destroy user specified video filters */
461     pp_vfilter = p_sys->pp_vfilters;
462     pp_end = pp_vfilter + p_sys->i_vfilters;
463     for( ; pp_vfilter < pp_end; pp_vfilter++ )
464     {
465         vlc_object_detach( *pp_vfilter );
466         if( (*pp_vfilter)->p_module )
467             module_Unneed( *pp_vfilter, (*pp_vfilter)->p_module );
468         vlc_object_destroy( *pp_vfilter );
469     }
470     free( p_sys->pp_vfilters );
471
472     vlc_mutex_lock( p_sys->p_lock );
473
474     p_bridge = GetBridge( p_stream );
475     p_es = p_sys->p_es;
476
477     p_es->b_empty = VLC_TRUE;
478     while ( p_es->p_picture )
479     {
480         picture_t *p_next = p_es->p_picture->p_next;
481         p_es->p_picture->pf_release( p_es->p_picture );
482         p_es->p_picture = p_next;
483     }
484
485     for ( i = 0; i < p_bridge->i_es_num; i++ )
486     {
487         if ( !p_bridge->pp_es[i]->b_empty )
488         {
489             b_last_es = VLC_FALSE;
490             break;
491         }
492     }
493
494     if ( b_last_es )
495     {
496         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
497         for ( i = 0; i < p_bridge->i_es_num; i++ )
498             free( p_bridge->pp_es[i] );
499         free( p_bridge->pp_es );
500         free( p_bridge );
501         var_Destroy( p_libvlc, "mosaic-struct" );
502     }
503
504     vlc_mutex_unlock( p_sys->p_lock );
505
506     if ( p_sys->p_image )
507     {
508         image_HandlerDelete( p_sys->p_image );
509     }
510
511     p_sys->b_inited = VLC_FALSE;
512
513     return VLC_SUCCESS;
514 }
515
516 /*****************************************************************************
517  * PushPicture : push a picture in the mosaic-struct structure
518  *****************************************************************************/
519 static void PushPicture( sout_stream_t *p_stream, picture_t *p_picture )
520 {
521     sout_stream_sys_t *p_sys = p_stream->p_sys;
522     bridged_es_t *p_es = p_sys->p_es;
523
524     vlc_mutex_lock( p_sys->p_lock );
525
526     *p_es->pp_last = p_picture;
527     p_picture->p_next = NULL;
528     p_es->pp_last = &p_picture->p_next;
529
530     vlc_mutex_unlock( p_sys->p_lock );
531 }
532
533 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
534                  block_t *p_buffer )
535 {
536     sout_stream_sys_t *p_sys = p_stream->p_sys;
537     picture_t *p_pic;
538
539     if ( (sout_stream_sys_t *)id != p_sys )
540     {
541         block_ChainRelease( p_buffer );
542         return VLC_SUCCESS;
543     }
544
545     while ( (p_pic = p_sys->p_decoder->pf_decode_video( p_sys->p_decoder,
546                                                         &p_buffer )) )
547     {
548         picture_t *p_new_pic;
549
550         if( p_sys->i_height || p_sys->i_width )
551         {
552             video_format_t fmt_out, fmt_in;
553
554             memset( &fmt_in, 0, sizeof(video_format_t) );
555             memset( &fmt_out, 0, sizeof(video_format_t) );
556             fmt_in = p_sys->p_decoder->fmt_out.video;
557
558
559             if( p_sys->i_chroma )
560                 fmt_out.i_chroma = p_sys->i_chroma;
561             else
562                 fmt_out.i_chroma = VLC_FOURCC('I','4','2','0');
563
564             if ( !p_sys->i_height )
565             {
566                 fmt_out.i_width = p_sys->i_width;
567                 fmt_out.i_height = (p_sys->i_width * VOUT_ASPECT_FACTOR
568                     * p_sys->i_sar_num / p_sys->i_sar_den / fmt_in.i_aspect)
569                       & ~0x1;
570             }
571             else if ( !p_sys->i_width )
572             {
573                 fmt_out.i_height = p_sys->i_height;
574                 fmt_out.i_width = (p_sys->i_height * fmt_in.i_aspect
575                     * p_sys->i_sar_den / p_sys->i_sar_num / VOUT_ASPECT_FACTOR)
576                       & ~0x1;
577             }
578             else
579             {
580                 fmt_out.i_width = p_sys->i_width;
581                 fmt_out.i_height = p_sys->i_height;
582             }
583             fmt_out.i_visible_width = fmt_out.i_width;
584             fmt_out.i_visible_height = fmt_out.i_height;
585
586             p_new_pic = image_Convert( p_sys->p_image,
587                                        p_pic, &fmt_in, &fmt_out );
588             if ( p_new_pic == NULL )
589             {
590                 msg_Err( p_stream, "image conversion failed" );
591                 continue;
592             }
593         }
594         else
595         {
596             /* TODO: chroma conversion if needed */
597
598             p_new_pic = (picture_t*)malloc( sizeof(picture_t) );
599             if( p_new_pic == NULL )
600             {
601                 msg_Err( p_stream, "image conversion failed" );
602                 continue;
603             }
604
605             if( vout_AllocatePicture(
606                                   p_stream, p_new_pic, p_pic->format.i_chroma,
607                                   p_pic->format.i_width, p_pic->format.i_height,
608                                   p_sys->p_decoder->fmt_out.video.i_aspect )
609                 != VLC_SUCCESS )
610             {
611                 free( p_new_pic );
612                 msg_Err( p_stream, "image allocation failed" );
613                 continue;
614             }
615
616             vout_CopyPicture( p_stream, p_new_pic, p_pic );
617         }
618
619         p_new_pic->i_refcount = 1;
620         p_new_pic->i_status = DESTROYED_PICTURE;
621         p_new_pic->i_type   = DIRECT_PICTURE;
622         p_new_pic->p_sys = (picture_sys_t *)p_new_pic->pf_release;
623         p_new_pic->pf_release = ReleasePicture;
624         p_new_pic->date = p_pic->date;
625         p_pic->pf_release( p_pic );
626
627         if( p_sys->pp_vfilters )
628         {
629             /* Apply user specified video filters */
630             filter_t **pp_vfilter = p_sys->pp_vfilters;
631             filter_t **pp_end = pp_vfilter + p_sys->i_vfilters;
632             for( ; pp_vfilter < pp_end; pp_vfilter++ )
633             {
634                 (*pp_vfilter)->fmt_in.i_codec = p_new_pic->format.i_chroma;
635                 (*pp_vfilter)->fmt_out.i_codec = p_new_pic->format.i_chroma;
636                 (*pp_vfilter)->fmt_in.video = p_new_pic->format;
637                 (*pp_vfilter)->fmt_out.video = p_new_pic->format;
638                 p_new_pic = (*pp_vfilter)->pf_video_filter( *pp_vfilter,
639                                                              p_new_pic );
640                 if( !p_new_pic )
641                 {
642                     msg_Err( p_stream, "video filter failed" );
643                     break;
644                 }
645             }
646             if( !p_new_pic ) continue;
647         }
648
649         PushPicture( p_stream, p_new_pic );
650     }
651
652     return VLC_SUCCESS;
653 }
654
655 struct picture_sys_t
656 {
657     vlc_object_t *p_owner;
658     vlc_bool_t b_dead;
659 };
660
661 static void video_release_buffer_decoder( picture_t *p_pic )
662 {
663     if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
664     {
665         video_del_buffer_decoder( (decoder_t *)p_pic->p_sys->p_owner, p_pic );
666     }
667     else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
668 }
669
670 static void video_release_buffer_filter( picture_t *p_pic )
671 {
672     if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
673     {
674         video_del_buffer_filter( (filter_t *)p_pic->p_sys->p_owner, p_pic );
675     }
676     else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
677 }
678
679 inline static picture_t *video_new_buffer_decoder( decoder_t *p_dec )
680 {
681     return video_new_buffer( VLC_OBJECT( p_dec ),
682                              (decoder_owner_sys_t *)p_dec->p_owner,
683                              &p_dec->fmt_out,
684                              video_release_buffer_decoder );
685 }
686
687 inline static picture_t *video_new_buffer_filter( filter_t *p_filter )
688 {
689     return video_new_buffer( VLC_OBJECT( p_filter ),
690                              (decoder_owner_sys_t *)p_filter->p_owner,
691                              &p_filter->fmt_out,
692                              video_release_buffer_filter );
693 }
694
695 static picture_t *video_new_buffer( vlc_object_t *p_this,
696                                     decoder_owner_sys_t *p_sys,
697                                     es_format_t *fmt_out,
698                                     void ( *pf_release )( picture_t * ) )
699 {
700     picture_t **pp_ring = p_sys->pp_pics;
701     picture_t *p_pic;
702     int i;
703
704     if( fmt_out->video.i_width != p_sys->video.i_width ||
705         fmt_out->video.i_height != p_sys->video.i_height ||
706         fmt_out->video.i_chroma != p_sys->video.i_chroma ||
707         fmt_out->video.i_aspect != p_sys->video.i_aspect )
708     {
709         if( !fmt_out->video.i_sar_num ||
710             !fmt_out->video.i_sar_den )
711         {
712             fmt_out->video.i_sar_num =
713                 fmt_out->video.i_aspect * fmt_out->video.i_height;
714
715             fmt_out->video.i_sar_den =
716                 VOUT_ASPECT_FACTOR * fmt_out->video.i_width;
717         }
718
719         vlc_ureduce( &fmt_out->video.i_sar_num,
720                      &fmt_out->video.i_sar_den,
721                      fmt_out->video.i_sar_num,
722                      fmt_out->video.i_sar_den, 0 );
723
724         if( !fmt_out->video.i_visible_width ||
725             !fmt_out->video.i_visible_height )
726         {
727             fmt_out->video.i_visible_width = fmt_out->video.i_width;
728             fmt_out->video.i_visible_height = fmt_out->video.i_height;
729         }
730
731         fmt_out->video.i_chroma = fmt_out->i_codec;
732         p_sys->video = fmt_out->video;
733
734         for( i = 0; i < PICTURE_RING_SIZE; i++ )
735         {
736             if ( pp_ring[i] != NULL )
737             {
738                 if ( pp_ring[i]->i_status == DESTROYED_PICTURE )
739                 {
740                     if ( pp_ring[i]->p_data_orig != NULL )
741                         free( pp_ring[i]->p_data_orig );
742                     free( pp_ring[i]->p_sys );
743                     free( pp_ring[i] );
744                 }
745                 else
746                 {
747                     pp_ring[i]->p_sys->b_dead = VLC_TRUE;
748                 }
749                 pp_ring[i] = NULL;
750             }
751         }
752     }
753
754     /* Find an empty space in the picture ring buffer */
755     for( i = 0; i < PICTURE_RING_SIZE; i++ )
756     {
757         if( pp_ring[i] != NULL && pp_ring[i]->i_status == DESTROYED_PICTURE )
758         {
759             pp_ring[i]->i_status = RESERVED_PICTURE;
760             return pp_ring[i];
761         }
762     }
763     for( i = 0; i < PICTURE_RING_SIZE; i++ )
764     {
765         if( pp_ring[i] == NULL ) break;
766     }
767
768     if( i == PICTURE_RING_SIZE )
769     {
770         msg_Err( p_this, "decoder/filter is leaking pictures, "
771                  "resetting its ring buffer" );
772
773         for( i = 0; i < PICTURE_RING_SIZE; i++ )
774         {
775             pp_ring[i]->pf_release( pp_ring[i] );
776         }
777
778         i = 0;
779     }
780
781     p_pic = malloc( sizeof(picture_t) );
782     fmt_out->video.i_chroma = fmt_out->i_codec;
783     vout_AllocatePicture( p_this, p_pic,
784                           fmt_out->video.i_chroma,
785                           fmt_out->video.i_width,
786                           fmt_out->video.i_height,
787                           fmt_out->video.i_aspect );
788
789     if( !p_pic->i_planes )
790     {
791         free( p_pic );
792         return NULL;
793     }
794
795     p_pic->pf_release = pf_release;
796     p_pic->p_sys = malloc( sizeof(picture_sys_t) );
797     p_pic->p_sys->p_owner = p_this;
798     p_pic->p_sys->b_dead = VLC_FALSE;
799     p_pic->i_status = RESERVED_PICTURE;
800
801     pp_ring[i] = p_pic;
802
803     return p_pic;
804 }
805
806 inline static void video_del_buffer_decoder( decoder_t *p_this,
807                                              picture_t *p_pic )
808 {
809     video_del_buffer( VLC_OBJECT( p_this ), p_pic );
810 }
811
812 inline static void video_del_buffer_filter( filter_t *p_this,
813                                             picture_t *p_pic )
814 {
815     video_del_buffer( VLC_OBJECT( p_this ), p_pic );
816 }
817
818 static void video_del_buffer( vlc_object_t *p_this, picture_t *p_pic )
819 {
820     p_pic->i_refcount = 0;
821     p_pic->i_status = DESTROYED_PICTURE;
822     if ( p_pic->p_sys->b_dead )
823     {
824         if ( p_pic->p_data_orig != NULL )
825             free( p_pic->p_data_orig );
826         free( p_pic->p_sys );
827         free( p_pic );
828     }
829 }
830
831 static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
832 {
833     p_pic->i_refcount++;
834 }
835
836 static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
837 {
838     video_release_buffer_decoder( p_pic );
839 }
840
841
842 /**********************************************************************
843  * Callback to update (some) params on the fly
844  **********************************************************************/
845 static int MosaicBridgeCallback( vlc_object_t *p_this, char const *psz_var,
846                                  vlc_value_t oldval, vlc_value_t newval,
847                                  void *p_data )
848 {
849     sout_stream_t *p_stream = (sout_stream_t *)p_data;
850     sout_stream_sys_t *p_sys = p_stream->p_sys;
851     int i_ret = VLC_SUCCESS;
852
853 #define VAR_IS( a ) !strcmp( psz_var, CFG_PREFIX a )
854     if( VAR_IS( "height" ) )
855     {
856         /* We create the handler before updating the value in p_sys
857          * so we don't have to worry about locking */
858         if( !p_sys->p_image && newval.i_int )
859             p_sys->p_image = image_HandlerCreate( p_stream );
860         p_sys->i_height = newval.i_int;
861     }
862     else if( VAR_IS( "width" ) )
863     {
864         /* We create the handler before updating the value in p_sys
865          * so we don't have to worry about locking */
866         if( !p_sys->p_image && newval.i_int )
867             p_sys->p_image = image_HandlerCreate( p_stream );
868         p_sys->i_width = newval.i_int;
869     }
870 #undef VAR_IS
871
872     return i_ret;
873 }