video_output.c 84.2 KB
Newer Older
1
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
2 3 4
 * video_output.c : video output thread
 * This module describes the programming interface for video output threads.
 * It includes functions allowing to open a new thread, send pictures to a
5 6 7
 * thread, and destroy a previously oppened video output thread.
 *****************************************************************************
 * Copyright (C) 2000 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
8
 * $Id: video_output.c,v 1.123 2001/05/06 04:32:02 sam Exp $
9
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
10
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
11 12 13 14 15
 *
 * 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.
Sam Hocevar's avatar
Sam Hocevar committed
16
 *
17 18
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
21
 *
22 23 24
 * 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.
25
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
26

27
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
28
 * Preamble
29
 *****************************************************************************/
30 31
#include "defs.h"

32 33 34 35
#include <errno.h>                                                 /* ENOMEM */
#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */
Vincent Seguin's avatar
Vincent Seguin committed
36

37 38 39 40
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
41
#include "config.h"
42 43
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
44
#include "mtime.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
45 46
#include "modules.h"

Michel Kaempf's avatar
Michel Kaempf committed
47 48
#include "video.h"
#include "video_output.h"
Vincent Seguin's avatar
Vincent Seguin committed
49
#include "video_text.h"
Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
50
#include "video_spu.h"
51
#include "video_yuv.h"
52

Vincent Seguin's avatar
Vincent Seguin committed
53
#include "intf_msg.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
54

55
#include "main.h"
Vincent Seguin's avatar
Vincent Seguin committed
56

57
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
58
 * Local prototypes
59
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
60 61
static int      BinaryLog         ( u32 i );
static void     MaskToShift       ( int *pi_left, int *pi_right, u32 i_mask );
62 63 64 65 66
static int      InitThread        ( vout_thread_t *p_vout );
static void     RunThread         ( vout_thread_t *p_vout );
static void     ErrorThread       ( vout_thread_t *p_vout );
static void     EndThread         ( vout_thread_t *p_vout );
static void     DestroyThread     ( vout_thread_t *p_vout, int i_status );
67
static void     Print             ( vout_thread_t *p_vout, int i_x, int i_y,
68 69 70 71 72 73 74 75 76 77 78
                                    int i_h_align, int i_v_align,
                                    unsigned char *psz_text );
static void     SetBufferArea     ( vout_thread_t *p_vout, int i_x, int i_y,
                                    int i_w, int i_h );
static void     SetBufferPicture  ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderPicture     ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderSubPicture  ( vout_thread_t *p_vout,
                                    subpicture_t *p_subpic );
static void     RenderInterface   ( vout_thread_t *p_vout );
static int      RenderIdle        ( vout_thread_t *p_vout );
Renaud Dartus's avatar
 
Renaud Dartus committed
79
static int      RenderSplash      ( vout_thread_t *p_vout );
80 81 82 83 84
static void     RenderInfo        ( vout_thread_t *p_vout );
static int      Manage            ( vout_thread_t *p_vout );
static int      Align             ( vout_thread_t *p_vout, int *pi_x,
                                    int *pi_y, int i_width, int i_height,
                                    int i_h_align, int i_v_align );
85 86
static void     SetPalette        ( p_vout_thread_t p_vout, u16 *red,
                                    u16 *green, u16 *blue, u16 *transp );
Michel Kaempf's avatar
Michel Kaempf committed
87

Sam Hocevar's avatar
 
Sam Hocevar committed
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
/*****************************************************************************
 * vout_InitBank: initialize the video output bank.
 *****************************************************************************/
void vout_InitBank ( void )
{
    p_vout_bank->i_count = 0;

    vlc_mutex_init( &p_vout_bank->lock );
}

/*****************************************************************************
 * vout_EndBank: empty the video output bank.
 *****************************************************************************
 * This function ends all unused video outputs and empties the bank in
 * case of success.
 *****************************************************************************/
void vout_EndBank ( void )
{
    /* Ask all remaining video outputs to die */
    while( p_vout_bank->i_count )
    {
        vout_DestroyThread(
                p_vout_bank->pp_vout[ --p_vout_bank->i_count ], NULL );
    }                       

    vlc_mutex_destroy( &p_vout_bank->lock );
}

116
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
117
 * vout_CreateThread: creates a new video output thread
118
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
119 120 121
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
 * If pi_status is NULL, then the function will block until the thread is ready.
122
 * If not, it will be updated using one of the THREAD_* constants.
123
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
124
vout_thread_t * vout_CreateThread   ( int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
125
{
126 127 128
    vout_thread_t * p_vout;                             /* thread descriptor */
    int             i_status;                               /* thread status */
    int             i_index;               /* index for array initialization */
Michel Kaempf's avatar
Michel Kaempf committed
129

130
    /* Allocate descriptor */
Michel Kaempf's avatar
Michel Kaempf committed
131
    p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
132
    if( p_vout == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
133
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
134 135
        intf_ErrMsg( "vout error: vout thread creation returned %s",
                     strerror(ENOMEM) );
Michel Kaempf's avatar
Michel Kaempf committed
136 137
        return( NULL );
    }
138

Sam Hocevar's avatar
 
Sam Hocevar committed
139
    /* Choose the best module */
Sam Hocevar's avatar
 
Sam Hocevar committed
140
    p_vout->p_module = module_Need( MODULE_CAPABILITY_VOUT, NULL );
141

Sam Hocevar's avatar
 
Sam Hocevar committed
142
    if( p_vout->p_module == NULL )
Sam Hocevar's avatar
Sam Hocevar committed
143
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
144
        intf_ErrMsg( "vout error: no suitable vout module" );
Sam Hocevar's avatar
Sam Hocevar committed
145 146 147 148
        free( p_vout );
        return( NULL );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162
#define f p_vout->p_module->p_functions->vout.functions.vout
    p_vout->pf_create     = f.pf_create;
    p_vout->pf_init       = f.pf_init;
    p_vout->pf_end        = f.pf_end;
    p_vout->pf_destroy    = f.pf_destroy;
    p_vout->pf_manage     = f.pf_manage;
    p_vout->pf_display    = f.pf_display;
    p_vout->pf_setpalette = f.pf_setpalette;
#undef f

    if( p_vout->pf_setpalette == NULL )
    {
        p_vout->pf_setpalette = SetPalette;
    }
Sam Hocevar's avatar
Sam Hocevar committed
163

164
    /* Initialize thread properties - thread id and locks will be initialized
165
     * later */
166
    p_vout->b_die               = 0;
167
    p_vout->b_error             = 0;
168 169
    p_vout->b_active            = 0;
    p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
170
    *p_vout->pi_status          = THREAD_CREATE;
171

172 173 174
    /* Initialize some fields used by the system-dependant method - these
     * fields will probably be modified by the method, and are only
     * preferences */
Renaud Dartus's avatar
 
Renaud Dartus committed
175
    p_vout->i_changes             = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
176 177 178 179 180
    p_vout->i_width               = main_GetIntVariable( VOUT_WIDTH_VAR,
                                                         VOUT_WIDTH_DEFAULT );
    p_vout->i_height              = main_GetIntVariable( VOUT_HEIGHT_VAR,
                                                         VOUT_HEIGHT_DEFAULT );
    p_vout->i_bytes_per_line      = p_vout->i_width * 2;
Renaud Dartus's avatar
 
Renaud Dartus committed
181 182 183
    p_vout->i_screen_depth        = 15;
    p_vout->i_bytes_per_pixel     = 2;
    p_vout->f_gamma               = VOUT_GAMMA;
184
    p_vout->b_need_render         = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
185 186
    p_vout->b_YCbr                = 0;
    
Renaud Dartus's avatar
 
Renaud Dartus committed
187
    p_vout->b_grayscale           = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
Sam Hocevar's avatar
 
Sam Hocevar committed
188
                                                     VOUT_GRAYSCALE_DEFAULT );
Renaud Dartus's avatar
 
Renaud Dartus committed
189 190 191
    p_vout->b_info                = 0;
    p_vout->b_interface           = 0;
    p_vout->b_scale               = 1;
192

Sam Hocevar's avatar
 
Sam Hocevar committed
193 194 195
    intf_WarnMsg( 1, "wished configuration: %dx%d, %d/%d bpp (%d Bpl)",
                  p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
                  p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line );
196

197
    /* Initialize idle screen */
198
    p_vout->last_display_date   = 0;
199
    p_vout->last_idle_date      = 0;
200
    p_vout->init_display_date   = mdate();
201
    p_vout->render_time         = 10000;
202

203
    /* Initialize statistics fields */
204
    p_vout->c_fps_samples       = 0;
205 206 207 208

    /* Initialize buffer index */
    p_vout->i_buffer_index      = 0;

209
    /* Initialize pictures and subpictures - translation tables and functions
210
     * will be initialized later in InitThread */
211 212
    for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
    {
213 214
        p_vout->p_picture[i_index].i_type   =   EMPTY_PICTURE;
        p_vout->p_picture[i_index].i_status =   FREE_PICTURE;
215 216 217
    }
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
218 219
        p_vout->p_subpicture[i_index].i_type  = EMPTY_SUBPICTURE;
        p_vout->p_subpicture[i_index].i_status= FREE_SUBPICTURE;
220
    }
221
    p_vout->i_pictures = 0;
222

223 224
    /* Create and initialize system-dependant method - this function issues its
     * own error messages */
Sam Hocevar's avatar
 
Sam Hocevar committed
225
    if( p_vout->pf_create( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
226
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
227
        module_Unneed( p_vout->p_module );
228 229
        free( p_vout );
        return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
230
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
231 232 233 234 235 236
    intf_WarnMsg( 1, "actual configuration: %dx%d, %d/%d bpp (%d Bpl), "
                  "masks: 0x%x/0x%x/0x%x",
                  p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
                  p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line,
                  p_vout->i_red_mask, p_vout->i_green_mask,
                  p_vout->i_blue_mask );
Vincent Seguin's avatar
Vincent Seguin committed
237 238

    /* Calculate shifts from system-updated masks */
239 240 241 242 243 244
    MaskToShift( &p_vout->i_red_lshift, &p_vout->i_red_rshift,
                 p_vout->i_red_mask );
    MaskToShift( &p_vout->i_green_lshift, &p_vout->i_green_rshift,
                 p_vout->i_green_mask );
    MaskToShift( &p_vout->i_blue_lshift, &p_vout->i_blue_rshift,
                 p_vout->i_blue_mask );
Vincent Seguin's avatar
Vincent Seguin committed
245

246
    /* Set some useful colors */
247
    p_vout->i_white_pixel = RGB2PIXEL( p_vout, 255, 255, 255 );
Vincent Seguin's avatar
Vincent Seguin committed
248 249
    p_vout->i_black_pixel = RGB2PIXEL( p_vout, 0, 0, 0 );
    p_vout->i_gray_pixel  = RGB2PIXEL( p_vout, 128, 128, 128 );
250
    p_vout->i_blue_pixel  = RGB2PIXEL( p_vout, 0, 0, 50 );
251

252 253
    /* Load fonts - fonts must be initialized after the system method since
     * they may be dependant on screen depth and other thread properties */
Sam Hocevar's avatar
 
Sam Hocevar committed
254
    p_vout->p_default_font = vout_LoadFont( VOUT_DEFAULT_FONT );
Vincent Seguin's avatar
Vincent Seguin committed
255 256
    if( p_vout->p_default_font == NULL )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
257
        intf_ErrMsg( "vout error: could not load default font" );
258
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
259 260

    p_vout->p_large_font = vout_LoadFont( VOUT_LARGE_FONT );
Vincent Seguin's avatar
Vincent Seguin committed
261 262
    if( p_vout->p_large_font == NULL )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
263
        intf_ErrMsg( "vout error: could not load large font" );
264
    }
Vincent Seguin's avatar
Vincent Seguin committed
265

Michel Kaempf's avatar
Michel Kaempf committed
266
    /* Create thread and set locks */
Vincent Seguin's avatar
Vincent Seguin committed
267
    vlc_mutex_init( &p_vout->picture_lock );
268 269
    vlc_mutex_init( &p_vout->subpicture_lock );
    vlc_mutex_init( &p_vout->change_lock );
270

271 272
    if( vlc_thread_create( &p_vout->thread_id, "video output",
                           (void *) RunThread, (void *) p_vout) )
Michel Kaempf's avatar
Michel Kaempf committed
273
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
274
        intf_ErrMsg("vout error: %s", strerror(ENOMEM));
Vincent Seguin's avatar
Vincent Seguin committed
275
        vout_UnloadFont( p_vout->p_default_font );
276
        vout_UnloadFont( p_vout->p_large_font );
Sam Hocevar's avatar
 
Sam Hocevar committed
277
        p_vout->pf_destroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
278 279
        free( p_vout );
        return( NULL );
280
    }
Michel Kaempf's avatar
Michel Kaempf committed
281

Sam Hocevar's avatar
 
Sam Hocevar committed
282 283
    intf_Msg( "vout: video display initialized (%dx%d, %d/%d bpp)",
              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
284
              p_vout->i_bytes_per_pixel * 8 );
285

Michel Kaempf's avatar
Michel Kaempf committed
286 287 288 289
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
290
        {
Michel Kaempf's avatar
Michel Kaempf committed
291
            msleep( THREAD_SLEEP );
292
        }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
Michel Kaempf's avatar
Michel Kaempf committed
293 294 295
                && (i_status != THREAD_FATAL) );
        if( i_status != THREAD_READY )
        {
296 297
            return( NULL );
        }
Michel Kaempf's avatar
Michel Kaempf committed
298 299 300 301
    }
    return( p_vout );
}

302
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
303
 * vout_DestroyThread: destroys a previously created thread
304 305
 *****************************************************************************
 * Destroy a terminated thread.
Michel Kaempf's avatar
Michel Kaempf committed
306
 * The function will request a destruction of the specified thread. If pi_error
307
 * is NULL, it will return once the thread is destroyed. Else, it will be
Michel Kaempf's avatar
Michel Kaempf committed
308
 * update using one of the THREAD_* constants.
309
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
310
void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
311 312
{
    int     i_status;                                       /* thread status */
Michel Kaempf's avatar
Michel Kaempf committed
313 314

    /* Set status */
Sam Hocevar's avatar
 
Sam Hocevar committed
315
    intf_DbgMsg("");
Michel Kaempf's avatar
Michel Kaempf committed
316
    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
317 318
    *p_vout->pi_status = THREAD_DESTROY;

Michel Kaempf's avatar
Michel Kaempf committed
319 320 321 322
    /* Request thread destruction */
    p_vout->b_die = 1;

    /* If status is NULL, wait until thread has been destroyed */
323
    if( pi_status == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
324 325 326 327
    {
        do
        {
            msleep( THREAD_SLEEP );
328 329
        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                && (i_status != THREAD_FATAL) );
Michel Kaempf's avatar
Michel Kaempf committed
330 331 332
    }
}

333
/*****************************************************************************
334
 * vout_DisplaySubPicture: display a subpicture unit
335 336
 *****************************************************************************
 * Remove the reservation flag of an subpicture, which will cause it to be ready
337
 * for display. The picture does not need to be locked, since it is ignored by
Vincent Seguin's avatar
Vincent Seguin committed
338
 * the output thread if is reserved.
339
 *****************************************************************************/
340
void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
341
{
342
#ifdef TRACE_VOUT
343 344
    char        psz_begin_date[MSTRTIME_MAX_SIZE]; /* buffer for date string */
    char        psz_end_date[MSTRTIME_MAX_SIZE];   /* buffer for date string */
Vincent Seguin's avatar
Vincent Seguin committed
345 346 347 348
#endif

#ifdef DEBUG
    /* Check if status is valid */
349
    if( p_subpic->i_status != RESERVED_SUBPICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
350
    {
351
        intf_ErrMsg("error: subpicture %p has invalid status %d", p_subpic,
352 353
                    p_subpic->i_status );
    }
Vincent Seguin's avatar
Vincent Seguin committed
354 355 356
#endif

    /* Remove reservation flag */
357
    p_subpic->i_status = READY_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
358

359
#ifdef TRACE_VOUT
Sam Hocevar's avatar
Sam Hocevar committed
360
    /* Send subpicture information */
Sam Hocevar's avatar
 
Sam Hocevar committed
361
    intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s",
362 363 364
                p_subpic, p_subpic->i_type,
                mstrtime( psz_begin_date, p_subpic->begin_date ),
                mstrtime( psz_end_date, p_subpic->end_date ) );
Vincent Seguin's avatar
Vincent Seguin committed
365 366 367
#endif
}

368
/*****************************************************************************
Sam Hocevar's avatar
Sam Hocevar committed
369
 * vout_CreateSubPicture: allocate a subpicture in the video output heap.
370 371
 *****************************************************************************
 * This function create a reserved subpicture in the video output heap.
Vincent Seguin's avatar
Vincent Seguin committed
372
 * A null pointer is returned if the function fails. This method provides an
373
 * already allocated zone of memory in the spu data fields. It needs locking
374 375 376
 * since several pictures can be created by several producers threads.
 *****************************************************************************/
subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type,
377
                                     int i_size )
Vincent Seguin's avatar
Vincent Seguin committed
378
{
379 380 381
    int                 i_subpic;                        /* subpicture index */
    subpicture_t *      p_free_subpic = NULL;       /* first free subpicture */
    subpicture_t *      p_destroyed_subpic = NULL; /* first destroyed subpic */
382 383 384 385

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

386 387
    /*
     * Look for an empty place
388
     */
389
    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
390
    {
391 392 393 394 395 396
        if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
        {
            /* Subpicture is marked for destruction, but is still allocated */
            if( (p_vout->p_subpicture[i_subpic].i_type  == i_type)   &&
                (p_vout->p_subpicture[i_subpic].i_size  >= i_size) )
            {
Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
397 398 399 400
                /* Memory size do match or is smaller : memory will not be
                 * reallocated, and function can end immediately - this is
                 * the best possible case, since no memory allocation needs
                 * to be done */
401
                p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
402
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
403
                intf_DbgMsg("subpicture %p (in destroyed subpicture slot)",
404
                            &p_vout->p_subpicture[i_subpic] );
405
#endif
406 407 408 409 410 411 412 413 414 415
                vlc_mutex_unlock( &p_vout->subpicture_lock );
                return( &p_vout->p_subpicture[i_subpic] );
            }
            else if( p_destroyed_subpic == NULL )
            {
                /* Memory size do not match, but subpicture index will be kept in
                 * case no other place are left */
                p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];
            }
        }
416
        else if( (p_free_subpic == NULL) &&
417 418
                 (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
        {
419
            /* Subpicture is empty and ready for allocation */
420 421 422 423
            p_free_subpic = &p_vout->p_subpicture[i_subpic];
        }
    }

Sam Hocevar's avatar
Sam Hocevar committed
424
    /* If no free subpictures are available, use a destroyed subpicture */
425
    if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
426
    {
Sam Hocevar's avatar
Sam Hocevar committed
427
        /* No free subpicture or matching destroyed subpictures have been
Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
428
         * found, but a destroyed subpicture is still avalaible */
429
        free( p_destroyed_subpic->p_data );
430 431 432 433 434 435 436 437 438 439 440
        p_free_subpic = p_destroyed_subpic;
    }

    /*
     * Prepare subpicture
     */
    if( p_free_subpic != NULL )
    {
        /* Allocate memory */
        switch( i_type )
        {
441 442
        case TEXT_SUBPICTURE:                             /* text subpicture */
            p_free_subpic->p_data = malloc( i_size + 1 );
443
            break;
444 445 446
        case DVD_SUBPICTURE:                          /* DVD subpicture unit */
            p_free_subpic->p_data = malloc( i_size );
            break;
447 448
#ifdef DEBUG
        default:
449
            intf_ErrMsg("error: unknown subpicture type %d", i_type );
450 451 452
            p_free_subpic->p_data   =  NULL;
            break;
#endif
453 454 455
        }

        if( p_free_subpic->p_data != NULL )
Sam Hocevar's avatar
Sam Hocevar committed
456 457
        {
            /* Copy subpicture information, set some default values */
458 459
            p_free_subpic->i_type                      = i_type;
            p_free_subpic->i_status                    = RESERVED_SUBPICTURE;
460
            p_free_subpic->i_size                      = i_size;
461
            p_free_subpic->i_x                         = 0;
462
            p_free_subpic->i_y                         = 0;
463 464
            p_free_subpic->i_width                     = 0;
            p_free_subpic->i_height                    = 0;
465 466
            p_free_subpic->i_horizontal_align          = CENTER_RALIGN;
            p_free_subpic->i_vertical_align            = CENTER_RALIGN;
467 468 469 470
        }
        else
        {
            /* Memory allocation failed : set subpicture as empty */
471 472 473
            p_free_subpic->i_type   =  EMPTY_SUBPICTURE;
            p_free_subpic->i_status =  FREE_SUBPICTURE;
            p_free_subpic =            NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
474 475
            intf_ErrMsg( "vout error: spu allocation returned %s",
                         strerror( ENOMEM ) );
476
        }
477

478
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
479
        intf_DbgMsg("subpicture %p (in free subpicture slot)", p_free_subpic );
480 481 482 483
#endif
        vlc_mutex_unlock( &p_vout->subpicture_lock );
        return( p_free_subpic );
    }
484

485
    /* No free or destroyed subpicture could be found */
Sam Hocevar's avatar
 
Sam Hocevar committed
486
    intf_DbgMsg( "warning: subpicture heap is full" );
487 488
    vlc_mutex_unlock( &p_vout->subpicture_lock );
    return( NULL );
Vincent Seguin's avatar
Vincent Seguin committed
489 490
}

491
/*****************************************************************************
492
 * vout_DestroySubPicture: remove a subpicture from the heap
493
 *****************************************************************************
494
 * This function frees a previously reserved subpicture.
Vincent Seguin's avatar
Vincent Seguin committed
495
 * It is meant to be used when the construction of a picture aborted.
496
 * This function does not need locking since reserved subpictures are ignored
497
 * by the output thread.
498
 *****************************************************************************/
499
void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
500 501
{
#ifdef DEBUG
502 503
   /* Check if status is valid */
   if( p_subpic->i_status != RESERVED_SUBPICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
504
   {
505
       intf_ErrMsg("error: subpicture %p has invalid status %d",
506 507
                   p_subpic, p_subpic->i_status );
   }
Vincent Seguin's avatar
Vincent Seguin committed
508 509
#endif

510
    p_subpic->i_status = DESTROYED_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
511

512
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
513
    intf_DbgMsg("subpicture %p", p_subpic);
Vincent Seguin's avatar
Vincent Seguin committed
514 515 516
#endif
}

517
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
518
 * vout_DisplayPicture: display a picture
519
 *****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
520
 * Remove the reservation flag of a picture, which will cause it to be ready for
521
 * display. The picture won't be displayed until vout_DatePicture has been
522
 * called.
523
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
524
void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
525
{
526 527
    vlc_mutex_lock( &p_vout->picture_lock );
    switch( p_pic->i_status )
528
    {
529
    case RESERVED_PICTURE:
530
        p_pic->i_status = RESERVED_DISP_PICTURE;
531
        break;
532 533
    case RESERVED_DATED_PICTURE:
        p_pic->i_status = READY_PICTURE;
534
        break;
535
#ifdef DEBUG
536
    default:
537
        intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
538
        break;
539
#endif
540
    }
Michel Kaempf's avatar
Michel Kaempf committed
541

542
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
543
    intf_DbgMsg("picture %p", p_pic);
544
#endif
545
    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
546 547
}

548
/*****************************************************************************
549
 * vout_DatePicture: date a picture
550
 *****************************************************************************
551
 * Remove the reservation flag of a picture, which will cause it to be ready for
552
 * display. The picture won't be displayed until vout_DisplayPicture has been
553
 * called.
554
 *****************************************************************************/
555 556
void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
{
557
#ifdef TRACE_VOUT
558
    char        psz_date[MSTRTIME_MAX_SIZE];                         /* date */
Vincent Seguin's avatar
Vincent Seguin committed
559 560
#endif

561
    vlc_mutex_lock( &p_vout->picture_lock );
562
    p_pic->date = date;
563 564
    switch( p_pic->i_status )
    {
565
    case RESERVED_PICTURE:
566
        p_pic->i_status = RESERVED_DATED_PICTURE;
567
        break;
568 569
    case RESERVED_DISP_PICTURE:
        p_pic->i_status = READY_PICTURE;
570
        break;
571
#ifdef DEBUG
572
    default:
573
        intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
574
        break;
575 576 577
#endif
    }

578
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
579
    intf_DbgMsg("picture %p, display date: %s", p_pic, mstrtime( psz_date, p_pic->date) );
580
#endif
581
    vlc_mutex_unlock( &p_vout->picture_lock );
582 583
}

584
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
585
 * vout_CreatePicture: allocate a picture in the video output heap.
586 587
 *****************************************************************************
 * This function create a reserved image in the video output heap.
Vincent Seguin's avatar
Vincent Seguin committed
588
 * A null pointer is returned if the function fails. This method provides an
589
 * already allocated zone of memory in the picture data fields. It needs locking
590 591 592
 * since several pictures can be created by several producers threads.
 *****************************************************************************/
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
593
                               int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
594
{
595 596 597 598
    int         i_picture;                                  /* picture index */
    int         i_chroma_width = 0;                          /* chroma width */
    picture_t * p_free_picture = NULL;                 /* first free picture */
    picture_t * p_destroyed_picture = NULL;       /* first destroyed picture */
Michel Kaempf's avatar
Michel Kaempf committed
599

Vincent Seguin's avatar
Vincent Seguin committed
600
    /* Get lock */
Vincent Seguin's avatar
Vincent Seguin committed
601
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
602

603 604
    /*
     * Look for an empty place
Vincent Seguin's avatar
Vincent Seguin committed
605
     */
606
    for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
Vincent Seguin's avatar
Vincent Seguin committed
607
    {
608 609 610
        if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
        {
            /* Picture is marked for destruction, but is still allocated - note
611
             * that if width and type are the same for two pictures, chroma_width
612
             * should also be the same */
613 614 615 616 617
            if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
                (p_vout->p_picture[i_picture].i_height         == i_height) &&
                (p_vout->p_picture[i_picture].i_width          == i_width) )
            {
                /* Memory size do match : memory will not be reallocated, and function
Vincent Seguin's avatar
Vincent Seguin committed
618 619
                 * can end immediately - this is the best possible case, since no
                 * memory allocation needs to be done */
620
                p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
621
                p_vout->i_pictures++;
622
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
623
                intf_DbgMsg("picture %p (in destroyed picture slot)",
624
                            &p_vout->p_picture[i_picture] );
625
#endif
626 627 628 629 630 631 632 633 634 635
                vlc_mutex_unlock( &p_vout->picture_lock );
                return( &p_vout->p_picture[i_picture] );
            }
            else if( p_destroyed_picture == NULL )
            {
                /* Memory size do not match, but picture index will be kept in
                 * case no other place are left */
                p_destroyed_picture = &p_vout->p_picture[i_picture];
            }
        }
636
        else if( (p_free_picture == NULL) &&
Vincent Seguin's avatar
Vincent Seguin committed
637 638
                 (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
        {
639
            /* Picture is empty and ready for allocation */
640
            p_free_picture = &p_vout->p_picture[i_picture];
Vincent Seguin's avatar
Vincent Seguin committed
641
        }
Michel Kaempf's avatar
Michel Kaempf committed
642 643
    }

Vincent Seguin's avatar
Vincent Seguin committed
644 645
    /* If no free picture is available, use a destroyed picture */
    if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
646
    {
647 648
        /* No free picture or matching destroyed picture has been found, but
         * a destroyed picture is still avalaible */
649 650
        free( p_destroyed_picture->p_data );
        p_free_picture = p_destroyed_picture;
Michel Kaempf's avatar
Michel Kaempf committed
651 652
    }

Vincent Seguin's avatar
Vincent Seguin committed
653 654 655 656 657 658 659
    /*
     * Prepare picture
     */
    if( p_free_picture != NULL )
    {
        /* Allocate memory */
        switch( i_type )
Michel Kaempf's avatar
Michel Kaempf committed
660
        {
661 662
        case YUV_420_PICTURE:        /* YUV 420: 1,1/4,1/4 samples per pixel */
            i_chroma_width = i_width / 2;
Vincent Seguin's avatar
Vincent Seguin committed
663
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
664
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
665 666
            p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
            p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
667
            break;
668 669
        case YUV_422_PICTURE:        /* YUV 422: 1,1/2,1/2 samples per pixel */
            i_chroma_width = i_width / 2;
670 671
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
672 673
            p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
            p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
674
            break;
675 676
        case YUV_444_PICTURE:            /* YUV 444: 1,1,1 samples per pixel */
            i_chroma_width = i_width;
677 678
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
679 680
            p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
            p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
681
            break;
Vincent Seguin's avatar
Vincent Seguin committed
682 683
#ifdef DEBUG
        default:
684
            intf_ErrMsg("error: unknown picture type %d", i_type );
685 686 687
            p_free_picture->p_data   =  NULL;
            break;
#endif
Michel Kaempf's avatar
Michel Kaempf committed
688 689
        }

Vincent Seguin's avatar
Vincent Seguin committed
690
        if( p_free_picture->p_data != NULL )
691
        {
Sam Hocevar's avatar
Sam Hocevar committed
692
            /* Copy picture information, set some default values */
693 694
            p_free_picture->i_type                      = i_type;
            p_free_picture->i_status                    = RESERVED_PICTURE;
695
            p_free_picture->i_matrix_coefficients       = 1;
696 697
            p_free_picture->i_width                     = i_width;
            p_free_picture->i_height                    = i_height;
698
            p_free_picture->i_chroma_width              = i_chroma_width;
699 700 701
            p_free_picture->i_size                      = i_width * i_height;
            p_free_picture->i_chroma_size               = i_chroma_width
                                                            * i_height;
702
            p_free_picture->i_display_horizontal_offset = 0;
703
            p_free_picture->i_display_vertical_offset   = 0;
704 705
            p_free_picture->i_display_width             = i_width;
            p_free_picture->i_display_height            = i_height;
706 707
            p_free_picture->i_aspect_ratio              = AR_SQUARE_PICTURE;
            p_free_picture->i_refcount                  = 0;
708
            p_vout->i_pictures++;
Vincent Seguin's avatar
Vincent Seguin committed
709 710 711 712
        }
        else
        {
            /* Memory allocation failed : set picture as empty */
713 714 715
            p_free_picture->i_type   =  EMPTY_PICTURE;
            p_free_picture->i_status =  FREE_PICTURE;
            p_free_picture =            NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
716 717
            intf_ErrMsg( "vout error: picture allocation returned %s",
                         strerror( ENOMEM ) );
Vincent Seguin's avatar
Vincent Seguin committed
718
        }
719

720
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
721
        intf_DbgMsg("picture %p (in free picture slot)", p_free_picture );
722
#endif
Vincent Seguin's avatar
Vincent Seguin committed
723
        vlc_mutex_unlock( &p_vout->picture_lock );
Henri Fallon's avatar
 
Henri Fallon committed
724 725 726 727

        /* Initialize mutex */
        vlc_mutex_init( &(p_free_picture->lock_deccount) );
        
Vincent Seguin's avatar
Vincent Seguin committed
728
        return( p_free_picture );
Michel Kaempf's avatar
Michel Kaempf committed
729
    }
730

731
    /* No free or destroyed picture could be found */
Sam Hocevar's avatar
 
Sam Hocevar committed
732
    intf_DbgMsg( "warning: picture heap is full" );
Vincent Seguin's avatar
Vincent Seguin committed
733
    vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
734
    return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
735 736
}

737
/*****************************************************************************
738
 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
739
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
740
 * This function frees a previously reserved picture or a permanent
Vincent Seguin's avatar
Vincent Seguin committed
741 742
 * 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 !
743
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
744
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
745
{
Henri Fallon's avatar
 
Henri Fallon committed
746
    vlc_mutex_lock( &p_vout->picture_lock );
747

748
#ifdef DEBUG
Henri Fallon's avatar
 
Henri Fallon committed
749 750 751 752 753
    /* Check if picture status is valid */
    if( (p_pic->i_status != RESERVED_PICTURE) &&
        (p_pic->i_status != RESERVED_DATED_PICTURE) &&
        (p_pic->i_status != RESERVED_DISP_PICTURE) )
    {
754
        intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
Henri Fallon's avatar
 
Henri Fallon committed
755
    }
756 757
#endif

Henri Fallon's avatar
 
Henri Fallon committed
758 759
    p_pic->i_status = DESTROYED_PICTURE;
    p_vout->i_pictures--;
760

761
#ifdef TRACE_VOUT
Henri Fallon's avatar
 
Henri Fallon committed
762
    intf_DbgMsg("picture %p", p_pic);
763
#endif
Henri Fallon's avatar
 
Henri Fallon committed
764

Henri Fallon's avatar
 
Henri Fallon committed
765 766
    /* destroy the lock that had been initialized in CreatePicture */
    vlc_mutex_destroy( &(p_pic->lock_deccount) );
Henri Fallon's avatar
 
Henri Fallon committed
767
   
Henri Fallon's avatar
 
Henri Fallon committed
768
    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
769 770
}

771
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
772
 * vout_LinkPicture: increment reference counter of a picture
773
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
774
 * This function increment the reference counter of a picture in the video
775
 * heap. It needs a lock since several producer threads can access the picture.
776
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
777 778
void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
779
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
780
    p_pic->i_refcount++;
781

782
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
783
    intf_DbgMsg("picture %p refcount=%d", p_pic, p_pic->i_refcount );
784
#endif
785 786

    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
787 788
}

789
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
790
 * vout_UnlinkPicture: decrement reference counter of a picture
791
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
792
 * This function decrement the reference counter of a picture in the video heap.
793
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
794 795
void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
796
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
797
    p_pic->i_refcount--;
798

799
#ifdef TRACE_VOUT
800 801
    if( p_pic->i_refcount < 0 )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
802
        intf_DbgMsg("error: refcount < 0");
803 804
        p_pic->i_refcount = 0;
    }
805 806
#endif