1 /*****************************************************************************
2 * vout_xvideo.c: Xvideo video output display method
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: vout_xvideo.c,v 1.40 2001/12/17 03:38:21 sam Exp $
7 * Authors: Shane Harper <shanegh@optusnet.com.au>
8 * Vincent Seguin <seguin@via.ecp.fr>
9 * Samuel Hocevar <sam@zoy.org>
10 * David Kennedy <dkennedy@tinytoad.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 #define MODULE_NAME xvideo
28 #include "modules_inner.h"
30 /*****************************************************************************
32 *****************************************************************************/
35 #include <errno.h> /* ENOMEM */
36 #include <stdlib.h> /* free() */
37 #include <string.h> /* strerror() */
39 #ifdef HAVE_MACHINE_PARAM_H
41 #include <machine/param.h>
42 #include <sys/types.h> /* typedef ushort */
47 #include <netinet/in.h> /* BSD: struct in_addr */
50 #include <sys/shm.h> /* shmget(), shmctl() */
52 #include <X11/Xutil.h>
53 #include <X11/keysym.h>
54 #include <X11/extensions/XShm.h>
55 #include <X11/extensions/Xv.h>
56 #include <X11/extensions/Xvlib.h>
57 #include <X11/extensions/dpms.h>
66 #include "video_output.h"
67 #include "vout_common.h"
69 #include "interface.h"
70 #include "netutils.h" /* network_ChannelJoin */
72 #include "stream_control.h" /* needed by input_ext-intf.h... */
73 #include "input_ext-intf.h"
76 #include "modules_export.h"
78 #define XVIDEO_MAX_DIRECTBUFFERS 5
79 #define GUID_YUV12_PLANAR 0x32315659
81 /*****************************************************************************
83 *****************************************************************************/
84 static int vout_Probe ( probedata_t * );
85 static int vout_Create ( vout_thread_t * );
86 static int vout_Init ( vout_thread_t * );
87 static void vout_End ( vout_thread_t * );
88 static void vout_Destroy ( vout_thread_t * );
89 static void vout_Display ( vout_thread_t *, picture_t * );
91 static int XVideoNewPicture ( vout_thread_t *, picture_t * );
93 static XvImage *CreateShmImage ( Display *, int, XShmSegmentInfo *, int, int );
94 static void DestroyShmImage( Display *, XvImage *, XShmSegmentInfo * );
96 static int CheckForXVideo ( Display * );
97 static int GetXVideoPort ( Display * );
99 /*static void XVideoSetAttribute ( vout_thread_t *, char *, float );*/
101 /*****************************************************************************
102 * Functions exported as capabilities. They are declared as static so that
103 * we don't pollute the namespace too much.
104 *****************************************************************************/
105 void _M( vout_getfunctions )( function_list_t * p_function_list )
107 p_function_list->pf_probe = vout_Probe;
108 p_function_list->functions.vout.pf_create = vout_Create;
109 p_function_list->functions.vout.pf_init = vout_Init;
110 p_function_list->functions.vout.pf_end = vout_End;
111 p_function_list->functions.vout.pf_destroy = vout_Destroy;
112 p_function_list->functions.vout.pf_manage = _M( vout_Manage );
113 p_function_list->functions.vout.pf_display = vout_Display;
114 p_function_list->functions.vout.pf_setpalette = NULL;
117 /*****************************************************************************
118 * vout_Probe: probe the video driver and return a score
119 *****************************************************************************
120 * This returns a score to the plugin manager so that it can select the best
122 *****************************************************************************/
123 static int vout_Probe( probedata_t *p_data )
125 Display *p_display; /* display pointer */
128 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
129 psz_display = XDisplayName( main_GetPszVariable(VOUT_DISPLAY_VAR, NULL) );
130 p_display = XOpenDisplay( psz_display );
131 if( p_display == NULL ) /* error */
133 intf_WarnMsg( 3, "vout: Xvideo cannot open display %s", psz_display );
134 intf_WarnMsg( 3, "vout: Xvideo not supported" );
138 if( !CheckForXVideo( p_display ) )
140 intf_WarnMsg( 3, "vout: Xvideo not supported" );
141 XCloseDisplay( p_display );
145 if( GetXVideoPort( p_display ) < 0 )
147 intf_WarnMsg( 3, "vout: Xvideo not supported" );
148 XCloseDisplay( p_display );
152 /* Clean-up everyting */
153 XCloseDisplay( p_display );
155 if( TestMethod( VOUT_METHOD_VAR, "xvideo" ) )
163 /*****************************************************************************
164 * vout_Create: allocate XVideo video thread output method
165 *****************************************************************************
166 * This function allocates and initialize a XVideo vout method. It uses some of
167 * the vout properties to choose the window size, and change them according to
168 * the actual properties of the display.
169 *****************************************************************************/
170 static int vout_Create( vout_thread_t *p_vout )
175 /* Allocate structure */
176 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
177 if( p_vout->p_sys == NULL )
179 intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
183 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
184 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
185 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
187 if( p_vout->p_sys->p_display == NULL ) /* error */
189 intf_ErrMsg( "vout error: cannot open display %s", psz_display );
190 free( p_vout->p_sys );
193 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
195 if( !CheckForXVideo( p_vout->p_sys->p_display ) )
197 intf_ErrMsg( "vout error: no XVideo extension" );
198 XCloseDisplay( p_vout->p_sys->p_display );
199 free( p_vout->p_sys );
203 /* Check we have access to a video port */
204 if( (p_vout->p_sys->i_xvport = GetXVideoPort(p_vout->p_sys->p_display)) <0 )
206 intf_ErrMsg( "vout error: cannot get XVideo port" );
207 XCloseDisplay( p_vout->p_sys->p_display );
208 free( p_vout->p_sys );
211 intf_DbgMsg( "Using xv port %d" , p_vout->p_sys->xv_port );
213 /* Create blank cursor (for mouse cursor autohiding) */
214 p_vout->p_sys->b_mouse_pointer_visible = 1;
215 p_vout->p_sys->cursor_pixmap = XCreatePixmap( p_vout->p_sys->p_display,
217 p_vout->p_sys->p_display),
220 XParseColor( p_vout->p_sys->p_display,
221 XCreateColormap( p_vout->p_sys->p_display,
223 p_vout->p_sys->p_display ),
225 p_vout->p_sys->p_display,
226 p_vout->p_sys->i_screen ),
228 "black", &cursor_color );
230 p_vout->p_sys->blank_cursor = XCreatePixmapCursor(
231 p_vout->p_sys->p_display,
232 p_vout->p_sys->cursor_pixmap,
233 p_vout->p_sys->cursor_pixmap,
235 &cursor_color, 1, 1 );
237 /* Spawn base window - this window will include the video output window,
238 * but also command buttons, subtitles and other indicators */
239 if( _M( XCommonCreateWindow ) ( p_vout ) )
241 intf_ErrMsg( "vout error: no suitable Xvideo image input port" );
242 _M( XCommonDestroyWindow ) ( p_vout );
243 XCloseDisplay( p_vout->p_sys->p_display );
244 free( p_vout->p_sys );
249 /* XXX The brightness and contrast values should be read from environment
250 * XXX variables... */
251 XVideoSetAttribute( p_vout, "XV_BRIGHTNESS", 0.5 );
252 XVideoSetAttribute( p_vout, "XV_CONTRAST", 0.5 );
255 /* Disable screen saver and return */
256 _M( XCommonDisableScreenSaver ) ( p_vout );
261 /*****************************************************************************
262 * vout_Init: initialize XVideo video thread output method
263 *****************************************************************************/
264 static int vout_Init( vout_thread_t *p_vout )
269 I_OUTPUTPICTURES = 0;
271 /* Initialize the output structure */
272 switch( p_vout->render.i_chroma )
274 case YUV_420_PICTURE:
275 p_vout->output.i_chroma = p_vout->render.i_chroma;
276 p_vout->output.i_width = p_vout->render.i_width;
277 p_vout->output.i_height = p_vout->render.i_height;
278 p_vout->output.i_aspect = p_vout->render.i_aspect;
285 /* Try to initialize up to XVIDEO_MAX_DIRECTBUFFERS direct buffers */
286 while( I_OUTPUTPICTURES < XVIDEO_MAX_DIRECTBUFFERS )
290 /* Find an empty picture slot */
291 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
293 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
295 p_pic = p_vout->p_picture + i_index;
300 /* Allocate the picture */
301 if( XVideoNewPicture( p_vout, p_pic ) )
306 p_pic->i_status = DESTROYED_PICTURE;
307 p_pic->i_type = DIRECT_PICTURE;
309 p_pic->i_left_margin =
310 p_pic->i_right_margin =
311 p_pic->i_top_margin =
312 p_pic->i_bottom_margin = 0;
314 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
322 /*****************************************************************************
323 * vout_End: terminate XVideo video thread output method
324 *****************************************************************************
325 * Destroy the XvImage. It is called at the end of the thread, but also each
326 * time the image is resized.
327 *****************************************************************************/
328 static void vout_End( vout_thread_t *p_vout )
332 /* Free the direct buffers we allocated */
333 for( i_index = I_OUTPUTPICTURES ; i_index ; )
336 DestroyShmImage( p_vout->p_sys->p_display,
337 PP_OUTPUTPICTURE[ i_index ]->p_sys->p_xvimage,
338 &PP_OUTPUTPICTURE[ i_index ]->p_sys->shminfo );
339 free( PP_OUTPUTPICTURE[ i_index ]->p_sys );
343 /*****************************************************************************
344 * vout_Destroy: destroy XVideo video thread output method
345 *****************************************************************************
346 * Terminate an output method created by vout_Create
347 *****************************************************************************/
348 static void vout_Destroy( vout_thread_t *p_vout )
350 /* Restore cursor if it was blanked */
351 if( !p_vout->p_sys->b_mouse_pointer_visible )
353 _M( XCommonToggleMousePointer ) ( p_vout );
356 /* Destroy blank cursor pixmap */
357 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
359 _M( XCommonEnableScreenSaver ) ( p_vout );
360 _M( XCommonDestroyWindow ) ( p_vout );
361 XCloseDisplay( p_vout->p_sys->p_display );
363 /* Destroy structure */
364 free( p_vout->p_sys );
367 /*****************************************************************************
368 * vout_Display: displays previously rendered output
369 *****************************************************************************
370 * This function sends the currently rendered image to X11 server.
371 * (The Xv extension takes care of "double-buffering".)
372 *****************************************************************************/
373 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
375 int i_width, i_height, i_x, i_y;
377 vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
378 &i_x, &i_y, &i_width, &i_height );
380 XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->i_xvport,
381 p_vout->p_sys->yuv_window, p_vout->p_sys->gc,
382 p_pic->p_sys->p_xvimage, 0 /*src_x*/, 0 /*src_y*/,
383 p_vout->output.i_width, p_vout->output.i_height,
384 0 /*dest_x*/, 0 /*dest_y*/, i_width, i_height,
387 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
390 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
394 /* following functions are local */
396 /*****************************************************************************
397 * CheckForXVideo: check for the XVideo extension
398 *****************************************************************************/
399 static int CheckForXVideo( Display *p_display )
403 switch( XvQueryExtension( p_display, &i, &i, &i, &i, &i ) )
409 intf_WarnMsg( 3, "vout error: XvBadExtension" );
413 intf_WarnMsg( 3, "vout error: XvBadAlloc" );
417 intf_WarnMsg( 3, "vout error: XvQueryExtension failed" );
422 /*****************************************************************************
423 * XVideoNewPicture: allocate a picture
424 *****************************************************************************
425 * Returns 0 on success, -1 otherwise
426 *****************************************************************************/
427 static int XVideoNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
429 int i_width = p_vout->output.i_width;
430 int i_height = p_vout->output.i_height;
432 switch( p_vout->output.i_chroma )
434 case YUV_420_PICTURE:
435 /* We know this chroma, allocate a buffer which will be used
436 * directly by the decoder */
437 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
439 if( p_pic->p_sys == NULL )
444 /* Create XvImage using XShm extension */
445 p_pic->p_sys->p_xvimage =
446 CreateShmImage( p_vout->p_sys->p_display,
447 p_vout->p_sys->i_xvport,
448 &p_pic->p_sys->shminfo,
449 p_vout->output.i_width,
450 p_vout->output.i_height );
451 if( p_pic->p_sys->p_xvimage == NULL )
453 free( p_pic->p_sys );
458 /* Precalculate some values */
459 p_pic->i_size = i_width * i_height;
460 p_pic->i_chroma_width = i_width / 2;
461 p_pic->i_chroma_size = i_height * ( i_width / 2 );
463 /* FIXME: try to get the right i_bytes value from p_xvimage */
464 p_pic->planes[Y_PLANE].p_data = p_pic->p_sys->p_xvimage->data;
465 p_pic->planes[Y_PLANE].i_bytes = p_pic->i_size * sizeof(u8);
466 p_pic->planes[Y_PLANE].i_line_bytes = i_width * sizeof(u8);
468 p_pic->planes[U_PLANE].p_data = (u8*)p_pic->p_sys->p_xvimage->data
469 + p_pic->i_size * 5 / 4;
470 p_pic->planes[U_PLANE].i_bytes = p_pic->i_size * sizeof(u8) / 4;
471 p_pic->planes[U_PLANE].i_line_bytes = p_pic->i_chroma_width
474 p_pic->planes[V_PLANE].p_data = (u8*)p_pic->p_sys->p_xvimage->data
476 p_pic->planes[V_PLANE].i_bytes = p_pic->i_size * sizeof(u8) / 4;
477 p_pic->planes[V_PLANE].i_line_bytes = p_pic->i_chroma_width
485 /* Unknown chroma, tell the guy to get lost */
492 /*****************************************************************************
493 * CreateShmImage: create an XvImage using shared memory extension
494 *****************************************************************************
495 * Prepare an XvImage for display function.
496 * The order of the operations respects the recommandations of the mit-shm
497 * document by J.Corbet and K.Packard. Most of the parameters were copied from
499 *****************************************************************************/
500 static XvImage *CreateShmImage( Display* p_display, int i_xvport,
501 XShmSegmentInfo *p_shminfo,
502 int i_width, int i_height )
506 p_xvimage = XvShmCreateImage( p_display, i_xvport,
507 GUID_YUV12_PLANAR, 0,
510 if( p_xvimage == NULL )
512 intf_ErrMsg( "vout error: XvShmCreateImage failed." );
516 p_shminfo->shmid = shmget( IPC_PRIVATE, p_xvimage->data_size,
518 if( p_shminfo->shmid < 0 ) /* error */
520 intf_ErrMsg( "vout error: cannot allocate shared image data (%s)",
525 p_shminfo->shmaddr = p_xvimage->data = shmat( p_shminfo->shmid, 0, 0 );
526 p_shminfo->readOnly = False;
528 if( !XShmAttach( p_display, p_shminfo ) )
530 intf_ErrMsg( "vout error: XShmAttach failed" );
531 shmctl( p_shminfo->shmid, IPC_RMID, 0 );
532 shmdt( p_shminfo->shmaddr );
536 /* Send image to X server. This instruction is required, since having
537 * built a Shm XImage and not using it causes an error on XCloseDisplay */
538 XSync( p_display, False );
541 /* Mark the shm segment to be removed when there are no more
542 * attachements, so it is automatic on process exit or after shmdt */
543 shmctl( p_shminfo->shmid, IPC_RMID, 0 );
549 /*****************************************************************************
551 *****************************************************************************
552 * Destroy XImage AND associated data. Detach shared memory segment from
553 * server and process, then free it. If pointer is NULL, the image won't be
554 * destroyed (see vout_Manage())
555 *****************************************************************************/
556 static void DestroyShmImage( Display *p_display, XvImage *p_xvimage,
557 XShmSegmentInfo *p_shminfo )
559 /* Detach from server */
560 XShmDetach( p_display, p_shminfo );
561 XSync( p_display, False );
564 XDestroyImage( p_xvimage ); /* XXX */
569 if( shmdt( p_shminfo->shmaddr ) ) /* detach shared memory from process */
571 intf_ErrMsg( "vout error: cannot detach shared memory (%s)",
576 /*****************************************************************************
577 * GetXVideoPort: get YUV12 port
578 *****************************************************************************
580 *****************************************************************************/
581 static int GetXVideoPort( Display *dpy )
583 XvAdaptorInfo *p_adaptor;
584 int i_adaptor, i_num_adaptors, i_requested_adaptor;
587 switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
588 &i_num_adaptors, &p_adaptor ) )
594 intf_WarnMsg( 3, "vout error: XvBadExtension for XvQueryAdaptors" );
598 intf_WarnMsg( 3, "vout error: XvBadAlloc for XvQueryAdaptors" );
602 intf_WarnMsg( 3, "vout error: XvQueryAdaptors failed" );
606 i_selected_port = -1;
607 i_requested_adaptor = main_GetIntVariable( VOUT_XVADAPTOR_VAR, -1 );
609 /* No special xv port has been requested so try all of them */
610 for( i_adaptor = 0; i_adaptor < i_num_adaptors; ++i_adaptor )
612 XvImageFormatValues *p_formats;
613 int i_format, i_num_formats;
616 /* If we requested an adaptor and it's not this one, we aren't
618 if( i_requested_adaptor != -1 && i_adaptor != i_requested_adaptor )
623 /* If the adaptor doesn't have the required properties, skip it */
624 if( !( p_adaptor[ i_adaptor ].type & XvInputMask ) ||
625 !( p_adaptor[ i_adaptor ].type & XvImageMask ) )
630 /* Check that port supports YUV12 planar format... */
631 p_formats = XvListImageFormats( dpy, p_adaptor[i_adaptor].base_id,
634 for( i_format = 0; i_format < i_num_formats; i_format++ )
636 XvEncodingInfo *p_enc;
637 int i_enc, i_num_encodings;
639 int i_attr, i_num_attributes;
641 /* If this is not the format we want, forget it */
642 if( p_formats[ i_format ].id != GUID_YUV12_PLANAR )
647 /* Look for the first available port supporting this format */
648 for( i_port = p_adaptor[i_adaptor].base_id;
649 ( i_port < p_adaptor[i_adaptor].base_id
650 + p_adaptor[i_adaptor].num_ports )
651 && ( i_selected_port == -1 );
654 if( XvGrabPort( dpy, i_port, CurrentTime ) == Success )
656 i_selected_port = i_port;
660 /* If no free port was found, forget it */
661 if( i_selected_port == -1 )
666 /* If we found a port, print information about it */
667 intf_WarnMsg( 3, "vout: GetXVideoPort found adaptor %i, port %i",
668 i_adaptor, i_selected_port );
669 intf_WarnMsg( 3, " image format 0x%x (%4.4s) %s supported",
670 p_formats[ i_format ].id,
671 (char *)&p_formats[ i_format ].id,
672 ( p_formats[ i_format ].format
673 == XvPacked ) ? "packed" : "planar" );
675 intf_WarnMsg( 4, " encoding list:" );
677 if( XvQueryEncodings( dpy, i_selected_port,
678 &i_num_encodings, &p_enc )
681 intf_WarnMsg( 4, " XvQueryEncodings failed" );
685 for( i_enc = 0; i_enc < i_num_encodings; i_enc++ )
687 intf_WarnMsg( 4, " id=%ld, name=%s, size=%ldx%ld,"
688 " numerator=%d, denominator=%d",
689 p_enc[i_enc].encoding_id, p_enc[i_enc].name,
690 p_enc[i_enc].width, p_enc[i_enc].height,
691 p_enc[i_enc].rate.numerator,
692 p_enc[i_enc].rate.denominator );
697 XvFreeEncodingInfo( p_enc );
700 intf_WarnMsg( 4, " attribute list:" );
701 p_attr = XvQueryPortAttributes( dpy, i_selected_port,
703 for( i_attr = 0; i_attr < i_num_attributes; i_attr++ )
706 " name=%s, flags=[%s%s ], min=%i, max=%i",
708 (p_attr[i_attr].flags & XvGettable) ? " get" : "",
709 (p_attr[i_attr].flags & XvSettable) ? " set" : "",
710 p_attr[i_attr].min_value, p_attr[i_attr].max_value );
719 if( p_formats != NULL )
726 if( i_num_adaptors > 0 )
728 XvFreeAdaptorInfo( p_adaptor );
731 if( i_selected_port == -1 )
733 if( i_requested_adaptor == -1 )
735 intf_WarnMsg( 3, "vout: no free XVideo port found for YV12" );
739 intf_WarnMsg( 3, "vout: XVideo adaptor %i does not have a free "
740 "XVideo port for YV12", i_requested_adaptor );
744 return( i_selected_port );
748 /*****************************************************************************
750 *****************************************************************************
751 * This function can be used to set attributes, e.g. XV_BRIGHTNESS and
752 * XV_CONTRAST. "f_value" should be in the range of 0 to 1.
753 *****************************************************************************/
754 static void XVideoSetAttribute( vout_thread_t *p_vout,
755 char *attr_name, float f_value )
758 XvAttribute *p_attrib;
759 Display *p_display = p_vout->p_sys->p_display;
760 int i_xvport = p_vout->p_sys->i_xvport;
762 p_attrib = XvQueryPortAttributes( p_display, i_xvport, &i_attrib );
768 if( i_attrib >= 0 && !strcmp( p_attrib[ i_attrib ].name, attr_name ) )
770 int i_sv = f_value * ( p_attrib[ i_attrib ].max_value
771 - p_attrib[ i_attrib ].min_value + 1 )
772 + p_attrib[ i_attrib ].min_value;
774 XvSetPortAttribute( p_display, i_xvport,
775 XInternAtom( p_display, attr_name, False ), i_sv );
779 } while( i_attrib > 0 );