deinterlace.c 16.1 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
2
 * deinterlace.c : deinterlacer plugin for vlc
Sam Hocevar's avatar
 
Sam Hocevar committed
3 4
 *****************************************************************************
 * Copyright (C) 2000, 2001 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
5
 * $Id: deinterlace.c,v 1.2 2002/01/02 14:37:42 sam Exp $
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <errno.h>
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>

Sam Hocevar's avatar
 
Sam Hocevar committed
31
#include <videolan/vlc.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
32 33 34 35

#include "video.h"
#include "video_output.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
36 37
#include "filter_common.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
38 39
#define DEINTERLACE_MODE_BOB     1
#define DEINTERLACE_MODE_BLEND   2
Sam Hocevar's avatar
 
Sam Hocevar committed
40 41 42 43 44 45

/*****************************************************************************
 * Capabilities defined in the other files.
 *****************************************************************************/
static void vout_getfunctions( function_list_t * p_function_list );

Sam Hocevar's avatar
 
Sam Hocevar committed
46 47
static void *memblend( void *, const void *, const void *, size_t );

Sam Hocevar's avatar
 
Sam Hocevar committed
48 49 50 51 52 53 54
/*****************************************************************************
 * Build configuration tree.
 *****************************************************************************/
MODULE_CONFIG_START
MODULE_CONFIG_STOP

MODULE_INIT_START
Sam Hocevar's avatar
 
Sam Hocevar committed
55 56 57 58 59
    SET_DESCRIPTION( "deinterlacing module" )
    /* Capability score set to 0 because we don't want to be spawned
     * as a video output unless explicitly requested to */
    ADD_CAPABILITY( VOUT, 0 )
    ADD_SHORTCUT( "deinterlace" )
Sam Hocevar's avatar
 
Sam Hocevar committed
60 61 62 63 64 65 66 67 68 69
MODULE_INIT_STOP

MODULE_ACTIVATE_START
    vout_getfunctions( &p_module->p_functions->vout );
MODULE_ACTIVATE_STOP

MODULE_DEACTIVATE_START
MODULE_DEACTIVATE_STOP

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
70
 * vout_sys_t: Deinterlace video output method descriptor
Sam Hocevar's avatar
 
Sam Hocevar committed
71 72
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
Sam Hocevar's avatar
 
Sam Hocevar committed
73
 * It describes the Deinterlace specific properties of an output thread.
Sam Hocevar's avatar
 
Sam Hocevar committed
74 75 76
 *****************************************************************************/
typedef struct vout_sys_s
{
Sam Hocevar's avatar
 
Sam Hocevar committed
77
    int i_mode;
Sam Hocevar's avatar
 
Sam Hocevar committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    struct vout_thread_s *p_vout;

} vout_sys_t;

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  vout_Probe     ( probedata_t *p_data );
static int  vout_Create    ( struct vout_thread_s * );
static int  vout_Init      ( struct vout_thread_s * );
static void vout_End       ( struct vout_thread_s * );
static void vout_Destroy   ( struct vout_thread_s * );
static int  vout_Manage    ( struct vout_thread_s * );
static void vout_Display   ( struct vout_thread_s *, struct picture_s * );

/*****************************************************************************
 * Functions exported as capabilities. They are declared as static so that
 * we don't pollute the namespace too much.
 *****************************************************************************/
static void vout_getfunctions( function_list_t * p_function_list )
{
    p_function_list->pf_probe = vout_Probe;
    p_function_list->functions.vout.pf_create     = vout_Create;
    p_function_list->functions.vout.pf_init       = vout_Init;
    p_function_list->functions.vout.pf_end        = vout_End;
    p_function_list->functions.vout.pf_destroy    = vout_Destroy;
    p_function_list->functions.vout.pf_manage     = vout_Manage;
    p_function_list->functions.vout.pf_display    = vout_Display;
    p_function_list->functions.vout.pf_setpalette = NULL;
}

/*****************************************************************************
 * intf_Probe: return a score
 *****************************************************************************/
static int vout_Probe( probedata_t *p_data )
{
    return( 0 );
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
118
 * vout_Create: allocates Deinterlace video thread output method
Sam Hocevar's avatar
 
Sam Hocevar committed
119
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
120
 * This function allocates and initializes a Deinterlace vout method.
Sam Hocevar's avatar
 
Sam Hocevar committed
121 122 123
 *****************************************************************************/
static int vout_Create( vout_thread_t *p_vout )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
124 125
    char *psz_method;

Sam Hocevar's avatar
 
Sam Hocevar committed
126 127 128 129 130 131 132 133
    /* Allocate structure */
    p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_vout->p_sys == NULL )
    {
        intf_ErrMsg("error: %s", strerror(ENOMEM) );
        return( 1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    /* Look what method was requested */
    psz_method = main_GetPszVariable( VOUT_FILTER_VAR, "" );

    while( *psz_method && *psz_method != ':' )
    {
        psz_method++;
    }

    if( !strcmp( psz_method, ":bob" ) )
    {
        p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
    }
    else if( !strcmp( psz_method, ":blend" ) )
    {
        p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
    }
    else
    {
        intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
                     "using deinterlace:bob" );
        p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
157 158 159 160
    return( 0 );
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
161
 * vout_Init: initialize Deinterlace video thread output method
Sam Hocevar's avatar
 
Sam Hocevar committed
162 163 164 165 166 167 168 169 170 171 172 173 174
 *****************************************************************************/
static int vout_Init( vout_thread_t *p_vout )
{
    int i_index;
    char *psz_filter;
    picture_t *p_pic;
    
    I_OUTPUTPICTURES = 0;

    /* Initialize the output structure, full of directbuffers since we want
     * the decoder to output directly to our structures. */
    switch( p_vout->render.i_chroma )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
175 176 177 178
        case FOURCC_I420:
        case FOURCC_IYUV:
        case FOURCC_YV12:
        case FOURCC_I422:
Sam Hocevar's avatar
 
Sam Hocevar committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
            p_vout->output.i_chroma = p_vout->render.i_chroma;
            p_vout->output.i_width  = p_vout->render.i_width;
            p_vout->output.i_height = p_vout->render.i_height;
            p_vout->output.i_aspect = p_vout->render.i_aspect;
            break;

        default:
            return( 0 ); /* unknown chroma */
            break;
    }

    /* Try to open the real video output, with half the height our images */
    psz_filter = main_GetPszVariable( VOUT_FILTER_VAR, "" );
    main_PutPszVariable( VOUT_FILTER_VAR, "" );

    intf_WarnMsg( 1, "filter: spawning the real video output" );
Sam Hocevar's avatar
 
Sam Hocevar committed
195 196 197

    switch( p_vout->render.i_chroma )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
198 199 200
    case FOURCC_I420:
    case FOURCC_IYUV:
    case FOURCC_YV12:
Sam Hocevar's avatar
 
Sam Hocevar committed
201 202 203
        switch( p_vout->p_sys->i_mode )
        {
        case DEINTERLACE_MODE_BOB:
Sam Hocevar's avatar
 
Sam Hocevar committed
204 205
            p_vout->p_sys->p_vout =
                vout_CreateThread( NULL,
Sam Hocevar's avatar
 
Sam Hocevar committed
206 207
                       p_vout->output.i_width, p_vout->output.i_height / 2,
                       p_vout->output.i_chroma, p_vout->output.i_aspect );
Sam Hocevar's avatar
 
Sam Hocevar committed
208 209
            break;

Sam Hocevar's avatar
 
Sam Hocevar committed
210
        case DEINTERLACE_MODE_BLEND:
Sam Hocevar's avatar
 
Sam Hocevar committed
211 212
            p_vout->p_sys->p_vout =
                vout_CreateThread( NULL,
Sam Hocevar's avatar
 
Sam Hocevar committed
213 214
                       p_vout->output.i_width, p_vout->output.i_height,
                       p_vout->output.i_chroma, p_vout->output.i_aspect );
Sam Hocevar's avatar
 
Sam Hocevar committed
215
            break;
Sam Hocevar's avatar
 
Sam Hocevar committed
216 217
        }
        break;
Sam Hocevar's avatar
 
Sam Hocevar committed
218

Sam Hocevar's avatar
 
Sam Hocevar committed
219
    case FOURCC_I422:
Sam Hocevar's avatar
 
Sam Hocevar committed
220 221 222
        p_vout->p_sys->p_vout =
            vout_CreateThread( NULL,
                       p_vout->output.i_width, p_vout->output.i_height,
Sam Hocevar's avatar
 
Sam Hocevar committed
223
                       FOURCC_I420, p_vout->output.i_aspect );
Sam Hocevar's avatar
 
Sam Hocevar committed
224 225 226 227
        break;

    default:
        break;
Sam Hocevar's avatar
 
Sam Hocevar committed
228
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
229 230 231 232 233 234 235 236 237 238 239

    /* Everything failed */
    if( p_vout->p_sys->p_vout == NULL )
    {
        intf_ErrMsg( "filter error: can't open vout, aborting" );

        return( 0 );
    }
 
    main_PutPszVariable( VOUT_FILTER_VAR, psz_filter );

Sam Hocevar's avatar
 
Sam Hocevar committed
240
    ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
Sam Hocevar's avatar
 
Sam Hocevar committed
241 242 243 244 245

    return( 0 );
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
246
 * vout_End: terminate Deinterlace video thread output method
Sam Hocevar's avatar
 
Sam Hocevar committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260
 *****************************************************************************/
static void vout_End( vout_thread_t *p_vout )
{
    int i_index;

    /* Free the fake output buffers we allocated */
    for( i_index = I_OUTPUTPICTURES ; i_index ; )
    {
        i_index--;
        free( PP_OUTPUTPICTURE[ i_index ]->planes[ 0 ].p_data );
    }
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
261
 * vout_Destroy: destroy Deinterlace video thread output method
Sam Hocevar's avatar
 
Sam Hocevar committed
262
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
263
 * Terminate an output method created by DeinterlaceCreateOutputMethod
Sam Hocevar's avatar
 
Sam Hocevar committed
264 265 266 267 268 269 270 271 272
 *****************************************************************************/
static void vout_Destroy( vout_thread_t *p_vout )
{
    vout_DestroyThread( p_vout->p_sys->p_vout, NULL );

    free( p_vout->p_sys );
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
273
 * vout_Manage: handle Deinterlace events
Sam Hocevar's avatar
 
Sam Hocevar committed
274 275 276 277 278 279 280 281 282 283 284 285
 *****************************************************************************
 * This function should be called regularly by video output thread. It manages
 * console events. It returns a non null value on error.
 *****************************************************************************/
static int vout_Manage( vout_thread_t *p_vout )
{
    return( 0 );
}

/*****************************************************************************
 * vout_Display: displays previously rendered output
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
286 287 288
 * This function send the currently rendered image to Deinterlace image,
 * waits until it is displayed and switch the two rendering buffers, preparing
 * next frame.
Sam Hocevar's avatar
 
Sam Hocevar committed
289
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
290
static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
Sam Hocevar's avatar
 
Sam Hocevar committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
{
    picture_t *p_outpic;
    int i_index, i_field;

    for( i_field = 0 ; i_field < 2 ; i_field++ )
    {
        /* Get a structure from the video_output. */
        while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout,
                                                0, 0, 0 ) )
                  == NULL )
        {
            if( p_vout->b_die || p_vout->b_error )
            {
                return;
            }
            msleep( VOUT_OUTMEM_SLEEP );
        }   

        /* XXX: completely arbitrary values ! */
        vout_DatePicture( p_vout->p_sys->p_vout, p_outpic,
                          mdate() + (mtime_t)(50000 + i_field * 20000) );

        /* Copy image and skip lines */
Sam Hocevar's avatar
 
Sam Hocevar committed
314
        for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
315
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
316 317
            pixel_data_t *p_in, *p_out_end, *p_out;
            int i_increment;
Sam Hocevar's avatar
 
Sam Hocevar committed
318

Sam Hocevar's avatar
 
Sam Hocevar committed
319 320
            p_in = p_pic->planes[ i_index ].p_data
                       + i_field * p_pic->planes[ i_index ].i_line_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
321 322

            p_out = p_outpic->planes[ i_index ].p_data;
Sam Hocevar's avatar
 
Sam Hocevar committed
323
            p_out_end = p_out + p_outpic->planes[ i_index ].i_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
324

Sam Hocevar's avatar
 
Sam Hocevar committed
325
            switch( p_vout->render.i_chroma )
Sam Hocevar's avatar
 
Sam Hocevar committed
326
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
327 328 329
            case FOURCC_I420:
            case FOURCC_IYUV:
            case FOURCC_YV12:
Sam Hocevar's avatar
 
Sam Hocevar committed
330

Sam Hocevar's avatar
 
Sam Hocevar committed
331 332 333
                switch( p_vout->p_sys->i_mode )
                {
                case DEINTERLACE_MODE_BOB:
Sam Hocevar's avatar
 
Sam Hocevar committed
334 335
                    for( ; p_out < p_out_end ; )
                    {
Sam Hocevar's avatar
 
Sam Hocevar committed
336
                        FAST_MEMCPY( p_out, p_in,
Sam Hocevar's avatar
 
Sam Hocevar committed
337 338 339 340 341 342 343
                                     p_pic->planes[ i_index ].i_line_bytes );

                        p_out += p_pic->planes[ i_index ].i_line_bytes;
                        p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
                    }
                    break;

Sam Hocevar's avatar
 
Sam Hocevar committed
344 345
                case DEINTERLACE_MODE_BLEND:
                    if( i_index != Y_PLANE )
Sam Hocevar's avatar
 
Sam Hocevar committed
346 347 348
                    {
                        for( ; p_out < p_out_end ; )
                        {
Sam Hocevar's avatar
 
Sam Hocevar committed
349
                            FAST_MEMCPY( p_out, p_in,
Sam Hocevar's avatar
 
Sam Hocevar committed
350
                                     p_pic->planes[ i_index ].i_line_bytes );
Sam Hocevar's avatar
 
Sam Hocevar committed
351

Sam Hocevar's avatar
 
Sam Hocevar committed
352
                            p_out += p_pic->planes[ i_index ].i_line_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
353 354

                            FAST_MEMCPY( p_out, p_in,
Sam Hocevar's avatar
 
Sam Hocevar committed
355
                                     p_pic->planes[ i_index ].i_line_bytes );
Sam Hocevar's avatar
 
Sam Hocevar committed
356

Sam Hocevar's avatar
 
Sam Hocevar committed
357
                            p_out += p_pic->planes[ i_index ].i_line_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
358
                            p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
359
                        }
Sam Hocevar's avatar
 
Sam Hocevar committed
360
                        break;
Sam Hocevar's avatar
 
Sam Hocevar committed
361
                    }
Sam Hocevar's avatar
 
Sam Hocevar committed
362 363

                    if( i_field == 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
364
                    {
Sam Hocevar's avatar
 
Sam Hocevar committed
365
                        FAST_MEMCPY( p_out, p_in,
Sam Hocevar's avatar
 
Sam Hocevar committed
366
                                     p_pic->planes[ i_index ].i_line_bytes );
Sam Hocevar's avatar
 
Sam Hocevar committed
367 368
                        p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
                        p_out += p_pic->planes[ i_index ].i_line_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
369 370
                    }

Sam Hocevar's avatar
 
Sam Hocevar committed
371 372 373 374 375 376 377 378 379 380 381 382
                    for( ; p_out < p_out_end ; )
                    {
                        FAST_MEMCPY( p_out, p_in,
                                     p_pic->planes[ i_index ].i_line_bytes );

                        p_out += p_pic->planes[ i_index ].i_line_bytes;

                        memblend( p_out, p_in, p_in + 2 * p_pic->planes[ i_index ].i_line_bytes, p_pic->planes[ i_index ].i_line_bytes );

                        p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
                        p_out += p_pic->planes[ i_index ].i_line_bytes;
                    }
Sam Hocevar's avatar
 
Sam Hocevar committed
383
                    break;
Sam Hocevar's avatar
 
Sam Hocevar committed
384 385 386
                }
                break;

Sam Hocevar's avatar
 
Sam Hocevar committed
387
            case FOURCC_I422:
Sam Hocevar's avatar
 
Sam Hocevar committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

                i_increment = 2 * p_pic->planes[ i_index ].i_line_bytes;

                if( i_index == Y_PLANE )
                {
                    for( ; p_out < p_out_end ; )
                    {
                        FAST_MEMCPY( p_out, p_in,
                                 p_pic->planes[ i_index ].i_line_bytes );
                        p_out += p_pic->planes[ i_index ].i_line_bytes;
                        FAST_MEMCPY( p_out, p_in,
                                 p_pic->planes[ i_index ].i_line_bytes );
                        p_out += p_pic->planes[ i_index ].i_line_bytes;
                        p_in += i_increment;
                    }
                }
                else
                {
                    for( ; p_out < p_out_end ; )
                    {
                        FAST_MEMCPY( p_out, p_in,
                                 p_pic->planes[ i_index ].i_line_bytes );
                        p_out += p_pic->planes[ i_index ].i_line_bytes;
                        p_in += i_increment;
                    }
                }
                break;

            default:
                break;
Sam Hocevar's avatar
 
Sam Hocevar committed
418 419 420 421 422 423 424
            }
        }

        vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
    }
}

Sam Hocevar's avatar
 
Sam Hocevar committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
static void *memblend( void *p_dest, const void *p_s1,
                       const void *p_s2, size_t i_bytes )
{
    u8* p_end = (u8*)p_dest + i_bytes - 8;

    while( (u8*)p_dest < p_end )
    {
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
    }

    p_end += 8;

    while( (u8*)p_dest < p_end )
    {
        *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
    }

    return p_dest;
}