1 /*****************************************************************************
2 * vout_xvideo.c: Xvideo video output display method
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: vout_xvideo.c,v 1.37 2001/12/09 17:01:37 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 * );
98 static void XVideoOutputCoords ( const picture_t *, const boolean_t,
100 int *, int *, int *, int * );
102 /*static void XVideoSetAttribute ( vout_thread_t *, char *, float );*/
104 /*****************************************************************************
105 * Functions exported as capabilities. They are declared as static so that
106 * we don't pollute the namespace too much.
107 *****************************************************************************/
108 void _M( vout_getfunctions )( function_list_t * p_function_list )
110 p_function_list->pf_probe = vout_Probe;
111 p_function_list->functions.vout.pf_create = vout_Create;
112 p_function_list->functions.vout.pf_init = vout_Init;
113 p_function_list->functions.vout.pf_end = vout_End;
114 p_function_list->functions.vout.pf_destroy = vout_Destroy;
115 p_function_list->functions.vout.pf_manage = _M( vout_Manage );
116 p_function_list->functions.vout.pf_display = vout_Display;
117 p_function_list->functions.vout.pf_setpalette = NULL;
120 /*****************************************************************************
121 * vout_Probe: probe the video driver and return a score
122 *****************************************************************************
123 * This returns a score to the plugin manager so that it can select the best
125 *****************************************************************************/
126 static int vout_Probe( probedata_t *p_data )
128 Display *p_display; /* display pointer */
131 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
132 psz_display = XDisplayName( main_GetPszVariable(VOUT_DISPLAY_VAR, NULL) );
133 p_display = XOpenDisplay( psz_display );
134 if( p_display == NULL ) /* error */
136 intf_WarnMsg( 3, "vout: Xvideo cannot open display %s", psz_display );
137 intf_WarnMsg( 3, "vout: Xvideo not supported" );
141 if( !CheckForXVideo( p_display ) )
143 intf_WarnMsg( 3, "vout: Xvideo not supported" );
144 XCloseDisplay( p_display );
148 if( GetXVideoPort( p_display ) < 0 )
150 intf_WarnMsg( 3, "vout: Xvideo not supported" );
151 XCloseDisplay( p_display );
155 /* Clean-up everyting */
156 XCloseDisplay( p_display );
158 if( TestMethod( VOUT_METHOD_VAR, "xvideo" ) )
166 /*****************************************************************************
167 * vout_Create: allocate XVideo video thread output method
168 *****************************************************************************
169 * This function allocates and initialize a XVideo vout method. It uses some of
170 * the vout properties to choose the window size, and change them according to
171 * the actual properties of the display.
172 *****************************************************************************/
173 static int vout_Create( vout_thread_t *p_vout )
178 /* Allocate structure */
179 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
180 if( p_vout->p_sys == NULL )
182 intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
186 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
187 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
188 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
190 if( p_vout->p_sys->p_display == NULL ) /* error */
192 intf_ErrMsg( "vout error: cannot open display %s", psz_display );
193 free( p_vout->p_sys );
196 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
198 if( !CheckForXVideo( p_vout->p_sys->p_display ) )
200 intf_ErrMsg( "vout error: no XVideo extension" );
201 XCloseDisplay( p_vout->p_sys->p_display );
202 free( p_vout->p_sys );
206 /* Check we have access to a video port */
207 if( (p_vout->p_sys->i_xvport = GetXVideoPort(p_vout->p_sys->p_display)) <0 )
209 intf_ErrMsg( "vout error: cannot get XVideo port" );
210 XCloseDisplay( p_vout->p_sys->p_display );
211 free( p_vout->p_sys );
214 intf_DbgMsg( "Using xv port %d" , p_vout->p_sys->xv_port );
216 /* Create blank cursor (for mouse cursor autohiding) */
217 p_vout->p_sys->b_mouse_pointer_visible = 1;
218 p_vout->p_sys->cursor_pixmap = XCreatePixmap( p_vout->p_sys->p_display,
220 p_vout->p_sys->p_display),
223 XParseColor( p_vout->p_sys->p_display,
224 XCreateColormap( p_vout->p_sys->p_display,
226 p_vout->p_sys->p_display ),
228 p_vout->p_sys->p_display,
229 p_vout->p_sys->i_screen ),
231 "black", &cursor_color );
233 p_vout->p_sys->blank_cursor = XCreatePixmapCursor(
234 p_vout->p_sys->p_display,
235 p_vout->p_sys->cursor_pixmap,
236 p_vout->p_sys->cursor_pixmap,
238 &cursor_color, 1, 1 );
240 /* Spawn base window - this window will include the video output window,
241 * but also command buttons, subtitles and other indicators */
242 if( _M( XCommonCreateWindow ) ( p_vout ) )
244 intf_ErrMsg( "vout error: no suitable Xvideo image input port" );
245 _M( XCommonDestroyWindow ) ( p_vout );
246 XCloseDisplay( p_vout->p_sys->p_display );
247 free( p_vout->p_sys );
252 /* XXX The brightness and contrast values should be read from environment
253 * XXX variables... */
254 XVideoSetAttribute( p_vout, "XV_BRIGHTNESS", 0.5 );
255 XVideoSetAttribute( p_vout, "XV_CONTRAST", 0.5 );
258 /* Disable screen saver and return */
259 _M( XCommonDisableScreenSaver ) ( p_vout );
264 /*****************************************************************************
265 * vout_Init: initialize XVideo video thread output method
266 *****************************************************************************/
267 static int vout_Init( vout_thread_t *p_vout )
272 /* Try to initialize up to XVIDEO_MAX_DIRECTBUFFERS direct buffers */
273 while( i_index < XVIDEO_MAX_DIRECTBUFFERS )
275 p_pic = &p_vout->p_picture[ i_index ];
277 if( XVideoNewPicture( p_vout, p_pic ) )
282 p_pic->i_status = DESTROYED_PICTURE;
284 p_pic->b_directbuffer = 1;
286 p_pic->i_left_margin =
287 p_pic->i_right_margin =
288 p_pic->i_top_margin =
289 p_pic->i_bottom_margin = 0;
294 /* How many directbuffers did we create ? */
295 p_vout->i_directbuffers = i_index;
300 /*****************************************************************************
301 * vout_End: terminate XVideo video thread output method
302 *****************************************************************************
303 * Destroy the XvImage. It is called at the end of the thread, but also each
304 * time the image is resized.
305 *****************************************************************************/
306 static void vout_End( vout_thread_t *p_vout )
310 /* Free the direct buffers we allocated */
311 for( i_index = p_vout->i_directbuffers ; i_index ; )
314 DestroyShmImage( p_vout->p_sys->p_display,
315 p_vout->p_picture[ i_index ].p_sys->p_xvimage,
316 &p_vout->p_picture[ i_index ].p_sys->shminfo );
317 free( p_vout->p_picture[ i_index ].p_sys );
321 /*****************************************************************************
322 * vout_Destroy: destroy XVideo video thread output method
323 *****************************************************************************
324 * Terminate an output method created by vout_Create
325 *****************************************************************************/
326 static void vout_Destroy( vout_thread_t *p_vout )
328 /* Restore cursor if it was blanked */
329 if( !p_vout->p_sys->b_mouse_pointer_visible )
331 _M( XCommonToggleMousePointer ) ( p_vout );
334 /* Destroy blank cursor pixmap */
335 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
337 _M( XCommonEnableScreenSaver ) ( p_vout );
338 _M( XCommonDestroyWindow ) ( p_vout );
339 XCloseDisplay( p_vout->p_sys->p_display );
341 /* Destroy structure */
342 free( p_vout->p_sys );
345 /*****************************************************************************
346 * vout_Display: displays previously rendered output
347 *****************************************************************************
348 * This function sends the currently rendered image to X11 server.
349 * (The Xv extension takes care of "double-buffering".)
350 *****************************************************************************/
351 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
353 int i_dest_width, i_dest_height, i_dest_x, i_dest_y;
355 //printf("pic: %i %i, disp: %i %i\n", p_pic->i_width, p_pic->i_height, p_vout->p_sys->i_window_width, p_vout->p_sys->i_window_height);
356 //printf("pic aspect ratio: %i\n", p_pic->i_aspect_ratio);
357 XVideoOutputCoords( p_pic, p_vout->b_scale,
358 p_vout->p_sys->i_window_width,
359 p_vout->p_sys->i_window_height,
360 &i_dest_x, &i_dest_y,
361 &i_dest_width, &i_dest_height);
362 //printf("resized to %i %i, moved at %i %i\n", i_dest_width, i_dest_height, i_dest_x, i_dest_y);
364 XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->i_xvport,
365 p_vout->p_sys->yuv_window, p_vout->p_sys->gc,
366 p_pic->p_sys->p_xvimage,
367 0 /*src_x*/, 0 /*src_y*/, p_pic->i_width, p_pic->i_height,
368 0 /*dest_x*/, 0 /*dest_y*/, i_dest_width, i_dest_height,
371 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
372 i_dest_width, i_dest_height );
374 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
375 i_dest_x, i_dest_y );
379 /* following functions are local */
381 /*****************************************************************************
382 * CheckForXVideo: check for the XVideo extension
383 *****************************************************************************/
384 static int CheckForXVideo( Display *p_display )
388 switch( XvQueryExtension( p_display, &i, &i, &i, &i, &i ) )
394 intf_WarnMsg( 3, "vout error: XvBadExtension" );
398 intf_WarnMsg( 3, "vout error: XvBadAlloc" );
402 intf_WarnMsg( 3, "vout error: XvQueryExtension failed" );
407 /*****************************************************************************
408 * XVideoNewPicture: allocate a picture
409 *****************************************************************************
410 * Returns 0 on success, -1 otherwise
411 *****************************************************************************/
412 static int XVideoNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
414 switch( p_vout->i_chroma )
416 case YUV_420_PICTURE:
417 /* We know this chroma, allocate a buffer which will be used
418 * directly by the decoder */
419 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
421 if( p_pic->p_sys == NULL )
426 /* Create XvImage using XShm extension */
427 p_pic->p_sys->p_xvimage =
428 CreateShmImage( p_vout->p_sys->p_display,
429 p_vout->p_sys->i_xvport,
430 &p_pic->p_sys->shminfo,
431 p_vout->i_width, p_vout->i_height );
432 if( p_pic->p_sys->p_xvimage == NULL )
434 free( p_pic->p_sys );
438 p_pic->i_chroma = p_vout->i_chroma; /* YUV_420_PICTURE */
439 p_pic->i_width = p_vout->i_width;
440 p_pic->i_height = p_vout->i_height;
442 /* Precalculate some values */
443 p_pic->i_size = p_vout->i_width * p_vout->i_height;
444 p_pic->i_chroma_width = p_vout->i_width / 2;
445 p_pic->i_chroma_size = p_vout->i_height * p_pic->i_chroma_width;
447 /* FIXME: try to get the right i_bytes value from p_overlay */
448 p_pic->planes[ Y_PLANE ].p_data = p_pic->p_sys->p_xvimage->data;
449 p_pic->planes[ Y_PLANE ].i_bytes = p_pic->i_size * sizeof(u8);
450 p_pic->planes[ U_PLANE ].p_data = (u8*)p_pic->p_sys->p_xvimage->data
451 + p_pic->i_size * 5 / 4;
452 p_pic->planes[ U_PLANE ].i_bytes = p_pic->i_size * sizeof(u8) / 4;
453 p_pic->planes[ V_PLANE ].p_data = (u8*)p_pic->p_sys->p_xvimage->data
455 p_pic->planes[ V_PLANE ].i_bytes = p_pic->i_size * sizeof(u8) / 4;
461 /* Unknown chroma, tell the guy to get lost */
468 /*****************************************************************************
469 * CreateShmImage: create an XvImage using shared memory extension
470 *****************************************************************************
471 * Prepare an XvImage for display function.
472 * The order of the operations respects the recommandations of the mit-shm
473 * document by J.Corbet and K.Packard. Most of the parameters were copied from
475 *****************************************************************************/
476 static XvImage *CreateShmImage( Display* p_display, int i_xvport,
477 XShmSegmentInfo *p_shminfo,
478 int i_width, int i_height )
482 p_xvimage = XvShmCreateImage( p_display, i_xvport,
483 GUID_YUV12_PLANAR, 0,
486 if( p_xvimage == NULL )
488 intf_ErrMsg( "vout error: XvShmCreateImage failed." );
492 p_shminfo->shmid = shmget( IPC_PRIVATE, p_xvimage->data_size,
494 if( p_shminfo->shmid < 0 ) /* error */
496 intf_ErrMsg( "vout error: cannot allocate shared image data (%s)",
501 p_shminfo->shmaddr = p_xvimage->data = shmat( p_shminfo->shmid, 0, 0 );
502 p_shminfo->readOnly = False;
504 if( !XShmAttach( p_display, p_shminfo ) )
506 intf_ErrMsg( "vout error: XShmAttach failed" );
507 shmctl( p_shminfo->shmid, IPC_RMID, 0 );
508 shmdt( p_shminfo->shmaddr );
512 /* Send image to X server. This instruction is required, since having
513 * built a Shm XImage and not using it causes an error on XCloseDisplay */
514 XSync( p_display, False );
517 /* Mark the shm segment to be removed when there are no more
518 * attachements, so it is automatic on process exit or after shmdt */
519 shmctl( p_shminfo->shmid, IPC_RMID, 0 );
525 /*****************************************************************************
527 *****************************************************************************
528 * Destroy XImage AND associated data. Detach shared memory segment from
529 * server and process, then free it. If pointer is NULL, the image won't be
530 * destroyed (see vout_Manage())
531 *****************************************************************************/
532 static void DestroyShmImage( Display *p_display, XvImage *p_xvimage,
533 XShmSegmentInfo *p_shminfo )
535 /* Detach from server */
536 XShmDetach( p_display, p_shminfo );
537 XSync( p_display, False );
540 XDestroyImage( p_xvimage ); /* XXX */
545 if( shmdt( p_shminfo->shmaddr ) ) /* detach shared memory from process */
547 intf_ErrMsg( "vout error: cannot detach shared memory (%s)",
552 /* This based on some code in SetBufferPicture... At the moment it's only
553 * used by the xvideo plugin, but others may want to use it. */
554 static void XVideoOutputCoords( const picture_t *p_pic, const boolean_t scale,
555 const int win_w, const int win_h,
556 int *dx, int *dy, int *w, int *h )
561 *h = p_pic->i_height;
566 switch( p_pic->i_aspect_ratio )
572 case AR_16_9_PICTURE:
576 case AR_221_1_PICTURE:
577 *w = win_h * 221 / 100;
580 case AR_SQUARE_PICTURE:
582 *w = win_h * p_pic->i_width / p_pic->i_height;
587 /* Set picture position */
588 *dx = (win_w - *w) / 2;
589 *dy = (win_h - *h) / 2;
592 /*****************************************************************************
593 * GetXVideoPort: get YUV12 port
594 *****************************************************************************
596 *****************************************************************************/
597 static int GetXVideoPort( Display *dpy )
599 XvAdaptorInfo *p_adaptor;
600 int i_adaptor, i_num_adaptors, i_requested_adaptor;
603 switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
604 &i_num_adaptors, &p_adaptor ) )
610 intf_WarnMsg( 3, "vout error: XvBadExtension for XvQueryAdaptors" );
614 intf_WarnMsg( 3, "vout error: XvBadAlloc for XvQueryAdaptors" );
618 intf_WarnMsg( 3, "vout error: XvQueryAdaptors failed" );
622 i_selected_port = -1;
623 i_requested_adaptor = main_GetIntVariable( VOUT_XVADAPTOR_VAR, -1 );
625 /* No special xv port has been requested so try all of them */
626 for( i_adaptor = 0; i_adaptor < i_num_adaptors; ++i_adaptor )
630 /* If we requested an adaptor and it's not this one, we aren't
632 if( i_requested_adaptor != -1 && i_adaptor != i_requested_adaptor )
637 /* If the adaptor doesn't have the required properties, skip it */
638 if( !( p_adaptor[ i_adaptor ].type & XvInputMask ) ||
639 !( p_adaptor[ i_adaptor ].type & XvImageMask ) )
644 for( i_port = p_adaptor[i_adaptor].base_id;
645 i_port < p_adaptor[i_adaptor].base_id
646 + p_adaptor[i_adaptor].num_ports;
649 XvImageFormatValues *p_formats;
650 int i_format, i_num_formats;
652 /* If we already found a port, we aren't interested */
653 if( i_selected_port != -1 )
658 /* Check that port supports YUV12 planar format... */
659 p_formats = XvListImageFormats( dpy, i_port, &i_num_formats );
661 for( i_format = 0; i_format < i_num_formats; i_format++ )
663 XvEncodingInfo *p_enc;
664 int i_enc, i_num_encodings;
666 int i_attr, i_num_attributes;
668 if( p_formats[ i_format ].id != GUID_YUV12_PLANAR )
673 /* Found a matching port, print a description of this port */
674 i_selected_port = i_port;
676 intf_WarnMsg( 3, "vout: GetXVideoPort found adaptor %i port %i",
678 intf_WarnMsg( 3, " image format 0x%x (%4.4s) %s supported",
679 p_formats[ i_format ].id,
680 (char *)&p_formats[ i_format ].id,
681 ( p_formats[ i_format ].format
682 == XvPacked ) ? "packed" : "planar" );
684 intf_WarnMsg( 4, " encoding list:" );
686 if( XvQueryEncodings( dpy, i_port, &i_num_encodings, &p_enc )
689 intf_WarnMsg( 4, " XvQueryEncodings failed" );
693 for( i_enc = 0; i_enc < i_num_encodings; i_enc++ )
695 intf_WarnMsg( 4, " id=%ld, name=%s, size=%ldx%ld,"
696 " numerator=%d, denominator=%d",
697 p_enc[i_enc].encoding_id, p_enc[i_enc].name,
698 p_enc[i_enc].width, p_enc[i_enc].height,
699 p_enc[i_enc].rate.numerator,
700 p_enc[i_enc].rate.denominator );
705 XvFreeEncodingInfo( p_enc );
708 intf_WarnMsg( 4, " attribute list:" );
709 p_attr = XvQueryPortAttributes( dpy, i_port,
711 for( i_attr = 0; i_attr < i_num_attributes; i_attr++ )
714 " name=%s, flags=[%s%s ], min=%i, max=%i",
716 (p_attr[i_attr].flags & XvGettable) ? " get" : "",
717 (p_attr[i_attr].flags & XvSettable) ? " set" : "",
718 p_attr[i_attr].min_value, p_attr[i_attr].max_value );
727 if( p_formats != NULL )
734 if( i_num_adaptors > 0 )
736 XvFreeAdaptorInfo( p_adaptor );
739 if( i_selected_port == -1 )
741 if( i_requested_adaptor == -1 )
743 intf_WarnMsg( 3, "vout: no XVideo port found supporting YUV12" );
747 intf_WarnMsg( 3, "vout: XVideo adaptor %i does not support YUV12",
748 i_requested_adaptor );
752 return( i_selected_port );
756 /*****************************************************************************
758 *****************************************************************************
759 * This function can be used to set attributes, e.g. XV_BRIGHTNESS and
760 * XV_CONTRAST. "f_value" should be in the range of 0 to 1.
761 *****************************************************************************/
762 static void XVideoSetAttribute( vout_thread_t *p_vout,
763 char *attr_name, float f_value )
766 XvAttribute *p_attrib;
767 Display *p_display = p_vout->p_sys->p_display;
768 int i_xvport = p_vout->p_sys->i_xvport;
770 p_attrib = XvQueryPortAttributes( p_display, i_xvport, &i_attrib );
776 if( i_attrib >= 0 && !strcmp( p_attrib[ i_attrib ].name, attr_name ) )
778 int i_sv = f_value * ( p_attrib[ i_attrib ].max_value
779 - p_attrib[ i_attrib ].min_value + 1 )
780 + p_attrib[ i_attrib ].min_value;
782 XvSetPortAttribute( p_display, i_xvport,
783 XInternAtom( p_display, attr_name, False ), i_sv );
787 } while( i_attrib > 0 );