00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <errno.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031
00032 #include <vlc/vlc.h>
00033 #include <vlc/sout.h>
00034 #include <vlc/decoder.h>
00035
00036 #include "vlc_image.h"
00037
00038 #include "../video_filter/mosaic.h"
00039
00040
00041
00042
00043 struct sout_stream_sys_t
00044 {
00045 bridged_es_t *p_es;
00046 vlc_mutex_t *p_lock;
00047
00048 decoder_t *p_decoder;
00049 image_handler_t *p_image;
00050 int i_height, i_width;
00051 int i_sar_num, i_sar_den;
00052 char *psz_id;
00053 vlc_bool_t b_inited;
00054 };
00055
00056 #define PICTURE_RING_SIZE 4
00057 struct decoder_owner_sys_t
00058 {
00059 picture_t *pp_pics[PICTURE_RING_SIZE];
00060 };
00061
00062 typedef void (* pf_release_t)( picture_t * );
00063 static void ReleasePicture( picture_t *p_pic )
00064 {
00065 p_pic->i_refcount--;
00066
00067 if ( p_pic->i_refcount <= 0 )
00068 {
00069 if ( p_pic->p_sys != NULL )
00070 {
00071 pf_release_t pf_release = (pf_release_t)p_pic->p_sys;
00072 p_pic->p_sys = NULL;
00073 pf_release( p_pic );
00074 }
00075 else
00076 {
00077 if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
00078 if( p_pic ) free( p_pic );
00079 }
00080 }
00081 }
00082
00083
00084
00085
00086 static int Open ( vlc_object_t * );
00087 static void Close ( vlc_object_t * );
00088 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
00089 static int Del ( sout_stream_t *, sout_stream_id_t * );
00090 static int Send( sout_stream_t *, sout_stream_id_t *, block_t * );
00091
00092 static void video_del_buffer( decoder_t *, picture_t * );
00093 static picture_t *video_new_buffer( decoder_t * );
00094 static void video_link_picture_decoder( decoder_t *, picture_t * );
00095 static void video_unlink_picture_decoder( decoder_t *, picture_t * );
00096
00097
00098
00099
00100 #define ID_TEXT N_("ID")
00101 #define ID_LONGTEXT N_( \
00102 "Specify an identifier string for this subpicture" )
00103
00104 #define WIDTH_TEXT N_("Video width")
00105 #define WIDTH_LONGTEXT N_( \
00106 "Allows you to specify the output video width." )
00107 #define HEIGHT_TEXT N_("Video height")
00108 #define HEIGHT_LONGTEXT N_( \
00109 "Allows you to specify the output video height." )
00110 #define RATIO_TEXT N_("Sample aspect ratio")
00111 #define RATIO_LONGTEXT N_( \
00112 "Sample aspect ratio of the destination (1:1, 3:4, 2:3)." )
00113
00114 #define SOUT_CFG_PREFIX "sout-mosaic-bridge-"
00115
00116 vlc_module_begin();
00117 set_shortname( _( "Mosaic bridge" ) );
00118 set_description(_("Mosaic bridge stream output") );
00119 set_capability( "sout stream", 0 );
00120 add_shortcut( "mosaic-bridge" );
00121
00122 add_string( SOUT_CFG_PREFIX "id", "Id", NULL, ID_TEXT, ID_LONGTEXT,
00123 VLC_FALSE );
00124 add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
00125 WIDTH_LONGTEXT, VLC_TRUE );
00126 add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
00127 HEIGHT_LONGTEXT, VLC_TRUE );
00128 add_string( SOUT_CFG_PREFIX "sar", "1:1", NULL, RATIO_TEXT,
00129 RATIO_LONGTEXT, VLC_FALSE );
00130
00131 set_callbacks( Open, Close );
00132
00133 var_Create( p_module->p_libvlc, "mosaic-lock", VLC_VAR_MUTEX );
00134 vlc_module_end();
00135
00136 static const char *ppsz_sout_options[] = {
00137 "id", "width", "height", "sar", NULL
00138 };
00139
00140
00141
00142
00143 static int Open( vlc_object_t *p_this )
00144 {
00145 sout_stream_t *p_stream = (sout_stream_t *)p_this;
00146 sout_stream_sys_t *p_sys;
00147 libvlc_t *p_libvlc = p_this->p_libvlc;
00148 vlc_value_t val;
00149
00150 sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
00151 p_stream->p_cfg );
00152
00153 p_sys = malloc( sizeof( sout_stream_sys_t ) );
00154 p_stream->p_sys = p_sys;
00155 p_sys->b_inited = VLC_FALSE;
00156
00157 var_Get( p_libvlc, "mosaic-lock", &val );
00158 p_sys->p_lock = val.p_address;
00159
00160 var_Get( p_stream, SOUT_CFG_PREFIX "id", &val );
00161 p_sys->psz_id = val.psz_string;
00162
00163 var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
00164 p_sys->i_height = val.i_int;
00165
00166 var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
00167 p_sys->i_width = val.i_int;
00168
00169 var_Get( p_stream, SOUT_CFG_PREFIX "sar", &val );
00170 if ( val.psz_string )
00171 {
00172 char *psz_parser = strchr( val.psz_string, ':' );
00173
00174 if( psz_parser )
00175 {
00176 *psz_parser++ = '\0';
00177 p_sys->i_sar_num = atoi( val.psz_string );
00178 p_sys->i_sar_den = atoi( psz_parser );
00179 vlc_ureduce( &p_sys->i_sar_num, &p_sys->i_sar_den,
00180 p_sys->i_sar_num, p_sys->i_sar_den, 0 );
00181 }
00182 else
00183 {
00184 msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
00185 p_sys->i_sar_num = p_sys->i_sar_den = 1;
00186 }
00187
00188 free( val.psz_string );
00189 }
00190 else
00191 {
00192 p_sys->i_sar_num = p_sys->i_sar_den = 1;
00193 }
00194
00195 p_stream->pf_add = Add;
00196 p_stream->pf_del = Del;
00197 p_stream->pf_send = Send;
00198
00199 p_stream->p_sout->i_out_pace_nocontrol++;
00200
00201 return VLC_SUCCESS;
00202 }
00203
00204
00205
00206
00207 static void Close( vlc_object_t * p_this )
00208 {
00209 sout_stream_t *p_stream = (sout_stream_t*)p_this;
00210 sout_stream_sys_t *p_sys = p_stream->p_sys;
00211
00212 p_stream->p_sout->i_out_pace_nocontrol--;
00213
00214 if ( p_sys->psz_id )
00215 free( p_sys->psz_id );
00216
00217 free( p_sys );
00218 }
00219
00220 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
00221 {
00222 sout_stream_sys_t *p_sys = p_stream->p_sys;
00223 bridge_t *p_bridge;
00224 bridged_es_t *p_es;
00225 int i;
00226
00227 if ( p_sys->b_inited )
00228 {
00229 return NULL;
00230 }
00231
00232
00233 p_sys->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
00234 vlc_object_attach( p_sys->p_decoder, p_stream );
00235 p_sys->p_decoder->p_module = NULL;
00236 p_sys->p_decoder->fmt_in = *p_fmt;
00237 p_sys->p_decoder->b_pace_control = VLC_FALSE;
00238 p_sys->p_decoder->fmt_out = p_sys->p_decoder->fmt_in;
00239 p_sys->p_decoder->fmt_out.i_extra = 0;
00240 p_sys->p_decoder->fmt_out.p_extra = 0;
00241 p_sys->p_decoder->pf_decode_video = 0;
00242 p_sys->p_decoder->pf_vout_buffer_new = video_new_buffer;
00243 p_sys->p_decoder->pf_vout_buffer_del = video_del_buffer;
00244 p_sys->p_decoder->pf_picture_link = video_link_picture_decoder;
00245 p_sys->p_decoder->pf_picture_unlink = video_unlink_picture_decoder;
00246 p_sys->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
00247 for( i = 0; i < PICTURE_RING_SIZE; i++ )
00248 p_sys->p_decoder->p_owner->pp_pics[i] = 0;
00249
00250
00251 p_sys->p_decoder->p_module =
00252 module_Need( p_sys->p_decoder, "decoder", "$codec", 0 );
00253
00254 if( !p_sys->p_decoder->p_module )
00255 {
00256 msg_Err( p_stream, "cannot find decoder" );
00257 vlc_object_detach( p_sys->p_decoder );
00258 vlc_object_destroy( p_sys->p_decoder );
00259 return NULL;
00260 }
00261
00262 p_sys->b_inited = VLC_TRUE;
00263 vlc_mutex_lock( p_sys->p_lock );
00264
00265 p_bridge = GetBridge( p_stream );
00266 if ( p_bridge == NULL )
00267 {
00268 libvlc_t *p_libvlc = p_stream->p_libvlc;
00269 vlc_value_t val;
00270
00271 p_bridge = malloc( sizeof( bridge_t ) );
00272
00273 var_Create( p_libvlc, "mosaic-struct", VLC_VAR_ADDRESS );
00274 val.p_address = p_bridge;
00275 var_Set( p_libvlc, "mosaic-struct", val );
00276
00277 p_bridge->i_es_num = 0;
00278 p_bridge->pp_es = NULL;
00279 }
00280
00281 for ( i = 0; i < p_bridge->i_es_num; i++ )
00282 {
00283 if ( p_bridge->pp_es[i]->b_empty )
00284 break;
00285 }
00286
00287 if ( i == p_bridge->i_es_num )
00288 {
00289 p_bridge->pp_es = realloc( p_bridge->pp_es,
00290 (p_bridge->i_es_num + 1)
00291 * sizeof(bridged_es_t *) );
00292 p_bridge->i_es_num++;
00293 p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
00294 }
00295
00296 p_sys->p_es = p_es = p_bridge->pp_es[i];
00297
00298
00299 p_es->psz_id = p_sys->psz_id;
00300 p_es->p_picture = NULL;
00301 p_es->pp_last = &p_es->p_picture;
00302 p_es->b_empty = VLC_FALSE;
00303
00304 vlc_mutex_unlock( p_sys->p_lock );
00305
00306 if ( p_sys->i_height || p_sys->i_width )
00307 {
00308 p_sys->p_image = image_HandlerCreate( p_stream );
00309 }
00310
00311 msg_Dbg( p_stream, "mosaic bridge id=%s pos=%d", p_es->psz_id, i );
00312
00313 return (sout_stream_id_t *)p_sys;
00314 }
00315
00316 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
00317 {
00318 sout_stream_sys_t *p_sys = p_stream->p_sys;
00319 bridge_t *p_bridge;
00320 bridged_es_t *p_es;
00321 vlc_bool_t b_last_es = VLC_TRUE;
00322 int i;
00323
00324 if ( !p_sys->b_inited )
00325 {
00326 return VLC_SUCCESS;
00327 }
00328
00329 if ( p_sys->p_decoder )
00330 {
00331 if( p_sys->p_decoder->p_module )
00332 module_Unneed( p_sys->p_decoder, p_sys->p_decoder->p_module );
00333 vlc_object_detach( p_sys->p_decoder );
00334 vlc_object_destroy( p_sys->p_decoder );
00335 }
00336
00337 vlc_mutex_lock( p_sys->p_lock );
00338
00339 p_bridge = GetBridge( p_stream );
00340 p_es = p_sys->p_es;
00341
00342 p_es->b_empty = VLC_TRUE;
00343 while ( p_es->p_picture )
00344 {
00345 picture_t *p_next = p_es->p_picture->p_next;
00346 p_es->p_picture->pf_release( p_es->p_picture );
00347 p_es->p_picture = p_next;
00348 }
00349
00350 for ( i = 0; i < p_bridge->i_es_num; i++ )
00351 {
00352 if ( !p_bridge->pp_es[i]->b_empty )
00353 {
00354 b_last_es = VLC_FALSE;
00355 break;
00356 }
00357 }
00358
00359 if ( b_last_es )
00360 {
00361 libvlc_t *p_libvlc = p_stream->p_libvlc;
00362 for ( i = 0; i < p_bridge->i_es_num; i++ )
00363 free( p_bridge->pp_es[i] );
00364 free( p_bridge->pp_es );
00365 free( p_bridge );
00366 var_Destroy( p_libvlc, "mosaic-struct" );
00367 }
00368
00369 vlc_mutex_unlock( p_sys->p_lock );
00370
00371 if ( p_sys->i_height || p_sys->i_width )
00372 {
00373 image_HandlerDelete( p_sys->p_image );
00374 }
00375
00376 p_sys->b_inited = VLC_FALSE;
00377
00378 return VLC_SUCCESS;
00379 }
00380
00381
00382
00383
00384 static void PushPicture( sout_stream_t *p_stream, picture_t *p_picture )
00385 {
00386 sout_stream_sys_t *p_sys = p_stream->p_sys;
00387 bridged_es_t *p_es = p_sys->p_es;
00388
00389 vlc_mutex_lock( p_sys->p_lock );
00390
00391 *p_es->pp_last = p_picture;
00392 p_picture->p_next = NULL;
00393 p_es->pp_last = &p_picture->p_next;
00394
00395 vlc_mutex_unlock( p_sys->p_lock );
00396 }
00397
00398 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
00399 block_t *p_buffer )
00400 {
00401 sout_stream_sys_t *p_sys = p_stream->p_sys;
00402 picture_t *p_pic;
00403
00404 if ( (sout_stream_sys_t *)id != p_sys )
00405 {
00406 block_ChainRelease( p_buffer );
00407 return VLC_SUCCESS;
00408 }
00409
00410 while ( (p_pic = p_sys->p_decoder->pf_decode_video( p_sys->p_decoder,
00411 &p_buffer )) )
00412 {
00413 picture_t *p_new_pic;
00414
00415 if ( p_sys->i_height || p_sys->i_width )
00416 {
00417 video_format_t fmt_out = {0}, fmt_in = {0};
00418 fmt_in = p_sys->p_decoder->fmt_out.video;
00419
00420 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
00421
00422 if ( !p_sys->i_height )
00423 {
00424 fmt_out.i_width = p_sys->i_width;
00425 fmt_out.i_height = (p_sys->i_width * VOUT_ASPECT_FACTOR
00426 * p_sys->i_sar_num / p_sys->i_sar_den / fmt_in.i_aspect)
00427 & ~0x1;
00428 }
00429 else if ( !p_sys->i_width )
00430 {
00431 fmt_out.i_height = p_sys->i_height;
00432 fmt_out.i_width = (p_sys->i_height * fmt_in.i_aspect
00433 * p_sys->i_sar_den / p_sys->i_sar_num / VOUT_ASPECT_FACTOR)
00434 & ~0x1;
00435 }
00436 else
00437 {
00438 fmt_out.i_width = p_sys->i_width;
00439 fmt_out.i_height = p_sys->i_height;
00440 }
00441 fmt_out.i_visible_width = fmt_out.i_width;
00442 fmt_out.i_visible_height = fmt_out.i_height;
00443
00444 p_new_pic = image_Convert( p_sys->p_image,
00445 p_pic, &fmt_in, &fmt_out );
00446 if ( p_new_pic == NULL )
00447 {
00448 msg_Err( p_stream, "image conversion failed" );
00449 continue;
00450 }
00451 }
00452 else
00453 {
00454 p_new_pic = (picture_t*)malloc( sizeof(picture_t) );
00455 vout_AllocatePicture( p_stream, p_new_pic, p_pic->format.i_chroma,
00456 p_pic->format.i_width, p_pic->format.i_height,
00457 p_sys->p_decoder->fmt_out.video.i_aspect );
00458
00459 vout_CopyPicture( p_stream, p_new_pic, p_pic );
00460 }
00461
00462 p_new_pic->i_refcount = 1;
00463 p_new_pic->i_status = DESTROYED_PICTURE;
00464 p_new_pic->i_type = DIRECT_PICTURE;
00465 p_new_pic->p_sys = (picture_sys_t *)p_new_pic->pf_release;
00466 p_new_pic->pf_release = ReleasePicture;
00467 p_new_pic->date = p_pic->date;
00468
00469 p_pic->pf_release( p_pic );
00470 PushPicture( p_stream, p_new_pic );
00471 }
00472
00473 return VLC_SUCCESS;
00474 }
00475
00476 struct picture_sys_t
00477 {
00478 vlc_object_t *p_owner;
00479 };
00480
00481 static void video_release_buffer( picture_t *p_pic )
00482 {
00483 if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
00484 {
00485 video_del_buffer( (decoder_t *)p_pic->p_sys->p_owner, p_pic );
00486 }
00487 else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
00488 }
00489
00490 static picture_t *video_new_buffer( decoder_t *p_dec )
00491 {
00492 picture_t **pp_ring = p_dec->p_owner->pp_pics;
00493 picture_t *p_pic;
00494 int i;
00495
00496
00497 for( i = 0; i < PICTURE_RING_SIZE; i++ )
00498 {
00499 if( pp_ring[i] != 0 && pp_ring[i]->i_status == DESTROYED_PICTURE )
00500 {
00501 pp_ring[i]->i_status = RESERVED_PICTURE;
00502 return pp_ring[i];
00503 }
00504 }
00505 for( i = 0; i < PICTURE_RING_SIZE; i++ )
00506 {
00507 if( pp_ring[i] == 0 ) break;
00508 }
00509
00510 if( i == PICTURE_RING_SIZE )
00511 {
00512 msg_Err( p_dec, "decoder/filter is leaking pictures, "
00513 "resetting its ring buffer" );
00514
00515 for( i = 0; i < PICTURE_RING_SIZE; i++ )
00516 {
00517 pp_ring[i]->pf_release( pp_ring[i] );
00518 }
00519
00520 i = 0;
00521 }
00522
00523 p_pic = malloc( sizeof(picture_t) );
00524 p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
00525 vout_AllocatePicture( VLC_OBJECT(p_dec), p_pic,
00526 p_dec->fmt_out.video.i_chroma,
00527 p_dec->fmt_out.video.i_width,
00528 p_dec->fmt_out.video.i_height,
00529 p_dec->fmt_out.video.i_aspect );
00530
00531 if( !p_pic->i_planes )
00532 {
00533 free( p_pic );
00534 return 0;
00535 }
00536
00537 p_pic->pf_release = video_release_buffer;
00538 p_pic->p_sys = malloc( sizeof(picture_sys_t) );
00539 p_pic->p_sys->p_owner = VLC_OBJECT(p_dec);
00540 p_pic->i_status = RESERVED_PICTURE;
00541
00542 pp_ring[i] = p_pic;
00543
00544 return p_pic;
00545 }
00546
00547 static void video_del_buffer( decoder_t *p_this, picture_t *p_pic )
00548 {
00549 p_pic->i_refcount = 0;
00550 p_pic->i_status = DESTROYED_PICTURE;
00551 }
00552
00553 static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
00554 {
00555 p_pic->i_refcount++;
00556 }
00557
00558 static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
00559 {
00560 video_release_buffer( p_pic );
00561 }
00562