| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2020 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / DirectX audio and video render module | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC 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 Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "dx_hw.h" | |
| #define DDCONTEXT DDContext *dd = (DDContext *)dr->opaque; | |
| #define DDBACK DDSurface *pBack = (DDSurface *) gf_list_get(dd->surfaces, 0); | |
| enum | |
| { | |
| GF_PIXEL_IYUV = GF_4CC('I', 'Y', 'U', 'V'), | |
| GF_PIXEL_I420 = GF_4CC('I', '4', '2', '0'), | |
| /*!YUV packed format*/ | |
| GF_PIXEL_UYNV = GF_4CC('U', 'Y', 'N', 'V'), | |
| /*!YUV packed format*/ | |
| GF_PIXEL_YUNV = GF_4CC('Y', 'U', 'N', 'V'), | |
| /*!YUV packed format*/ | |
| GF_PIXEL_V422 = GF_4CC('V', '4', '2', '2'), | |
| GF_PIXEL_YV12 = GF_4CC('Y', 'V', '1', '2'), | |
| GF_PIXEL_Y422 = GF_4CC('Y', '4', '2', '2'), | |
| GF_PIXEL_YUY2 = GF_4CC('Y', 'U', 'Y', '2'), | |
| }; | |
| static Bool pixelformat_yuv(u32 pixel_format) | |
| { | |
| switch (pixel_format) { | |
| case GF_PIXEL_YUYV: | |
| case GF_PIXEL_YVYU: | |
| case GF_PIXEL_UYVY: | |
| case GF_PIXEL_VYUY: | |
| case GF_PIXEL_UYNV: | |
| case GF_PIXEL_YUNV: | |
| case GF_PIXEL_V422: | |
| case GF_PIXEL_YUV: | |
| case GF_PIXEL_IYUV: | |
| case GF_PIXEL_I420: | |
| case GF_PIXEL_YUV_10: | |
| case GF_PIXEL_YUV444: | |
| case GF_PIXEL_YUV444_10: | |
| case GF_PIXEL_YUV422: | |
| case GF_PIXEL_YUV422_10: | |
| return GF_TRUE; | |
| default: | |
| return GF_FALSE; | |
| } | |
| } | |
| static u32 get_win_4CC(u32 pixel_format) | |
| { | |
| switch (pixel_format) { | |
| case GF_PIXEL_YUYV: | |
| return mmioFOURCC('Y', 'U', 'Y', '2'); | |
| case GF_PIXEL_YVYU: | |
| return mmioFOURCC('Y', 'V', 'Y', 'U'); | |
| case GF_PIXEL_UYVY: | |
| return mmioFOURCC('U', 'Y', 'V', 'Y'); | |
| case GF_PIXEL_VYUY: | |
| return mmioFOURCC('V', 'Y', 'U', 'Y'); | |
| case GF_PIXEL_YUV422: | |
| return mmioFOURCC('Y', '4', '2', '2'); | |
| case GF_PIXEL_UYNV: | |
| return mmioFOURCC('U', 'Y', 'N', 'V'); | |
| case GF_PIXEL_YUNV: | |
| return mmioFOURCC('Y', 'U', 'N', 'V'); | |
| case GF_PIXEL_V422: | |
| return mmioFOURCC('V', '4', '2', '2'); | |
| case GF_PIXEL_YUV: | |
| return mmioFOURCC('Y', 'V', '1', '2'); | |
| case GF_PIXEL_IYUV: | |
| return mmioFOURCC('I', 'Y', 'U', 'V'); | |
| case GF_PIXEL_I420: | |
| return mmioFOURCC('I', '4', '2', '0'); | |
| case GF_PIXEL_YUV_10: | |
| return mmioFOURCC('P', '0', '1', '0'); | |
| case GF_PIXEL_YUV444: | |
| return mmioFOURCC('Y', '4', '4', '4'); | |
| case GF_PIXEL_YUV444_10: | |
| return mmioFOURCC('Y', '4', '1', '0'); | |
| case GF_PIXEL_YUV422_10: | |
| return mmioFOURCC('Y', '2', '1', '0'); | |
| default: | |
| return 0; | |
| } | |
| } | |
| /*!\hideinitializer transfoms a 32-bits color into a 16-bits one.\note alpha component is lost*/ | |
| #define GF_COL_TO_565(c) (((GF_COL_R(c) & 248)<<8) + ((GF_COL_G(c) & 252)<<3) + (GF_COL_B(c)>>3)) | |
| /*!\hideinitializer transfoms a 32-bits color into a 15-bits one.\note alpha component is lost*/ | |
| #define GF_COL_TO_555(c) (((GF_COL_R(c) & 248)<<7) + ((GF_COL_G(c) & 248)<<2) + (GF_COL_B(c)>>3)) | |
| static GF_Err DD_ClearBackBuffer(GF_VideoOutput *dr, u32 color) | |
| { | |
| HRESULT hr; | |
| RECT rc; | |
| DDBLTFX ddbltfx; | |
| DDCONTEXT; | |
| // Erase the background | |
| ZeroMemory( &ddbltfx, sizeof(ddbltfx) ); | |
| ddbltfx.dwSize = sizeof(ddbltfx); | |
| switch (dd->pixelFormat) { | |
| case GF_PIXEL_RGB_565: | |
| ddbltfx.dwFillColor = GF_COL_TO_565(color); | |
| break; | |
| case GF_PIXEL_RGB_555: | |
| ddbltfx.dwFillColor = GF_COL_TO_555(color); | |
| break; | |
| default: | |
| ddbltfx.dwFillColor = color; | |
| break; | |
| } | |
| rc.left = rc.top = 0; | |
| rc.right = dd->width; | |
| rc.bottom = dd->height; | |
| hr = dd->pBack->lpVtbl->Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx); | |
| return FAILED(hr) ? GF_IO_ERR : GF_OK; | |
| } | |
| GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_system_memory) | |
| { | |
| Bool force_reinit; | |
| HRESULT hr; | |
| const char *opt; | |
| DDSURFDESC ddsd; | |
| DDCONTEXT; | |
| opt = gf_module_get_key((GF_BaseInterface*)dr, "hwvmem"); | |
| if (opt) { | |
| if (use_system_memory) { | |
| if (!strcmp(opt, "always")) use_system_memory = GF_FALSE; | |
| } else { | |
| if (!strcmp(opt, "never")) use_system_memory = GF_TRUE; | |
| else if (!strcmp(opt, "auto")) dd->force_video_mem_for_yuv = GF_TRUE; | |
| } | |
| } | |
| force_reinit = GF_FALSE; | |
| if (use_system_memory && !dd->systems_memory) force_reinit = GF_TRUE; | |
| else if (!use_system_memory && dd->systems_memory) force_reinit = GF_TRUE; | |
| if (dd->pBack && !force_reinit&& !dd->fullscreen && (dd->width == Width) && (dd->height == Height) ) { | |
| return GF_OK; | |
| } | |
| SAFE_DD_RELEASE(dd->pBack); | |
| /*create backbuffer*/ | |
| ZeroMemory(&ddsd, sizeof(ddsd)); | |
| ddsd.dwSize = sizeof(ddsd); | |
| ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; | |
| ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; | |
| if (dd->systems_memory==2) { | |
| ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; | |
| } else { | |
| if (!use_system_memory) { | |
| dd->systems_memory = GF_FALSE; | |
| ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; | |
| } else { | |
| ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; | |
| dd->systems_memory = GF_TRUE; | |
| } | |
| } | |
| if (dd->systems_memory) dr->hw_caps &= ~GF_VIDEO_HW_HAS_RGB; | |
| else dr->hw_caps |= GF_VIDEO_HW_HAS_RGB; | |
| ddsd.dwWidth = Width; | |
| ddsd.dwHeight = Height; | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); | |
| if (FAILED(hr)) { | |
| if (!dd->systems_memory) { | |
| ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY; | |
| ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; | |
| dd->systems_memory = 2; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Hardware Video not available for backbuffer)\n")); | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); | |
| } | |
| if (FAILED(hr)) return GF_IO_ERR; | |
| } | |
| /*store size*/ | |
| if (!dd->fullscreen) { | |
| dd->width = Width; | |
| dd->height = Height; | |
| } | |
| DD_ClearBackBuffer(dr, 0xFF000000); | |
| if (!dd->yuv_init) DD_InitYUV(dr); | |
| GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] BackBuffer %d x %d created on %s memory\n", Width, Height, dd->systems_memory ? "System" : "Video")); | |
| return GF_OK; | |
| } | |
| GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) | |
| { | |
| HRESULT hr; | |
| DWORD cooplev; | |
| LPDIRECTDRAW ddraw; | |
| DDSURFDESC ddsd; | |
| DDPIXELFORMAT pixelFmt; | |
| LPDIRECTDRAWCLIPPER pcClipper; | |
| DDCONTEXT; | |
| if (!dd->cur_hwnd || !Width || !Height || !dd->DirectDrawCreate) return GF_BAD_PARAM; | |
| DestroyObjects(dd); | |
| if( FAILED( hr = dd->DirectDrawCreate(NULL, &ddraw, NULL ) ) ) | |
| return GF_IO_ERR; | |
| hr = ddraw->lpVtbl->QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); | |
| ddraw->lpVtbl->Release(ddraw); | |
| if (FAILED(hr)) return GF_IO_ERR; | |
| dd->switch_res = GF_FALSE; | |
| cooplev = DDSCL_NORMAL; | |
| /*Setup FS*/ | |
| if (dd->fullscreen) { | |
| /*change display mode*/ | |
| if (dd->switch_res) { | |
| #ifdef USE_DX_3 | |
| hr = dd->pDD->lpVtbl->SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp); | |
| #else | |
| hr = dd->pDD->lpVtbl->SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp, 0, 0 ); | |
| #endif | |
| if( FAILED(hr)) return GF_IO_ERR; | |
| } | |
| dd->NeedRestore = 1; | |
| // cooplev = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN; | |
| } | |
| hr = dd->pDD->lpVtbl->SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev); | |
| if( FAILED(hr) ) return GF_IO_ERR; | |
| /*create primary*/ | |
| ZeroMemory( &ddsd, sizeof( ddsd ) ); | |
| ddsd.dwSize = sizeof( ddsd ); | |
| ddsd.dwFlags = DDSD_CAPS; | |
| ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL); | |
| if( FAILED(hr) ) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DX Out] Failed creating primary surface: error %08x\n", hr)); | |
| return GF_IO_ERR; | |
| } | |
| /*get pixel format of video board*/ | |
| memset (&pixelFmt, 0, sizeof(pixelFmt)); | |
| pixelFmt.dwSize = sizeof(pixelFmt); | |
| hr = dd->pPrimary->lpVtbl->GetPixelFormat(dd->pPrimary, &pixelFmt); | |
| if( FAILED(hr) ) return GF_IO_ERR; | |
| switch(pixelFmt.dwRGBBitCount) { | |
| case 16: | |
| if ((pixelFmt.dwRBitMask == 0xf800) && (pixelFmt.dwGBitMask == 0x07e0) && (pixelFmt.dwBBitMask == 0x001f)) | |
| dd->pixelFormat = GF_PIXEL_RGB_565; | |
| else if ((pixelFmt.dwRBitMask == 0x7c00) && (pixelFmt.dwGBitMask == 0x03e0) && (pixelFmt.dwBBitMask == 0x001f)) | |
| dd->pixelFormat = GF_PIXEL_RGB_555; | |
| else | |
| return GF_NOT_SUPPORTED; | |
| dd->video_bpp = 16; | |
| break; | |
| case 24: | |
| if ((pixelFmt.dwRBitMask == 0x0000FF) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0xFF0000)) | |
| dd->pixelFormat = GF_PIXEL_BGR; | |
| else if ((pixelFmt.dwRBitMask == 0xFF0000) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0x0000FF)) | |
| dd->pixelFormat = GF_PIXEL_RGB; | |
| dd->video_bpp = 24; | |
| break; | |
| case 32: | |
| if ((pixelFmt.dwRBitMask == 0x0000FF) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0xFF0000)) { | |
| dd->pixelFormat = dd->force_alpha ? GF_PIXEL_RGBA : GF_PIXEL_BGRX; | |
| } else if ((pixelFmt.dwRBitMask == 0xFF0000) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0x0000FF)) { | |
| dd->pixelFormat = dd->force_alpha ? GF_PIXEL_ARGB : GF_PIXEL_RGBX; | |
| } | |
| dd->video_bpp = 32; | |
| break; | |
| default: | |
| return GF_IO_ERR; | |
| } | |
| hr = dd->pDD->lpVtbl->CreateClipper(dd->pDD, 0, &pcClipper, NULL); | |
| if( FAILED(hr)) | |
| return GF_IO_ERR; | |
| hr = pcClipper->lpVtbl->SetHWnd(pcClipper, 0, dd->cur_hwnd); | |
| if( FAILED(hr) ) { | |
| pcClipper->lpVtbl->Release(pcClipper); | |
| return GF_IO_ERR; | |
| } | |
| hr = dd->pPrimary->lpVtbl->SetClipper(dd->pPrimary, pcClipper); | |
| if( FAILED(hr)) { | |
| pcClipper->lpVtbl->Release(pcClipper); | |
| return GF_IO_ERR; | |
| } | |
| pcClipper->lpVtbl->Release(pcClipper); | |
| dd->ddraw_init = GF_TRUE; | |
| /*if YUV not initialize, init using HW video memory to setup HW caps*/ | |
| return GF_OK; | |
| } | |
| static GF_Err DD_LockSurface(DDContext *dd, GF_VideoSurface *vi, LPDDRAWSURFACE surface) | |
| { | |
| HRESULT hr; | |
| DDSURFDESC desc; | |
| if (!dd || !vi || !surface) return GF_BAD_PARAM; | |
| ZeroMemory(&desc, sizeof(desc)); | |
| desc.dwSize = sizeof(DDSURFACEDESC); | |
| hr = surface->lpVtbl->Lock(surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL); | |
| if (FAILED(hr)) | |
| return GF_IO_ERR; | |
| vi->video_buffer = (char*)desc.lpSurface; | |
| vi->width = desc.dwWidth; | |
| vi->height = desc.dwHeight; | |
| vi->pitch_x = 0; | |
| vi->pitch_y = desc.lPitch; | |
| vi->is_hardware_memory = dd->systems_memory ? GF_FALSE : GF_TRUE; | |
| return GF_OK; | |
| } | |
| static GF_Err DD_UnlockSurface(DDContext *dd, LPDDRAWSURFACE surface) | |
| { | |
| HRESULT hr; | |
| if (!dd || !dd->ddraw_init) return GF_IO_ERR; | |
| hr = surface->lpVtbl->Unlock(surface, NULL); | |
| return FAILED(hr) ? GF_IO_ERR : GF_OK; | |
| } | |
| static GF_Err DD_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock) | |
| { | |
| DDCONTEXT; | |
| if (do_lock) { | |
| memset(vi, 0, sizeof(GF_VideoSurface)); | |
| vi->pixel_format = dd->pixelFormat; | |
| return DD_LockSurface(dd, vi, dd->pBack); | |
| } | |
| else return DD_UnlockSurface(dd, dd->pBack); | |
| } | |
| static void *LockOSContext(GF_VideoOutput *dr, Bool do_lock) | |
| { | |
| DDCONTEXT; | |
| if (!dd->pBack) return NULL; | |
| if (do_lock) { | |
| if (!dd->lock_hdc && ! dd->pBack->lpVtbl->IsLost(dd->pBack)) { | |
| if (FAILED(dd->pBack->lpVtbl->GetDC(dd->pBack, &dd->lock_hdc)) ) | |
| dd->lock_hdc = NULL; | |
| } | |
| } else if (dd->lock_hdc) { | |
| dd->pBack->lpVtbl->ReleaseDC(dd->pBack, dd->lock_hdc); | |
| dd->lock_hdc = NULL; | |
| } | |
| return (void *)dd->lock_hdc ; | |
| } | |
| static GF_Err DD_BlitSurface(DDContext *dd, DDSurface *src, GF_Window *src_wnd, GF_Window *dst_wnd, GF_ColorKey *key) | |
| { | |
| HRESULT hr; | |
| u32 dst_w, dst_h, src_w, src_h, flags; | |
| RECT r_dst, r_src; | |
| u32 left, top; | |
| src_w = src_wnd ? src_wnd->w : src->width; | |
| src_h = src_wnd ? src_wnd->h : src->height; | |
| dst_w = dst_wnd ? dst_wnd->w : dd->width; | |
| dst_h = dst_wnd ? dst_wnd->h : dd->height; | |
| if (src_wnd != NULL) MAKERECT(r_src, src_wnd); | |
| if (dst_wnd != NULL) MAKERECT(r_dst, dst_wnd); | |
| if (key) { | |
| u32 col; | |
| DDCOLORKEY ck; | |
| col = GF_COL_ARGB(0xFF, key->r, key->g, key->b); | |
| ck.dwColorSpaceHighValue = ck.dwColorSpaceLowValue = col; | |
| hr = src->pSurface->lpVtbl->SetColorKey(src->pSurface, DDCKEY_SRCBLT, &ck); | |
| if (FAILED(hr)) | |
| return GF_IO_ERR; | |
| } | |
| if ((dst_w==src_w) && (dst_h==src_h)) { | |
| flags = DDBLTFAST_WAIT; | |
| if (key) flags |= DDBLTFAST_SRCCOLORKEY; | |
| if (dst_wnd) { | |
| left = r_dst.left; | |
| top = r_dst.top; | |
| } | |
| else left = top = 0; | |
| hr = dd->pBack->lpVtbl->BltFast(dd->pBack, left, top, src->pSurface, src_wnd ? &r_src : NULL, flags); | |
| } else { | |
| flags = DDBLT_WAIT; | |
| if (key) flags |= DDBLT_KEYSRC; | |
| hr = dd->pBack->lpVtbl->Blt(dd->pBack, dst_wnd ? &r_dst : NULL, src->pSurface, src_wnd ? &r_src : NULL, flags, NULL); | |
| } | |
| if (src->is_yuv && dd->force_video_mem_for_yuv && dd->systems_memory) return GF_IO_ERR; | |
| #ifndef GPAC_DISABLE_LOG | |
| if (hr) { | |
| GF_LOG(dd->systems_memory ? GF_LOG_INFO : GF_LOG_ERROR, GF_LOG_MMIO, ("[DX Out] Failed blitting %s %s memory: error %08x\n", src->is_yuv ? "YUV" : "RGB", dd->systems_memory ? "systems" : "hardware", hr)); | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX Out] %s blit %s memory %dx%d -> %dx%d\n", src->is_yuv ? "YUV" : "RGB", dd->systems_memory ? "systems" : "hardware", src_w, src_h, dst_w, dst_h)); | |
| } | |
| #endif | |
| return FAILED(hr) ? GF_IO_ERR : GF_OK; | |
| } | |
| static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 pixel_format, Bool check_caps) | |
| { | |
| DDSURFDESC ddsd; | |
| HRESULT hr; | |
| DDCONTEXT; | |
| if (!dd->pDD) return NULL; | |
| /*yuv format*/ | |
| if (pixelformat_yuv(pixel_format)) { | |
| /*some drivers give broken result if YUV surface dimensions are not even*/ | |
| while (width%2) width++; | |
| while (height%2) height++; | |
| if (dr->yuv_pixel_format) { | |
| DDSurface *yuvp = &dd->yuv_pool; | |
| /*don't recreate a surface if not needed*/ | |
| if (yuvp->pSurface && (yuvp->width >= width) && (yuvp->height >= height) ) return yuvp; | |
| width = MAX(yuvp->width, width); | |
| height = MAX(yuvp->height, height); | |
| SAFE_DD_RELEASE(yuvp->pSurface); | |
| memset(yuvp, 0, sizeof(DDSurface)); | |
| yuvp->is_yuv = GF_TRUE; | |
| memset (&ddsd, 0, sizeof(ddsd)); | |
| ddsd.dwSize = sizeof(ddsd); | |
| ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat); | |
| ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; | |
| ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; | |
| if (!dd->offscreen_yuv_to_rgb && (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY)) | |
| ddsd.ddsCaps.dwCaps |= DDSCAPS_OVERLAY; | |
| ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; | |
| ddsd.dwWidth = width; | |
| ddsd.dwHeight = height; | |
| ddsd.ddpfPixelFormat.dwFourCC = get_win_4CC(dr->yuv_pixel_format); | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL); | |
| if (FAILED(hr) ) { | |
| if (!check_caps) return NULL; | |
| /*try without offscreen yuv->rgbcap*/ | |
| if (dd->offscreen_yuv_to_rgb) { | |
| dd->offscreen_yuv_to_rgb = GF_FALSE; | |
| if (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) { | |
| ddsd.ddsCaps.dwCaps |= DDSCAPS_OVERLAY; | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL); | |
| if( FAILED(hr) ) { | |
| return NULL; | |
| } | |
| } else { | |
| return NULL; | |
| } | |
| } | |
| /*try without overlay cap*/ | |
| if (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) { | |
| dr->hw_caps &= ~GF_VIDEO_HW_HAS_YUV_OVERLAY; | |
| ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL); | |
| if( FAILED(hr) ) { | |
| return NULL; | |
| } | |
| } | |
| /*YUV not supported*/ | |
| else { | |
| return NULL; | |
| } | |
| } | |
| yuvp->format = dr->yuv_pixel_format; | |
| yuvp->width = width; | |
| yuvp->height = height; | |
| return yuvp; | |
| } | |
| } | |
| /*don't recreate a surface if not needed*/ | |
| if ((dd->rgb_pool.width >= width) && (dd->rgb_pool.height >= height) ) return &dd->rgb_pool; | |
| width = MAX(dd->rgb_pool.width, width); | |
| height = MAX(dd->rgb_pool.height, height); | |
| SAFE_DD_RELEASE(dd->rgb_pool.pSurface); | |
| memset(&dd->rgb_pool, 0, sizeof(DDSurface)); | |
| memset (&ddsd, 0, sizeof(ddsd)); | |
| ddsd.dwSize = sizeof(ddsd); | |
| ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; | |
| ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; | |
| ddsd.dwWidth = width; | |
| ddsd.dwHeight = height; | |
| hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL); | |
| if (FAILED(hr)) | |
| return NULL; | |
| dd->rgb_pool.width = width; | |
| dd->rgb_pool.height = height; | |
| dd->rgb_pool.format = dd->pixelFormat; | |
| return &dd->rgb_pool; | |
| } | |
| static GF_Err DD_Blit(GF_VideoOutput *dr, GF_VideoSurface *video_src, GF_Window *src_wnd, GF_Window *dst_wnd, u32 overlay_type) | |
| { | |
| GF_VideoSurface temp_surf; | |
| GF_Err e; | |
| GF_Window src_wnd_2; | |
| u32 w, h; | |
| DDSurface *pool; | |
| DDCONTEXT; | |
| if (!video_src) { | |
| if (overlay_type && dd->yuv_pool.pSurface) | |
| dd->yuv_pool.pSurface->lpVtbl->UpdateOverlay(dd->yuv_pool.pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); | |
| return GF_OK; | |
| } | |
| if (src_wnd) { | |
| w = src_wnd->w; | |
| h = src_wnd->h; | |
| } else { | |
| w = video_src->width; | |
| h = video_src->height; | |
| } | |
| /*get RGB or YUV pool surface*/ | |
| //if (video_src->pixel_format==GF_PIXEL_YUVD) return GF_NOT_SUPPORTED; | |
| if (video_src->pixel_format==GF_PIXEL_YUVD) video_src->pixel_format = GF_PIXEL_YUV; | |
| pool = DD_GetSurface(dr, w, h, video_src->pixel_format, GF_FALSE); | |
| if (!pool) | |
| return GF_IO_ERR; | |
| memset(&temp_surf, 0, sizeof(GF_VideoSurface)); | |
| temp_surf.pixel_format = pool->format; | |
| e = DD_LockSurface(dd, &temp_surf, pool->pSurface); | |
| if (e) return e; | |
| /*copy pixels to pool*/ | |
| e = gf_stretch_bits(&temp_surf, video_src, NULL, src_wnd, 0xFF, GF_FALSE, NULL, NULL); | |
| DD_UnlockSurface(dd, pool->pSurface); | |
| if (e) return e; | |
| if (overlay_type) { | |
| HRESULT hr; | |
| RECT dst_rc, src_rc; | |
| POINT pt; | |
| GF_Window dst = *dst_wnd; | |
| GF_Window src = *src_wnd; | |
| src.x = src.y = 0; | |
| MAKERECT(src_rc, (&src)); | |
| pt.x = dst.x; | |
| pt.y = dst.y; | |
| ClientToScreen(dd->cur_hwnd, &pt); | |
| dst.x = pt.x; | |
| dst.y = pt.y; | |
| MAKERECT(dst_rc, (&dst)); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX] Blit surface to dest %d x %d - overlay type %s\n", dst.w, dst.h, (overlay_type==1) ? "Top-Level" : "ColorKey" )); | |
| #if 1 | |
| if (overlay_type==1) { | |
| hr = pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_AUTOFLIP, NULL); | |
| } else { | |
| DDOVERLAYFX ddofx; | |
| memset(&ddofx, 0, sizeof(DDOVERLAYFX)); | |
| ddofx.dwSize = sizeof(DDOVERLAYFX); | |
| ddofx.dckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key; | |
| ddofx.dckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key; | |
| hr = pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE | DDOVER_AUTOFLIP, &ddofx); | |
| } | |
| if (FAILED(hr)) { | |
| pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); | |
| } | |
| #else | |
| if (overlay_type==1) { | |
| hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, 0, NULL); | |
| } else { | |
| DDBLTFX ddfx; | |
| memset(&ddfx, 0, sizeof(DDBLTFX)); | |
| ddfx.dwSize = sizeof(DDBLTFX); | |
| ddfx.ddckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key; | |
| ddfx.ddckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key; | |
| hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, DDBLT_WAIT | DDBLT_KEYDESTOVERRIDE, &ddfx); | |
| } | |
| #endif | |
| return FAILED(hr) ? GF_IO_ERR : GF_OK; | |
| } | |
| src_wnd_2.x = src_wnd_2.y = 0; | |
| if (src_wnd) { | |
| src_wnd_2.w = src_wnd->w; | |
| src_wnd_2.h = src_wnd->h; | |
| } else { | |
| src_wnd_2.w = video_src->width; | |
| src_wnd_2.h = video_src->height; | |
| } | |
| /*and blit surface*/ | |
| return DD_BlitSurface(dd, pool, &src_wnd_2, dst_wnd, NULL); | |
| } | |
| static GFINLINE u32 is_yuv_supported(u32 win_4cc) | |
| { | |
| if (win_4cc==get_win_4CC(GF_PIXEL_YV12)) return GF_PIXEL_YUV; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_I420)) return GF_PIXEL_I420; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_IYUV)) return GF_PIXEL_IYUV; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_UYVY)) return GF_PIXEL_UYVY; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_Y422)) return GF_PIXEL_YUV422; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_UYNV)) return GF_PIXEL_UYNV; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_YUY2)) return GF_PIXEL_YUYV; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_YUNV)) return GF_PIXEL_YUNV; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_V422)) return GF_PIXEL_V422; | |
| else if (win_4cc==get_win_4CC(GF_PIXEL_YVYU)) return GF_PIXEL_YVYU; | |
| else return 0; | |
| } | |
| static GFINLINE Bool is_yuv_planar(u32 format) | |
| { | |
| switch (format) { | |
| case GF_PIXEL_YUV: | |
| case GF_PIXEL_YUV_10: | |
| case GF_PIXEL_I420: | |
| case GF_PIXEL_IYUV: | |
| case GF_PIXEL_YUV422: | |
| case GF_PIXEL_YUV422_10: | |
| case GF_PIXEL_YUV444: | |
| case GF_PIXEL_YUV444_10: | |
| return GF_TRUE; | |
| default: | |
| return GF_FALSE; | |
| } | |
| } | |
| #define YUV_NUM_TEST 20 | |
| /*gets fastest YUV format for YUV to RGB blit from YUV overlay (support is quite random on most cards)*/ | |
| void DD_InitYUV(GF_VideoOutput *dr) | |
| { | |
| u32 w, h, j, i, num_yuv; | |
| Bool force_yv12 = GF_FALSE; | |
| DWORD numCodes; | |
| DWORD formats[30]; | |
| DWORD *codes; | |
| u32 now, min_packed = 0xFFFFFFFF, min_planar = 0xFFFFFFFF; | |
| u32 best_packed = 0, best_planar = 0; | |
| Bool checkPacked = TRUE; | |
| const char *opt; | |
| DDCONTEXT; | |
| w = 720; | |
| h = 576; | |
| opt = gf_module_get_key((GF_BaseInterface *)dr, "yuv-4cc"); | |
| if (opt) { | |
| char a, b, c, d; | |
| if (sscanf(opt, "%c%c%c%c", &a, &b, &c, &d)==4) { | |
| dr->yuv_pixel_format = GF_4CC(a, b, c, d); | |
| dr->hw_caps |= GF_VIDEO_HW_HAS_YUV; | |
| if (gf_module_get_bool((GF_BaseInterface *)dr, "yuv-overlay")) | |
| dr->hw_caps |= GF_VIDEO_HW_HAS_YUV_OVERLAY; | |
| dd->yuv_init = GF_TRUE; | |
| num_yuv = 1; | |
| } | |
| } | |
| /*first run on this machine, to some benchmark*/ | |
| if (! dd->yuv_init) { | |
| char szOpt[100]; | |
| dd->yuv_init = GF_TRUE; | |
| dr->hw_caps |= GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY; | |
| dd->pDD->lpVtbl->GetFourCCCodes(dd->pDD, &numCodes, NULL); | |
| if (!numCodes) return; | |
| codes = (DWORD *)gf_malloc(numCodes*sizeof(DWORD)); | |
| dd->pDD->lpVtbl->GetFourCCCodes(dd->pDD, &numCodes, codes); | |
| num_yuv = 0; | |
| for (i=0; i<numCodes; i++) { | |
| formats[num_yuv] = is_yuv_supported(codes[i]); | |
| if (formats[num_yuv]) num_yuv++; | |
| } | |
| gf_free(codes); | |
| /*too bad*/ | |
| if (!num_yuv) { | |
| dr->hw_caps &= ~(GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY); | |
| dr->yuv_pixel_format = 0; | |
| return; | |
| } | |
| for (i=0; i<num_yuv; i++) { | |
| /*check planar first*/ | |
| if (!checkPacked && !is_yuv_planar(formats[i])) goto go_on; | |
| /*then check packed */ | |
| if (checkPacked && is_yuv_planar(formats[i])) goto go_on; | |
| SAFE_DD_RELEASE(dd->yuv_pool.pSurface); | |
| memset(&dd->yuv_pool, 0, sizeof(DDSurface)); | |
| dd->yuv_pool.is_yuv = GF_TRUE; | |
| dr->yuv_pixel_format = formats[i]; | |
| if (DD_GetSurface(dr, w, h, dr->yuv_pixel_format, GF_TRUE) == NULL) | |
| goto rem_fmt; | |
| now = gf_sys_clock(); | |
| /*perform blank blit*/ | |
| for (j=0; j<YUV_NUM_TEST; j++) { | |
| if (DD_BlitSurface(dd, &dd->yuv_pool, NULL, NULL, NULL) != GF_OK) | |
| goto rem_fmt; | |
| } | |
| now = gf_sys_clock() - now; | |
| if (formats[i]== GF_PIXEL_YUV) | |
| force_yv12 = GF_TRUE; | |
| if (!checkPacked) { | |
| if (now<min_planar) { | |
| min_planar = now; | |
| best_planar = dr->yuv_pixel_format; | |
| } | |
| } else { | |
| if (now<min_packed) { | |
| min_packed = now; | |
| best_packed = dr->yuv_pixel_format; | |
| } | |
| } | |
| go_on: | |
| if (checkPacked && (i+1==num_yuv)) { | |
| i = -1; | |
| checkPacked = FALSE; | |
| } | |
| continue; | |
| rem_fmt: | |
| for (j=i; j<num_yuv-1; j++) { | |
| formats[j] = formats[j+1]; | |
| } | |
| i--; | |
| num_yuv--; | |
| } | |
| SAFE_DD_RELEASE(dd->yuv_pool.pSurface); | |
| memset(&dd->yuv_pool, 0, sizeof(DDSurface)); | |
| dd->yuv_pool.is_yuv = GF_TRUE; | |
| if (best_planar && (min_planar <= min_packed )) { | |
| dr->yuv_pixel_format = best_planar; | |
| } else { | |
| min_planar = min_packed; | |
| dr->yuv_pixel_format = best_packed; | |
| } | |
| if (force_yv12) | |
| dr->yuv_pixel_format = GF_PIXEL_YUV; | |
| /*store our options*/ | |
| sprintf(szOpt, "%c%c%c%c", (dr->yuv_pixel_format>>24) & 0xFF, (dr->yuv_pixel_format>>16) & 0xFF, (dr->yuv_pixel_format>>8) & 0xFF, (dr->yuv_pixel_format) & 0xFF); | |
| gf_opts_set_key("directx", "yuv-4cc", szOpt); | |
| gf_opts_set_key("directx", "yuv-overlay", (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) ? "yes" : "no"); | |
| } | |
| opt = gf_module_get_key((GF_BaseInterface*)dr, "hwvmem"); | |
| if (opt && !strcmp(opt, "never")) num_yuv = 0; | |
| /*too bad*/ | |
| if (!num_yuv) { | |
| dr->hw_caps &= ~(GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY); | |
| dr->yuv_pixel_format = 0; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] YUV hardware not available\n")); | |
| return; | |
| } | |
| GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Picked YUV format %s - drawn in %d ms\n", gf_4cc_to_str(dr->yuv_pixel_format), min_planar)); | |
| /*enable YUV->RGB on the backbuffer ?*/ | |
| if (gf_module_get_bool((GF_BaseInterface *)dr, "offscreen-yuv")) { | |
| dr->hw_caps &= ~GF_VIDEO_HW_HAS_YUV; | |
| } else { | |
| dd->offscreen_yuv_to_rgb = GF_TRUE; | |
| } | |
| /*get YUV overlay key*/ | |
| opt = gf_module_get_key((GF_BaseInterface *)dr, "overlay-color-key"); | |
| /*no set is the default*/ | |
| if (!opt) { | |
| opt = "0101FE"; | |
| gf_opts_set_key("directx", "overlay-color-key", "0101FE"); | |
| } | |
| sscanf(opt, "%06x", &dr->overlay_color_key); | |
| if (dr->overlay_color_key) dr->overlay_color_key |= 0xFF000000; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] YUV->RGB enabled: %s - ColorKey enabled: %s (key %x)\n", | |
| (dr->hw_caps & GF_VIDEO_HW_HAS_YUV) ? "Yes" : "No", | |
| dr->overlay_color_key ? "Yes" : "No", dr->overlay_color_key | |
| )); | |
| } | |
| GF_Err DD_SetBackBufferSize(GF_VideoOutput *dr, u32 width, u32 height, Bool use_system_memory) | |
| { | |
| GF_Err e; | |
| DDCONTEXT; | |
| #ifndef GPAC_DISABLE_3D | |
| if (dd->output_3d) return GF_BAD_PARAM; | |
| #endif | |
| if (!dd->ddraw_init) { | |
| e = InitDirectDraw(dr, width, height); | |
| if (e) return e; | |
| } | |
| return CreateBackBuffer(dr, width, height, use_system_memory); | |
| } | |
| static GF_GPACArg DirectDrawArgs[] = { | |
| GF_DEF_ARG("yuv-overlay", NULL, "enable yuv overlays", "false", NULL, GF_ARG_BOOL, 0), | |
| GF_DEF_ARG("yuv-4cc", NULL, "prefered YUV 4CC for overlays", NULL, NULL, GF_ARG_STRING, 0), | |
| GF_DEF_ARG("offscreen-yuv", NULL, "enable offscreen yuv", "true", NULL, GF_ARG_BOOL, 0), | |
| GF_DEF_ARG("overlay-color-key", NULL, "color key value for overlay", "0101FE", NULL, GF_ARG_STRING, 0), | |
| GF_DEF_ARG("no-vsync", NULL, "disable vertical synchro", "false", NULL, GF_ARG_BOOL, 0), | |
| GF_DEF_ARG("switch-vres", NULL, "enable resolution switching of display", "false", NULL, GF_ARG_BOOL, 0), | |
| GF_DEF_ARG("hwvmem", NULL, "specify (2D rendering only) memory type of main video backbuffer. Depending on the scene type, this may drastically change the playback speed\n" | |
| "- always: always on hardware\n" | |
| "- never: always on system memory\n" | |
| "- auto: selected by GPAC based on content type (graphics or video)", "auto", "auto|always|never", GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO), | |
| {0}, | |
| }; | |
| void DD_SetupDDraw(GF_VideoOutput *driv) | |
| { | |
| driv->hw_caps |= GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_STRETCH; | |
| driv->Blit = DD_Blit; | |
| driv->LockBackBuffer = DD_LockBackBuffer; | |
| driv->LockOSContext = LockOSContext; | |
| driv->args = DirectDrawArgs; | |
| driv->description = "Video output using DirectDraw"; | |
| } | |