- /* No free or destroyed picture could be found */
- intf_DbgMsg( "warning: picture heap is full" );
- vlc_mutex_unlock( &p_vout->picture_lock );
- return( NULL );
-}
-
-/*****************************************************************************
- * vout_DestroyPicture: remove a permanent or reserved picture from the heap
- *****************************************************************************
- * 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 !
- *****************************************************************************/
-void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
-{
- vlc_mutex_lock( &p_vout->picture_lock );
-
-#ifdef DEBUG
- /* 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) )
- {
- intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
- }
-#endif
-
- p_pic->i_status = DESTROYED_PICTURE;
- p_vout->i_pictures--;
-
-#ifdef TRACE_VOUT
- intf_DbgMsg("picture %p", p_pic);
-#endif
-
- /* destroy the lock that had been initialized in CreatePicture */
- vlc_mutex_destroy( &(p_pic->lock_deccount) );
-
- vlc_mutex_unlock( &p_vout->picture_lock );
-}
-
-/*****************************************************************************
- * vout_LinkPicture: increment reference counter of a picture
- *****************************************************************************
- * This function increment the reference counter of a picture in the video
- * heap. It needs a lock since several producer threads can access the picture.
- *****************************************************************************/
-void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
-{
- vlc_mutex_lock( &p_vout->picture_lock );
- p_pic->i_refcount++;
-
-#ifdef TRACE_VOUT
- intf_DbgMsg("picture %p refcount=%d", p_pic, p_pic->i_refcount );
-#endif
-
- vlc_mutex_unlock( &p_vout->picture_lock );
-}
-
-/*****************************************************************************
- * vout_UnlinkPicture: decrement reference counter of a picture
- *****************************************************************************
- * This function decrement the reference counter of a picture in the video heap.
- *****************************************************************************/
-void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
-{
- vlc_mutex_lock( &p_vout->picture_lock );
- p_pic->i_refcount--;
-
-#ifdef TRACE_VOUT
- if( p_pic->i_refcount < 0 )
- {
- intf_DbgMsg("error: refcount < 0");
- p_pic->i_refcount = 0;
- }
-#endif
-
- if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
- {
- p_pic->i_status = DESTROYED_PICTURE;
- p_vout->i_pictures--;
- }
-
-#ifdef TRACE_VOUT
- intf_DbgMsg("picture %p refcount=%d", p_pic, p_pic->i_refcount );
-#endif
-
- vlc_mutex_unlock( &p_vout->picture_lock );
-}
-
-/*****************************************************************************
- * vout_SetBuffers: set buffers adresses
- *****************************************************************************
- * This function is called by system drivers to set buffers video memory
- * adresses.
- *****************************************************************************/
-void vout_SetBuffers( vout_thread_t *p_vout, void *p_buf1, void *p_buf2 )
-{
- /* No picture previously */
- p_vout->p_buffer[0].i_pic_x = 0;
- p_vout->p_buffer[0].i_pic_y = 0;
- p_vout->p_buffer[0].i_pic_width = 0;
- p_vout->p_buffer[0].i_pic_height = 0;
- p_vout->p_buffer[1].i_pic_x = 0;
- p_vout->p_buffer[1].i_pic_y = 0;
- p_vout->p_buffer[1].i_pic_width = 0;
- p_vout->p_buffer[1].i_pic_height = 0;
-
- /* The first area covers all the screen */
- p_vout->p_buffer[0].i_areas = 1;
- p_vout->p_buffer[0].pi_area_begin[0] = 0;
- p_vout->p_buffer[0].pi_area_end[0] = p_vout->i_height - 1;
- p_vout->p_buffer[1].i_areas = 1;
- p_vout->p_buffer[1].pi_area_begin[0] = 0;
- p_vout->p_buffer[1].pi_area_end[0] = p_vout->i_height - 1;
-
- /* Set adresses */
- p_vout->p_buffer[0].p_data = p_buf1;
- p_vout->p_buffer[1].p_data = p_buf2;
-}
-
-/*****************************************************************************
- * vout_Pixel2RGB: return red, green and blue from pixel value
- *****************************************************************************
- * Return color values, in 0-255 range, of the decomposition of a pixel. This
- * is a slow routine and should only be used for initialization phase.
- *****************************************************************************/
-void vout_Pixel2RGB( vout_thread_t *p_vout, u32 i_pixel, int *pi_red, int *pi_green, int *pi_blue )
-{
- *pi_red = i_pixel & p_vout->i_red_mask;
- *pi_green = i_pixel & p_vout->i_green_mask;
- *pi_blue = i_pixel & p_vout->i_blue_mask;
-}
-
-/* following functions are local */
-
-/*****************************************************************************
- * BinaryLog: computes the base 2 log of a binary value
- *****************************************************************************
- * This functions is used by MaskToShift, to get a bit index from a binary
- * value.
- *****************************************************************************/
-static int BinaryLog(u32 i)
-{
- int i_log = 0;
-
- if(i & 0xffff0000)
- {
- i_log += 16;
- }
- if(i & 0xff00ff00)
- {
- i_log += 8;
- }
- if(i & 0xf0f0f0f0)
- {
- i_log += 4;
- }
- if(i & 0xcccccccc)
- {
- i_log += 2;
- }
- if(i & 0xaaaaaaaa)
- {
- i_log += 1;
- }
-
- if (i != ((u32)1 << i_log))
- {
- intf_DbgMsg("internal error: binary log overflow");
- }
-
- return( i_log );
-}
-
-/*****************************************************************************
- * MaskToShift: transform a color mask into right and left shifts
- *****************************************************************************
- * This function is used for obtaining color shifts from masks.
- *****************************************************************************/
-static void MaskToShift( int *pi_left, int *pi_right, u32 i_mask )
-{
- u32 i_low, i_high; /* lower hand higher bits of the mask */
-
- /* Get bits */
- i_low = i_mask & (- i_mask); /* lower bit of the mask */
- i_high = i_mask + i_low; /* higher bit of the mask */
-
- /* Transform bits into an index */
- i_low = BinaryLog (i_low);
- i_high = BinaryLog (i_high);
-
- /* Update pointers and return */
- *pi_left = i_low;
- *pi_right = (8 - i_high + i_low);
-}
-
-/*****************************************************************************
- * InitThread: initialize video output thread
- *****************************************************************************
- * This function is called from RunThread and performs the second step of the
- * initialization. It returns 0 on success. Note that the thread's flag are not
- * modified inside this function.
- *****************************************************************************/
-static int InitThread( vout_thread_t *p_vout )
-{
- /* Update status */
- *p_vout->pi_status = THREAD_START;
-
- vlc_mutex_lock( &p_vout->change_lock );
-
-#ifdef STATS
- p_vout->c_loops = 0;
-#endif
-
- /* Initialize output method - this function issues its own error messages */
- if( p_vout->pf_init( p_vout ) )
- {
- return( 1 );
- }
-
- /* Initialize convertion tables and functions */
- if( vout_InitYUV( p_vout ) )
- {
- intf_ErrMsg("vout error: can't allocate YUV translation tables");
- return( 1 );
- }
-
- /* Mark thread as running and return */
- p_vout->b_active = 1;
- *p_vout->pi_status = THREAD_READY;
-
-
- intf_DbgMsg("thread ready");
- return( 0 );
-}
-
-/*****************************************************************************
- * RunThread: video output thread
- *****************************************************************************
- * Video output thread. This function does only returns when the thread is
- * terminated. It handles the pictures arriving in the video heap and the
- * display device events.
- *****************************************************************************/
-static void RunThread( vout_thread_t *p_vout)
-{
- int i_index; /* index in heap */
- mtime_t current_date; /* current date */
- mtime_t display_date; /* display date */
- boolean_t b_display; /* display flag */
- picture_t * p_pic; /* picture pointer */
- subpicture_t * p_subpic; /* subpicture pointer */
-
- /*
- * Initialize thread
- */
- p_vout->b_error = InitThread( p_vout );
- if( p_vout->b_error )
- {
- DestroyThread( p_vout, THREAD_ERROR );
- return;
- }
-
- /*
- * Main loop - it is not executed if an error occured during
- * initialization
- */
- while( (!p_vout->b_die) && (!p_vout->b_error) )
- {
- /* Initialize loop variables */
- p_pic = NULL;
- p_subpic = NULL;
- display_date = 0;
- current_date = mdate();
-#ifdef STATS
- p_vout->c_loops++;
- if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
- {
- intf_Msg("vout stats: picture heap: %d/%d",
- p_vout->i_pictures, VOUT_MAX_PICTURES);
- }
-#endif
-
- /*
- * Find the picture to display - this operation does not need lock,
- * since only READY_PICTUREs are handled
- */
- for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
- {
- if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
- ( (p_pic == NULL) ||
- (p_vout->p_picture[i_index].date < display_date) ) )
- {
- p_pic = &p_vout->p_picture[i_index];
- display_date = p_pic->date;
- }
- }
-
- if( p_pic )
- {
- /* Computes FPS rate */
- p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
-
- if( display_date < current_date - p_vout->render_time )
- {
- /* Picture is late: it will be destroyed and the thread
- * will sleep and go to next picture */
-
- vlc_mutex_lock( &p_vout->picture_lock );
- if( p_pic->i_refcount )
- {
- p_pic->i_status = DISPLAYED_PICTURE;
- }
- else
- {
- p_pic->i_status = DESTROYED_PICTURE;
- p_vout->i_pictures--;
- }
- intf_WarnMsg( 3,
- "warning: late picture skipped (%p)", p_pic );
- vlc_mutex_unlock( &p_vout->picture_lock );
-
- continue;
- }
- else if( display_date > current_date + VOUT_DISPLAY_DELAY )
- {
- /* A picture is ready to be rendered, but its rendering date
- * is far from the current one so the thread will perform an
- * empty loop as if no picture were found. The picture state
- * is unchanged */
- p_pic = NULL;
- display_date = 0;
- }
- }
- /*
- * Find the subpictures to display - this operation does not need
- * lock, since only READY_SUBPICTURE are handled. If no picture
- * has been selected, display_date will depend on the subpicture.
- * We get an easily parsable chained list of subpictures which
- * ends with NULL since p_subpic was initialized to NULL.
- */
- for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
- {
- if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
- {
- p_vout->p_subpicture[i_index].p_next = p_subpic;
- p_subpic = &p_vout->p_subpicture[i_index];
- }
- }
-
- /*
- * Perform rendering, sleep and display rendered picture
- */
- if( p_pic ) /* picture and perhaps subpicture */
- {
- b_display = p_vout->b_active;
- p_vout->last_display_date = display_date;
- p_vout->p_rendered_pic = p_pic;
-
-
-
-
- /* Set picture dimensions and clear buffer */
- SetBufferPicture( p_vout, p_pic );
-
- /* FIXME: if b_need_render == 0 we need to do something with
- * the subpictures one day. */
-
- if( p_vout->b_need_render && b_display )
- {
- /* Render picture and information */
- RenderPicture( p_vout, p_pic );
- if( p_vout->b_info )
- {
- RenderPictureInfo( p_vout, p_pic );
- RenderInfo( p_vout );
- }
- if( p_subpic )
- {
- RenderSubPicture( p_vout, p_subpic );
- }
- }
-
- /* Render interface and subpicture */
- if( b_display && p_vout->b_interface && p_vout->b_need_render )
- {
- RenderInterface( p_vout );
- }
-
- }
- else if( p_vout->b_active && p_vout->b_need_render
- && p_vout->init_display_date == 0)
- {
- /* Idle or interface screen alone */
-
- if( p_vout->b_interface && 0 /* && XXX?? intf_change */ )
- {
- /* Interface has changed, so a new rendering is required - force
- * it by setting last idle date to 0 */
- p_vout->last_idle_date = 0;
- }
-
- /* Render idle screen and update idle date, then render interface if
- * required */
- b_display = RenderIdle( p_vout );
- if( b_display )
- {
- p_vout->last_idle_date = current_date;
- if( p_vout->b_interface )
- {
- RenderInterface( p_vout );
- }
- }
-
- }
- else
- {
- b_display = 0;
- }
-
-
- /*
- * Check for the current time and
- * display splash screen if everything is on time
- */
- if( p_vout->init_display_date > 0 && p_vout->b_need_render )
- {
- if( p_vout->b_active &&
- mdate()-p_vout->init_display_date < 5000000)
- {
- /* there is something to display ! */
- b_display = 1;
- RenderSplash( p_vout );
-
- } else {
- /* no splash screen ! */
- p_vout->init_display_date=0;
- }
- }
-
-
- /*
- * Sleep, wake up and display rendered picture
- */
-
- if( display_date != 0 )
- {
- /* Store render time using Bresenham algorithm */
- p_vout->render_time += mdate() - current_date;
- p_vout->render_time >>= 1;
- }
-
- /* Give back change lock */
- vlc_mutex_unlock( &p_vout->change_lock );
-
- /* Sleep a while or until a given date */
- if( display_date != 0 )
- {
- mwait( display_date - VOUT_MWAIT_TOLERANCE );
- }
- else
- {
- msleep( VOUT_IDLE_SLEEP );
- }
-
- /* On awakening, take back lock and send immediately picture to display,
- * then swap buffers */
- vlc_mutex_lock( &p_vout->change_lock );
-#ifdef TRACE_VOUT
- intf_DbgMsg( "picture %p, subpicture %p in buffer %d, display=%d", p_pic, p_subpic,
- p_vout->i_buffer_index, b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ );
-#endif
- if( b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ )
- {
- p_vout->pf_display( p_vout );
-#ifndef SYS_BEOS
- p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
-#endif
- }
-
- if( p_pic )
- {
- /* Remove picture from heap */
- vlc_mutex_lock( &p_vout->picture_lock );
- if( p_pic->i_refcount )
- {
- p_pic->i_status = DISPLAYED_PICTURE;
- }
- else
- {
- p_pic->i_status = DESTROYED_PICTURE;
- p_vout->i_pictures--;
- }
- vlc_mutex_unlock( &p_vout->picture_lock );
- }
-
-
- /*
- * Check events and manage thread
- */
- if( p_vout->pf_manage( p_vout ) | Manage( p_vout ) )
- {
- /* A fatal error occured, and the thread must terminate immediately,
- * without displaying anything - setting b_error to 1 cause the
- * immediate end of the main while() loop. */
- p_vout->b_error = 1;
- }
- }
-
- /*
- * Error loop - wait until the thread destruction is requested
- */
- if( p_vout->b_error )
- {
- ErrorThread( p_vout );
- }
-
- /* End of thread */
- EndThread( p_vout );
- DestroyThread( p_vout, THREAD_OVER );
- intf_DbgMsg( "thread end" );
-}
-
-/*****************************************************************************
- * ErrorThread: RunThread() error loop
- *****************************************************************************
- * This function is called when an error occured during thread main's loop. The
- * thread can still receive feed, but must be ready to terminate as soon as
- * possible.
- *****************************************************************************/
-static void ErrorThread( vout_thread_t *p_vout )
-{
- /* Wait until a `die' order */
- while( !p_vout->b_die )
- {
- /* Sleep a while */
- msleep( VOUT_IDLE_SLEEP );
- }
-}
-
-/*****************************************************************************
- * EndThread: thread destruction
- *****************************************************************************
- * This function is called when the thread ends after a sucessful
- * initialization. It frees all ressources allocated by InitThread.
- *****************************************************************************/
-static void EndThread( vout_thread_t *p_vout )
-{
- int i_index; /* index in heap */
-
- /* Store status */
- *p_vout->pi_status = THREAD_END;
-
-#ifdef STATS
- {
- struct tms cpu_usage;
- times( &cpu_usage );
-
- intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
- cpu_usage.tms_utime, cpu_usage.tms_stime );
- }
-#endif
-
- /* Destroy all remaining pictures and subpictures */
- for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
- {
- if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
- {
- free( p_vout->p_picture[i_index].p_data );
- }
- }
-
- for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
- {
- if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
- {
- free( p_vout->p_subpicture[i_index].p_data );
- }
- }
-
- /* Destroy translation tables */
- vout_EndYUV( p_vout );
- p_vout->pf_end( p_vout );
-
- /* Release the change lock */
- vlc_mutex_unlock( &p_vout->change_lock );
-}
-
-/*****************************************************************************
- * DestroyThread: thread destruction
- *****************************************************************************
- * This function is called when the thread ends. It frees all ressources
- * allocated by CreateThread. Status is available at this stage.
- *****************************************************************************/
-static void DestroyThread( vout_thread_t *p_vout, int i_status )
-{
- int *pi_status; /* status adress */
-
- /* Store status adress */
- pi_status = p_vout->pi_status;
-
- /* Destroy thread structures allocated by Create and InitThread */
- vout_UnloadFont( p_vout->p_default_font );
- vout_UnloadFont( p_vout->p_large_font );
- p_vout->pf_destroy( p_vout );
-
- /* Destroy the locks */
- vlc_mutex_destroy( &p_vout->picture_lock );
- vlc_mutex_destroy( &p_vout->subpicture_lock );
- vlc_mutex_destroy( &p_vout->change_lock );
-
- /* Release the module */
- module_Unneed( p_vout->p_module );
-
- /* Free structure */
- free( p_vout );
- *pi_status = i_status;
-}
-
-/*****************************************************************************
- * Print: print simple text on a picture
- *****************************************************************************
- * This function will print a simple text on the picture. It is designed to
- * print debugging or general information.
- *****************************************************************************/
-void Print( vout_thread_t *p_vout, int i_x, int i_y, int i_h_align, int i_v_align, unsigned char *psz_text )
-{
- int i_text_height; /* total text height */
- int i_text_width; /* total text width */
-
- /* Update upper left coordinates according to alignment */
- vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
- if( !Align( p_vout, &i_x, &i_y, i_text_width, i_text_height, i_h_align, i_v_align ) )
- {
- /* Set area and print text */
- SetBufferArea( p_vout, i_x, i_y, i_text_width, i_text_height );
- vout_Print( p_vout->p_default_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
- i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
- p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
- p_vout->i_white_pixel, 0, 0,
- 0, psz_text, 100 );
- }
-}
-
-/*****************************************************************************
- * SetBufferArea: activate an area in current buffer
- *****************************************************************************
- * This function is called when something is rendered on the current buffer.
- * It set the area as active and prepare it to be cleared on next rendering.
- * Pay attention to the fact that in this functions, i_h is in fact the end y
- * coordinate of the new area.
- *****************************************************************************/
-static void SetBufferArea( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h )
-{
- vout_buffer_t * p_buffer; /* current buffer */
- int i_area_begin, i_area_end; /* area vertical extension */
- int i_area, i_area_copy; /* area index */
- int i_area_shift; /* shift distance for areas */
-
- /* Choose buffer and modify h to end of area position */
- p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ];
- i_h += i_y - 1;
-
- /*
- * Remove part of the area which is inside the picture - this is done
- * by calling again SetBufferArea with the correct areas dimensions.
- */
- if( (i_x >= p_buffer->i_pic_x) && (i_x + i_w <= p_buffer->i_pic_x + p_buffer->i_pic_width) )
- {
- i_area_begin = p_buffer->i_pic_y;
- i_area_end = i_area_begin + p_buffer->i_pic_height - 1;