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 Print ( vout_thread_t *p_vout, int i_x, int i_y, int i_halign, int i_valign, unsigned char *psz_text );
38 static void RenderBlank ( vout_thread_t *p_vout );
39 static int RenderPicture ( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank );
40 static int RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank );
41 static int RenderIdle ( vout_thread_t *p_vout, boolean_t b_blank );
42 static int RenderInfo ( vout_thread_t *p_vout, boolean_t b_balnk );
43 static int Manage ( vout_thread_t *p_vout );
45 /******************************************************************************
46 * vout_CreateThread: creates a new video output thread
47 ******************************************************************************
48 * This function creates a new video output thread, and returns a pointer
49 * to its description. On error, it returns NULL.
50 * If pi_status is NULL, then the function will block until the thread is ready.
51 * If not, it will be updated using one of the THREAD_* constants.
52 ******************************************************************************/
53 vout_thread_t * vout_CreateThread ( char *psz_display, int i_root_window,
54 int i_width, int i_height, int *pi_status )
56 vout_thread_t * p_vout; /* thread descriptor */
57 int i_status; /* thread status */
59 /* Allocate descriptor */
61 p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
64 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
68 /* Initialize thread properties */
72 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
73 *p_vout->pi_status = THREAD_CREATE;
75 /* Initialize some fields used by the system-dependant method - these fields will
76 * probably be modified by the method, and are only preferences */
77 p_vout->b_grayscale = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
78 VOUT_GRAYSCALE_DEFAULT );
79 p_vout->i_width = i_width;
80 p_vout->i_height = i_height;
81 p_vout->i_bytes_per_line = i_width * 2;
82 p_vout->i_screen_depth = 15;
83 p_vout->i_bytes_per_pixel = 2;
84 p_vout->f_gamma = VOUT_GAMMA;
90 intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
91 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
92 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
94 /* Create and initialize system-dependant method - this function issues its
95 * own error messages */
96 if( vout_SysCreate( p_vout, psz_display, i_root_window ) )
101 intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
102 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
103 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
106 p_vout->p_default_font = vout_LoadFont( VOUT_DEFAULT_FONT );
107 if( p_vout->p_default_font == NULL )
109 vout_SysDestroy( p_vout );
113 p_vout->p_large_font = vout_LoadFont( VOUT_LARGE_FONT );
114 if( p_vout->p_large_font == NULL )
116 vout_UnloadFont( p_vout->p_default_font );
117 vout_SysDestroy( p_vout );
123 /* Initialize statistics fields */
124 p_vout->render_time = 0;
125 p_vout->c_fps_samples = 0;
128 /* Initialize running properties */
129 p_vout->i_changes = 0;
130 p_vout->last_picture_date = 0;
131 p_vout->last_display_date = 0;
133 /* Create thread and set locks */
134 vlc_mutex_init( &p_vout->picture_lock );
135 vlc_mutex_init( &p_vout->subtitle_lock );
136 vlc_mutex_init( &p_vout->change_lock );
137 vlc_mutex_lock( &p_vout->change_lock );
138 if( vlc_thread_create( &p_vout->thread_id, "video output", (void *) RunThread, (void *) p_vout) )
140 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
141 vout_UnloadFont( p_vout->p_default_font );
142 vout_UnloadFont( p_vout->p_large_font );
143 vout_SysDestroy( p_vout );
148 intf_Msg("Video display initialized (%dx%d, %d bpp)\n",
149 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );
151 /* If status is NULL, wait until the thread is created */
152 if( pi_status == NULL )
156 msleep( THREAD_SLEEP );
157 }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
158 && (i_status != THREAD_FATAL) );
159 if( i_status != THREAD_READY )
167 /******************************************************************************
168 * vout_DestroyThread: destroys a previously created thread
169 ******************************************************************************
170 * Destroy a terminated thread.
171 * The function will request a destruction of the specified thread. If pi_error
172 * is NULL, it will return once the thread is destroyed. Else, it will be
173 * update using one of the THREAD_* constants.
174 ******************************************************************************/
175 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
177 int i_status; /* thread status */
181 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
182 *p_vout->pi_status = THREAD_DESTROY;
184 /* Request thread destruction */
187 /* If status is NULL, wait until thread has been destroyed */
188 if( pi_status == NULL )
192 msleep( THREAD_SLEEP );
193 }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
194 && (i_status != THREAD_FATAL) );
198 /******************************************************************************
199 * vout_DisplaySubtitle: display a subtitle
200 ******************************************************************************
201 * Remove the reservation flag of a subtitle, which will cause it to be ready for
202 * display. The picture does not need to be locked, since it is ignored by
203 * the output thread if is reserved.
204 ******************************************************************************/
205 void vout_DisplaySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
208 char psz_begin_date[MSTRTIME_MAX_SIZE]; /* buffer for date string */
209 char psz_end_date[MSTRTIME_MAX_SIZE]; /* buffer for date string */
213 /* Check if status is valid */
214 if( p_sub->i_status != RESERVED_SUBTITLE )
216 intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );
220 /* Remove reservation flag */
221 p_sub->i_status = READY_SUBTITLE;
224 /* Send subtitle informations */
225 intf_DbgMsg("subtitle %p: type=%d, begin date=%s, end date=%s\n", p_sub, p_sub->i_type,
226 mstrtime( psz_begin_date, p_sub->begin_date ),
227 mstrtime( psz_end_date, p_sub->end_date ) );
231 /******************************************************************************
232 * vout_CreateSubtitle: allocate a subtitle in the video output heap.
233 ******************************************************************************
234 * This function create a reserved subtitle in the video output heap.
235 * A null pointer is returned if the function fails. This method provides an
236 * already allocated zone of memory in the subtitle data fields. It needs locking
237 * since several pictures can be created by several producers threads.
238 ******************************************************************************/
239 subtitle_t *vout_CreateSubtitle( vout_thread_t *p_vout, int i_type,
245 /******************************************************************************
246 * vout_DestroySubtitle: remove a permanent or reserved subtitle from the heap
247 ******************************************************************************
248 * This function frees a previously reserved subtitle.
249 * It is meant to be used when the construction of a picture aborted.
250 * This function does not need locking since reserved subtitles are ignored by
252 ******************************************************************************/
253 void vout_DestroySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
256 /* Check if subtitle status is valid */
257 if( p_sub->i_status != RESERVED_SUBTITLE )
259 intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );
263 p_sub->i_status = DESTROYED_SUBTITLE;
266 intf_DbgMsg("subtitle %p\n", p_sub);
270 /******************************************************************************
271 * vout_DisplayPicture: display a picture
272 ******************************************************************************
273 * Remove the reservation flag of a picture, which will cause it to be ready for
274 * display. The picture won't be displayed until vout_DatePicture has been
276 ******************************************************************************/
277 void vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
279 vlc_mutex_lock( &p_vout->picture_lock );
280 switch( p_pic->i_status )
282 case RESERVED_PICTURE:
283 p_pic->i_status = RESERVED_DISP_PICTURE;
285 case RESERVED_DATED_PICTURE:
286 p_pic->i_status = READY_PICTURE;
290 intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );
296 intf_DbgMsg("picture %p\n", p_pic );
299 vlc_mutex_unlock( &p_vout->picture_lock );
302 /******************************************************************************
303 * vout_DatePicture: date a picture
304 ******************************************************************************
305 * Remove the reservation flag of a picture, which will cause it to be ready for
306 * display. The picture won't be displayed until vout_DisplayPicture has been
308 ******************************************************************************/
309 void vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
311 vlc_mutex_lock( &p_vout->picture_lock );
313 switch( p_pic->i_status )
315 case RESERVED_PICTURE:
316 p_pic->i_status = RESERVED_DATED_PICTURE;
318 case RESERVED_DISP_PICTURE:
319 p_pic->i_status = READY_PICTURE;
323 intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );
329 intf_DbgMsg("picture %p\n", p_pic);
332 vlc_mutex_unlock( &p_vout->picture_lock );
335 /******************************************************************************
336 * vout_CreatePicture: allocate a picture in the video output heap.
337 ******************************************************************************
338 * This function create a reserved image in the video output heap.
339 * A null pointer is returned if the function fails. This method provides an
340 * already allocated zone of memory in the picture data fields. It needs locking
341 * since several pictures can be created by several producers threads.
342 ******************************************************************************/
343 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
344 int i_width, int i_height )
346 int i_picture; /* picture index */
347 int i_chroma_width = 0; /* chroma width */
348 picture_t * p_free_picture = NULL; /* first free picture */
349 picture_t * p_destroyed_picture = NULL; /* first destroyed picture */
352 vlc_mutex_lock( &p_vout->picture_lock );
355 * Look for an empty place
358 i_picture < VOUT_MAX_PICTURES;
361 if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
363 /* Picture is marked for destruction, but is still allocated - note
364 * that if width and type are the same for two pictures, chroma_width
365 * should also be the same */
366 if( (p_vout->p_picture[i_picture].i_type == i_type) &&
367 (p_vout->p_picture[i_picture].i_height == i_height) &&
368 (p_vout->p_picture[i_picture].i_width == i_width) )
370 /* Memory size do match : memory will not be reallocated, and function
371 * can end immediately - this is the best possible case, since no
372 * memory allocation needs to be done */
373 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
375 intf_DbgMsg("picture %p (in destroyed picture slot)\n",
376 &p_vout->p_picture[i_picture] );
378 vlc_mutex_unlock( &p_vout->picture_lock );
379 return( &p_vout->p_picture[i_picture] );
381 else if( p_destroyed_picture == NULL )
383 /* Memory size do not match, but picture index will be kept in
384 * case no other place are left */
385 p_destroyed_picture = &p_vout->p_picture[i_picture];
388 else if( (p_free_picture == NULL) &&
389 (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
391 /* Picture is empty and ready for allocation */
392 p_free_picture = &p_vout->p_picture[i_picture];
396 /* If no free picture is available, use a destroyed picture */
397 if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
399 /* No free picture or matching destroyed picture has been found, but
400 * a destroyed picture is still avalaible */
401 free( p_destroyed_picture->p_data );
402 p_free_picture = p_destroyed_picture;
408 if( p_free_picture != NULL )
410 /* Allocate memory */
413 case YUV_420_PICTURE: /* YUV 420: 1,1/4,1/4 samples per pixel */
414 i_chroma_width = i_width / 2;
415 p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
416 p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
417 p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
418 p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
420 case YUV_422_PICTURE: /* YUV 422: 1,1/2,1/2 samples per pixel */
421 i_chroma_width = i_width / 2;
422 p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
423 p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
424 p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
425 p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
427 case YUV_444_PICTURE: /* YUV 444: 1,1,1 samples per pixel */
428 i_chroma_width = i_width;
429 p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
430 p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
431 p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
432 p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
436 intf_DbgMsg("error: unknown picture type %d\n", i_type );
437 p_free_picture->p_data = NULL;
442 if( p_free_picture->p_data != NULL )
444 /* Copy picture informations, set some default values */
445 p_free_picture->i_type = i_type;
446 p_free_picture->i_status = RESERVED_PICTURE;
447 p_free_picture->i_matrix_coefficients = 1;
448 p_free_picture->i_width = i_width;
449 p_free_picture->i_height = i_height;
450 p_free_picture->i_chroma_width = i_chroma_width;
451 p_free_picture->i_display_horizontal_offset = 0;
452 p_free_picture->i_display_vertical_offset = 0;
453 p_free_picture->i_display_width = i_width;
454 p_free_picture->i_display_height = i_height;
455 p_free_picture->i_aspect_ratio = AR_SQUARE_PICTURE;
456 p_free_picture->i_refcount = 0;
460 /* Memory allocation failed : set picture as empty */
461 p_free_picture->i_type = EMPTY_PICTURE;
462 p_free_picture->i_status = FREE_PICTURE;
463 p_free_picture = NULL;
464 intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );
468 intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );
470 vlc_mutex_unlock( &p_vout->picture_lock );
471 return( p_free_picture );
474 // No free or destroyed picture could be found
475 intf_DbgMsg( "warning: heap is full\n" );
476 vlc_mutex_unlock( &p_vout->picture_lock );
480 /******************************************************************************
481 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
482 ******************************************************************************
483 * This function frees a previously reserved picture or a permanent
484 * picture. It is meant to be used when the construction of a picture aborted.
485 * Note that the picture will be destroyed even if it is linked !
486 * This function does not need locking since reserved pictures are ignored by
488 ******************************************************************************/
489 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
492 /* Check if picture status is valid */
493 if( (p_pic->i_status != RESERVED_PICTURE) &&
494 (p_pic->i_status != RESERVED_DATED_PICTURE) &&
495 (p_pic->i_status != RESERVED_DISP_PICTURE) )
497 intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );
501 p_pic->i_status = DESTROYED_PICTURE;
504 intf_DbgMsg("picture %p\n", p_pic);
508 /******************************************************************************
509 * vout_LinkPicture: increment reference counter of a picture
510 ******************************************************************************
511 * This function increment the reference counter of a picture in the video
512 * heap. It needs a lock since several producer threads can access the picture.
513 ******************************************************************************/
514 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
516 vlc_mutex_lock( &p_vout->picture_lock );
520 intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );
523 vlc_mutex_unlock( &p_vout->picture_lock );
526 /******************************************************************************
527 * vout_UnlinkPicture: decrement reference counter of a picture
528 ******************************************************************************
529 * This function decrement the reference counter of a picture in the video heap.
530 ******************************************************************************/
531 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
533 vlc_mutex_lock( &p_vout->picture_lock );
537 if( p_pic->i_refcount < 0 )
539 intf_DbgMsg("error: refcount < 0\n");
540 p_pic->i_refcount = 0;
544 if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
546 p_pic->i_status = DESTROYED_PICTURE;
550 intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );
553 vlc_mutex_unlock( &p_vout->picture_lock );
556 /* following functions are local */
558 /******************************************************************************
559 * InitThread: initialize video output thread
560 ******************************************************************************
561 * This function is called from RunThread and performs the second step of the
562 * initialization. It returns 0 on success. Note that the thread's flag are not
563 * modified inside this function.
564 ******************************************************************************/
565 static int InitThread( vout_thread_t *p_vout )
567 int i_index; /* generic index */
571 *p_vout->pi_status = THREAD_START;
573 /* Initialize output method - this function issues its own error messages */
574 if( vout_SysInit( p_vout ) )
576 *p_vout->pi_status = THREAD_ERROR;
580 /* Initialize pictures and subtitles */
581 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
583 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
584 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
585 p_vout->p_subtitle[i_index].i_type = EMPTY_SUBTITLE;
586 p_vout->p_subtitle[i_index].i_status= FREE_SUBTITLE;
589 /* Initialize convertion tables and functions */
590 if( vout_InitTables( p_vout ) )
592 intf_ErrMsg("error: can't allocate translation tables\n");
596 /* Mark thread as running and return */
597 p_vout->b_active = 1;
598 *p_vout->pi_status = THREAD_READY;
599 intf_DbgMsg("thread ready\n");
603 /******************************************************************************
604 * RunThread: video output thread
605 ******************************************************************************
606 * Video output thread. This function does only returns when the thread is
607 * terminated. It handles the pictures arriving in the video heap and the
608 * display device events.
609 ******************************************************************************/
610 static void RunThread( vout_thread_t *p_vout)
612 int i_picture; /* picture index */
613 mtime_t current_date; /* current date */
614 mtime_t pic_date = 0; /* picture date */
615 boolean_t b_display; /* display flag */
616 picture_t * p_pic; /* picture pointer */
619 * Initialize thread and free configuration
621 p_vout->b_error = InitThread( p_vout );
622 if( p_vout->b_error )
625 free( p_vout ); /* destroy descriptor */
631 * Main loop - it is not executed if an error occured during
634 while( (!p_vout->b_die) && (!p_vout->b_error) )
637 * Find the picture to display - this operation does not need lock,
638 * since only READY_PICTURES are handled
641 current_date = mdate();
642 for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
644 if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
646 (p_vout->p_picture[i_picture].date < pic_date) ) )
648 p_pic = &p_vout->p_picture[i_picture];
649 pic_date = p_pic->date;
654 * Render picture if any
659 /* Computes FPS rate */
660 p_vout->fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = pic_date;
662 if( pic_date < current_date )
664 /* Picture is late: it will be destroyed and the thread will sleep and
665 * go to next picture */
666 vlc_mutex_lock( &p_vout->picture_lock );
667 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
669 intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
671 vlc_mutex_unlock( &p_vout->picture_lock );
674 else if( pic_date > current_date + VOUT_DISPLAY_DELAY )
676 /* A picture is ready to be rendered, but its rendering date is
677 * far from the current one so the thread will perform an empty loop
678 * as if no picture were found. The picture state is unchanged */
684 * Perform rendering, sleep and display rendered picture
688 /* A picture is ready to be displayed : render it */
689 if( p_vout->b_active )
691 b_display = RenderPicture( p_vout, p_pic, 1 );
694 b_display |= RenderPictureInfo( p_vout, p_pic, b_display );
695 b_display |= RenderInfo( p_vout, b_display );
703 /* Remove picture from heap */
704 vlc_mutex_lock( &p_vout->picture_lock );
705 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
706 vlc_mutex_unlock( &p_vout->picture_lock );
710 /* No picture. However, an idle screen may be ready to display */
711 if( p_vout->b_active )
713 b_display = RenderIdle( p_vout, 1 );
716 b_display |= RenderInfo( p_vout, b_display );
725 /* Give back change lock */
726 vlc_mutex_unlock( &p_vout->change_lock );
728 /* Sleep a while or until a given date */
735 msleep( VOUT_IDLE_SLEEP );
738 /* On awakening, take back lock and send immediately picture to display */
739 vlc_mutex_lock( &p_vout->change_lock );
740 if( b_display && p_vout->b_active &&
741 !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
743 vout_SysDisplay( p_vout );
747 * Check events and manage thread
749 if( vout_SysManage( p_vout ) | Manage( p_vout ) )
751 /* A fatal error occured, and the thread must terminate immediately,
752 * without displaying anything - setting b_error to 1 cause the
753 * immediate end of the main while() loop. */
761 if( p_vout->b_error )
763 ErrorThread( p_vout );
768 intf_DbgMsg( "thread end\n" );
771 /******************************************************************************
772 * ErrorThread: RunThread() error loop
773 ******************************************************************************
774 * This function is called when an error occured during thread main's loop. The
775 * thread can still receive feed, but must be ready to terminate as soon as
777 ******************************************************************************/
778 static void ErrorThread( vout_thread_t *p_vout )
780 /* Wait until a `die' order */
782 while( !p_vout->b_die )
785 msleep( VOUT_IDLE_SLEEP );
789 /******************************************************************************
790 * EndThread: thread destruction
791 ******************************************************************************
792 * This function is called when the thread ends after a sucessfull
794 ******************************************************************************/
795 static void EndThread( vout_thread_t *p_vout )
797 int * pi_status; /* thread status */
802 pi_status = p_vout->pi_status;
803 *pi_status = THREAD_END;
805 /* Destroy all remaining pictures */
806 for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
808 if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
810 free( p_vout->p_picture[i_picture].p_data );
814 /* Destroy translation tables */
815 vout_EndTables( p_vout );
817 /* Destroy thread structures allocated by Create and InitThread */
818 vout_SysEnd( p_vout );
819 vout_UnloadFont( p_vout->p_default_font );
820 vout_UnloadFont( p_vout->p_large_font );
821 vout_SysDestroy( p_vout );
825 *pi_status = THREAD_OVER;
828 /*******************************************************************************
829 * Print: print simple text on a picture
830 *******************************************************************************
831 * This function will print a simple text on the picture. It is designed to
832 * print debugging or general informations.
833 *******************************************************************************/
834 void Print( vout_thread_t *p_vout, int i_x, int i_y, int i_halign, int i_valign, unsigned char *psz_text )
836 int i_text_height; /* total text height */
837 int i_text_width; /* total text width */
839 /* Update upper left coordinates according to alignment */
840 vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
843 case 0: /* centered */
844 i_x -= i_text_width / 2;
846 case 1: /* right aligned */
852 case 0: /* centered */
853 i_y -= i_text_height / 2;
855 case 1: /* bottom aligned */
856 i_y -= i_text_height;
861 if( (i_y < 0) || (i_y + i_text_height > p_vout->i_height) ||
862 (i_x < 0) || (i_x + i_text_width > p_vout->i_width) )
864 intf_DbgMsg("'%s' would print outside the screen\n", psz_text);
869 vout_Print( p_vout->p_default_font, vout_SysGetPicture( p_vout ) +
870 i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
871 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
872 0xffffffff, 0x00000000, 0x00000000, 0, psz_text );
875 /******************************************************************************
876 * RenderBlank: render a blank screen
877 ******************************************************************************
878 * This function is called by all other rendering functions when they arrive on
879 * a non blanked screen.
880 ******************************************************************************/
881 static void RenderBlank( vout_thread_t *p_vout )
884 int i_index; /* current 64 bits sample */
885 int i_width; /* number of 64 bits samples */
886 u64 *p_pic; /* pointer to 64 bits samples */
888 /* Initialize variables */
889 p_pic = vout_SysGetPicture( p_vout );
890 i_width = p_vout->i_bytes_per_line * p_vout->i_height / 256;
892 /* Clear beginning of screen by 256 bytes blocks */
893 for( i_index = 0; i_index < i_width; i_index++ )
895 *p_pic++ = 0; *p_pic++ = 0;
896 *p_pic++ = 0; *p_pic++ = 0;
897 *p_pic++ = 0; *p_pic++ = 0;
898 *p_pic++ = 0; *p_pic++ = 0;
899 *p_pic++ = 0; *p_pic++ = 0;
900 *p_pic++ = 0; *p_pic++ = 0;
901 *p_pic++ = 0; *p_pic++ = 0;
902 *p_pic++ = 0; *p_pic++ = 0;
903 *p_pic++ = 0; *p_pic++ = 0;
904 *p_pic++ = 0; *p_pic++ = 0;
905 *p_pic++ = 0; *p_pic++ = 0;
906 *p_pic++ = 0; *p_pic++ = 0;
907 *p_pic++ = 0; *p_pic++ = 0;
908 *p_pic++ = 0; *p_pic++ = 0;
909 *p_pic++ = 0; *p_pic++ = 0;
910 *p_pic++ = 0; *p_pic++ = 0;
913 /* Clear last pixels */
918 /******************************************************************************
919 * RenderPicture: render a picture
920 ******************************************************************************
921 * This function convert a picture from a video heap to a pixel-encoded image
922 * and copy it to the current rendering buffer. No lock is required, since the
923 * rendered picture has been determined as existant, and will only be destroyed
924 * by the vout thread later.
925 ******************************************************************************/
926 static int RenderPicture( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank )
928 int i_display_height, i_display_width; /* display dimensions */
929 int i_height, i_width; /* source picture dimensions */
930 int i_scaled_height; /* scaled height of the picture */
931 int i_aspect_scale; /* aspect ratio vertical scale */
932 int i_eol; /* end of line offset for source */
933 byte_t * p_convert_dst; /* convertion destination */
936 /* Start recording render time */
937 p_vout->render_time = mdate();
940 /* Mark last picture date */
941 p_vout->last_picture_date = p_pic->date;
942 i_width = p_pic->i_width;
943 i_height = p_pic->i_height;
944 i_display_width = p_vout->i_width;
945 i_display_height = p_vout->i_height;
947 /* Select scaling depending of aspect ratio */
948 switch( p_pic->i_aspect_ratio )
951 i_aspect_scale = (4 * i_height - 3 * i_width) ?
952 1 + 3 * i_width / ( 4 * i_height - 3 * i_width ) : 0;
954 case AR_16_9_PICTURE:
955 i_aspect_scale = ( 16 * i_height - 9 * i_width ) ?
956 1 + 9 * i_width / ( 16 * i_height - 9 * i_width ) : 0;
958 case AR_221_1_PICTURE:
959 i_aspect_scale = ( 221 * i_height - 100 * i_width ) ?
960 1 + 100 * i_width / ( 221 * i_height - 100 * i_width ) : 0;
962 case AR_SQUARE_PICTURE:
966 i_scaled_height = (i_aspect_scale ? i_height * (i_aspect_scale - 1) / i_aspect_scale : i_height);
968 /* Crop picture if too large for the screen */
969 if( i_width > i_display_width )
971 i_eol = i_width - i_display_width / 16 * 16;
972 i_width = i_display_width / 16 * 16;
978 if( i_scaled_height > i_display_height )
980 i_height = (i_aspect_scale * i_display_height / (i_aspect_scale - 1)) / 2 * 2;
981 i_scaled_height = i_display_height;
983 p_convert_dst = vout_SysGetPicture( p_vout ) +
984 ( i_display_width - i_width ) / 2 * p_vout->i_bytes_per_pixel +
985 ( i_display_height - i_scaled_height ) / 2 * p_vout->i_bytes_per_line;
988 * Choose appropriate rendering function and render picture
990 switch( p_pic->i_type )
992 case YUV_420_PICTURE:
993 p_vout->p_ConvertYUV420( p_vout, p_convert_dst,
994 p_pic->p_y, p_pic->p_u, p_pic->p_v,
995 i_width, i_height, i_eol,
996 p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel - i_width,
997 i_aspect_scale, p_pic->i_matrix_coefficients );
999 case YUV_422_PICTURE:
1000 p_vout->p_ConvertYUV422( p_vout, p_convert_dst,
1001 p_pic->p_y, p_pic->p_u, p_pic->p_v,
1002 i_width, i_height, i_eol,
1003 p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel - i_width,
1004 i_aspect_scale, p_pic->i_matrix_coefficients );
1006 case YUV_444_PICTURE:
1007 p_vout->p_ConvertYUV444( p_vout, p_convert_dst,
1008 p_pic->p_y, p_pic->p_u, p_pic->p_v,
1009 i_width, i_height, i_eol,
1010 p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel - i_width,
1011 i_aspect_scale, p_pic->i_matrix_coefficients );
1015 intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
1021 /* End recording render time */
1022 p_vout->render_time = mdate() - p_vout->render_time;
1027 /******************************************************************************
1028 * RenderPictureInfo: print additionnal informations on a picture
1029 ******************************************************************************
1030 * This function will print informations such as fps and other picture
1031 * dependant informations.
1032 ******************************************************************************/
1033 static int RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank )
1035 char psz_buffer[256]; /* string buffer */
1039 * Print FPS rate in upper right corner
1041 if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
1043 sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
1044 ( p_vout->fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
1045 p_vout->fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );
1046 Print( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
1050 * Print frames count and loop time in upper left corner
1052 sprintf( psz_buffer, "%ld frames render time: %lu us",
1053 p_vout->c_fps_samples, (long unsigned) p_vout->render_time );
1054 Print( p_vout, 0, 0, -1, -1, psz_buffer );
1059 * Print picture information in lower right corner
1061 sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s)",
1062 (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
1063 ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
1064 ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
1065 p_pic->i_width, p_pic->i_height,
1066 p_pic->i_display_width, p_pic->i_display_height,
1067 p_pic->i_display_horizontal_offset, p_pic->i_display_vertical_offset,
1068 (p_pic->i_aspect_ratio == AR_SQUARE_PICTURE) ? "sq" :
1069 ((p_pic->i_aspect_ratio == AR_3_4_PICTURE) ? "4:3" :
1070 ((p_pic->i_aspect_ratio == AR_16_9_PICTURE) ? "16:9" :
1071 ((p_pic->i_aspect_ratio == AR_221_1_PICTURE) ? "2.21:1" : "ukn-ar" ))));
1072 Print( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );
1078 /******************************************************************************
1079 * RenderIdle: render idle picture
1080 ******************************************************************************
1081 * This function will clear the display or print a logo.
1082 ******************************************************************************/
1083 static int RenderIdle( vout_thread_t *p_vout, boolean_t b_blank )
1085 /* Blank screen if required */
1086 if( (mdate() - p_vout->last_picture_date > VOUT_IDLE_DELAY) &&
1087 (p_vout->last_picture_date > p_vout->last_display_date) &&
1090 RenderBlank( p_vout );
1091 p_vout->last_display_date = mdate();
1092 Print( p_vout, p_vout->i_width / 2, p_vout->i_height / 2, 0, 0,
1100 /******************************************************************************
1101 * RenderInfo: render additionnal informations
1102 ******************************************************************************
1103 * This function render informations which do not depend of the current picture
1105 ******************************************************************************/
1106 static int RenderInfo( vout_thread_t *p_vout, boolean_t b_blank )
1108 char psz_buffer[256]; /* string buffer */
1110 int i_ready_pic = 0; /* ready pictures */
1111 int i_reserved_pic = 0; /* reserved pictures */
1112 int i_picture; /* picture index */
1117 * Print thread state in lower left corner
1119 for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
1121 switch( p_vout->p_picture[i_picture].i_status )
1123 case RESERVED_PICTURE:
1124 case RESERVED_DATED_PICTURE:
1125 case RESERVED_DISP_PICTURE:
1133 sprintf( psz_buffer, "%dx%d:%d g%+.2f pic: %d/%d/%d",
1134 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
1135 p_vout->f_gamma, i_reserved_pic, i_ready_pic,
1136 VOUT_MAX_PICTURES );
1137 Print( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );
1143 /******************************************************************************
1144 * Manage: manage thread
1145 ******************************************************************************
1146 * This function will handle changes in thread configuration.
1147 ******************************************************************************/
1148 static int Manage( vout_thread_t *p_vout )
1150 /* On gamma or grayscale change, rebuild tables */
1151 if( p_vout->i_changes & (VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE) )
1153 vout_ResetTables( p_vout );
1156 /* Clear changes flags which does not need management or have been handled */
1157 p_vout->i_changes &= ~(VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE |
1160 /* Detect unauthorized changes */
1161 if( p_vout->i_changes )
1163 /* Some changes were not acknowledged by vout_SysManage or this function,
1164 * it means they should not be authorized */
1165 intf_ErrMsg( "error: unauthorized changes in the video output thread\n" );