]> git.sesse.net Git - vlc/blob - modules/stream_out/mosaic_bridge.c
* ALL: converted the video output module "picture" to a stream output
[vlc] / modules / stream_out / mosaic_bridge.c
1 /*****************************************************************************
2  * mosaic_bridge.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2005 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, 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/decoder.h>
35
36 #include "vlc_image.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     char *psz_id;
52     vlc_bool_t b_inited;
53 };
54
55 #define PICTURE_RING_SIZE 64
56 struct decoder_owner_sys_t
57 {
58     picture_t *pp_pics[PICTURE_RING_SIZE];
59 };
60
61 typedef void (* pf_release_t)( picture_t * );
62 static void ReleasePicture( picture_t *p_pic )
63 {
64     p_pic->i_refcount--;
65
66     if ( p_pic->i_refcount <= 0 )
67     {
68         if ( p_pic->p_sys != NULL )
69         {
70             pf_release_t pf_release = (pf_release_t)p_pic->p_sys;
71             p_pic->p_sys = NULL;
72             pf_release( p_pic );
73         }
74         else
75         {
76             if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
77             if( p_pic ) free( p_pic );
78         }
79     }
80 }
81
82 /*****************************************************************************
83  * Local prototypes
84  *****************************************************************************/
85 static int  Open    ( vlc_object_t * );
86 static void Close   ( vlc_object_t * );
87 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
88 static int               Del ( sout_stream_t *, sout_stream_id_t * );
89 static int               Send( sout_stream_t *, sout_stream_id_t *, block_t * );
90
91 static void video_del_buffer( decoder_t *, picture_t * );
92 static picture_t *video_new_buffer( decoder_t * );
93 static void video_link_picture_decoder( decoder_t *, picture_t * );
94 static void video_unlink_picture_decoder( decoder_t *, picture_t * );
95
96 /*****************************************************************************
97  * Module descriptor
98  *****************************************************************************/
99 #define ID_TEXT N_("ID")
100 #define ID_LONGTEXT N_( \
101     "Specify an identifier string for this subpicture" )
102
103 #define WIDTH_TEXT N_("Video width")
104 #define WIDTH_LONGTEXT N_( \
105     "Allows you to specify the output video width." )
106 #define HEIGHT_TEXT N_("Video height")
107 #define HEIGHT_LONGTEXT N_( \
108     "Allows you to specify the output video height." )
109
110 #define SOUT_CFG_PREFIX "sout-mosaic-bridge-"
111
112 vlc_module_begin();
113     set_shortname( _( "Mosaic bridge" ) );
114     set_description(_("Mosaic bridge stream output") );
115     set_capability( "sout stream", 0 );
116     add_shortcut( "mosaic-bridge" );
117
118     add_string( SOUT_CFG_PREFIX "id", "Id", NULL, ID_TEXT, ID_LONGTEXT,
119                 VLC_FALSE );
120     add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
121                  WIDTH_LONGTEXT, VLC_TRUE );
122     add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
123                  HEIGHT_LONGTEXT, VLC_TRUE );
124
125     set_callbacks( Open, Close );
126
127     var_Create( p_module->p_libvlc, "mosaic-lock", VLC_VAR_MUTEX );
128 vlc_module_end();
129
130 static const char *ppsz_sout_options[] = {
131     "id", "width", "height", NULL
132 };
133
134 /*****************************************************************************
135  * Open
136  *****************************************************************************/
137 static int Open( vlc_object_t *p_this )
138 {
139     sout_stream_t     *p_stream = (sout_stream_t *)p_this;
140     sout_stream_sys_t *p_sys;
141     libvlc_t *p_libvlc = p_this->p_libvlc;
142     vlc_value_t val;
143
144     sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
145                    p_stream->p_cfg );
146
147     p_sys          = malloc( sizeof( sout_stream_sys_t ) );
148     p_stream->p_sys = p_sys;
149     p_sys->b_inited = VLC_FALSE;
150
151     var_Get( p_libvlc, "mosaic-lock", &val );
152     p_sys->p_lock = val.p_address;
153
154     var_Get( p_stream, SOUT_CFG_PREFIX "id", &val );
155     p_sys->psz_id = val.psz_string;
156
157     var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
158     p_sys->i_height = val.i_int; 
159
160     var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
161     p_sys->i_width = val.i_int; 
162
163     if ( p_sys->i_height || p_sys->i_width )
164     {
165         p_sys->p_image = image_HandlerCreate( p_stream );
166     }
167
168     p_stream->pf_add    = Add;
169     p_stream->pf_del    = Del;
170     p_stream->pf_send   = Send;
171
172     p_stream->p_sout->i_out_pace_nocontrol++;
173
174     return VLC_SUCCESS;
175 }
176
177 /*****************************************************************************
178  * Close
179  *****************************************************************************/
180 static void Close( vlc_object_t * p_this )
181 {
182     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
183     sout_stream_sys_t *p_sys = p_stream->p_sys;
184
185     p_stream->p_sout->i_out_pace_nocontrol--;
186
187     if ( p_sys->psz_id )
188         free( p_sys->psz_id );
189
190     free( p_sys );
191 }
192
193 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
194 {
195     sout_stream_sys_t *p_sys = p_stream->p_sys;
196     bridge_t *p_bridge;
197     bridged_es_t *p_es;
198     int i;
199
200     if ( p_sys->b_inited )
201     {
202         return NULL;
203     }
204
205     /* Create decoder object */
206     p_sys->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
207     vlc_object_attach( p_sys->p_decoder, p_stream );
208     p_sys->p_decoder->p_module = NULL;
209     p_sys->p_decoder->fmt_in = *p_fmt;
210     p_sys->p_decoder->b_pace_control = VLC_FALSE;
211     p_sys->p_decoder->fmt_out = p_sys->p_decoder->fmt_in;
212     p_sys->p_decoder->fmt_out.i_extra = 0;
213     p_sys->p_decoder->fmt_out.p_extra = 0;
214     p_sys->p_decoder->pf_decode_video = 0;
215     p_sys->p_decoder->pf_vout_buffer_new = video_new_buffer;
216     p_sys->p_decoder->pf_vout_buffer_del = video_del_buffer;
217     p_sys->p_decoder->pf_picture_link    = video_link_picture_decoder;
218     p_sys->p_decoder->pf_picture_unlink  = video_unlink_picture_decoder;
219     p_sys->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
220     for( i = 0; i < PICTURE_RING_SIZE; i++ )
221         p_sys->p_decoder->p_owner->pp_pics[i] = 0;
222     //p_sys->p_decoder->p_cfg = p_sys->p_video_cfg;
223
224     p_sys->p_decoder->p_module =
225         module_Need( p_sys->p_decoder, "decoder", "$codec", 0 );
226
227     if( !p_sys->p_decoder->p_module )
228     {
229         msg_Err( p_stream, "cannot find decoder" );
230         vlc_object_detach( p_sys->p_decoder );
231         vlc_object_destroy( p_sys->p_decoder );
232         return NULL;
233     }
234
235     p_sys->b_inited = VLC_TRUE;
236     vlc_mutex_lock( p_sys->p_lock );
237
238     p_bridge = GetBridge( p_stream );
239     if ( p_bridge == NULL )
240     {
241         libvlc_t *p_libvlc = p_stream->p_libvlc;
242         vlc_value_t val;
243
244         p_bridge = malloc( sizeof( bridge_t ) );
245
246         var_Create( p_libvlc, "mosaic-struct", VLC_VAR_ADDRESS );
247         val.p_address = p_bridge;
248         var_Set( p_libvlc, "mosaic-struct", val );
249
250         p_bridge->i_es_num = 0;
251         p_bridge->pp_es = NULL;
252     }
253
254     for ( i = 0; i < p_bridge->i_es_num; i++ )
255     {
256         if ( p_bridge->pp_es[i]->b_empty )
257             break;
258     }
259
260     if ( i == p_bridge->i_es_num )
261     {
262         p_bridge->pp_es = realloc( p_bridge->pp_es,
263                                    (p_bridge->i_es_num + 1)
264                                      * sizeof(bridged_es_t *) );
265         p_bridge->i_es_num++;
266         p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
267     }
268
269     p_sys->p_es = p_es = p_bridge->pp_es[i];
270
271     //p_es->fmt = *p_fmt;
272     p_es->psz_id = p_sys->psz_id;
273     p_es->p_picture = NULL;
274     p_es->pp_last = &p_es->p_picture;
275     p_es->b_empty = VLC_FALSE;
276
277     vlc_mutex_unlock( p_sys->p_lock );
278
279     msg_Dbg( p_stream, "mosaic bridge id=%s pos=%d", p_es->psz_id, i );
280
281     return (sout_stream_id_t *)p_sys;
282 }
283
284 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
285 {
286     sout_stream_sys_t *p_sys = p_stream->p_sys;
287     bridge_t *p_bridge;
288     bridged_es_t *p_es;
289     vlc_bool_t b_last_es = VLC_TRUE;
290     int i;
291
292     if ( !p_sys->b_inited )
293     {
294         return VLC_SUCCESS;
295     }
296
297     if ( p_sys->p_decoder )
298     {
299         if( p_sys->p_decoder->p_module )
300             module_Unneed( p_sys->p_decoder, p_sys->p_decoder->p_module );
301         vlc_object_detach( p_sys->p_decoder );
302         vlc_object_destroy( p_sys->p_decoder );
303     }
304
305     vlc_mutex_lock( p_sys->p_lock );
306
307     p_bridge = GetBridge( p_stream );
308     p_es = p_sys->p_es;
309
310     p_es->b_empty = VLC_TRUE;
311     while ( p_es->p_picture )
312     {
313         picture_t *p_next = p_es->p_picture->p_next;
314         p_es->p_picture->pf_release( p_es->p_picture );
315         p_es->p_picture = p_next;
316     }
317
318     for ( i = 0; i < p_bridge->i_es_num; i++ )
319     {
320         if ( !p_bridge->pp_es[i]->b_empty )
321         {
322             b_last_es = VLC_FALSE;
323             break;
324         }
325     }
326
327     if ( b_last_es )
328     {
329         libvlc_t *p_libvlc = p_stream->p_libvlc;
330         for ( i = 0; i < p_bridge->i_es_num; i++ )
331             free( p_bridge->pp_es[i] );
332         free( p_bridge->pp_es );
333         free( p_bridge );
334         var_Destroy( p_libvlc, "mosaic-struct" );
335     }
336
337     vlc_mutex_unlock( p_sys->p_lock );
338
339     if ( p_sys->i_height || p_sys->i_width )
340     {
341         image_HandlerDelete( p_sys->p_image );
342     }
343
344     p_sys->b_inited = VLC_FALSE;
345
346     return VLC_SUCCESS;
347 }
348
349 /*****************************************************************************
350  * PushPicture : push a picture in the mosaic-struct structure
351  *****************************************************************************/
352 static void PushPicture( sout_stream_t *p_stream, picture_t *p_picture )
353 {
354     sout_stream_sys_t *p_sys = p_stream->p_sys;
355     bridged_es_t *p_es = p_sys->p_es;
356
357     vlc_mutex_lock( p_sys->p_lock );
358
359     *p_es->pp_last = p_picture;
360     p_picture->p_next = NULL;
361     p_es->pp_last = &p_picture->p_next;
362
363     vlc_mutex_unlock( p_sys->p_lock );
364 }
365
366 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
367                  block_t *p_buffer )
368 {
369     sout_stream_sys_t *p_sys = p_stream->p_sys;
370     picture_t *p_pic;
371
372     if ( (sout_stream_sys_t *)id != p_sys )
373     {
374         block_ChainRelease( p_buffer );
375         return VLC_SUCCESS;
376     }
377
378     while ( (p_pic = p_sys->p_decoder->pf_decode_video( p_sys->p_decoder,
379                                                         &p_buffer )) )
380     {
381         picture_t *p_new_pic;
382
383         if ( p_sys->i_height || p_sys->i_width )
384         {
385             video_format_t fmt_out = {0}, fmt_in = {0};
386             fmt_in = p_sys->p_decoder->fmt_out.video;
387
388             fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
389             fmt_out.i_width = p_sys->i_width;
390             fmt_out.i_height = p_sys->i_height;
391             fmt_out.i_visible_width = fmt_out.i_width;
392             fmt_out.i_visible_height = fmt_out.i_height;
393
394             p_new_pic = image_Convert( p_sys->p_image,
395                                        p_pic, &fmt_in, &fmt_out );
396             if ( p_new_pic == NULL )
397             {
398                 msg_Err( p_stream, "image conversion failed" );
399                 continue;
400             }
401         }
402         else
403         {
404             p_new_pic = (picture_t*)malloc( sizeof(picture_t) );
405             vout_AllocatePicture( p_stream, p_new_pic, p_pic->format.i_chroma,
406                                   p_pic->format.i_width, p_pic->format.i_height,
407                                   p_sys->p_decoder->fmt_out.video.i_aspect );
408
409             vout_CopyPicture( p_stream, p_new_pic, p_pic );
410         }
411         p_pic->pf_release( p_pic );
412
413         p_new_pic->i_refcount = 1;
414         p_new_pic->i_status = DESTROYED_PICTURE;
415         p_new_pic->i_type   = DIRECT_PICTURE;
416         p_new_pic->p_sys = (picture_sys_t *)p_new_pic->pf_release;
417         p_new_pic->pf_release = ReleasePicture;
418         p_new_pic->date = p_pic->date;
419
420         PushPicture( p_stream, p_new_pic );
421     }
422
423     return VLC_SUCCESS;
424 }
425
426 struct picture_sys_t
427 {
428     vlc_object_t *p_owner;
429 };
430
431 static void video_release_buffer( picture_t *p_pic )
432 {
433     if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
434     {
435         video_del_buffer( (decoder_t *)p_pic->p_sys->p_owner, p_pic );
436     }
437     else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
438 }
439
440 static picture_t *video_new_buffer( decoder_t *p_dec )
441 {
442     picture_t **pp_ring = p_dec->p_owner->pp_pics;
443     picture_t *p_pic;
444     int i;
445
446     /* Find an empty space in the picture ring buffer */
447     for( i = 0; i < PICTURE_RING_SIZE; i++ )
448     {
449         if( pp_ring[i] != 0 && pp_ring[i]->i_status == DESTROYED_PICTURE )
450         {
451             pp_ring[i]->i_status = RESERVED_PICTURE;
452             return pp_ring[i];
453         }
454     }
455     for( i = 0; i < PICTURE_RING_SIZE; i++ )
456     {
457         if( pp_ring[i] == 0 ) break;
458     }
459
460     if( i == PICTURE_RING_SIZE )
461     {
462         msg_Err( p_dec, "decoder/filter is leaking pictures, "
463                  "resetting its ring buffer" );
464
465         for( i = 0; i < PICTURE_RING_SIZE; i++ )
466         {
467             pp_ring[i]->pf_release( pp_ring[i] );
468         }
469
470         i = 0;
471     }
472
473     p_pic = malloc( sizeof(picture_t) );
474     p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
475     vout_AllocatePicture( VLC_OBJECT(p_dec), p_pic,
476                           p_dec->fmt_out.video.i_chroma,
477                           p_dec->fmt_out.video.i_width,
478                           p_dec->fmt_out.video.i_height,
479                           p_dec->fmt_out.video.i_aspect );
480
481     if( !p_pic->i_planes )
482     {
483         free( p_pic );
484         return 0;
485     }
486
487     p_pic->pf_release = video_release_buffer;
488     p_pic->p_sys = malloc( sizeof(picture_sys_t) );
489     p_pic->p_sys->p_owner = VLC_OBJECT(p_dec);
490     p_pic->i_status = RESERVED_PICTURE;
491
492     pp_ring[i] = p_pic;
493
494     return p_pic;
495 }
496
497 static void video_del_buffer( decoder_t *p_this, picture_t *p_pic )
498 {
499     p_pic->i_refcount = 0;
500     p_pic->i_status = DESTROYED_PICTURE;
501 }
502
503 static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
504 {
505     p_pic->i_refcount++;
506 }
507
508 static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
509 {
510     video_release_buffer( p_pic );
511 }
512