1 /******************************************************************************
2 * video_output.c : video output thread
4 ******************************************************************************
5 * This module describes the programming interface for video output threads.
6 * It includes functions allowing to open a new thread, send pictures to a
7 * thread, and destroy a previously oppenned video output thread.
8 ******************************************************************************/
10 /******************************************************************************
12 ******************************************************************************/
21 #include "vlc_thread.h"
23 #include "video_output.h"
24 #include "video_text.h"
25 #include "video_sys.h"
26 #include "video_yuv.h"
30 /******************************************************************************
32 ******************************************************************************/
33 static int InitThread ( vout_thread_t *p_vout );
34 static void RunThread ( vout_thread_t *p_vout );
35 static void ErrorThread ( vout_thread_t *p_vout );
36 static void EndThread ( vout_thread_t *p_vout );
37 static void DestroyThread ( vout_thread_t *p_vout, int i_status );
38 static void Print ( vout_thread_t *p_vout, int i_x, int i_y, int i_halign, int i_valign, unsigned char *psz_text );
40 static void SetBufferArea ( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h );
41 static void SetBufferPicture ( vout_thread_t *p_vout, picture_t *p_pic );
42 static void RenderPicture ( vout_thread_t *p_vout, picture_t *p_pic );
43 static void RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic );
44 static void RenderSubPictureUnit ( vout_thread_t *p_vout, spu_t *p_spu );
45 static void RenderInterface ( vout_thread_t *p_vout );
46 static void RenderIdle ( vout_thread_t *p_vout );
47 static void RenderInfo ( vout_thread_t *p_vout );
48 static int Manage ( vout_thread_t *p_vout );
50 /******************************************************************************
51 * vout_CreateThread: creates a new video output thread
52 ******************************************************************************
53 * This function creates a new video output thread, and returns a pointer
54 * to its description. On error, it returns NULL.
55 * If pi_status is NULL, then the function will block until the thread is ready.
56 * If not, it will be updated using one of the THREAD_* constants.
57 ******************************************************************************/
58 vout_thread_t * vout_CreateThread ( char *psz_display, int i_root_window,
59 int i_width, int i_height, int *pi_status )
61 vout_thread_t * p_vout; /* thread descriptor */
62 int i_status; /* thread status */
63 int i_index; /* index for array initialization */
65 /* Allocate descriptor */
67 p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
70 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
74 /* Initialize thread properties - thread id and locks will be initialized
79 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
80 *p_vout->pi_status = THREAD_CREATE;
82 /* Initialize some fields used by the system-dependant method - these fields will
83 * probably be modified by the method, and are only preferences */
84 p_vout->i_width = i_width;
85 p_vout->i_height = i_height;
86 p_vout->i_bytes_per_line = i_width * 2;
87 p_vout->i_screen_depth = 15;
88 p_vout->i_bytes_per_pixel = 2;
89 p_vout->f_gamma = VOUT_GAMMA;
91 p_vout->b_grayscale = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
92 VOUT_GRAYSCALE_DEFAULT );
94 p_vout->b_interface = 0;
97 intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
98 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
99 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
102 /* Initialize statistics fields */
103 p_vout->render_time = 0;
104 p_vout->c_fps_samples = 0;
107 /* Initialize running properties */
108 p_vout->i_changes = 0;
109 p_vout->last_picture_date = 0;
110 p_vout->last_display_date = 0;
112 /* Initialize buffer index */
113 p_vout->i_buffer_index = 0;
115 /* Initialize pictures and spus - translation tables and functions
116 * will be initialized later in InitThread */
117 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
119 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
120 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
121 p_vout->p_spu[i_index].i_type = EMPTY_SPU;
122 p_vout->p_spu[i_index].i_status= FREE_SPU;
125 /* Create and initialize system-dependant method - this function issues its
126 * own error messages */
127 if( vout_SysCreate( p_vout, psz_display, i_root_window ) )
132 intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
133 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
134 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
136 /* Load fonts - fonts must be initialized after the systme method since
137 * they may be dependant of screen depth and other thread properties */
138 p_vout->p_default_font = vout_LoadFont( VOUT_DEFAULT_FONT );
139 if( p_vout->p_default_font == NULL )
141 vout_SysDestroy( p_vout );
145 p_vout->p_large_font = vout_LoadFont( VOUT_LARGE_FONT );
146 if( p_vout->p_large_font == NULL )
148 vout_UnloadFont( p_vout->p_default_font );
149 vout_SysDestroy( p_vout );
154 /* Create thread and set locks */
155 vlc_mutex_init( &p_vout->picture_lock );
156 vlc_mutex_init( &p_vout->spu_lock );
157 vlc_mutex_init( &p_vout->change_lock );
158 vlc_mutex_lock( &p_vout->change_lock );
159 if( vlc_thread_create( &p_vout->thread_id, "video output", (void *) RunThread, (void *) p_vout) )
161 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
162 vout_UnloadFont( p_vout->p_default_font );
163 vout_UnloadFont( p_vout->p_large_font );
164 vout_SysDestroy( p_vout );
169 intf_Msg("Video display initialized (%dx%d, %d bpp)\n",
170 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );
172 /* If status is NULL, wait until the thread is created */
173 if( pi_status == NULL )
177 msleep( THREAD_SLEEP );
178 }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
179 && (i_status != THREAD_FATAL) );
180 if( i_status != THREAD_READY )
188 /******************************************************************************
189 * vout_DestroyThread: destroys a previously created thread
190 ******************************************************************************
191 * Destroy a terminated thread.
192 * The function will request a destruction of the specified thread. If pi_error
193 * is NULL, it will return once the thread is destroyed. Else, it will be
194 * update using one of the THREAD_* constants.
195 ******************************************************************************/
196 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
198 int i_status; /* thread status */
202 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
203 *p_vout->pi_status = THREAD_DESTROY;
205 /* Request thread destruction */
208 /* If status is NULL, wait until thread has been destroyed */
209 if( pi_status == NULL )
213 msleep( THREAD_SLEEP );
214 }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
215 && (i_status != THREAD_FATAL) );
219 /******************************************************************************
220 * vout_DisplaySubPictureUnit: display a sub picture unit
221 ******************************************************************************
222 * Remove the reservation flag of an spu, which will cause it to be ready for
223 * display. The picture does not need to be locked, since it is ignored by
224 * the output thread if is reserved.
225 ******************************************************************************/
226 void vout_DisplaySubPictureUnit( vout_thread_t *p_vout, spu_t *p_spu )
229 char psz_begin_date[MSTRTIME_MAX_SIZE]; /* buffer for date string */
230 char psz_end_date[MSTRTIME_MAX_SIZE]; /* buffer for date string */
234 /* Check if status is valid */
235 if( p_spu->i_status != RESERVED_SPU )
237 intf_DbgMsg("error: spu %p has invalid status %d\n", p_spu, p_spu->i_status );
241 /* Remove reservation flag */
242 p_spu->i_status = READY_SPU;
245 /* Send subpicture informations */
246 intf_DbgMsg("spu %p: type=%d, begin date=%s, end date=%s\n", p_spu, p_spu->i_type,
247 mstrtime( psz_begin_date, p_spu->begin_date ),
248 mstrtime( psz_end_date, p_spu->end_date ) );
252 /******************************************************************************
253 * vout_CreateSubPictureUnit: allocate an spu in the video output heap.
254 ******************************************************************************
255 * This function create a reserved spu in the video output heap.
256 * A null pointer is returned if the function fails. This method provides an
257 * already allocated zone of memory in the spu data fields. It needs locking
258 * since several pictures can be created by several producers threads.
259 ******************************************************************************/
260 spu_t *vout_CreateSubPictureUnit( vout_thread_t *p_vout, int i_type,
266 /******************************************************************************
267 * vout_DestroySubPictureUnit: remove a permanent or reserved spu from the heap
268 ******************************************************************************
269 * This function frees a previously reserved spu.
270 * It is meant to be used when the construction of a picture aborted.
271 * This function does not need locking since reserved spus are ignored by
273 ******************************************************************************/
274 void vout_DestroySubPictureUnit( vout_thread_t *p_vout, spu_t *p_spu )
277 /* Check if spu status is valid */
278 if( p_spu->i_status != RESERVED_SPU )
280 intf_DbgMsg("error: spu %p has invalid status %d\n", p_spu, p_spu->i_status );
284 p_spu->i_status = DESTROYED_SPU;
287 intf_DbgMsg("spu %p\n", p_spu);
291 /******************************************************************************
292 * vout_DisplayPicture: display a picture
293 ******************************************************************************
294 * Remove the reservation flag of a picture, which will cause it to be ready for
295 * display. The picture won't be displayed until vout_DatePicture has been
297 ******************************************************************************/
298 void vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
300 vlc_mutex_lock( &p_vout->picture_lock );
301 switch( p_pic->i_status )
303 case RESERVED_PICTURE:
304 p_pic->i_status = RESERVED_DISP_PICTURE;
306 case RESERVED_DATED_PICTURE:
307 p_pic->i_status = READY_PICTURE;
311 intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );
317 intf_DbgMsg("picture %p\n", p_pic );
320 vlc_mutex_unlock( &p_vout->picture_lock );
323 /******************************************************************************
324 * vout_DatePicture: date a picture
325 ******************************************************************************
326 * Remove the reservation flag of a picture, which will cause it to be ready for
327 * display. The picture won't be displayed until vout_DisplayPicture has been
329 ******************************************************************************/
330 void vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
332 vlc_mutex_lock( &p_vout->picture_lock );
334 switch( p_pic->i_status )
336 case RESERVED_PICTURE:
337 p_pic->i_status = RESERVED_DATED_PICTURE;
339 case RESERVED_DISP_PICTURE:
340 p_pic->i_status = READY_PICTURE;
344 intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );
350 intf_DbgMsg("picture %p\n", p_pic);
353 vlc_mutex_unlock( &p_vout->picture_lock );
356 /******************************************************************************
357 * vout_CreatePicture: allocate a picture in the video output heap.
358 ******************************************************************************
359 * This function create a reserved image in the video output heap.
360 * A null pointer is returned if the function fails. This method provides an
361 * already allocated zone of memory in the picture data fields. It needs locking
362 * since several pictures can be created by several producers threads.
363 ******************************************************************************/
364 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
365 int i_width, int i_height )
367 int i_picture; /* picture index */
368 int i_chroma_width = 0; /* chroma width */
369 picture_t * p_free_picture = NULL; /* first free picture */
370 picture_t * p_destroyed_picture = NULL; /* first destroyed picture */
373 vlc_mutex_lock( &p_vout->picture_lock );
376 * Look for an empty place
379 i_picture < VOUT_MAX_PICTURES;
382 if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
384 /* Picture is marked for destruction, but is still allocated - note
385 * that if width and type are the same for two pictures, chroma_width
386 * should also be the same */
387 if( (p_vout->p_picture[i_picture].i_type == i_type) &&
388 (p_vout->p_picture[i_picture].i_height == i_height) &&
389 (p_vout->p_picture[i_picture].i_width == i_width) )
391 /* Memory size do match : memory will not be reallocated, and function
392 * can end immediately - this is the best possible case, since no
393 * memory allocation needs to be done */
394 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
396 intf_DbgMsg("picture %p (in destroyed picture slot)\n",
397 &p_vout->p_picture[i_picture] );
399 vlc_mutex_unlock( &p_vout->picture_lock );
400 return( &p_vout->p_picture[i_picture] );
402 else if( p_destroyed_picture == NULL )
404 /* Memory size do not match, but picture index will be kept in
405 * case no other place are left */
406 p_destroyed_picture = &p_vout->p_picture[i_picture];
409 else if( (p_free_picture == NULL) &&
410 (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
412 /* Picture is empty and ready for allocation */
413 p_free_picture = &p_vout->p_picture[i_picture];
417 /* If no free picture is available, use a destroyed picture */
418 if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
420 /* No free picture or matching destroyed picture has been found, but
421 * a destroyed picture is still avalaible */
422 free( p_destroyed_picture->p_data );
423 p_free_picture = p_destroyed_picture;
429 if( p_free_picture != NULL )
431 /* Allocate memory */
434 case YUV_420_PICTURE: /* YUV 420: 1,1/4,1/4 samples per pixel */
435 i_chroma_width = i_width / 2;
436 p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
437 p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
438 p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
439 p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
441 case YUV_422_PICTURE: /* YUV 422: 1,1/2,1/2 samples per pixel */
442 i_chroma_width = i_width / 2;
443 p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
444 p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
445 p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
446 p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
448 case YUV_444_PICTURE: /* YUV 444: 1,1,1 samples per pixel */
449 i_chroma_width = i_width;
450 p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
451 p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
452 p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
453 p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
457 intf_DbgMsg("error: unknown picture type %d\n", i_type );
458 p_free_picture->p_data = NULL;
463 if( p_free_picture->p_data != NULL )
465 /* Copy picture informations, set some default values */
466 p_free_picture->i_type = i_type;
467 p_free_picture->i_status = RESERVED_PICTURE;
468 p_free_picture->i_matrix_coefficients = 1;
469 p_free_picture->i_width = i_width;
470 p_free_picture->i_height = i_height;
471 p_free_picture->i_chroma_width = i_chroma_width;
472 p_free_picture->i_display_horizontal_offset = 0;
473 p_free_picture->i_display_vertical_offset = 0;
474 p_free_picture->i_display_width = i_width;
475 p_free_picture->i_display_height = i_height;
476 p_free_picture->i_aspect_ratio = AR_SQUARE_PICTURE;
477 p_free_picture->i_refcount = 0;
481 /* Memory allocation failed : set picture as empty */
482 p_free_picture->i_type = EMPTY_PICTURE;
483 p_free_picture->i_status = FREE_PICTURE;
484 p_free_picture = NULL;
485 intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );
489 intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );
491 vlc_mutex_unlock( &p_vout->picture_lock );
492 return( p_free_picture );
495 // No free or destroyed picture could be found
496 intf_DbgMsg( "warning: heap is full\n" );
497 vlc_mutex_unlock( &p_vout->picture_lock );
501 /******************************************************************************
502 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
503 ******************************************************************************
504 * This function frees a previously reserved picture or a permanent
505 * picture. It is meant to be used when the construction of a picture aborted.
506 * Note that the picture will be destroyed even if it is linked !
507 * This function does not need locking since reserved pictures are ignored by
509 ******************************************************************************/
510 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
513 /* Check if picture status is valid */
514 if( (p_pic->i_status != RESERVED_PICTURE) &&
515 (p_pic->i_status != RESERVED_DATED_PICTURE) &&
516 (p_pic->i_status != RESERVED_DISP_PICTURE) )
518 intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );
522 p_pic->i_status = DESTROYED_PICTURE;
525 intf_DbgMsg("picture %p\n", p_pic);
529 /******************************************************************************
530 * vout_LinkPicture: increment reference counter of a picture
531 ******************************************************************************
532 * This function increment the reference counter of a picture in the video
533 * heap. It needs a lock since several producer threads can access the picture.
534 ******************************************************************************/
535 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
537 vlc_mutex_lock( &p_vout->picture_lock );
541 intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );
544 vlc_mutex_unlock( &p_vout->picture_lock );
547 /******************************************************************************
548 * vout_UnlinkPicture: decrement reference counter of a picture
549 ******************************************************************************
550 * This function decrement the reference counter of a picture in the video heap.
551 ******************************************************************************/
552 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
554 vlc_mutex_lock( &p_vout->picture_lock );
558 if( p_pic->i_refcount < 0 )
560 intf_DbgMsg("error: refcount < 0\n");
561 p_pic->i_refcount = 0;
565 if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
567 p_pic->i_status = DESTROYED_PICTURE;
571 intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );
574 vlc_mutex_unlock( &p_vout->picture_lock );
577 /******************************************************************************
578 * vout_ClearBuffer: clear a whole buffer
579 ******************************************************************************
580 * This function is called when a buffer is initialized. It clears the whole
582 ******************************************************************************/
583 void vout_ClearBuffer( vout_thread_t *p_vout, vout_buffer_t *p_buffer )
585 /* No picture previously */
586 p_buffer->i_pic_x = 0;
587 p_buffer->i_pic_y = 0;
588 p_buffer->i_pic_width = 0;
589 p_buffer->i_pic_height = 0;
591 /* The first area covers all the screen */
592 p_buffer->i_areas = 1;
593 p_buffer->pi_area_begin[0] = 0;
594 p_buffer->pi_area_end[0] = p_vout->i_height - 1;
597 /* following functions are local */
599 /******************************************************************************
600 * InitThread: initialize video output thread
601 ******************************************************************************
602 * This function is called from RunThread and performs the second step of the
603 * initialization. It returns 0 on success. Note that the thread's flag are not
604 * modified inside this function.
605 ******************************************************************************/
606 static int InitThread( vout_thread_t *p_vout )
610 *p_vout->pi_status = THREAD_START;
612 /* Initialize output method - this function issues its own error messages */
613 if( vout_SysInit( p_vout ) )
618 /* Initialize convertion tables and functions */
619 if( vout_InitTables( p_vout ) )
621 intf_ErrMsg("error: can't allocate translation tables\n");
625 /* Mark thread as running and return */
626 p_vout->b_active = 1;
627 *p_vout->pi_status = THREAD_READY;
628 intf_DbgMsg("thread ready\n");
632 /******************************************************************************
633 * RunThread: video output thread
634 ******************************************************************************
635 * Video output thread. This function does only returns when the thread is
636 * terminated. It handles the pictures arriving in the video heap and the
637 * display device events.
638 ******************************************************************************/
639 static void RunThread( vout_thread_t *p_vout)
641 int i_index; /* index in heap */
642 mtime_t current_date; /* current date */
643 mtime_t display_date; /* display date */
644 boolean_t b_display; /* display flag */
645 picture_t * p_pic; /* picture pointer */
646 spu_t * p_spu; /* subpicture pointer */
651 p_vout->b_error = InitThread( p_vout );
652 if( p_vout->b_error )
654 DestroyThread( p_vout, THREAD_ERROR );
660 * Main loop - it is not executed if an error occured during
663 while( (!p_vout->b_die) && (!p_vout->b_error) )
665 /* Initialize loop variables */
669 current_date = mdate();
672 * Find the picture to display - this operation does not need lock,
673 * since only READY_PICTUREs are handled
675 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
677 if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
679 (p_vout->p_picture[i_index].date < display_date) ) )
681 p_pic = &p_vout->p_picture[i_index];
682 display_date = p_pic->date;
689 /* Computes FPS rate */
690 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
692 if( display_date < current_date )
694 /* Picture is late: it will be destroyed and the thread will sleep and
695 * go to next picture */
696 vlc_mutex_lock( &p_vout->picture_lock );
697 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
698 intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
699 vlc_mutex_unlock( &p_vout->picture_lock );
703 else if( display_date > current_date + VOUT_DISPLAY_DELAY )
705 /* A picture is ready to be rendered, but its rendering date is
706 * far from the current one so the thread will perform an empty loop
707 * as if no picture were found. The picture state is unchanged */
714 * Find the subpicture to display - this operation does not need lock, since
715 * only READY_SPUs are handled. If no picture has been selected,
716 * display_date will depend on the spu
721 * Perform rendering, sleep and display rendered picture
723 if( p_pic ) /* picture and perhaps spu */
725 b_display = p_vout->b_active;
729 /* Set picture dimensions and clear buffer */
730 SetBufferPicture( p_vout, p_pic );
732 /* Render picture and informations */
733 RenderPicture( p_vout, p_pic );
736 RenderPictureInfo( p_vout, p_pic );
737 RenderInfo( p_vout );
741 /* Remove picture from heap */
742 vlc_mutex_lock( &p_vout->picture_lock );
743 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
744 vlc_mutex_unlock( &p_vout->picture_lock );
746 /* Render interface and spus */
747 if( b_display && p_vout->b_interface )
749 RenderInterface( p_vout );
755 RenderSubPictureUnit( p_vout, p_spu );
758 /* Remove spu from heap */
759 vlc_mutex_lock( &p_vout->spu_lock );
760 p_spu->i_status = DESTROYED_SPU;
761 vlc_mutex_unlock( &p_vout->spu_lock );
765 else if( p_spu ) /* spu alone */
767 b_display = p_vout->b_active;
772 SetBufferPicture( p_vout, NULL );
774 /* Render informations, interface and spu */
777 RenderInfo( p_vout );
779 if( p_vout->b_interface )
781 RenderInterface( p_vout );
783 RenderSubPictureUnit( p_vout, p_spu );
786 /* Remove spu from heap */
787 vlc_mutex_lock( &p_vout->spu_lock );
788 p_spu->i_status = DESTROYED_SPU;
789 vlc_mutex_unlock( &p_vout->spu_lock );
791 else /* idle screen alone */
793 //??? render on idle screen or interface change
798 * Sleep, wake up and display rendered picture
802 /* Store render time */
803 p_vout->render_time = mdate() - current_date;
806 /* Give back change lock */
807 vlc_mutex_unlock( &p_vout->change_lock );
809 /* Sleep a while or until a given date */
810 if( display_date != 0 )
812 mwait( display_date );
816 msleep( VOUT_IDLE_SLEEP );
819 /* On awakening, take back lock and send immediately picture to display,
820 * then swap buffers */
821 vlc_mutex_lock( &p_vout->change_lock );
823 intf_DbgMsg( "picture %p, spu %p\n", p_pic, p_spu );
825 if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
827 vout_SysDisplay( p_vout );
828 p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
832 * Check events and manage thread
834 if( vout_SysManage( p_vout ) | Manage( p_vout ) )
836 /* A fatal error occured, and the thread must terminate immediately,
837 * without displaying anything - setting b_error to 1 cause the
838 * immediate end of the main while() loop. */
844 * Error loop - wait until the thread destruction is requested
846 if( p_vout->b_error )
848 ErrorThread( p_vout );
853 DestroyThread( p_vout, THREAD_OVER );
854 intf_DbgMsg( "thread end\n" );
857 /******************************************************************************
858 * ErrorThread: RunThread() error loop
859 ******************************************************************************
860 * This function is called when an error occured during thread main's loop. The
861 * thread can still receive feed, but must be ready to terminate as soon as
863 ******************************************************************************/
864 static void ErrorThread( vout_thread_t *p_vout )
866 /* Wait until a `die' order */
868 while( !p_vout->b_die )
871 msleep( VOUT_IDLE_SLEEP );
875 /*******************************************************************************
876 * EndThread: thread destruction
877 *******************************************************************************
878 * This function is called when the thread ends after a sucessfull
879 * initialization. It frees all ressources allocated by InitThread.
880 *******************************************************************************/
881 static void EndThread( vout_thread_t *p_vout )
883 int i_index; /* index in heap */
887 *p_vout->pi_status = THREAD_END;
889 /* Destroy all remaining pictures and spus */
890 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
892 if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
894 free( p_vout->p_picture[i_index].p_data );
896 if( p_vout->p_spu[i_index].i_status != FREE_SPU )
898 free( p_vout->p_spu[i_index].p_data );
902 /* Destroy translation tables */
903 vout_EndTables( p_vout );
904 vout_SysEnd( p_vout );
907 /*******************************************************************************
908 * DestroyThread: thread destruction
909 *******************************************************************************
910 * This function is called when the thread ends. It frees all ressources
911 * allocated by CreateThread. Status is available at this stage.
912 *******************************************************************************/
913 static void DestroyThread( vout_thread_t *p_vout, int i_status )
915 int *pi_status; /* status adress */
917 /* Store status adress */
919 pi_status = p_vout->pi_status;
921 /* Destroy thread structures allocated by Create and InitThread */
922 vout_UnloadFont( p_vout->p_default_font );
923 vout_UnloadFont( p_vout->p_large_font );
924 vout_SysDestroy( p_vout );
926 *pi_status = i_status;
929 /*******************************************************************************
930 * Print: print simple text on a picture
931 *******************************************************************************
932 * This function will print a simple text on the picture. It is designed to
933 * print debugging or general informations.
934 *******************************************************************************/
935 void Print( vout_thread_t *p_vout, int i_x, int i_y, int i_halign, int i_valign, unsigned char *psz_text )
937 int i_text_height; /* total text height */
938 int i_text_width; /* total text width */
940 /* Update upper left coordinates according to alignment */
941 vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
944 case 0: /* centered */
945 i_x -= i_text_width / 2;
947 case 1: /* right aligned */
953 case 0: /* centered */
954 i_y -= i_text_height / 2;
956 case 1: /* bottom aligned */
957 i_y -= i_text_height;
962 if( (i_y < 0) || (i_y + i_text_height > p_vout->i_height) ||
963 (i_x < 0) || (i_x + i_text_width > p_vout->i_width) )
965 intf_DbgMsg("'%s' would print outside the screen\n", psz_text);
969 /* Set area and print text */
970 SetBufferArea( p_vout, i_x, i_y, i_text_width, i_text_height );
971 vout_Print( p_vout->p_default_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
972 i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
973 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
974 0xffffffff, 0x00000000, 0x00000000, TRANSPARENT_TEXT, psz_text );
977 /*******************************************************************************
978 * SetBufferArea: activate an area in current buffer
979 *******************************************************************************
980 * This function is called when something is rendered on the current buffer.
981 * It set the area as active and prepare it to be cleared on next rendering.
982 * Pay attention to the fact that in this functions, i_h is in fact the end y
983 * coordinate of the new area.
984 *******************************************************************************/
985 static void SetBufferArea( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h )
987 vout_buffer_t * p_buffer; /* current buffer */
988 int i_area_begin, i_area_end; /* area vertical extension */
989 int i_area, i_area_copy; /* area index */
990 int i_area_shift; /* shift distance for areas */
992 /* Choose buffer and modify h to end of area position */
993 p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ];
997 * Remove part of the area which is inside the picture - this is done
998 * by calling again SetBufferArea with the correct areas dimensions.
1000 if( (i_x >= p_buffer->i_pic_x) && (i_x + i_w <= p_buffer->i_pic_x + p_buffer->i_pic_width) )
1002 i_area_begin = p_buffer->i_pic_y;
1003 i_area_end = i_area_begin + p_buffer->i_pic_height - 1;
1005 if( ((i_y >= i_area_begin) && (i_y <= i_area_end)) ||
1006 ((i_h >= i_area_begin) && (i_h <= i_area_end)) ||
1007 ((i_y < i_area_begin) && (i_h > i_area_end)) )
1009 /* Keep the stripe above the picture, if any */
1010 if( i_y < i_area_begin )
1012 SetBufferArea( p_vout, i_x, i_y, i_w, i_area_begin - i_y );
1014 /* Keep the stripe below the picture, if any */
1015 if( i_h > i_area_end )
1017 SetBufferArea( p_vout, i_x, i_area_end, i_w, i_h - i_area_end );
1023 /* Skip some extensions until interesting areas */
1025 (i_area < p_buffer->i_areas) &&
1026 (p_buffer->pi_area_end[i_area] + 1 <= i_y);
1032 if( i_area == p_buffer->i_areas )
1034 /* New area is below all existing ones: just add it at the end of the
1035 * array, if possible - else, append it to the last one */
1036 if( i_area < VOUT_MAX_AREAS )
1038 p_buffer->pi_area_begin[i_area] = i_y;
1039 p_buffer->pi_area_end[i_area] = i_h;
1040 p_buffer->i_areas++;
1045 intf_DbgMsg("areas overflow\n");
1047 p_buffer->pi_area_end[VOUT_MAX_AREAS - 1] = i_h;
1052 i_area_begin = p_buffer->pi_area_begin[i_area];
1053 i_area_end = p_buffer->pi_area_end[i_area];
1055 if( i_y < i_area_begin )
1057 if( i_h >= i_area_begin - 1 )
1059 /* Extend area above */
1060 p_buffer->pi_area_begin[i_area] = i_y;
1064 /* Create a new area above : merge last area if overflow, then
1065 * move all old areas down */
1066 if( p_buffer->i_areas == VOUT_MAX_AREAS )
1069 intf_DbgMsg("areas overflow\n");
1071 p_buffer->pi_area_end[VOUT_MAX_AREAS - 2] = p_buffer->pi_area_end[VOUT_MAX_AREAS - 1];
1075 p_buffer->i_areas++;
1077 for( i_area_copy = p_buffer->i_areas - 1; i_area_copy > i_area; i_area_copy++ )
1079 p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy - 1];
1080 p_buffer->pi_area_end[i_area_copy] = p_buffer->pi_area_end[i_area_copy - 1];
1082 p_buffer->pi_area_begin[i_area] = i_y;
1083 p_buffer->pi_area_end[i_area] = i_h;
1087 if( i_h > i_area_end )
1089 /* Find further areas which can be merged with the new one */
1090 for( i_area_copy = i_area + 1;
1091 (i_area_copy < p_buffer->i_areas) &&
1092 (p_buffer->pi_area_begin[i_area] <= i_h);
1099 if( i_area_copy != i_area )
1101 /* Merge with last possible areas */
1102 p_buffer->pi_area_end[i_area] = MAX( i_h, p_buffer->pi_area_end[i_area_copy] );
1104 /* Shift lower areas upward */
1105 i_area_shift = i_area_copy - i_area;
1106 p_buffer->i_areas -= i_area_shift;
1107 for( i_area_copy = i_area + 1; i_area_copy < p_buffer->i_areas; i_area_copy++ )
1109 p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy + i_area_shift];
1110 p_buffer->pi_area_end[i_area_copy] = p_buffer->pi_area_end[i_area_copy + i_area_shift];
1115 /* Extend area below */
1116 p_buffer->pi_area_end[i_area] = i_h;
1122 /*******************************************************************************
1123 * SetBufferPicture: clear buffer and set picture area
1124 *******************************************************************************
1125 * This function is called before any rendering. It clears the current
1126 * rendering buffer and set the new picture area. If the picture pointer is
1127 * NULL, then no picture area is defined. Floating operations are avoided since
1128 * some MMX calculations may follow.
1129 *******************************************************************************/
1130 static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic )
1132 vout_buffer_t * p_buffer; /* current buffer */
1133 int i_pic_x, i_pic_y; /* picture position */
1134 int i_pic_width, i_pic_height; /* picture dimensions */
1135 int i_old_pic_y, i_old_pic_height; /* old picture area */
1136 int i_vout_width, i_vout_height; /* display dimensions */
1137 int i_area; /* area index */
1138 int i_data_index; /* area data index */
1139 int i_data_size; /* area data size, in 256 bytes blocs */
1140 u64 * p_data; /* area data */
1142 /* Choose buffer and set display dimensions */
1143 p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ];
1144 i_vout_width = p_vout->i_width;
1145 i_vout_height = p_vout->i_height;
1148 * Computes new picture size
1152 /* Try horizontal scaling first */
1153 i_pic_width = ( p_vout->b_scale || (p_pic->i_width > i_vout_width)) ?
1154 i_vout_width : p_pic->i_width;
1155 i_pic_width = i_pic_width / 16 * 16; //?? currently, width must be multiple of 16
1156 switch( p_pic->i_aspect_ratio )
1158 case AR_3_4_PICTURE:
1159 i_pic_height = i_pic_width * 3 / 4;
1161 case AR_16_9_PICTURE:
1162 i_pic_height = i_pic_width * 9 / 16;
1164 case AR_221_1_PICTURE:
1165 i_pic_height = i_pic_width * 100 / 221;
1167 case AR_SQUARE_PICTURE:
1169 i_pic_height = p_pic->i_height * i_pic_width / p_pic->i_width;
1173 /* If picture dimensions using horizontal scaling are too large, use
1174 * vertical scaling */
1175 if( i_pic_height > i_vout_height )
1177 i_pic_height = ( p_vout->b_scale || (p_pic->i_height > i_vout_height)) ?
1178 i_vout_height : p_pic->i_height;
1179 switch( p_pic->i_aspect_ratio )
1181 case AR_3_4_PICTURE:
1182 i_pic_width = i_pic_height * 4 / 3;
1184 case AR_16_9_PICTURE:
1185 i_pic_width = i_pic_height * 16 / 9;
1187 case AR_221_1_PICTURE:
1188 i_pic_width = i_pic_height * 221 / 100;
1190 case AR_SQUARE_PICTURE:
1192 i_pic_width = p_pic->i_width * i_pic_height / p_pic->i_height;
1195 i_pic_width = i_pic_width / 16 * 16; //?? currently, width must be multiple of 16
1198 /* Set picture position */
1199 i_pic_x = (p_vout->i_width - i_pic_width) / 2;
1200 i_pic_y = (p_vout->i_height - i_pic_height) / 2;
1204 /* No picture: size is 0 */
1212 * Set new picture size - if is is smaller than the previous one, clear
1213 * around it. Since picture are centered, only their size is tested.
1215 if( (p_buffer->i_pic_width > i_pic_width) || (p_buffer->i_pic_height > i_pic_height) )
1217 i_old_pic_y = p_buffer->i_pic_y;
1218 i_old_pic_height = p_buffer->i_pic_height;
1219 p_buffer->i_pic_x = i_pic_x;
1220 p_buffer->i_pic_y = i_pic_y;
1221 p_buffer->i_pic_width = i_pic_width;
1222 p_buffer->i_pic_height = i_pic_height;
1223 SetBufferArea( p_vout, 0, i_old_pic_y, p_vout->i_width, i_old_pic_height );
1227 p_buffer->i_pic_x = i_pic_x;
1228 p_buffer->i_pic_y = i_pic_y;
1229 p_buffer->i_pic_width = i_pic_width;
1230 p_buffer->i_pic_height = i_pic_height;
1236 for( i_area = 0; i_area < p_buffer->i_areas; i_area++ )
1239 intf_DbgMsg("clearing picture %p area: %d-%d\n", p_pic,
1240 p_buffer->pi_area_begin[i_area], p_buffer->pi_area_end[i_area]);
1242 p_data = (u64*) (p_buffer->p_data + p_vout->i_bytes_per_line * p_buffer->pi_area_begin[i_area]);
1243 i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) *
1244 p_vout->i_bytes_per_line / 256;
1245 for( i_data_index = 0; i_data_index < i_data_size; i_data_index++ )
1247 /* Clear 256 bytes block */
1248 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1249 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1250 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1251 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1252 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1253 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1254 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1255 *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
1257 i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) *
1258 p_vout->i_bytes_per_line % 256 / 4;
1259 for( i_data_index = 0; i_data_index < i_data_size; i_data_index++ )
1261 /* Clear remaining 4 bytes blocks */
1269 p_buffer->i_areas = 0;
1272 /******************************************************************************
1273 * RenderPicture: render a picture
1274 ******************************************************************************
1275 * This function convert a picture from a video heap to a pixel-encoded image
1276 * and copy it to the current rendering buffer. No lock is required, since the
1277 * rendered picture has been determined as existant, and will only be destroyed
1278 * by the vout thread later.
1279 ******************************************************************************/
1280 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
1282 vout_buffer_t * p_buffer; /* rendering buffer */
1283 byte_t * p_convert_dst; /* convertion destination */
1284 int i_width, i_height, i_eol, i_pic_eol, i_scale; /* ?? tmp variables*/
1286 /* Get and set rendering informations */
1287 p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ];
1288 p_vout->last_picture_date = p_pic->date;
1289 p_convert_dst = p_buffer->p_data + p_buffer->i_pic_x * p_vout->i_bytes_per_pixel +
1290 p_buffer->i_pic_y * p_vout->i_bytes_per_line;
1292 // ?? temporary section: rebuild aspect scale from size informations.
1293 // ?? when definitive convertion prototype will be used, those info will
1294 // ?? no longer be required
1295 i_width = MIN( p_pic->i_width, p_buffer->i_pic_width );
1296 i_eol = p_pic->i_width - i_width / 16 * 16;
1297 i_pic_eol = p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel - i_width;
1298 if( p_pic->i_height == p_buffer->i_pic_height )
1304 i_scale = p_pic->i_height / (p_pic->i_height - p_buffer->i_pic_height);
1306 i_eol = p_pic->i_width - p_buffer->i_pic_width;
1307 i_height = p_pic->i_height * i_width / p_pic->i_width;
1308 // ?? end of temporary code
1311 * Choose appropriate rendering function and render picture
1313 switch( p_pic->i_type )
1315 case YUV_420_PICTURE:
1316 p_vout->p_ConvertYUV420( p_vout, p_convert_dst,
1317 p_pic->p_y, p_pic->p_u, p_pic->p_v,
1318 i_width, i_height, i_eol, i_pic_eol, i_scale,
1319 p_pic->i_matrix_coefficients );
1321 case YUV_422_PICTURE:
1322 p_vout->p_ConvertYUV422( p_vout, p_convert_dst,
1323 p_pic->p_y, p_pic->p_u, p_pic->p_v,
1324 i_width, i_height, i_eol, i_pic_eol, i_scale,
1325 p_pic->i_matrix_coefficients );
1327 case YUV_444_PICTURE:
1328 p_vout->p_ConvertYUV444( p_vout, p_convert_dst,
1329 p_pic->p_y, p_pic->p_u, p_pic->p_v,
1330 i_width, i_height, i_eol, i_pic_eol, i_scale,
1331 p_pic->i_matrix_coefficients );
1335 intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
1341 /******************************************************************************
1342 * RenderPictureInfo: print additionnal informations on a picture
1343 ******************************************************************************
1344 * This function will print informations such as fps and other picture
1345 * dependant informations.
1346 ******************************************************************************/
1347 static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
1349 #if defined(STATS) || defined(DEBUG)
1350 char psz_buffer[256]; /* string buffer */
1355 * Print FPS rate in upper right corner
1357 if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
1359 sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
1360 ( p_vout->p_fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
1361 p_vout->p_fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );
1362 Print( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
1366 * Print frames count and loop time in upper left corner
1368 sprintf( psz_buffer, "%ld frames rendering: %ld us",
1369 (long) p_vout->c_fps_samples, (long) p_vout->render_time );
1370 Print( p_vout, 0, 0, -1, -1, psz_buffer );
1375 * Print picture information in lower right corner
1377 sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
1378 (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
1379 ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
1380 ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
1381 p_pic->i_width, p_pic->i_height,
1382 p_pic->i_display_width, p_pic->i_display_height,
1383 p_pic->i_display_horizontal_offset, p_pic->i_display_vertical_offset,
1384 (p_pic->i_aspect_ratio == AR_SQUARE_PICTURE) ? "sq" :
1385 ((p_pic->i_aspect_ratio == AR_3_4_PICTURE) ? "4:3" :
1386 ((p_pic->i_aspect_ratio == AR_16_9_PICTURE) ? "16:9" :
1387 ((p_pic->i_aspect_ratio == AR_221_1_PICTURE) ? "2.21:1" : "ukn-ar" ))),
1388 p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_width,
1389 p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height,
1390 p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_x,
1391 p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y );
1392 Print( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );
1396 /******************************************************************************
1397 * RenderIdle: render idle picture
1398 ******************************************************************************
1399 * This function will print something on the screen.
1400 ******************************************************************************/
1401 static void RenderIdle( vout_thread_t *p_vout )
1404 Print( p_vout, p_vout->i_width / 2, p_vout->i_height / 2, 0, 0,
1408 /******************************************************************************
1409 * RenderInfo: render additionnal informations
1410 ******************************************************************************
1411 * This function render informations which do not depend of the current picture
1413 ******************************************************************************/
1414 static void RenderInfo( vout_thread_t *p_vout )
1417 char psz_buffer[256]; /* string buffer */
1418 int i_ready_pic = 0; /* ready pictures */
1419 int i_reserved_pic = 0; /* reserved pictures */
1420 int i_picture; /* picture index */
1425 * Print thread state in lower left corner
1427 for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
1429 switch( p_vout->p_picture[i_picture].i_status )
1431 case RESERVED_PICTURE:
1432 case RESERVED_DATED_PICTURE:
1433 case RESERVED_DISP_PICTURE:
1441 sprintf( psz_buffer, "pic: %d/%d/%d",
1442 i_reserved_pic, i_ready_pic, VOUT_MAX_PICTURES );
1443 Print( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );
1447 /*******************************************************************************
1448 * RenderSubPictureUnit: render an spu
1449 *******************************************************************************
1450 * This function render a sub picture unit.
1451 *******************************************************************************/
1452 static void RenderSubPictureUnit( vout_thread_t *p_vout, spu_t *p_spu )
1457 /*******************************************************************************
1458 * RenderInterface: render the interface
1459 *******************************************************************************
1460 * This function render the interface, if any.
1461 * ?? this is obviously only a temporary interface !
1462 *******************************************************************************/
1463 static void RenderInterface( vout_thread_t *p_vout )
1465 int i_height, i_text_height; /* total and text height */
1466 int i_width_1, i_width_2; /* text width */
1467 int i_byte; /* byte index */
1468 const char *psz_text_1 = "[1-9] Channel [i]nfo [c]olor [g/G]amma";
1469 const char *psz_text_2 = "[+/-] Volume [m]ute [s]caling [Q]uit";
1472 vout_TextSize( p_vout->p_large_font, OUTLINED_TEXT | TRANSPARENT_TEXT, psz_text_1, &i_width_1, &i_height );
1473 vout_TextSize( p_vout->p_large_font, OUTLINED_TEXT | TRANSPARENT_TEXT, psz_text_2, &i_width_2, &i_text_height );
1474 i_height += i_text_height;
1476 /* Render background - effective background color will depend of the screen
1478 for( i_byte = (p_vout->i_height - i_height) * p_vout->i_bytes_per_line;
1479 i_byte < p_vout->i_height * p_vout->i_bytes_per_line;
1482 p_vout->p_buffer[ p_vout->i_buffer_index ].p_data[ i_byte ] = 0x33;
1485 /* Render text, if not larger than screen */
1486 if( i_width_1 < p_vout->i_width )
1488 vout_Print( p_vout->p_large_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1489 (p_vout->i_height - i_height) * p_vout->i_bytes_per_line,
1490 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1491 0xffffffff, 0x00000000, 0x00000000,
1492 OUTLINED_TEXT | TRANSPARENT_TEXT, psz_text_1 );
1494 if( i_width_2 < p_vout->i_width )
1496 vout_Print( p_vout->p_large_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1497 (p_vout->i_height - i_height + i_text_height) * p_vout->i_bytes_per_line,
1498 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1499 0xffffffff, 0x00000000, 0x00000000,
1500 OUTLINED_TEXT | TRANSPARENT_TEXT, psz_text_2 );
1503 /* Activate modified area */
1504 SetBufferArea( p_vout, 0, p_vout->i_height - i_height, p_vout->i_width, i_height );
1507 /******************************************************************************
1508 * Manage: manage thread
1509 ******************************************************************************
1510 * This function will handle changes in thread configuration.
1511 ******************************************************************************/
1512 static int Manage( vout_thread_t *p_vout )
1515 if( p_vout->i_changes )
1517 intf_DbgMsg("changes: 0x%x (no display: 0x%x)\n", p_vout->i_changes,
1518 p_vout->i_changes & VOUT_NODISPLAY_CHANGE );
1522 /* On gamma or grayscale change, rebuild tables */
1523 if( p_vout->i_changes & (VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE) )
1525 vout_ResetTables( p_vout );
1528 /* Clear changes flags which does not need management or have been handled */
1529 p_vout->i_changes &= ~(VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE |
1530 VOUT_INFO_CHANGE | VOUT_INTF_CHANGE | VOUT_SCALE_CHANGE );
1532 /* Detect unauthorized changes */
1533 if( p_vout->i_changes )
1535 /* Some changes were not acknowledged by vout_SysManage or this function,
1536 * it means they should not be authorized */
1537 intf_ErrMsg( "error: unauthorized changes in the video output thread\n" );