mosaic_bridge.c 24.9 KB
Newer Older
1 2 3
/*****************************************************************************
 * mosaic_bridge.c:
 *****************************************************************************
4
 * Copyright (C) 2004-2007 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * $Id$
 *
 * Authors: Antoine Cellerier <dionoea@videolan.org>
 *          Christophe Massiot <massiot@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
35 36 37
#include <vlc_sout.h>
#include <vlc_block.h>
#include <vlc_codec.h>
38
#include <vlc_meta.h>
39

40 41
#include <vlc_image.h>
#include <vlc_filter.h>
42 43 44 45 46 47 48 49 50 51 52 53 54 55

#include "../video_filter/mosaic.h"

/*****************************************************************************
 * Local structures
 *****************************************************************************/
struct sout_stream_sys_t
{
    bridged_es_t *p_es;
    vlc_mutex_t *p_lock;

    decoder_t       *p_decoder;
    image_handler_t *p_image; /* filter for resizing */
    int i_height, i_width;
56
    unsigned int i_sar_num, i_sar_den;
57
    char *psz_id;
58
    bool b_inited;
59

60 61
    int i_chroma; /* force image format chroma */

62
    filter_chain_t *p_vf2;
63 64 65 66
};

struct decoder_owner_sys_t
{
67 68
    /* Current format in use by the output */
    video_format_t video;
69 70 71 72 73 74 75 76 77 78 79
};

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Open    ( vlc_object_t * );
static void Close   ( vlc_object_t * );
static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
static int               Del ( sout_stream_t *, sout_stream_id_t * );
static int               Send( sout_stream_t *, sout_stream_id_t *, block_t * );

80 81 82 83 84 85
inline static void video_del_buffer_decoder( decoder_t *, picture_t * );
inline static void video_del_buffer_filter( filter_t *, picture_t * );

inline static picture_t *video_new_buffer_decoder( decoder_t * );
inline static picture_t *video_new_buffer_filter( filter_t * );
static picture_t *video_new_buffer( vlc_object_t *, decoder_owner_sys_t *,
86
                                    es_format_t * );
87

88 89
static void video_link_picture_decoder( decoder_t *, picture_t * );
static void video_unlink_picture_decoder( decoder_t *, picture_t * );
90 91 92 93 94 95 96 97 98 99 100

static int HeightCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
static int WidthCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
static int alphaCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
static int xCallback( vlc_object_t *, char const *,
                      vlc_value_t, vlc_value_t, void * );
static int yCallback( vlc_object_t *, char const *,
                      vlc_value_t, vlc_value_t, void * );
101 102 103 104 105 106 107 108 109 110

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define ID_TEXT N_("ID")
#define ID_LONGTEXT N_( \
    "Specify an identifier string for this subpicture" )

#define WIDTH_TEXT N_("Video width")
#define WIDTH_LONGTEXT N_( \
111
    "Output video width." )
112 113
#define HEIGHT_TEXT N_("Video height")
#define HEIGHT_LONGTEXT N_( \
114
    "Output video height." )
115 116 117
#define RATIO_TEXT N_("Sample aspect ratio")
#define RATIO_LONGTEXT N_( \
    "Sample aspect ratio of the destination (1:1, 3:4, 2:3)." )
118

119 120
#define VFILTER_TEXT N_("Video filter")
#define VFILTER_LONGTEXT N_( \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
121
    "Video filters will be applied to the video stream." )
122 123 124 125

#define CHROMA_TEXT N_("Image chroma")
#define CHROMA_LONGTEXT N_( \
    "Force the use of a specific chroma. Use YUVA if you're planning " \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
126
    "to use the Alphamask or Bluescreen video filter." )
127

128 129 130 131 132 133 134 135 136 137 138 139
#define ALPHA_TEXT N_("Transparency")
#define ALPHA_LONGTEXT N_( \
    "Transparency of the mosaic picture." )

#define X_TEXT N_("X offset")
#define X_LONGTEXT N_( \
    "X coordinate of the upper left corner in the mosaic if non negative." )

#define Y_TEXT N_("Y offset")
#define Y_LONGTEXT N_( \
    "Y coordinate of the upper left corner in the mosaic if non negative." )

140
#define CFG_PREFIX "sout-mosaic-bridge-"
141

142 143 144 145 146
vlc_module_begin ()
    set_shortname( N_( "Mosaic bridge" ) )
    set_description(N_("Mosaic bridge stream output") )
    set_capability( "sout stream", 0 )
    add_shortcut( "mosaic-bridge" )
147

148
    add_string( CFG_PREFIX "id", "Id", NULL, ID_TEXT, ID_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
149
                false )
150
    add_integer( CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
151
                 WIDTH_LONGTEXT, true )
152
    add_integer( CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
153
                 HEIGHT_LONGTEXT, true )
154
    add_string( CFG_PREFIX "sar", "1:1", NULL, RATIO_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
155
                RATIO_LONGTEXT, false )
ivoire's avatar
ivoire committed
156
    add_string( CFG_PREFIX "chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
157
                false )
158 159

    add_module_list( CFG_PREFIX "vfilter", "video filter2",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
160
                     NULL, NULL, VFILTER_TEXT, VFILTER_LONGTEXT, false )
161

162
    add_integer_with_range( CFG_PREFIX "alpha", 255, 0, 255, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
163
                            ALPHA_TEXT, ALPHA_LONGTEXT, false )
164 165
    add_integer( CFG_PREFIX "x", -1, NULL, X_TEXT, X_LONGTEXT, false )
    add_integer( CFG_PREFIX "y", -1, NULL, Y_TEXT, Y_LONGTEXT, false )
166

167 168
    set_callbacks( Open, Close )
vlc_module_end ()
169

170
static const char *const ppsz_sout_options[] = {
171
    "id", "width", "height", "sar", "vfilter", "chroma", "alpha", "x", "y", NULL
172 173 174 175 176 177 178
};

/*****************************************************************************
 * Open
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
179 180
    sout_stream_t        *p_stream = (sout_stream_t *)p_this;
    sout_stream_sys_t    *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
181
    vlc_object_t         *p_libvlc = VLC_OBJECT( p_this->p_libvlc );
182
    vlc_value_t           val;
183

184
    config_ChainParse( p_stream, CFG_PREFIX, ppsz_sout_options,
185 186 187 188 189
                       p_stream->p_cfg );

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;
190 191

    p_stream->p_sys = p_sys;
192
    p_sys->b_inited = false;
193

194 195
    var_Create( p_libvlc, "mosaic-lock", VLC_VAR_MUTEX );
    var_Get( p_libvlc, "mosaic-lock", &val );
196 197
    p_sys->p_lock = val.p_address;

198
    p_sys->psz_id = var_CreateGetString( p_stream, CFG_PREFIX "id" );
199

200
    p_sys->i_height =
201
        var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "height" );
202
    var_AddCallback( p_stream, CFG_PREFIX "height", HeightCallback, p_stream );
203

204
    p_sys->i_width =
205
        var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "width" );
206
    var_AddCallback( p_stream, CFG_PREFIX "width", WidthCallback, p_stream );
207

208
    var_Get( p_stream, CFG_PREFIX "sar", &val );
209
    if( val.psz_string )
210 211 212 213 214 215 216 217
    {
        char *psz_parser = strchr( val.psz_string, ':' );

        if( psz_parser )
        {
            *psz_parser++ = '\0';
            p_sys->i_sar_num = atoi( val.psz_string );
            p_sys->i_sar_den = atoi( psz_parser );
218 219
            vlc_ureduce( &p_sys->i_sar_num, &p_sys->i_sar_den,
                         p_sys->i_sar_num, p_sys->i_sar_den, 0 );
220 221 222 223 224 225 226 227 228 229 230 231 232 233
        }
        else
        {
            msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
            p_sys->i_sar_num = p_sys->i_sar_den = 1;
        }

        free( val.psz_string );
    }
    else
    {
        p_sys->i_sar_num = p_sys->i_sar_den = 1;
    }

234 235 236 237 238 239 240
    p_sys->i_chroma = 0;
    val.psz_string = var_GetNonEmptyString( p_stream, CFG_PREFIX "chroma" );
    if( val.psz_string && strlen( val.psz_string ) >= 4 )
    {
        memcpy( &p_sys->i_chroma, val.psz_string, 4 );
        msg_Dbg( p_stream, "Forcing image chroma to 0x%.8x (%4.4s)", p_sys->i_chroma, (char*)&p_sys->i_chroma );
    }
ivoire's avatar
ivoire committed
241
    free( val.psz_string );
242

243
#define INT_COMMAND( a ) do { \
244 245 246
    var_Create( p_stream, CFG_PREFIX #a, \
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); \
    var_AddCallback( p_stream, CFG_PREFIX #a, a ## Callback, \
247 248 249 250 251 252
                     p_stream ); } while(0)
    INT_COMMAND( alpha );
    INT_COMMAND( x );
    INT_COMMAND( y );

#undef INT_COMMAND
253

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
    p_stream->pf_add    = Add;
    p_stream->pf_del    = Del;
    p_stream->pf_send   = Send;

    p_stream->p_sout->i_out_pace_nocontrol++;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    sout_stream_t     *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

271 272 273 274 275 276 277
    /* Delete the callbacks */
    var_DelCallback( p_stream, CFG_PREFIX "height", HeightCallback, p_stream );
    var_DelCallback( p_stream, CFG_PREFIX "width", WidthCallback, p_stream );
    var_DelCallback( p_stream, CFG_PREFIX "alpha", alphaCallback, p_stream );
    var_DelCallback( p_stream, CFG_PREFIX "x", xCallback, p_stream );
    var_DelCallback( p_stream, CFG_PREFIX "y", yCallback, p_stream );

278 279
    p_stream->p_sout->i_out_pace_nocontrol--;

280
    free( p_sys->psz_id );
281 282 283 284

    free( p_sys );
}

285 286 287 288 289 290 291 292
static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
{
    p_filter->pf_vout_buffer_new = video_new_buffer_filter;
    p_filter->pf_vout_buffer_del = video_del_buffer_filter;
    p_filter->p_owner = p_data;
    return VLC_SUCCESS;
}

293 294 295 296 297
static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    bridge_t *p_bridge;
    bridged_es_t *p_es;
298
    char *psz_chain;
299 300
    int i;

301
    if( p_sys->b_inited || p_fmt->i_cat != VIDEO_ES )
302 303 304 305
        return NULL;

    /* Create decoder object */
    p_sys->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
306 307
    if( !p_sys->p_decoder )
        return NULL;
308 309 310
    vlc_object_attach( p_sys->p_decoder, p_stream );
    p_sys->p_decoder->p_module = NULL;
    p_sys->p_decoder->fmt_in = *p_fmt;
311
    p_sys->p_decoder->b_pace_control = false;
312 313 314 315
    p_sys->p_decoder->fmt_out = p_sys->p_decoder->fmt_in;
    p_sys->p_decoder->fmt_out.i_extra = 0;
    p_sys->p_decoder->fmt_out.p_extra = 0;
    p_sys->p_decoder->pf_decode_video = 0;
316 317
    p_sys->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder;
    p_sys->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder;
318 319 320
    p_sys->p_decoder->pf_picture_link    = video_link_picture_decoder;
    p_sys->p_decoder->pf_picture_unlink  = video_unlink_picture_decoder;
    p_sys->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
321 322 323 324 325 326 327
    if( !p_sys->p_decoder->p_owner )
    {
        vlc_object_detach( p_sys->p_decoder );
        vlc_object_release( p_sys->p_decoder );
        return NULL;
    }

328
    p_sys->p_decoder->p_owner->video = p_fmt->video;
329 330 331
    //p_sys->p_decoder->p_cfg = p_sys->p_video_cfg;

    p_sys->p_decoder->p_module =
332
        module_need( p_sys->p_decoder, "decoder", "$codec", false );
333

334
    if( !p_sys->p_decoder->p_module || !p_sys->p_decoder->pf_decode_video )
335
    {
336 337 338
        if( p_sys->p_decoder->p_module )
        {
            msg_Err( p_stream, "instanciated a non video decoder" );
339
            module_unneed( p_sys->p_decoder, p_sys->p_decoder->p_module );
340 341 342 343 344 345
        }
        else
        {
            msg_Err( p_stream, "cannot find decoder" );
        }
        free( p_sys->p_decoder->p_owner );
346
        vlc_object_detach( p_sys->p_decoder );
347
        vlc_object_release( p_sys->p_decoder );
348 349 350
        return NULL;
    }

351
    p_sys->b_inited = true;
352 353 354 355 356
    vlc_mutex_lock( p_sys->p_lock );

    p_bridge = GetBridge( p_stream );
    if ( p_bridge == NULL )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
357
        vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
358 359
        vlc_value_t val;

360
        p_bridge = xmalloc( sizeof( bridge_t ) );
361

362
        var_Create( p_libvlc, "mosaic-struct", VLC_VAR_ADDRESS );
363
        val.p_address = p_bridge;
364
        var_Set( p_libvlc, "mosaic-struct", val );
365 366 367 368 369 370 371 372 373 374 375 376 377

        p_bridge->i_es_num = 0;
        p_bridge->pp_es = NULL;
    }

    for ( i = 0; i < p_bridge->i_es_num; i++ )
    {
        if ( p_bridge->pp_es[i]->b_empty )
            break;
    }

    if ( i == p_bridge->i_es_num )
    {
378
        p_bridge->pp_es = xrealloc( p_bridge->pp_es,
379
                          (p_bridge->i_es_num + 1) * sizeof(bridged_es_t *) );
380
        p_bridge->i_es_num++;
381
        p_bridge->pp_es[i] = xmalloc( sizeof(bridged_es_t) );
382 383 384 385
    }

    p_sys->p_es = p_es = p_bridge->pp_es[i];

386 387 388 389
    p_es->i_alpha = var_GetInteger( p_stream, CFG_PREFIX "alpha" );
    p_es->i_x = var_GetInteger( p_stream, CFG_PREFIX "x" );
    p_es->i_y = var_GetInteger( p_stream, CFG_PREFIX "y" );

390 391 392 393
    //p_es->fmt = *p_fmt;
    p_es->psz_id = p_sys->psz_id;
    p_es->p_picture = NULL;
    p_es->pp_last = &p_es->p_picture;
394
    p_es->b_empty = false;
395 396 397

    vlc_mutex_unlock( p_sys->p_lock );

398 399 400 401
    if ( p_sys->i_height || p_sys->i_width )
    {
        p_sys->p_image = image_HandlerCreate( p_stream );
    }
402 403 404 405
    else
    {
        p_sys->p_image = NULL;
    }
406

407 408
    msg_Dbg( p_stream, "mosaic bridge id=%s pos=%d", p_es->psz_id, i );

409 410
    /* Create user specified video filters */
    psz_chain = var_GetNonEmptyString( p_stream, CFG_PREFIX "vfilter" );
411
    msg_Dbg( p_stream, "psz_chain: %s", psz_chain );
412
    if( psz_chain )
413
    {
414 415 416 417 418
        p_sys->p_vf2 = filter_chain_New( p_stream, "video filter2", false,
                                         video_filter_buffer_allocation_init,
                                         NULL, p_sys->p_decoder->p_owner );
        es_format_t fmt;
        es_format_Copy( &fmt, &p_sys->p_decoder->fmt_out );
419
        if( p_sys->i_chroma )
420 421 422 423
            fmt.video.i_chroma = p_sys->i_chroma;
        filter_chain_Reset( p_sys->p_vf2, &fmt, &fmt );
        filter_chain_AppendFromString( p_sys->p_vf2, psz_chain );
        free( psz_chain );
424
    }
dionoea's avatar
dionoea committed
425 426 427 428
    else
    {
        p_sys->p_vf2 = NULL;
    }
429

430 431 432 433 434
    return (sout_stream_id_t *)p_sys;
}

static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
{
Rafaël Carré's avatar
Rafaël Carré committed
435
    VLC_UNUSED(id);
436 437 438
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    bridge_t *p_bridge;
    bridged_es_t *p_es;
439
    bool b_last_es = true;
440 441
    int i;

442
    if( !p_sys->b_inited )
443 444
        return VLC_SUCCESS;

445
    if( p_sys->p_decoder != NULL )
446
    {
447
        decoder_owner_sys_t *p_owner = p_sys->p_decoder->p_owner;
448

449
        if( p_sys->p_decoder->p_module )
450
            module_unneed( p_sys->p_decoder, p_sys->p_decoder->p_module );
451 452 453
        if( p_sys->p_decoder->p_description )
            vlc_meta_Delete( p_sys->p_decoder->p_description );

454
        vlc_object_detach( p_sys->p_decoder );
455
        vlc_object_release( p_sys->p_decoder );
456

457
        free( p_owner );
458 459
    }

460
    /* Destroy user specified video filters */
461 462
    if( p_sys->p_vf2 )
        filter_chain_Delete( p_sys->p_vf2 );
463

464 465 466 467 468
    vlc_mutex_lock( p_sys->p_lock );

    p_bridge = GetBridge( p_stream );
    p_es = p_sys->p_es;

469
    p_es->b_empty = true;
470 471 472
    while ( p_es->p_picture )
    {
        picture_t *p_next = p_es->p_picture->p_next;
473
        picture_Release( p_es->p_picture );
474 475 476 477 478 479 480
        p_es->p_picture = p_next;
    }

    for ( i = 0; i < p_bridge->i_es_num; i++ )
    {
        if ( !p_bridge->pp_es[i]->b_empty )
        {
481
            b_last_es = false;
482 483 484 485 486 487
            break;
        }
    }

    if ( b_last_es )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
488
        vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
489 490 491 492
        for ( i = 0; i < p_bridge->i_es_num; i++ )
            free( p_bridge->pp_es[i] );
        free( p_bridge->pp_es );
        free( p_bridge );
493
        var_Destroy( p_libvlc, "mosaic-struct" );
494 495 496 497
    }

    vlc_mutex_unlock( p_sys->p_lock );

498
    if ( p_sys->p_image )
499 500 501 502
    {
        image_HandlerDelete( p_sys->p_image );
    }

503
    p_sys->b_inited = false;
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541

    return VLC_SUCCESS;
}

/*****************************************************************************
 * PushPicture : push a picture in the mosaic-struct structure
 *****************************************************************************/
static void PushPicture( sout_stream_t *p_stream, picture_t *p_picture )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    bridged_es_t *p_es = p_sys->p_es;

    vlc_mutex_lock( p_sys->p_lock );

    *p_es->pp_last = p_picture;
    p_picture->p_next = NULL;
    p_es->pp_last = &p_picture->p_next;

    vlc_mutex_unlock( p_sys->p_lock );
}

static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
                 block_t *p_buffer )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    picture_t *p_pic;

    if ( (sout_stream_sys_t *)id != p_sys )
    {
        block_ChainRelease( p_buffer );
        return VLC_SUCCESS;
    }

    while ( (p_pic = p_sys->p_decoder->pf_decode_video( p_sys->p_decoder,
                                                        &p_buffer )) )
    {
        picture_t *p_new_pic;

542
        if( p_sys->i_height || p_sys->i_width )
543
        {
544 545 546 547
            video_format_t fmt_out, fmt_in;

            memset( &fmt_in, 0, sizeof(video_format_t) );
            memset( &fmt_out, 0, sizeof(video_format_t) );
548 549
            fmt_in = p_sys->p_decoder->fmt_out.video;

550 551 552

            if( p_sys->i_chroma )
                fmt_out.i_chroma = p_sys->i_chroma;
553
            else
554
                fmt_out.i_chroma = VLC_CODEC_I420;
555

Laurent Aimar's avatar
Laurent Aimar committed
556 557 558 559
            const unsigned i_fmt_in_aspect =
                (int64_t)VOUT_ASPECT_FACTOR *
                fmt_in.i_sar_num * fmt_in.i_width /
                (fmt_in.i_sar_den * fmt_in.i_height);
560 561 562 563
            if ( !p_sys->i_height )
            {
                fmt_out.i_width = p_sys->i_width;
                fmt_out.i_height = (p_sys->i_width * VOUT_ASPECT_FACTOR
Laurent Aimar's avatar
Laurent Aimar committed
564
                    * p_sys->i_sar_num / p_sys->i_sar_den / i_fmt_in_aspect)
565 566 567 568 569
                      & ~0x1;
            }
            else if ( !p_sys->i_width )
            {
                fmt_out.i_height = p_sys->i_height;
Laurent Aimar's avatar
Laurent Aimar committed
570
                fmt_out.i_width = (p_sys->i_height * i_fmt_in_aspect
571 572 573 574 575 576 577 578
                    * p_sys->i_sar_den / p_sys->i_sar_num / VOUT_ASPECT_FACTOR)
                      & ~0x1;
            }
            else
            {
                fmt_out.i_width = p_sys->i_width;
                fmt_out.i_height = p_sys->i_height;
            }
579 580 581 582 583
            fmt_out.i_visible_width = fmt_out.i_width;
            fmt_out.i_visible_height = fmt_out.i_height;

            p_new_pic = image_Convert( p_sys->p_image,
                                       p_pic, &fmt_in, &fmt_out );
584
            if( p_new_pic == NULL )
585 586
            {
                msg_Err( p_stream, "image conversion failed" );
587
                picture_Release( p_pic );
588 589 590 591 592
                continue;
            }
        }
        else
        {
593
            /* TODO: chroma conversion if needed */
594

595 596
            p_new_pic = picture_New( p_pic->format.i_chroma,
                                     p_pic->format.i_width, p_pic->format.i_height,
Laurent Aimar's avatar
Laurent Aimar committed
597 598
                                     p_sys->p_decoder->fmt_out.video.i_sar_num,
                                     p_sys->p_decoder->fmt_out.video.i_sar_den );
599
            if( !p_new_pic )
600
            {
601
                picture_Release( p_pic );
602 603 604 605
                msg_Err( p_stream, "image allocation failed" );
                continue;
            }

606
            picture_Copy( p_new_pic, p_pic );
607
        }
608
        picture_Release( p_pic );
609

610 611
        if( p_sys->p_vf2 )
            p_new_pic = filter_chain_VideoFilter( p_sys->p_vf2, p_new_pic );
612

613 614 615 616 617 618
        PushPicture( p_stream, p_new_pic );
    }

    return VLC_SUCCESS;
}

619
inline static picture_t *video_new_buffer_decoder( decoder_t *p_dec )
620
{
621 622
    return video_new_buffer( VLC_OBJECT( p_dec ),
                             (decoder_owner_sys_t *)p_dec->p_owner,
623
                             &p_dec->fmt_out );
624 625 626 627 628 629
}

inline static picture_t *video_new_buffer_filter( filter_t *p_filter )
{
    return video_new_buffer( VLC_OBJECT( p_filter ),
                             (decoder_owner_sys_t *)p_filter->p_owner,
630
                             &p_filter->fmt_out );
631 632 633 634
}

static picture_t *video_new_buffer( vlc_object_t *p_this,
                                    decoder_owner_sys_t *p_sys,
635
                                    es_format_t *fmt_out )
636
{
Pierre's avatar
Pierre committed
637
    VLC_UNUSED(p_this);
638 639 640
    if( fmt_out->video.i_width != p_sys->video.i_width ||
        fmt_out->video.i_height != p_sys->video.i_height ||
        fmt_out->video.i_chroma != p_sys->video.i_chroma ||
Laurent Aimar's avatar
Laurent Aimar committed
641 642
        (int64_t)fmt_out->video.i_sar_num * p_sys->video.i_sar_den !=
        (int64_t)fmt_out->video.i_sar_den * p_sys->video.i_sar_num )
643
    {
644 645 646 647
        vlc_ureduce( &fmt_out->video.i_sar_num,
                     &fmt_out->video.i_sar_den,
                     fmt_out->video.i_sar_num,
                     fmt_out->video.i_sar_den, 0 );
648

649 650
        if( !fmt_out->video.i_visible_width ||
            !fmt_out->video.i_visible_height )
651
        {
652 653
            fmt_out->video.i_visible_width = fmt_out->video.i_width;
            fmt_out->video.i_visible_height = fmt_out->video.i_height;
654 655
        }

656 657
        fmt_out->video.i_chroma = fmt_out->i_codec;
        p_sys->video = fmt_out->video;
658 659
    }

660
    /* */
661
    fmt_out->video.i_chroma = fmt_out->i_codec;
662

663
    return picture_NewFromFormat( &fmt_out->video );
664 665
}

666 667 668
inline static void video_del_buffer_decoder( decoder_t *p_this,
                                             picture_t *p_pic )
{
Rafaël Carré's avatar
Rafaël Carré committed
669
    VLC_UNUSED(p_this);
670
    picture_Release( p_pic );
671 672 673 674 675
}

inline static void video_del_buffer_filter( filter_t *p_this,
                                            picture_t *p_pic )
{
Rafaël Carré's avatar
Rafaël Carré committed
676
    VLC_UNUSED(p_this);
677
    picture_Release( p_pic );
678 679 680 681
}

static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
{
Rafaël Carré's avatar
Rafaël Carré committed
682
    VLC_UNUSED(p_dec);
683
    picture_Hold( p_pic );
684 685 686 687
}

static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
{
Rafaël Carré's avatar
Rafaël Carré committed
688
    VLC_UNUSED(p_dec);
689
    picture_Release( p_pic );
690 691
}

692 693 694 695

/**********************************************************************
 * Callback to update (some) params on the fly
 **********************************************************************/
696 697 698
static int HeightCallback( vlc_object_t *p_this, char const *psz_var,
                           vlc_value_t oldval, vlc_value_t newval,
                           void *p_data )
699
{
700
    VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(psz_var);
701 702 703
    sout_stream_t *p_stream = (sout_stream_t *)p_data;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

704 705 706 707 708 709 710 711
    /* We create the handler before updating the value in p_sys
     * so we don't have to worry about locking */
    if( !p_sys->p_image && newval.i_int )
        p_sys->p_image = image_HandlerCreate( p_stream );
    p_sys->i_height = newval.i_int;

    return VLC_SUCCESS;
}
712

713 714 715 716
static int WidthCallback( vlc_object_t *p_this, char const *psz_var,
                           vlc_value_t oldval, vlc_value_t newval,
                           void *p_data )
{
717
    VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(psz_var);
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
    sout_stream_t *p_stream = (sout_stream_t *)p_data;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

    /* We create the handler before updating the value in p_sys
     * so we don't have to worry about locking */
    if( !p_sys->p_image && newval.i_int )
        p_sys->p_image = image_HandlerCreate( p_stream );
    p_sys->i_width = newval.i_int;

    return VLC_SUCCESS;
}

static int alphaCallback( vlc_object_t *p_this, char const *psz_var,
                          vlc_value_t oldval, vlc_value_t newval,
                          void *p_data )
{
734
    VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(psz_var);
735 736 737 738 739 740 741 742 743 744 745 746 747
    sout_stream_t *p_stream = (sout_stream_t *)p_data;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

    if( p_sys->p_es )
        p_sys->p_es->i_alpha = newval.i_int;

    return VLC_SUCCESS;
}

static int xCallback( vlc_object_t *p_this, char const *psz_var,
                      vlc_value_t oldval, vlc_value_t newval,
                      void *p_data )
{
748
    VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(psz_var);
749 750 751 752 753 754 755 756 757 758 759 760 761
    sout_stream_t *p_stream = (sout_stream_t *)p_data;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

    if( p_sys->p_es )
        p_sys->p_es->i_x = newval.i_int;

    return VLC_SUCCESS;
}

static int yCallback( vlc_object_t *p_this, char const *psz_var,
                      vlc_value_t oldval, vlc_value_t newval,
                      void *p_data )
{
762
    VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(psz_var);
763 764 765 766 767 768 769
    sout_stream_t *p_stream = (sout_stream_t *)p_data;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

    if( p_sys->p_es )
        p_sys->p_es->i_y = newval.i_int;

    return VLC_SUCCESS;
770
}