video_output.c 84.6 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
8
 * $Id: video_output.c,v 1.124 2001/05/06 18:32:30 stef 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;
181 182
    p_vout->i_screen_depth        = main_GetIntVariable( VOUT_DEPTH_VAR,
                                                         VOUT_DEPTH_DEFAULT );
Renaud Dartus's avatar
 
Renaud Dartus committed
183
    p_vout->i_bytes_per_pixel     = 2;
184 185
    p_vout->f_gamma               = VOUT_GAMMA_DEFAULT; // FIXME: replace with 
                                                        // variable
186
    p_vout->b_need_render         = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
187 188
    p_vout->b_YCbr                = 0;
    
Renaud Dartus's avatar
 
Renaud Dartus committed
189
    p_vout->b_grayscale           = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
Sam Hocevar's avatar
 
Sam Hocevar committed
190
                                                     VOUT_GRAYSCALE_DEFAULT );
Renaud Dartus's avatar
 
Renaud Dartus committed
191 192 193
    p_vout->b_info                = 0;
    p_vout->b_interface           = 0;
    p_vout->b_scale               = 1;
194 195
    p_vout->b_fullscreen          = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
                                                     VOUT_FULLSCREEN_DEFAULT );
196

Sam Hocevar's avatar
 
Sam Hocevar committed
197 198 199
    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 );
200

201
    /* Initialize idle screen */
202
    p_vout->last_display_date   = 0;
203
    p_vout->last_idle_date      = 0;
204
    p_vout->init_display_date   = mdate();
205
    p_vout->render_time         = 10000;
206

207
    /* Initialize statistics fields */
208
    p_vout->c_fps_samples       = 0;
209 210 211 212

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

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

227 228
    /* Create and initialize system-dependant method - this function issues its
     * own error messages */
Sam Hocevar's avatar
 
Sam Hocevar committed
229
    if( p_vout->pf_create( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
230
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
231
        module_Unneed( p_vout->p_module );
232 233
        free( p_vout );
        return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
234
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
235 236 237 238 239 240
    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
241 242

    /* Calculate shifts from system-updated masks */
243 244 245 246 247 248
    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
249

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

256 257
    /* 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
258
    p_vout->p_default_font = vout_LoadFont( VOUT_DEFAULT_FONT );
Vincent Seguin's avatar
Vincent Seguin committed
259 260
    if( p_vout->p_default_font == NULL )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
261
        intf_ErrMsg( "vout error: could not load default font" );
262
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
263 264

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

Michel Kaempf's avatar
Michel Kaempf committed
270
    /* Create thread and set locks */
Vincent Seguin's avatar
Vincent Seguin committed
271
    vlc_mutex_init( &p_vout->picture_lock );
272 273
    vlc_mutex_init( &p_vout->subpicture_lock );
    vlc_mutex_init( &p_vout->change_lock );
274

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

Sam Hocevar's avatar
 
Sam Hocevar committed
286 287
    intf_Msg( "vout: video display initialized (%dx%d, %d/%d bpp)",
              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
288
              p_vout->i_bytes_per_pixel * 8 );
289

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

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

    /* Set status */
Sam Hocevar's avatar
 
Sam Hocevar committed
319
    intf_DbgMsg("");
Michel Kaempf's avatar
Michel Kaempf committed
320
    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
321 322
    *p_vout->pi_status = THREAD_DESTROY;

Michel Kaempf's avatar
Michel Kaempf committed
323 324 325 326
    /* Request thread destruction */
    p_vout->b_die = 1;

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

337
/*****************************************************************************
338
 * vout_DisplaySubPicture: display a subpicture unit
339 340
 *****************************************************************************
 * Remove the reservation flag of an subpicture, which will cause it to be ready
341
 * for display. The picture does not need to be locked, since it is ignored by
Vincent Seguin's avatar
Vincent Seguin committed
342
 * the output thread if is reserved.
343
 *****************************************************************************/
344
void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
345
{
346
#ifdef TRACE_VOUT
347 348
    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
349 350 351 352
#endif

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

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

363
#ifdef TRACE_VOUT
Sam Hocevar's avatar
Sam Hocevar committed
364
    /* Send subpicture information */
Sam Hocevar's avatar
 
Sam Hocevar committed
365
    intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s",
366 367 368
                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
369 370 371
#endif
}

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

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

390 391
    /*
     * Look for an empty place
392
     */
393
    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
394
    {
395 396 397 398 399 400
        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
401 402 403 404
                /* 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 */
405
                p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
406
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
407
                intf_DbgMsg("subpicture %p (in destroyed subpicture slot)",
408
                            &p_vout->p_subpicture[i_subpic] );
409
#endif
410 411 412 413 414 415 416 417 418 419
                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];
            }
        }
420
        else if( (p_free_subpic == NULL) &&
421 422
                 (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
        {
423
            /* Subpicture is empty and ready for allocation */
424 425 426 427
            p_free_subpic = &p_vout->p_subpicture[i_subpic];
        }
    }

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

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

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

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

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

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

514
    p_subpic->i_status = DESTROYED_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
515

516
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
517
    intf_DbgMsg("subpicture %p", p_subpic);
Vincent Seguin's avatar
Vincent Seguin committed
518 519 520
#endif
}

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

546
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
547
    intf_DbgMsg("picture %p", p_pic);
548
#endif
549
    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
550 551
}

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

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

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

588
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
589
 * vout_CreatePicture: allocate a picture in the video output heap.
590 591
 *****************************************************************************
 * This function create a reserved image in the video output heap.
Vincent Seguin's avatar
Vincent Seguin committed
592
 * A null pointer is returned if the function fails. This method provides an
593
 * already allocated zone of memory in the picture data fields. It needs locking
594 595 596
 * since several pictures can be created by several producers threads.
 *****************************************************************************/
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
597
                               int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
598
{
599 600 601 602
    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
603

Vincent Seguin's avatar
Vincent Seguin committed
604
    /* Get lock */
Vincent Seguin's avatar
Vincent Seguin committed
605
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
606

607 608
    /*
     * Look for an empty place
Vincent Seguin's avatar
Vincent Seguin committed
609
     */
610
    for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
Vincent Seguin's avatar
Vincent Seguin committed
611
    {
612 613 614
        if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
        {
            /* Picture is marked for destruction, but is still allocated - note
615
             * that if width and type are the same for two pictures, chroma_width
616
             * should also be the same */
617 618 619 620 621
            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
622 623
                 * can end immediately - this is the best possible case, since no
                 * memory allocation needs to be done */
624
                p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
625
                p_vout->i_pictures++;
626
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
627
                intf_DbgMsg("picture %p (in destroyed picture slot)",
628
                            &p_vout->p_picture[i_picture] );
629
#endif
630 631 632 633 634 635 636 637 638 639
                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];
            }
        }
640
        else if( (p_free_picture == NULL) &&
Vincent Seguin's avatar
Vincent Seguin committed
641 642
                 (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
        {
643
            /* Picture is empty and ready for allocation */
644
            p_free_picture = &p_vout->p_picture[i_picture];
Vincent Seguin's avatar
Vincent Seguin committed
645
        }
Michel Kaempf's avatar
Michel Kaempf committed
646 647
    }

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

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

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

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

        /* Initialize mutex */
        vlc_mutex_init( &(p_free_picture->lock_deccount) );
        
Vincent Seguin's avatar
Vincent Seguin committed
732
        return( p_free_picture );
Michel Kaempf's avatar
Michel Kaempf committed
733
    }
734

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

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

752
#ifdef DEBUG
Henri Fallon's avatar
 
Henri Fallon committed
753 754 755 756 757
    /* 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) )
    {
758
        intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
Henri Fallon's avatar
 
Henri Fallon committed
759
    }
760 761
#endif

Henri Fallon's avatar
 
Henri Fallon committed
762 763
    p_pic->i_status = DESTROYED_PICTURE;
    p_vout->i_pictures--;
764

765
#ifdef TRACE_VOUT
Henri Fallon's avatar
 
Henri Fallon committed
766
    intf_DbgMsg("picture %p", p_pic);
767
#endif
Henri Fallon's avatar
 
Henri Fallon committed
768

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

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

786
#ifdef TRACE_VOUT
Sam Hocevar's avatar
 
Sam Hocevar committed
787
    intf_DbgMsg("picture %p refcount=%d", p_pic, p_pic->i_refcount );
788
#endif
789 790

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

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

803
#ifdef TRACE_VOUT
804 805
    if( p_pic->i_refcount < 0 )
    {
Sam Hocevar's avatar