/* * Copyright (C) 2000-2005 the xine project * * This file is part of xine, a unix video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * video_out_directx.c, direct draw video output plugin for xine * by Matthew Grooms */ typedef unsigned char boolean; #include #include #include #define LOG_MODULE "video_out_directx" #define LOG_VERBOSE /* #define LOG */ #include "xine.h" #include "video_out.h" #include "xine_internal.h" #include "xine_internal.h" #include "yuv2rgb.h" #define NEW_YUV 1 /* Set to 1 for RGB support */ #define RGB_SUPPORT 0 #define BORDER_SIZE 8 #define IMGFMT_NATIVE 4 /***************************************************************************** * DirectDraw GUIDs. * Defining them here allows us to get rid of the dxguid library during * the linking stage. *****************************************************************************/ #if 1 static const GUID IID_IDirectDraw = { 0x6C14DB80,0xA733,0x11CE,{0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60} }; #endif #if 0 static const GUID IID_IDirectDraw2 = { 0xB3A6F3E0,0x2B43,0x11CF,{0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56} }; #endif #if 0 static const GUID IID_IDirectDraw4 = { 0x9C59509A,0x39BD,0x11D1,{0x8C,0x4A,0x00,0xC0,0x4F,0xD9,0x30,0xC5} }; #endif /* ----------------------------------------- * * vo_directx frame struct * * ----------------------------------------- */ typedef struct { vo_frame_t vo_frame; uint8_t *buffer; int format; uint32_t width; uint32_t height; int size; double ratio; } win32_frame_t; /* ----------------------------------------- * * vo_directx driver struct * * ----------------------------------------- */ typedef struct { vo_driver_t vo_driver; win32_visual_t *win32_visual; xine_t *xine; LPDIRECTDRAW ddobj; /* direct draw object */ LPDIRECTDRAWSURFACE primary; /* primary dd surface */ LPDIRECTDRAWSURFACE secondary; /* secondary dd surface */ LPDIRECTDRAWCLIPPER ddclipper; /* dd clipper object */ uint8_t * contents; /* secondary contents */ win32_frame_t *current; /* current frame */ int req_format; /* requested frame format */ int act_format; /* actual frame format */ uint32_t width; /* frame with */ uint32_t height; /* frame height */ double ratio; /* frame ratio */ yuv2rgb_factory_t *yuv2rgb_factory; /* used for format conversion */ yuv2rgb_t *yuv2rgb; /* used for format conversion */ int mode; /* rgb mode */ int bytespp; /* rgb bits per pixel */ alphablend_t alphablend_extra_data; } win32_driver_t; typedef struct { video_driver_class_t driver_class; config_values_t *config; xine_t *xine; } directx_class_t; /* ----------------------------------------- * * BEGIN : Direct Draw and win32 handlers * for xine video output plugins. * * ----------------------------------------- */ /* Display formatted error message in * popup message box.*/ static void Error( HWND hwnd, LPSTR szfmt, ... ) { char tempbuff[ 256 ]; *tempbuff = 0; wvsprintf( &tempbuff[ strlen( tempbuff ) ], szfmt, ( char * )( &szfmt + 1 ) ); MessageBox( hwnd, tempbuff, "Error", MB_ICONERROR | MB_OK | MB_APPLMODAL | MB_SYSTEMMODAL ); } /* Update our drivers current knowledge * of our windows video out posistion */ static void UpdateRect( win32_visual_t * win32_visual ) { if( win32_visual->FullScreen ) { SetRect( &win32_visual->WndRect, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ) ); } else { GetClientRect( win32_visual->WndHnd, &win32_visual->WndRect ); ClientToScreen( win32_visual->WndHnd, ( POINT * ) &win32_visual->WndRect ); ClientToScreen( win32_visual->WndHnd, ( POINT * ) &win32_visual->WndRect + 1 ); } } /* Create our direct draw object, primary * surface and clipper object. * * NOTE : The primary surface is more or * less a viewport into the parent desktop * window and will always have a pixel format * identical to the current display mode. */ static boolean CreatePrimary( win32_driver_t * win32_driver ) { LPDIRECTDRAW ddobj; DDSURFACEDESC ddsd; HRESULT result; /* create direct draw object */ result = DirectDrawCreate( 0, &ddobj, 0 ); if( result != DD_OK ) { Error( 0, "DirectDrawCreate : error %ld", result ); xprintf(win32_driver->xine, XINE_VERBOSITY_DEBUG, "vo_out_directx : DirectDrawCreate : error %ld\n", result ); return 0; } /* set cooperative level */ result = IDirectDraw_SetCooperativeLevel( ddobj, win32_driver->win32_visual->WndHnd, DDSCL_NORMAL ); if( result != DD_OK ) { Error( 0, "SetCooperativeLevel : error 0x%lx", result ); return 0; } /* try to get new interface */ result = IDirectDraw_QueryInterface( ddobj, &IID_IDirectDraw, (LPVOID *) &win32_driver->ddobj ); if( result != DD_OK ) { Error( 0, "ddobj->QueryInterface : DirectX required" ); return 0; } /* release our old interface */ IDirectDraw_Release( ddobj ); /* create primary_surface */ memset( &ddsd, 0, sizeof( ddsd ) ); ddsd.dwSize = sizeof( ddsd ); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; result = IDirectDraw_CreateSurface( win32_driver->ddobj, &ddsd, &win32_driver->primary, 0 ); if( result != DD_OK ) { Error( 0, "CreateSurface ( primary ) : error 0x%lx", result ); return 0; } /* create our clipper object */ result = IDirectDraw_CreateClipper( win32_driver->ddobj, 0, &win32_driver->ddclipper, 0 ); if( result != DD_OK ) { Error( 0, "CreateClipper : error 0x%lx", result ); return 0; } /* associate our clipper with our window */ result = IDirectDrawClipper_SetHWnd( win32_driver->ddclipper, 0, win32_driver->win32_visual->WndHnd ); if( result != DD_OK ) { Error( 0, "ddclipper->SetHWnd : error 0x%lx", result ); return 0; } /* associate our primary surface with our clipper */ result = IDirectDrawSurface_SetClipper( win32_driver->primary, win32_driver->ddclipper ); if( result != DD_OK ) { Error( 0, "ddclipper->SetHWnd : error 0x%lx", result ); return 0; } /* store our objects in our visual struct */ UpdateRect( win32_driver->win32_visual ); return 1; } /* Create our secondary ( off screen ) buffer. * The optimal secondary buffer is a h/w * overlay with the same pixel format as the * xine frame type. However, since this is * not always supported by the host h/w, * we will fall back to creating an rgb buffer * in video memory qith the same pixel format * as the primary surface. At least then we * can use h/w scaling if supported. */ static boolean CreateSecondary( win32_driver_t * win32_driver, int width, int height, int format ) { DDSURFACEDESC ddsd; HRESULT result; if( format == XINE_IMGFMT_YV12 ) xprintf(win32_driver->xine, XINE_VERBOSITY_DEBUG, "vo_out_directx : switching to YV12 overlay type\n" ); if( format == XINE_IMGFMT_YUY2 ) xprintf(win32_driver->xine, XINE_VERBOSITY_DEBUG, "vo_out_directx : switching to YUY2 overlay type\n" ); #if RGB_SUPPORT if( format == IMGFMT_RGB ) xprintf(win32_driver->xine, XINE_VERBOSITY_DEBUG, "vo_out_directx : switching to RGB overlay type\n" ); #endif if( !win32_driver->ddobj ) return FALSE; /* store our reqested format, * width and height */ win32_driver->req_format = format; win32_driver->width = width; win32_driver->height = height; /* if we already have a secondary * surface then release it */ if( win32_driver->secondary ) IDirectDrawSurface_Release( win32_driver->secondary ); memset( &ddsd, 0, sizeof( ddsd ) ); ddsd.dwSize = sizeof( ddsd ); ddsd.dwWidth = width; ddsd.dwHeight = height; if( format == XINE_IMGFMT_YV12 ) { /* the requested format is XINE_IMGFMT_YV12 */ ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OVERLAY; ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; ddsd.ddpfPixelFormat.dwYUVBitCount = 16; ddsd.ddpfPixelFormat.dwFourCC = mmioFOURCC( 'Y', 'V', '1', '2' ); lprintf("CreateSecondary() - act_format = (YV12) %d\n", XINE_IMGFMT_YV12); win32_driver->act_format = XINE_IMGFMT_YV12; } if( format == XINE_IMGFMT_YUY2 ) { /* the requested format is XINE_IMGFMT_YUY2 */ ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OVERLAY; ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; ddsd.ddpfPixelFormat.dwYUVBitCount = 16; ddsd.ddpfPixelFormat.dwFourCC = mmioFOURCC( 'Y', 'U', 'Y', '2' ); lprintf("CreateSecondary() - act_format = (YUY2) %d\n", XINE_IMGFMT_YUY2); win32_driver->act_format = XINE_IMGFMT_YUY2; } #if RGB_SUPPORT if( format == IMGFMT_RGB ) { /* the requested format is IMGFMT_RGB */ ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OVERLAY; ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; ddsd.ddpfPixelFormat.dwYUVBitCount = 24; ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00; ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff; lprintf("CreateSecondary() - act_format = (RGB) %d\n", IMGFMT_RGB); win32_driver->act_format = IMGFMT_RGB; } #endif /* RGB_SUPPORT */ lprintf("CreateSecondary() - IDirectDraw_CreateSurface()\n"); if( IDirectDraw_CreateSurface( win32_driver->ddobj, &ddsd, &win32_driver->secondary, 0 ) == DD_OK ) return TRUE; /* Our fallback method is to create a back buffer * with the same image format as the primary surface */ lprintf("CreateSecondary() - Falling back to back buffer same as primary\n"); lprintf("CreateSecondary() - act_format = (NATIVE) %d\n", IMGFMT_NATIVE); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; win32_driver->act_format = IMGFMT_NATIVE; if( IDirectDraw_CreateSurface( win32_driver->ddobj, &ddsd, &win32_driver->secondary, 0 ) == DD_OK ) return TRUE; /* Our second fallback - all w/o HW acceleration */ lprintf("CreateSecondary() - Falling back, disabling HW acceleration \n"); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; if( (result = IDirectDraw_CreateSurface( win32_driver->ddobj, &ddsd, &win32_driver->secondary, 0 )) == DD_OK ) return TRUE; /* This is bad. We cant even create a surface with * the same format as the primary surface. */ Error( 0, "CreateSurface ( Secondary ) : unable to create a suitable rendering surface: 0x%08lX", result ); return FALSE; } /* Destroy all direct draw driver allocated * resources. */ static void Destroy( win32_driver_t * win32_driver ) { if( win32_driver->ddclipper ) IDirectDrawClipper_Release( win32_driver->ddclipper ); if( win32_driver->primary ) IDirectDrawSurface_Release( win32_driver->primary ); if( win32_driver->secondary ) IDirectDrawSurface_Release( win32_driver->secondary ); if( win32_driver->ddobj ) IDirectDraw_Release( win32_driver->ddobj ); _x_alphablend_free(&win32_driver->alphablend_extra_data); free( win32_driver ); } /* Check the current pixel format of the * display mode. This is neccesary in case * the h/w does not support an overlay for * the native frame format. */ static boolean CheckPixelFormat( win32_driver_t * win32_driver ) { DDPIXELFORMAT ddpf; HRESULT result; /* get the pixel format of our primary surface */ memset( &ddpf, 0, sizeof( DDPIXELFORMAT )); ddpf.dwSize = sizeof( DDPIXELFORMAT ); result = IDirectDrawSurface_GetPixelFormat( win32_driver->primary, &ddpf ); if( result != DD_OK ) { Error( 0, "IDirectDrawSurface_GetPixelFormat ( CheckPixelFormat ) : error 0x%lx", result ); return 0; } /* TODO : support paletized video modes */ if( ( ddpf.dwFlags & DDPF_PALETTEINDEXED1 ) || ( ddpf.dwFlags & DDPF_PALETTEINDEXED2 ) || ( ddpf.dwFlags & DDPF_PALETTEINDEXED4 ) || ( ddpf.dwFlags & DDPF_PALETTEINDEXED8 ) || ( ddpf.dwFlags & DDPF_PALETTEINDEXEDTO8 ) ) return FALSE; /* store bytes per pixel */ win32_driver->bytespp = ddpf.dwRGBBitCount / 8; /* find the rgb mode for software * colorspace conversion */ if( ddpf.dwRGBBitCount == 32 ) { if( ddpf.dwRBitMask == 0xff0000 ) win32_driver->mode = MODE_32_RGB; else win32_driver->mode = MODE_32_BGR; } if( ddpf.dwRGBBitCount == 24 ) { if( ddpf.dwRBitMask == 0xff0000 ) win32_driver->mode = MODE_24_RGB; else win32_driver->mode = MODE_24_BGR; } if( ddpf.dwRGBBitCount == 16 ) { if( ddpf.dwRBitMask == 0xf800 ) win32_driver->mode = MODE_16_RGB; else win32_driver->mode = MODE_16_BGR; } if( ddpf.dwRGBBitCount == 15 ) { if( ddpf.dwRBitMask == 0x7C00 ) win32_driver->mode = MODE_15_RGB; else win32_driver->mode = MODE_15_BGR; } return TRUE; } /* Create a Direct draw surface from * a bitmap resource.. * * NOTE : This is not really useful * anymore since the xine logo code is * being pushed to the backend. */ static LPDIRECTDRAWSURFACE CreateBMP( win32_driver_t * win32_driver, int resource ) { LPDIRECTDRAWSURFACE bmp_surf; DDSURFACEDESC bmp_ddsd; HBITMAP bmp_hndl; BITMAP bmp_head; HDC hdc_dds; HDC hdc_mem; /* load our bitmap from a resource */ if( !( bmp_hndl = LoadBitmap( win32_driver->win32_visual->HInst, MAKEINTRESOURCE( resource ) ) ) ) { Error( 0, "CreateBitmap : could not load bmp resource" ); return 0; } /* create an off screen surface with * the same dimentions as our bitmap */ GetObject( bmp_hndl, sizeof( bmp_head ), &bmp_head ); memset( &bmp_ddsd, 0, sizeof( bmp_ddsd ) ); bmp_ddsd.dwSize = sizeof( bmp_ddsd ); bmp_ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; bmp_ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; bmp_ddsd.dwWidth = bmp_head.bmWidth; bmp_ddsd.dwHeight = bmp_head.bmHeight; if( IDirectDraw_CreateSurface( win32_driver->ddobj, &bmp_ddsd, &bmp_surf, 0 ) != DD_OK ) { Error( 0, "CreateSurface ( bitmap ) : could not create dd surface" ); return 0; } /* get a handle to our surface dc, * create a compat dc and load * our bitmap into the compat dc */ IDirectDrawSurface_GetDC( bmp_surf, &hdc_dds ); hdc_mem = CreateCompatibleDC( hdc_dds ); SelectObject( hdc_mem, bmp_hndl ); /* copy our bmp from the compat dc * into our dd surface */ BitBlt( hdc_dds, 0, 0, bmp_head.bmWidth, bmp_head.bmHeight, hdc_mem, 0, 0, SRCCOPY ); /* clean up */ DeleteDC( hdc_mem ); DeleteObject( bmp_hndl ); IDirectDrawSurface_ReleaseDC( bmp_surf, hdc_dds ); return bmp_surf; } /* Merge overlay with the current primary * surface. This funtion is only used when * a h/w overlay of the current frame type * is supported. */ static boolean Overlay( LPDIRECTDRAWSURFACE src_surface, RECT * src_rect, LPDIRECTDRAWSURFACE dst_surface, RECT * dst_rect, COLORREF color_key ) { DWORD dw_color_key; DDPIXELFORMAT ddpf; DDOVERLAYFX ddofx; int flags; HRESULT result; /* compute the colorkey pixel value from the RGB value we've got/ * NOTE : based on videolan colorkey code */ memset( &ddpf, 0, sizeof( DDPIXELFORMAT )); ddpf.dwSize = sizeof( DDPIXELFORMAT ); result = IDirectDrawSurface_GetPixelFormat( dst_surface, &ddpf ); if( result != DD_OK ) { Error( 0, "IDirectDrawSurface_GetPixelFormat : could not get surface pixel format" ); return FALSE; } dw_color_key = ( DWORD ) color_key; dw_color_key = ( DWORD ) ( ( ( dw_color_key * ddpf.dwRBitMask ) / 255 ) & ddpf.dwRBitMask ); memset( &ddofx, 0, sizeof( DDOVERLAYFX ) ); ddofx.dwSize = sizeof( DDOVERLAYFX ); ddofx.dckDestColorkey.dwColorSpaceLowValue = dw_color_key; ddofx.dckDestColorkey.dwColorSpaceHighValue = dw_color_key; /* set our overlay flags */ flags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE; /* attempt to overlay the surface */ result = IDirectDrawSurface_UpdateOverlay( src_surface, src_rect, dst_surface, dst_rect, flags, &ddofx ); if( result != DD_OK ) { if( result == DDERR_SURFACELOST ) { IDirectDrawSurface_Restore( src_surface ); IDirectDrawSurface_Restore( dst_surface ); IDirectDrawSurface_UpdateOverlay( src_surface, src_rect, dst_surface, dst_rect, flags, &ddofx ); } else { Error( 0, "IDirectDrawSurface_UpdateOverlay : error 0x%lx", result ); return FALSE; } } return TRUE; } /* Copy our off screen surface into our primary * surface. This funtion is only used when a * h/w overlay of the current frame format is * not supported. */ static boolean BltCopy( LPDIRECTDRAWSURFACE src_surface, RECT * src_rect, LPDIRECTDRAWSURFACE dst_surface, RECT * dst_rect ) { DDSURFACEDESC ddsd_target; HRESULT result; memset( &ddsd_target, 0, sizeof( ddsd_target ) ); ddsd_target.dwSize = sizeof( ddsd_target ); /* attempt to blt the surface sontents */ result = IDirectDrawSurface_Blt( dst_surface, dst_rect, src_surface, src_rect, DDBLT_WAIT, 0 ); if( result != DD_OK ) { if( result != DDERR_SURFACELOST ) { IDirectDrawSurface_Restore( src_surface ); IDirectDrawSurface_Restore( dst_surface ); IDirectDrawSurface_Blt( dst_surface, dst_rect, src_surface, src_rect, DDBLT_WAIT, 0 ); } else { Error( 0, "IDirectDrawSurface_Blt : error 0x%lx", result ); return FALSE; } } return TRUE; } /* Display our current frame. This function * corrects frame output ratio and clipps the * frame if nessesary. It will then handle * moving the image contents contained in our * secondary surface to our primary surface. */ static boolean DisplayFrame( win32_driver_t * win32_driver ) { int view_width; int view_height; int scaled_width; int scaled_height; int screen_width; int screen_height; RECT clipped; RECT centered; /* aspect ratio calculations */ /* TODO : account for screen ratio as well */ view_width = win32_driver->win32_visual->WndRect.right - win32_driver->win32_visual->WndRect.left; view_height = win32_driver->win32_visual->WndRect.bottom - win32_driver->win32_visual->WndRect.top; if( view_width / win32_driver->ratio < view_height ) { scaled_width = view_width - BORDER_SIZE; scaled_height = view_width / win32_driver->ratio - BORDER_SIZE; } else { scaled_width = view_height * win32_driver->ratio - BORDER_SIZE; scaled_height = view_height - BORDER_SIZE; } /* center our overlay in our view frame */ centered.left = ( view_width - scaled_width ) / 2 + win32_driver->win32_visual->WndRect.left; centered.right = centered.left + scaled_width; centered.top = ( view_height - scaled_height ) / 2 + win32_driver->win32_visual->WndRect.top; centered.bottom = centered.top + scaled_height; /* clip our overlay if it is off screen */ screen_width = GetSystemMetrics( SM_CXSCREEN ); screen_height = GetSystemMetrics( SM_CYSCREEN ); if( centered.left < 0 ) { double x_scale = ( double ) ( view_width + centered.left ) / ( double ) view_width; clipped.left = win32_driver->width - ( int ) ( win32_driver->width * x_scale ); centered.left = 0; } else clipped.left = 0; if( centered.top < 0 ) { double y_scale = ( double ) ( view_height + centered.top ) / ( double ) view_height; clipped.left = win32_driver->height - ( int ) ( win32_driver->height * y_scale ); centered.left = 0; } else clipped.top = 0; if( centered.right > screen_width ) { double x_scale = ( double ) ( view_width - ( centered.right - screen_width ) ) / ( double ) view_width; clipped.right = ( int ) ( win32_driver->width * x_scale ); centered.right = screen_width; } else clipped.right = win32_driver->width; if( centered.bottom > screen_height ) { double y_scale = ( double ) ( view_height - ( centered.bottom - screen_height ) ) / ( double ) view_height; clipped.bottom = ( int ) ( win32_driver->height * y_scale ); centered.bottom = screen_height; } else clipped.bottom = win32_driver->height; /* if surface is entirely off screen or the * width or height is 0 for the overlay or * the output view area, then return without * overlay update */ if( ( centered.left > screen_width ) || ( centered.top > screen_height ) || ( centered.right < 0 ) || ( centered.bottom < 0 ) || ( clipped.left >= clipped.right ) || ( clipped.top >= clipped.bottom ) || ( view_width <= 0 ) || ( view_height <= 0 ) ) return 1; /* we have a h/w supported overlay */ if( ( win32_driver->act_format == XINE_IMGFMT_YV12 ) || ( win32_driver->act_format == XINE_IMGFMT_YUY2 ) ) return Overlay( win32_driver->secondary, &clipped, win32_driver->primary, ¢ered, win32_driver->win32_visual->ColorKey ); /* we do not have a h/w supported overlay */ return BltCopy( win32_driver->secondary, &clipped, win32_driver->primary, ¢ered ); } /* Lock our back buffer to update its contents. */ static void * Lock( void * surface ) { LPDIRECTDRAWSURFACE lock_surface = ( LPDIRECTDRAWSURFACE ) surface; DDSURFACEDESC ddsd; HRESULT result; if( !surface ) return 0; memset( &ddsd, 0, sizeof( ddsd ) ); ddsd.dwSize = sizeof( ddsd ); result = IDirectDrawSurface_Lock( lock_surface, 0, &ddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, 0 ); if( result == DDERR_SURFACELOST ) { IDirectDrawSurface_Restore( lock_surface ); result = IDirectDrawSurface_Lock( lock_surface, 0, &ddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, 0 ); if( result != DD_OK ) return 0; } else if( result != DD_OK ) { if( result == DDERR_GENERIC ) { Error( 0, "surface->Lock : error, DDERR_GENERIC" ); exit( 1 ); } } return ddsd.lpSurface; } /* Unlock our back buffer to prepair for display. */ static void Unlock( void * surface ) { LPDIRECTDRAWSURFACE lock_surface = ( LPDIRECTDRAWSURFACE ) surface; if( !surface ) return; IDirectDrawSurface_Unlock( lock_surface, 0 ); } /* ----------------------------------------- * * BEGIN : Xine driver video output plugin * handlers. * * ----------------------------------------- */ static uint32_t win32_get_capabilities( vo_driver_t * vo_driver ) { uint32_t retVal; retVal = VO_CAP_YV12 | VO_CAP_YUY2; #if RGB_SUPPORT retVal |= VO_CAP_RGB; #endif /* RGB_SUPPORT */ return retVal; } static void win32_frame_field( vo_frame_t * vo_frame, int which_field ) { /* I have no idea what this even * does, frame interlace stuff? */ } static void win32_free_framedata(vo_frame_t* vo_frame) { win32_frame_t * frame = ( win32_frame_t * ) vo_frame; if(frame->vo_frame.base[0]) { free(frame->vo_frame.base[0]); frame->vo_frame.base[0] = NULL; } if(frame->vo_frame.base[1]) { free(frame->vo_frame.base[1]); frame->vo_frame.base[1] = NULL; } if(frame->vo_frame.base[2]) { free(frame->vo_frame.base[2]); frame->vo_frame.base[2] = NULL; } } static void win32_frame_dispose( vo_frame_t * vo_frame ) { win32_frame_t * win32_frame = ( win32_frame_t * ) vo_frame; free(win32_frame->buffer); win32_free_framedata(vo_frame); free( win32_frame ); } static vo_frame_t * win32_alloc_frame( vo_driver_t * vo_driver ) { win32_frame_t *win32_frame; win32_frame = calloc(1, sizeof(win32_frame_t)); if (!win32_frame) return NULL; pthread_mutex_init(&win32_frame->vo_frame.mutex, NULL); win32_frame->vo_frame.proc_slice = NULL; win32_frame->vo_frame.proc_frame = NULL; win32_frame->vo_frame.field = win32_frame_field; win32_frame->vo_frame.dispose = win32_frame_dispose; win32_frame->format = -1; return ( vo_frame_t * ) win32_frame; } static void win32_update_frame_format( vo_driver_t * vo_driver, vo_frame_t * vo_frame, uint32_t width, uint32_t height, double ratio, int format, int flags ) { win32_driver_t *win32_driver = ( win32_driver_t * ) vo_driver; win32_frame_t *win32_frame = ( win32_frame_t * ) vo_frame; /*printf("vo_out_directx : win32_update_frame_format() - width = %d, height=%d, ratio_code=%d, format=%d, flags=%d\n", width, height, ratio_code, format, flags);*/ if( ( win32_frame->format != format ) || ( win32_frame->width != width ) || ( win32_frame->height != height ) ) { /* free our allocated memory */ win32_free_framedata((vo_frame_t *)&win32_frame->vo_frame); /* create new render buffer */ if( format == XINE_IMGFMT_YV12 ) { win32_frame->vo_frame.pitches[0] = 8*((width + 7) / 8); win32_frame->vo_frame.pitches[1] = 8*((width + 15) / 16); win32_frame->vo_frame.pitches[2] = 8*((width + 15) / 16); win32_frame->vo_frame.base[0] = malloc(win32_frame->vo_frame.pitches[0] * height); win32_frame->vo_frame.base[1] = malloc(win32_frame->vo_frame.pitches[1] * ((height+1)/2)); win32_frame->vo_frame.base[2] = malloc(win32_frame->vo_frame.pitches[2] * ((height+1)/2)); win32_frame->size = win32_frame->vo_frame.pitches[0] * height * 2; } else if( format == XINE_IMGFMT_YUY2 ) { win32_frame->vo_frame.pitches[0] = 8*((width + 3) / 4); win32_frame->vo_frame.base[0] = malloc(win32_frame->vo_frame.pitches[0] * height * 2); win32_frame->vo_frame.base[1] = NULL; win32_frame->vo_frame.base[2] = NULL; win32_frame->size = win32_frame->vo_frame.pitches[0] * height * 2; } #if RGB_SUPPORT else if( format == IMGFMT_RGB ) { win32_frame->size = width * height * 3; win32_frame->buffer = malloc( win32_frame->size ); vo_frame->base[0] = win32_frame->buffer; } #endif else { xprintf (win32_driver->xine, XINE_VERBOSITY_DEBUG, "vo_out_directx : !!! unsupported image format %04x !!!\n", format ); exit (1); } win32_frame->format = format; win32_frame->width = width; win32_frame->height = height; win32_frame->ratio = ratio; } } static void win32_display_frame( vo_driver_t * vo_driver, vo_frame_t * vo_frame ) { win32_driver_t *win32_driver = ( win32_driver_t * ) vo_driver; win32_frame_t *win32_frame = ( win32_frame_t * ) vo_frame; int offset; int size; /* if the required width, height or format has changed * then recreate the secondary buffer */ if( ( win32_driver->req_format != win32_frame->format ) || ( win32_driver->width != win32_frame->width ) || ( win32_driver->height != win32_frame->height ) ) { CreateSecondary( win32_driver, win32_frame->width, win32_frame->height, win32_frame->format ); } /* determine desired ratio */ win32_driver->ratio = win32_frame->ratio; /* lock our surface to update its contents */ win32_driver->contents = Lock( win32_driver->secondary ); /* surface unavailable, skip frame render */ if( !win32_driver->contents ) { vo_frame->free( vo_frame ); return; } /* if our actual frame format is the native screen * pixel format, we need to convert it */ if( win32_driver->act_format == IMGFMT_NATIVE ) { /* use the software color conversion functions * to rebuild the frame in our native screen * pixel format ... this is slow */ if( win32_driver->req_format == XINE_IMGFMT_YV12 ) { /* convert from yv12 to native * screen pixel format */ #if NEW_YUV win32_driver->yuv2rgb->configure( win32_driver->yuv2rgb, win32_driver->width, win32_driver->height, win32_frame->vo_frame.pitches[0], win32_frame->vo_frame.pitches[1], win32_driver->width, win32_driver->height, win32_driver->width * win32_driver->bytespp); #else yuv2rgb_setup( win32_driver->yuv2rgb, win32_driver->width, win32_driver->height, win32_frame->vo_frame.pitches[0], win32_frame->vo_frame.pitches[1], win32_driver->width, win32_driver->height, win32_driver->width * win32_driver->bytespp ); #endif win32_driver->yuv2rgb->yuv2rgb_fun( win32_driver->yuv2rgb, win32_driver->contents, win32_frame->vo_frame.base[0], win32_frame->vo_frame.base[1], win32_frame->vo_frame.base[2] ); } if( win32_driver->req_format == XINE_IMGFMT_YUY2 ) { /* convert from yuy2 to native * screen pixel format */ #if NEW_YUV win32_driver->yuv2rgb->configure( win32_driver->yuv2rgb, win32_driver->width, win32_driver->height, win32_frame->vo_frame.pitches[0], win32_frame->vo_frame.pitches[0] / 2, win32_driver->width, win32_driver->height, win32_driver->width * win32_driver->bytespp ); #else yuv2rgb_setup( win32_driver->yuv2rgb, win32_driver->width, win32_driver->height, win32_frame->vo_frame.pitches[0], win32_frame->vo_frame.pitches[0] / 2, win32_driver->width, win32_driver->height, win32_driver->width * win32_driver->bytespp ); #endif win32_driver->yuv2rgb->yuy22rgb_fun( win32_driver->yuv2rgb, win32_driver->contents, win32_frame->vo_frame.base[0] ); } #if RGB_SUPPORT if( win32_driver->req_format == IMGFMT_RGB ) { /* convert from 24 bit rgb to native * screen pixel format */ /* TODO : rgb2rgb conversion */ } #endif } else { /* the actual format is identical to our * stream format. we just need to copy it */ switch(win32_frame->format) { case XINE_IMGFMT_YV12: { vo_frame_t *frame; uint8_t *img; frame = vo_frame; img = (uint8_t *)win32_driver->contents; offset = 0; size = frame->pitches[0] * frame->height; xine_fast_memcpy( img+offset, frame->base[0], size); offset += size; size = frame->pitches[2]* frame->height / 2; xine_fast_memcpy( img+offset, frame->base[2], size); offset += size; size = frame->pitches[1] * frame->height / 2; xine_fast_memcpy( img+offset, frame->base[1], size); } break; case XINE_IMGFMT_YUY2: xine_fast_memcpy( win32_driver->contents, win32_frame->vo_frame.base[0], win32_frame->vo_frame.pitches[0] * win32_frame->vo_frame.height * 2); break; default: xine_fast_memcpy( win32_driver->contents, win32_frame->vo_frame.base[0], win32_frame->vo_frame.pitches[0] * win32_frame->vo_frame.height * 2); break; } } /* unlock the surface */ Unlock( win32_driver->secondary ); /* scale, clip and display our frame */ DisplayFrame( win32_driver ); /* tag our frame as displayed */ if((win32_driver->current != NULL) && ((vo_frame_t *)win32_driver->current != vo_frame)) { vo_frame->free(&win32_driver->current->vo_frame); } win32_driver->current = (win32_frame_t *)vo_frame; } static void win32_overlay_blend( vo_driver_t * vo_driver, vo_frame_t * vo_frame, vo_overlay_t * vo_overlay ) { win32_frame_t * win32_frame = ( win32_frame_t * ) vo_frame; win32_driver_t * win32_driver = ( win32_driver_t * ) vo_driver; win32_driver->alphablend_extra_data.offset_x = vo_frame->overlay_offset_x; win32_driver->alphablend_extra_data.offset_y = vo_frame->overlay_offset_y; /* temporary overlay support, somthing more appropriate * for win32 will be devised at a later date */ if( vo_overlay->rle ) { if( vo_frame->format == XINE_IMGFMT_YV12 ) _x_blend_yuv( win32_frame->vo_frame.base, vo_overlay, win32_frame->width, win32_frame->height, win32_frame->vo_frame.pitches, &win32_driver->alphablend_extra_data ); else _x_blend_yuy2( win32_frame->vo_frame.base[0], vo_overlay, win32_frame->width, win32_frame->height, win32_frame->vo_frame.pitches[0], &win32_driver->alphablend_extra_data ); } } static int win32_get_property( vo_driver_t * vo_driver, int property ) { lprintf( "win32_get_property\n" ); return 0; } static int win32_set_property( vo_driver_t * vo_driver, int property, int value ) { return value; } static void win32_get_property_min_max( vo_driver_t * vo_driver, int property, int * min, int * max ) { *min = 0; *max = 0; } static int win32_gui_data_exchange( vo_driver_t * vo_driver, int data_type, void * data ) { win32_driver_t *win32_driver = ( win32_driver_t * ) vo_driver; switch( data_type ) { case GUI_WIN32_MOVED_OR_RESIZED: UpdateRect( win32_driver->win32_visual ); DisplayFrame( win32_driver ); break; } return 0; } static int win32_redraw_needed(vo_driver_t* this_gen) { int ret = 0; /* TC - May need to revisit this! */ #ifdef TC win32_driver_t *win32_driver = (win32_driver_t *) this_gen; if( _x_vo_scale_redraw_needed( &win32_driver->sc ) ) { win32_gui_data_exchange(this_gen, GUI_WIN32_MOVED_OR_RESIZED, 0); ret = 1; } #endif return ret; } static void win32_exit( vo_driver_t * vo_driver ) { win32_driver_t *win32_driver = ( win32_driver_t * ) vo_driver; free(win32_driver->win32_visual); Destroy( win32_driver ); } static vo_driver_t *open_plugin (video_driver_class_t *class_gen, const void *win32_visual) /*vo_driver_t *init_video_out_plugin( config_values_t * config, void * win32_visual )*/ { directx_class_t *class = (directx_class_t *)class_gen; win32_driver_t *win32_driver = calloc(1, sizeof(win32_driver_t)); _x_alphablend_init(&win32_driver->alphablend_extra_data, class->xine); win32_driver->xine = class->xine; /* Make sure that the DirectX drivers are available and present! */ /* Not complete yet */ win32_driver->win32_visual = (win32_visual_t *)win32_visual; win32_driver->vo_driver.get_capabilities = win32_get_capabilities; win32_driver->vo_driver.alloc_frame = win32_alloc_frame ; win32_driver->vo_driver.update_frame_format = win32_update_frame_format; win32_driver->vo_driver.display_frame = win32_display_frame; win32_driver->vo_driver.overlay_blend = win32_overlay_blend; win32_driver->vo_driver.get_property = win32_get_property; win32_driver->vo_driver.set_property = win32_set_property; win32_driver->vo_driver.get_property_min_max = win32_get_property_min_max; win32_driver->vo_driver.gui_data_exchange = win32_gui_data_exchange; win32_driver->vo_driver.dispose = win32_exit; win32_driver->vo_driver.redraw_needed = win32_redraw_needed; if (!CreatePrimary( win32_driver )) { Destroy( win32_driver ); return NULL; } if( !CheckPixelFormat( win32_driver ) ) { Error( 0, "vo_directx : Your screen pixel format is not supported" ); Destroy( win32_driver ); return NULL; } #if (NEW_YUV) win32_driver->yuv2rgb_factory = yuv2rgb_factory_init( win32_driver->mode, 0, 0 ); win32_driver->yuv2rgb = win32_driver->yuv2rgb_factory->create_converter(win32_driver->yuv2rgb_factory); #else win32_driver->yuv2rgb = yuv2rgb_init( win32_driver->mode, 0, 0 ); #endif return ( vo_driver_t * ) win32_driver; } static char* get_identifier (video_driver_class_t *this_gen) { return "DirectX"; } static char* get_description (video_driver_class_t *this_gen) { return _("xine video output plugin for win32 using directx"); } static void dispose_class (video_driver_class_t *this_gen) { directx_class_t *directx = (directx_class_t *) this_gen; free (directx); } static void *init_class (xine_t *xine, void *visual_gen) { directx_class_t *directx; /* * from this point on, nothing should go wrong anymore */ directx = calloc(1, sizeof (directx_class_t)); directx->driver_class.open_plugin = open_plugin; directx->driver_class.get_identifier = get_identifier; directx->driver_class.get_description = get_description; directx->driver_class.dispose = dispose_class; directx->xine = xine; directx->config = xine->config; return directx; } static const vo_info_t vo_info_win32 = { 7, /* priority */ XINE_VISUAL_TYPE_DIRECTX /* visual type */ }; /* * exported plugin catalog entry */ const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_VIDEO_OUT, 21, "vo_directx", XINE_VERSION_CODE, &vo_info_win32, init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };