vout_pictures.c 25 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1 2 3
/*****************************************************************************
 * vout_pictures.c : picture management functions
 *****************************************************************************
4
 * Copyright (C) 2000-2004 the VideoLAN team
5
 * $Id$
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
 *          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
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Sam Hocevar's avatar
 
Sam Hocevar committed
23 24 25 26 27 28
 *****************************************************************************/

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

29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
32
#include <assert.h>
33

34
#include <vlc_common.h>
35
#include <libvlc.h>
zorglub's avatar
zorglub committed
36 37
#include <vlc_vout.h>
#include <vlc_osd.h>
38
#include <vlc_filter.h>
39 40
#include <vlc_image.h>
#include <vlc_block.h>
Laurent Aimar's avatar
Laurent Aimar committed
41
#include <vlc_picture_fifo.h>
42
#include <vlc_picture_pool.h>
43

44
#include "vout_pictures.h"
45
#include "vout_internal.h"
46

47 48 49
/**
 * Display a picture
 *
gbazin's avatar
 
gbazin committed
50
 * Remove the reservation flag of a picture, which will cause it to be ready
51
 * for display.
52
 */
Sam Hocevar's avatar
 
Sam Hocevar committed
53
void vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Sam Hocevar's avatar
 
Sam Hocevar committed
54 55
{
    vlc_mutex_lock( &p_vout->picture_lock );
56 57

    if( p_pic->i_status == RESERVED_PICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
58
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
59
        p_pic->i_status = READY_PICTURE;
60
        vlc_cond_signal( &p_vout->p->picture_wait );
Sam Hocevar's avatar
 
Sam Hocevar committed
61
    }
62
    else
Sam Hocevar's avatar
 
Sam Hocevar committed
63
    {
64
        msg_Err( p_vout, "picture to display %p has invalid status %d",
65
                         p_pic, p_pic->i_status );
Sam Hocevar's avatar
 
Sam Hocevar committed
66
    }
67
    p_vout->p->i_picture_qtype = p_pic->i_qtype;
68
    p_vout->p->b_picture_interlaced = !p_pic->b_progressive;
Sam Hocevar's avatar
 
Sam Hocevar committed
69 70 71 72

    vlc_mutex_unlock( &p_vout->picture_lock );
}

73 74 75
/**
 * Allocate a picture in the video output heap.
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
76 77
 * This function creates a reserved image in the video output heap.
 * A null pointer is returned if the function fails. This method provides an
gbazin's avatar
 
gbazin committed
78 79 80
 * already allocated zone of memory in the picture data fields.
 * It needs locking since several pictures can be created by several producers
 * threads.
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
int vout_CountPictureAvailable( vout_thread_t *p_vout )
{
    int i_free = 0;
    int i_pic;

    vlc_mutex_lock( &p_vout->picture_lock );
    for( i_pic = 0; i_pic < I_RENDERPICTURES; i_pic++ )
    {
        picture_t *p_pic = PP_RENDERPICTURE[(p_vout->render.i_last_used_pic + i_pic + 1) % I_RENDERPICTURES];

        switch( p_pic->i_status )
        {
            case DESTROYED_PICTURE:
                i_free++;
                break;

            case FREE_PICTURE:
                i_free++;
                break;

            default:
                break;
        }
    }
    vlc_mutex_unlock( &p_vout->picture_lock );

    return i_free;
}

Sam Hocevar's avatar
 
Sam Hocevar committed
111
picture_t *vout_CreatePicture( vout_thread_t *p_vout,
112 113
                               bool b_progressive,
                               bool b_top_field_first,
114
                               unsigned int i_nb_fields )
Sam Hocevar's avatar
 
Sam Hocevar committed
115
{
Sam Hocevar's avatar
 
Sam Hocevar committed
116 117 118
    int         i_pic;                                      /* picture index */
    picture_t * p_pic;
    picture_t * p_freepic = NULL;                      /* first free picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
119 120 121 122 123

    /* Get lock */
    vlc_mutex_lock( &p_vout->picture_lock );

    /*
gbazin's avatar
 
gbazin committed
124
     * Look for an empty place in the picture heap.
Sam Hocevar's avatar
 
Sam Hocevar committed
125
     */
gbazin's avatar
 
gbazin committed
126
    for( i_pic = 0; i_pic < I_RENDERPICTURES; i_pic++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
127
    {
gbazin's avatar
 
gbazin committed
128 129
        p_pic = PP_RENDERPICTURE[(p_vout->render.i_last_used_pic + i_pic + 1)
                                 % I_RENDERPICTURES];
Sam Hocevar's avatar
 
Sam Hocevar committed
130

Sam Hocevar's avatar
 
Sam Hocevar committed
131
        switch( p_pic->i_status )
Sam Hocevar's avatar
 
Sam Hocevar committed
132
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
133 134 135 136
            case DESTROYED_PICTURE:
                /* Memory will not be reallocated, and function can end
                 * immediately - this is the best possible case, since no
                 * memory allocation needs to be done */
Sam Hocevar's avatar
 
Sam Hocevar committed
137 138
                p_pic->i_status   = RESERVED_PICTURE;
                p_pic->i_refcount = 0;
139
                p_pic->b_force    = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
140

Sam Hocevar's avatar
 
Sam Hocevar committed
141
                p_pic->b_progressive        = b_progressive;
142
                p_pic->i_nb_fields          = i_nb_fields;
Sam Hocevar's avatar
 
Sam Hocevar committed
143
                p_pic->b_top_field_first    = b_top_field_first;
Sam Hocevar's avatar
 
Sam Hocevar committed
144

gbazin's avatar
 
gbazin committed
145 146 147
                p_vout->render.i_last_used_pic =
                    ( p_vout->render.i_last_used_pic + i_pic + 1 )
                    % I_RENDERPICTURES;
Sam Hocevar's avatar
 
Sam Hocevar committed
148
                vlc_mutex_unlock( &p_vout->picture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
149
                return( p_pic );
Sam Hocevar's avatar
 
Sam Hocevar committed
150

Sam Hocevar's avatar
 
Sam Hocevar committed
151
            case FREE_PICTURE:
Sam Hocevar's avatar
 
Sam Hocevar committed
152
                /* Picture is empty and ready for allocation */
gbazin's avatar
 
gbazin committed
153 154 155
                p_vout->render.i_last_used_pic =
                    ( p_vout->render.i_last_used_pic + i_pic + 1 )
                    % I_RENDERPICTURES;
Sam Hocevar's avatar
 
Sam Hocevar committed
156
                p_freepic = p_pic;
Sam Hocevar's avatar
 
Sam Hocevar committed
157 158 159 160 161
                break;

            default:
                break;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
162 163 164 165 166
    }

    /*
     * Prepare picture
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
167
    if( p_freepic != NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
168
    {
gbazin's avatar
 
gbazin committed
169
        vout_AllocatePicture( VLC_OBJECT(p_vout),
170 171 172
                              p_freepic, p_vout->fmt_render.i_chroma,
                              p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
                              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den ); /* The right AR is in fmt_in and not fmt_render */
Sam Hocevar's avatar
 
Sam Hocevar committed
173

Sam Hocevar's avatar
 
Sam Hocevar committed
174
        if( p_freepic->i_planes )
Sam Hocevar's avatar
 
Sam Hocevar committed
175 176
        {
            /* Copy picture information, set some default values */
Sam Hocevar's avatar
 
Sam Hocevar committed
177 178
            p_freepic->i_status   = RESERVED_PICTURE;
            p_freepic->i_type     = MEMORY_PICTURE;
179
            p_freepic->b_slow     = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
180

Sam Hocevar's avatar
 
Sam Hocevar committed
181
            p_freepic->i_refcount = 0;
182
            p_freepic->b_force = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
183

Sam Hocevar's avatar
 
Sam Hocevar committed
184
            p_freepic->b_progressive        = b_progressive;
185
            p_freepic->i_nb_fields          = i_nb_fields;
Sam Hocevar's avatar
 
Sam Hocevar committed
186
            p_freepic->b_top_field_first    = b_top_field_first;
Sam Hocevar's avatar
 
Sam Hocevar committed
187

Sam Hocevar's avatar
 
Sam Hocevar committed
188 189 190 191
        }
        else
        {
            /* Memory allocation failed : set picture as empty */
Sam Hocevar's avatar
 
Sam Hocevar committed
192 193
            p_freepic->i_status = FREE_PICTURE;
            p_freepic = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
194

195
            msg_Err( p_vout, "picture allocation failed" );
Sam Hocevar's avatar
 
Sam Hocevar committed
196 197 198 199
        }

        vlc_mutex_unlock( &p_vout->picture_lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
200
        return( p_freepic );
Sam Hocevar's avatar
 
Sam Hocevar committed
201 202 203 204 205 206 207 208 209
    }

    /* No free or destroyed picture could be found, but the decoder
     * will try again in a while. */
    vlc_mutex_unlock( &p_vout->picture_lock );

    return( NULL );
}

210 211 212 213 214 215 216 217 218 219 220
/* */
static void DestroyPicture( vout_thread_t *p_vout, picture_t *p_picture )
{
    vlc_assert_locked( &p_vout->picture_lock );

    p_picture->i_status = DESTROYED_PICTURE;
    picture_CleanupQuant( p_picture );

    vlc_cond_signal( &p_vout->p->picture_wait );
}

221 222 223
/**
 * Remove a permanent or reserved picture from the heap
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
224 225 226
 * This function frees a previously reserved picture or a permanent
 * picture. It is meant to be used when the construction of a picture aborted.
 * Note that the picture will be destroyed even if it is linked !
227 228
 *
 * TODO remove it, vout_DropPicture should be used instead
229
 */
Sam Hocevar's avatar
 
Sam Hocevar committed
230
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
Sam Hocevar's avatar
 
Sam Hocevar committed
231
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
232
#ifndef NDEBUG
Sam Hocevar's avatar
 
Sam Hocevar committed
233
    /* Check if picture status is valid */
234
    vlc_mutex_lock( &p_vout->picture_lock );
235
    if( p_pic->i_status != RESERVED_PICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
236
    {
237 238
        msg_Err( p_vout, "picture to destroy %p has invalid status %d",
                         p_pic, p_pic->i_status );
Sam Hocevar's avatar
 
Sam Hocevar committed
239
    }
240
    vlc_mutex_unlock( &p_vout->picture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
241 242
#endif

243 244
    vout_DropPicture( p_vout, p_pic );
}
Sam Hocevar's avatar
 
Sam Hocevar committed
245

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
/* */
void vout_UsePictureLocked( vout_thread_t *p_vout, picture_t *p_picture )
{
    vlc_assert_locked( &p_vout->picture_lock );
    if( p_picture->i_refcount > 0 )
    {
        /* Pretend we displayed the picture, but don't destroy
         * it since the decoder might still need it. */
        p_picture->i_status = DISPLAYED_PICTURE;
    }
    else
    {
        /* Destroy the picture without displaying it */
        DestroyPicture( p_vout, p_picture );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
261 262
}

263 264 265 266 267 268 269 270 271
/* */
void vout_DropPicture( vout_thread_t *p_vout, picture_t *p_pic  )
{
    vlc_mutex_lock( &p_vout->picture_lock );

    if( p_pic->i_status == READY_PICTURE )
    {
        /* Grr cannot destroy ready picture by myself so be sure vout won't like it */
        p_pic->date = 1;
272
        vlc_cond_signal( &p_vout->p->picture_wait );
273 274 275
    }
    else
    {
276
        vout_UsePictureLocked( p_vout, p_pic );
277 278 279 280 281
    }

    vlc_mutex_unlock( &p_vout->picture_lock );
}

282 283 284
/**
 * Increment reference counter of a picture
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
285 286
 * This function increments the reference counter of a picture in the video
 * heap. It needs a lock since several producer threads can access the picture.
287
 */
Sam Hocevar's avatar
 
Sam Hocevar committed
288
void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
Sam Hocevar's avatar
 
Sam Hocevar committed
289 290
{
    vlc_mutex_lock( &p_vout->picture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
291
    p_pic->i_refcount++;
Sam Hocevar's avatar
 
Sam Hocevar committed
292 293 294
    vlc_mutex_unlock( &p_vout->picture_lock );
}

295 296 297
/**
 * Decrement reference counter of a picture
 *
gbazin's avatar
 
gbazin committed
298
 * This function decrement the reference counter of a picture in the video heap
299
 */
Sam Hocevar's avatar
 
Sam Hocevar committed
300
void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
Sam Hocevar's avatar
 
Sam Hocevar committed
301 302 303
{
    vlc_mutex_lock( &p_vout->picture_lock );

304 305 306 307 308 309
    if( p_pic->i_refcount > 0 )
        p_pic->i_refcount--;
    else
        msg_Err( p_vout, "Invalid picture reference count (%p, %d)",
                 p_pic, p_pic->i_refcount );

310 311
    if( p_pic->i_refcount == 0 &&
        ( p_pic->i_status == DISPLAYED_PICTURE || p_pic->i_status == RESERVED_PICTURE ) )
312
        DestroyPicture( p_vout, p_pic );
Sam Hocevar's avatar
 
Sam Hocevar committed
313 314 315 316

    vlc_mutex_unlock( &p_vout->picture_lock );
}

317 318 319
/**
 * Render a picture
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
320 321 322
 * This function chooses whether the current picture needs to be copied
 * before rendering, does the subpicture magic, and tells the video output
 * thread which direct buffer needs to be displayed.
323
 */
324
picture_t *vout_RenderPicture( vout_thread_t *p_vout, picture_t *p_pic,
325
                               subpicture_t *p_subpic, mtime_t render_date )
Sam Hocevar's avatar
 
Sam Hocevar committed
326
{
Sam Hocevar's avatar
 
Sam Hocevar committed
327
    if( p_pic == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
328
        return NULL;
329

330
    if( p_pic->i_type == DIRECT_PICTURE && !p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
331
    {
332 333 334
        /* No subtitles, picture is in a directbuffer so
         * we can display it directly (even if it is still
         * in use or not). */
Sam Hocevar's avatar
 
Sam Hocevar committed
335
        return p_pic;
Sam Hocevar's avatar
 
Sam Hocevar committed
336 337
    }

338 339 340 341 342 343 344
    /* It is either because:
     *  - the picture is not a direct buffer
     *  - we have to render subtitles (we can never do it on the given
     *  picture even if not referenced).
     */
    picture_t *p_render;
    if( p_subpic != NULL && PP_OUTPUTPICTURE[0]->b_slow )
345 346 347 348
    {
        /* The picture buffer is in slow memory. We'll use
         * the "2 * VOUT_MAX_PICTURES + 1" picture as a temporary
         * one for subpictures rendering. */
349 350
        p_render = &p_vout->p_picture[2 * VOUT_MAX_PICTURES];
        if( p_render->i_status == FREE_PICTURE )
351 352
        {
            vout_AllocatePicture( VLC_OBJECT(p_vout),
353
                                  p_render, p_vout->fmt_out.i_chroma,
354 355
                                  p_vout->fmt_out.i_width,
                                  p_vout->fmt_out.i_height,
Laurent Aimar's avatar
Laurent Aimar committed
356 357
                                  p_vout->fmt_out.i_sar_num,
                                  p_vout->fmt_out.i_sar_den );
358 359
            p_render->i_type = MEMORY_PICTURE;
            p_render->i_status = RESERVED_PICTURE;
360 361 362 363
        }
    }
    else
    {
364 365 366
        /* We can directly render into a direct buffer */
        p_render = PP_OUTPUTPICTURE[0];
    }
Laurent Aimar's avatar
Laurent Aimar committed
367 368 369 370

    /* Copy */
    picture_Copy( p_render, p_pic );

371 372
    /* Render the subtitles if present */
    if( p_subpic )
373
        spu_RenderSubpictures( p_vout->p->p_spu,
374
                               p_render, &p_vout->fmt_out,
375
                               p_subpic, &p_vout->fmt_in, render_date );
376 377 378
    /* Copy in case we used a temporary fast buffer */
    if( p_render != PP_OUTPUTPICTURE[0] )
        picture_Copy( PP_OUTPUTPICTURE[0], p_render );
Sam Hocevar's avatar
 
Sam Hocevar committed
379

380
    return PP_OUTPUTPICTURE[0];
Sam Hocevar's avatar
 
Sam Hocevar committed
381 382
}

383
#undef vout_AllocatePicture
384 385 386
/**
 * Allocate a new picture in the heap.
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
387 388 389
 * This function allocates a fake direct buffer in memory, which can be
 * used exactly like a video buffer. The video output thread then manages
 * how it gets displayed.
390
 */
391 392 393 394
int vout_AllocatePicture( vlc_object_t *p_this, picture_t *p_pic,
                          vlc_fourcc_t i_chroma,
                          int i_width, int i_height,
                          int i_sar_num, int i_sar_den )
Sam Hocevar's avatar
 
Sam Hocevar committed
395
{
Pierre's avatar
Pierre committed
396
    VLC_UNUSED(p_this);
397 398

    /* Make sure the real dimensions are a multiple of 16 */
399 400
    if( picture_Setup( p_pic, i_chroma, i_width, i_height,
                       i_sar_num, i_sar_den ) != VLC_SUCCESS )
401
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
402 403

    /* Calculate how big the new image should be */
404
    size_t i_bytes = 0;
405
    for( int i = 0; i < p_pic->i_planes; i++ )
406
    {
407 408 409 410
        const plane_t *p = &p_pic->p[i];

        if( p->i_pitch <= 0 || p->i_lines <= 0 ||
            p->i_pitch > (SIZE_MAX - i_bytes)/p->i_lines )
411
        {
412 413
            p_pic->i_planes = 0;
            return VLC_ENOMEM;
414
        }
415
        i_bytes += p->i_pitch * p->i_lines;
416
    }
gbazin's avatar
 
gbazin committed
417 418 419 420 421

    p_pic->p_data = vlc_memalign( &p_pic->p_data_orig, 16, i_bytes );
    if( p_pic->p_data == NULL )
    {
        p_pic->i_planes = 0;
422
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
423 424 425
    }

    /* Fill the p_pixels field for each plane */
426 427
    p_pic->p[0].p_pixels = p_pic->p_data;
    for( int i = 1; i < p_pic->i_planes; i++ )
gbazin's avatar
 
gbazin committed
428
    {
429 430
        p_pic->p[i].p_pixels = &p_pic->p[i-1].p_pixels[ p_pic->p[i-1].i_lines *
                                                        p_pic->p[i-1].i_pitch ];
gbazin's avatar
 
gbazin committed
431
    }
432 433

    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
434 435
}

436 437 438 439 440 441 442 443 444
/*****************************************************************************
 *
 *****************************************************************************/
static void PictureReleaseCallback( picture_t *p_picture )
{
    if( --p_picture->i_refcount > 0 )
        return;
    picture_Delete( p_picture );
}
445

Laurent Aimar's avatar
Laurent Aimar committed
446 447 448 449 450 451 452 453 454 455 456 457 458 459
/*****************************************************************************
 *
 *****************************************************************************/
void picture_Reset( picture_t *p_picture )
{
    /* */
    p_picture->date = VLC_TS_INVALID;
    p_picture->b_force = false;
    p_picture->b_progressive = false;
    p_picture->i_nb_fields = 0;
    p_picture->b_top_field_first = false;
    picture_CleanupQuant( p_picture );
}

460 461 462
/*****************************************************************************
 *
 *****************************************************************************/
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 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
typedef struct
{
    unsigned     i_plane_count;
    struct
    {
        struct
        {
            unsigned i_num;
            unsigned i_den;
        } w;
        struct
        {
            unsigned i_num;
            unsigned i_den;
        } h;
    } p[VOUT_MAX_PLANES];
    unsigned i_pixel_size;

} chroma_description_t;

#define PLANAR(n, w_den, h_den) \
    { n, { {{1,1}, {1,1}}, {{1,w_den}, {1,h_den}}, {{1,w_den}, {1,h_den}}, {{1,1}, {1,1}} }, 1 }
#define PACKED(size) \
    { 1, { {{1,1}, {1,1}} }, size }

static const struct
{
    vlc_fourcc_t            p_fourcc[5];
    chroma_description_t    description;
} p_chromas[] = {
    { { VLC_CODEC_I411, 0 },                                 PLANAR(3, 4, 1) },
    { { VLC_CODEC_I410, VLC_CODEC_YV9, 0 },                  PLANAR(3, 4, 4) },
    { { VLC_CODEC_YV12, VLC_CODEC_I420, VLC_CODEC_J420, 0 }, PLANAR(3, 2, 2) },
    { { VLC_CODEC_I422, VLC_CODEC_J422, 0 },                 PLANAR(3, 2, 1) },
    { { VLC_CODEC_I440, VLC_CODEC_J440, 0 },                 PLANAR(3, 1, 2) },
    { { VLC_CODEC_I444, VLC_CODEC_J444, 0 },                 PLANAR(3, 1, 1) },
    { { VLC_CODEC_YUVA, 0 },                                 PLANAR(4, 1, 1) },

    { { VLC_CODEC_UYVY, VLC_CODEC_VYUY, VLC_CODEC_YUYV, VLC_CODEC_YVYU, 0 }, PACKED(2) },
    { { VLC_CODEC_RGB8, VLC_CODEC_GREY, VLC_CODEC_YUVP, VLC_CODEC_RGBP, 0 }, PACKED(1) },
    { { VLC_CODEC_RGB16, VLC_CODEC_RGB15, 0 },                               PACKED(2) },
    { { VLC_CODEC_RGB24, 0 },                                                PACKED(3) },
    { { VLC_CODEC_RGB32, VLC_CODEC_RGBA, 0 },                                PACKED(4) },

    { { VLC_CODEC_Y211, 0 }, { 1, { {{1,4}, {1,1}} }, 4 } },

    { {0}, { 0, {}, 0 } }
};

#undef PACKED
#undef PLANAR

static const chroma_description_t *vlc_fourcc_GetChromaDescription( vlc_fourcc_t i_fourcc )
{
    for( unsigned i = 0; p_chromas[i].p_fourcc[0]; i++ )
    {
        const vlc_fourcc_t *p_fourcc = p_chromas[i].p_fourcc;
        for( unsigned j = 0; p_fourcc[j]; j++ )
        {
            if( p_fourcc[j] == i_fourcc )
                return &p_chromas[i].description;
        }
    }
    return NULL;
}

static int LCM( int a, int b )
{
    return a * b / GCD( a, b );
}

534 535
int picture_Setup( picture_t *p_picture, vlc_fourcc_t i_chroma,
                   int i_width, int i_height, int i_sar_num, int i_sar_den )
536 537
{
    /* Store default values */
538 539
    p_picture->i_planes = 0;
    for( unsigned i = 0; i < VOUT_MAX_PLANES; i++ )
540
    {
541 542 543
        plane_t *p = &p_picture->p[i];
        p->p_pixels = NULL;
        p->i_pixel_pitch = 0;
544 545 546
    }

    p_picture->pf_release = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
547
    p_picture->p_release_sys = NULL;
548 549 550 551 552 553
    p_picture->i_refcount = 0;

    p_picture->i_qtype = QTYPE_NONE;
    p_picture->i_qstride = 0;
    p_picture->p_q = NULL;

554
    video_format_Setup( &p_picture->format, i_chroma, i_width, i_height,
555
                        i_sar_num, i_sar_den );
556

557 558 559 560
    const chroma_description_t *p_dsc =
        vlc_fourcc_GetChromaDescription( p_picture->format.i_chroma );
    if( !p_dsc )
        return VLC_EGENERIC;
561

562 563 564 565 566 567 568 569 570 571
    /* We want V (width/height) to respect:
        (V * p_dsc->p[i].w.i_num) % p_dsc->p[i].w.i_den == 0
        (V * p_dsc->p[i].w.i_num/p_dsc->p[i].w.i_den * p_dsc->i_pixel_size) % 16 == 0
       Which is respected if you have
       V % lcm( p_dsc->p[0..planes].w.i_den * 16) == 0
    */
    int i_modulo_w = 1;
    int i_modulo_h = 1;
    int i_ratio_h  = 1;
    for( unsigned i = 0; i < p_dsc->i_plane_count; i++ )
572
    {
573 574 575 576 577
        i_modulo_w = LCM( i_modulo_w, 16 * p_dsc->p[i].w.i_den );
        i_modulo_h = LCM( i_modulo_h, 16 * p_dsc->p[i].h.i_den );
        if( i_ratio_h < p_dsc->p[i].h.i_den )
            i_ratio_h = p_dsc->p[i].h.i_den;
    }
578

579 580 581 582 583 584
    const int i_width_aligned  = ( i_width  + i_modulo_w - 1 ) / i_modulo_w * i_modulo_w;
    const int i_height_aligned = ( i_height + i_modulo_h - 1 ) / i_modulo_h * i_modulo_h;
    const int i_height_extra   = 2 * i_ratio_h; /* This one is a hack for some ASM functions */
    for( unsigned i = 0; i < p_dsc->i_plane_count; i++ )
    {
        plane_t *p = &p_picture->p[i];
585

586 587 588 589 590
        p->i_lines         = (i_height_aligned + i_height_extra ) * p_dsc->p[i].h.i_num / p_dsc->p[i].h.i_den;
        p->i_visible_lines = i_height * p_dsc->p[i].h.i_num / p_dsc->p[i].h.i_den;
        p->i_pitch         = i_width_aligned * p_dsc->p[i].w.i_num / p_dsc->p[i].w.i_den * p_dsc->i_pixel_size;
        p->i_visible_pitch = i_width * p_dsc->p[i].w.i_num / p_dsc->p[i].w.i_den * p_dsc->i_pixel_size;
        p->i_pixel_pitch   = p_dsc->i_pixel_size;
591

592
        assert( (p->i_pitch % 16) == 0 );
593
    }
594
    p_picture->i_planes  = p_dsc->i_plane_count;
595 596 597 598

    return VLC_SUCCESS;
}

599 600 601
/*****************************************************************************
 *
 *****************************************************************************/
602
picture_t *picture_NewFromResource( const video_format_t *p_fmt, const picture_resource_t *p_resource )
603
{
604 605 606 607
    video_format_t fmt = *p_fmt;

    /* It is needed to be sure all informations are filled */
    video_format_Setup( &fmt, p_fmt->i_chroma,
608
                              p_fmt->i_width, p_fmt->i_height,
Laurent Aimar's avatar
Laurent Aimar committed
609
                              p_fmt->i_sar_num, p_fmt->i_sar_den );
610 611

    /* */
ivoire's avatar
ivoire committed
612
    picture_t *p_picture = calloc( 1, sizeof(*p_picture) );
613 614 615
    if( !p_picture )
        return NULL;

616
    if( p_resource )
617
    {
618 619
        if( picture_Setup( p_picture, fmt.i_chroma, fmt.i_width, fmt.i_height,
                           fmt.i_sar_num, fmt.i_sar_den ) )
620 621 622 623 624
        {
            free( p_picture );
            return NULL;
        }
        p_picture->p_sys = p_resource->p_sys;
625

626 627 628 629 630 631 632 633 634
        for( int i = 0; i < p_picture->i_planes; i++ )
        {
            p_picture->p[i].p_pixels = p_resource->p[i].p_pixels;
            p_picture->p[i].i_lines  = p_resource->p[i].i_lines;
            p_picture->p[i].i_pitch  = p_resource->p[i].i_pitch;
        }
    }
    else
    {
635 636 637
        if( vout_AllocatePicture( (vlc_object_t *)NULL, p_picture,
                                  fmt.i_chroma, fmt.i_width, fmt.i_height,
                                  fmt.i_sar_num, fmt.i_sar_den ) )
638 639 640 641 642 643 644
        {
            free( p_picture );
            return NULL;
        }
    }
    /* */
    p_picture->format = fmt;
645 646 647 648 649 650
    p_picture->i_refcount = 1;
    p_picture->pf_release = PictureReleaseCallback;
    p_picture->i_status = RESERVED_PICTURE;

    return p_picture;
}
651 652 653 654
picture_t *picture_NewFromFormat( const video_format_t *p_fmt )
{
    return picture_NewFromResource( p_fmt, NULL );
}
655
picture_t *picture_New( vlc_fourcc_t i_chroma, int i_width, int i_height, int i_sar_num, int i_sar_den )
656 657 658 659
{
    video_format_t fmt;

    memset( &fmt, 0, sizeof(fmt) );
660
    video_format_Setup( &fmt, i_chroma, i_width, i_height,
661
                        i_sar_num, i_sar_den );
662 663 664

    return picture_NewFromFormat( &fmt );
}
665 666 667 668 669 670 671

/*****************************************************************************
 *
 *****************************************************************************/
void picture_Delete( picture_t *p_picture )
{
    assert( p_picture && p_picture->i_refcount == 0 );
Laurent Aimar's avatar
Laurent Aimar committed
672
    assert( p_picture->p_release_sys == NULL );
673

674
    free( p_picture->p_q );
675 676 677 678 679 680 681 682 683 684
    free( p_picture->p_data_orig );
    free( p_picture->p_sys );
    free( p_picture );
}

/*****************************************************************************
 *
 *****************************************************************************/
void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src )
{
685 686 687
    int i;

    for( i = 0; i < p_src->i_planes ; i++ )
688 689
        plane_CopyPixels( p_dst->p+i, p_src->p+i );
}
690

691 692 693 694 695 696
void plane_CopyPixels( plane_t *p_dst, const plane_t *p_src )
{
    const unsigned i_width  = __MIN( p_dst->i_visible_pitch,
                                     p_src->i_visible_pitch );
    const unsigned i_height = __MIN( p_dst->i_visible_lines,
                                     p_src->i_visible_lines );
697

698 699 700 701 702 703 704 705 706 707 708 709
    if( p_src->i_pitch == p_dst->i_pitch )
    {
        /* There are margins, but with the same width : perfect ! */
        vlc_memcpy( p_dst->p_pixels, p_src->p_pixels,
                    p_src->i_pitch * i_height );
    }
    else
    {
        /* We need to proceed line by line */
        uint8_t *p_in = p_src->p_pixels;
        uint8_t *p_out = p_dst->p_pixels;
        int i_line;
710

711 712 713 714 715 716 717 718
        assert( p_in );
        assert( p_out );

        for( i_line = i_height; i_line--; )
        {
            vlc_memcpy( p_out, p_in, i_width );
            p_in += p_src->i_pitch;
            p_out += p_dst->i_pitch;
719 720 721
        }
    }
}
Laurent Aimar's avatar
Laurent Aimar committed
722

723 724 725
/*****************************************************************************
 *
 *****************************************************************************/
726 727 728 729 730
int picture_Export( vlc_object_t *p_obj,
                    block_t **pp_image,
                    video_format_t *p_fmt,
                    picture_t *p_picture,
                    vlc_fourcc_t i_format,
731
                    int i_override_width, int i_override_height )
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
{
    /* */
    video_format_t fmt_in = p_picture->format;
    if( fmt_in.i_sar_num <= 0 || fmt_in.i_sar_den <= 0 )
    {
        fmt_in.i_sar_num =
        fmt_in.i_sar_den = 1;
    }

    /* */
    video_format_t fmt_out;
    memset( &fmt_out, 0, sizeof(fmt_out) );
    fmt_out.i_sar_num =
    fmt_out.i_sar_den = 1;
    fmt_out.i_chroma  = i_format;

748 749 750 751
    /* compute original width/height */
    unsigned int i_original_width;
    unsigned int i_original_height;
    if( fmt_in.i_sar_num >= fmt_in.i_sar_den )
752
    {
753 754
        i_original_width = fmt_in.i_width * fmt_in.i_sar_num / fmt_in.i_sar_den;
        i_original_height = fmt_in.i_height;
755 756 757
    }
    else
    {
758 759 760 761 762
        i_original_width =  fmt_in.i_width;
        i_original_height = fmt_in.i_height * fmt_in.i_sar_den / fmt_in.i_sar_num;
    }

    /* */
763 764 765 766
    fmt_out.i_width  = ( i_override_width < 0 ) ?
                       i_original_width : i_override_width;
    fmt_out.i_height = ( i_override_height < 0 ) ?
                       i_original_height : i_override_height;
767 768 769 770 771 772 773 774 775 776 777

    /* scale if only one direction is provided */
    if( fmt_out.i_height == 0 && fmt_out.i_width > 0 )
    {
        fmt_out.i_height = fmt_in.i_height * fmt_out.i_width
                     * fmt_in.i_sar_den / fmt_in.i_width / fmt_in.i_sar_num;
    }
    else if( fmt_out.i_width == 0 && fmt_out.i_height > 0 )
    {
        fmt_out.i_width  = fmt_in.i_width * fmt_out.i_height
                     * fmt_in.i_sar_num / fmt_in.i_height / fmt_in.i_sar_den;
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
    }

    image_handler_t *p_image = image_HandlerCreate( p_obj );

    block_t *p_block = image_Write( p_image, p_picture, &fmt_in, &fmt_out );

    image_HandlerDelete( p_image );

    if( !p_block )
        return VLC_EGENERIC;

    p_block->i_pts =
    p_block->i_dts = p_picture->date;

    if( p_fmt )
        *p_fmt = fmt_out;
    *pp_image = p_block;

    return VLC_SUCCESS;
}