2 Copyright 2004-2005 Chris Tallon
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
24 #include "videowin.h"
\r
25 #include "surfacewin.h"
\r
27 #include "dsallocator.h"
\r
28 #include "message.h"
\r
29 #include "command.h"
\r
31 #define BACKBUFFER_WIDTH 1280
\r
32 #define BACKBUFFER_HEIGHT 720
\r
36 typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
\r
37 typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
\r
39 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL;
\r
40 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL;
\r
42 //This is stuff for rendering the OSD
\r
52 evrstate=EVR_pres_off;
\r
55 external_driving=false;
\r
57 filter_type=D3DTEXF_FORCE_DWORD;
\r
58 lastrendertime=timeGetTime();
\r
59 event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL);
\r
60 d3dmutex = CreateMutex(NULL,FALSE,NULL);
\r
62 dxvadevicehandle=NULL;
\r
65 hlib=LoadLibrary("dxva2.dll");
\r
70 ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
\r
71 if (!ptrDXVA2CreateDirect3DDeviceManager9){
\r
76 hlib=LoadLibrary("evr.dll");
\r
82 ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface");
\r
83 if (!ptrMFCreateVideoSampleFromSurface){
\r
99 CloseHandle(d3dmutex);
\r
102 int OsdWin::getFD()
\r
104 if (!initted) return 0;
\r
108 Surface * OsdWin::createNewSurface(){
\r
109 return (Surface*)new SurfaceWin();
\r
112 int OsdWin::init(void* device)
\r
114 if (initted) return 0;
\r
115 Video* video = Video::getInstance();
\r
116 window=*((HWND*)device);
\r
117 //First Create Direct 3D Object
\r
118 d3d=Direct3DCreate9(D3D_SDK_VERSION);
\r
121 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
\r
124 // then create the Device
\r
125 D3DPRESENT_PARAMETERS d3dparas;
\r
126 ZeroMemory(&d3dparas,sizeof(d3dparas));
\r
127 d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;
\r
128 d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;
\r
129 d3dparas.Windowed=TRUE;
\r
130 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
\r
131 if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device),
\r
132 D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) {
\r
133 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
\r
136 d3ddevice->GetRenderTarget(0,&d3drtsurf);
\r
139 if (!InitVertexBuffer()) {
\r
140 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
\r
143 /* We have to determine which kind of filtering is supported*/
\r
145 d3ddevice->GetDeviceCaps(&caps);
\r
146 if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0)
\r
147 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) {
\r
148 if (filter_type==D3DTEXF_FORCE_DWORD) {
\r
149 filter_type=D3DTEXF_LINEAR;
\r
152 if (filter_type==D3DTEXF_LINEAR)
\r
154 filter_type=D3DTEXF_POINT;
\r
158 if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0)
\r
159 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) {
\r
160 if (filter_type==D3DTEXF_FORCE_DWORD)
\r
162 filter_type=D3DTEXF_POINT;
\r
165 if (filter_type==D3DTEXF_POINT) {
\r
166 filter_type=D3DTEXF_NONE;
\r
169 if (filter_type==D3DTEXF_FORCE_DWORD) {
\r
170 filter_type=D3DTEXF_NONE;
\r
175 if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false;
\r
177 d3ddevman->ResetDevice(d3ddevice,dxvatoken);
\r
184 //Now we will create the Screen
\r
185 screen = new SurfaceWin(Surface::SCREEN);
\r
186 SetEvent(event);//Now all devices are ready
\r
187 screen->create(video->getScreenWidth(), video->getScreenHeight());
\r
189 initted = 1; // must set this here or create surface won't work
\r
195 void OsdWin::LockDevice()
\r
197 if (!evrsupported) return;
\r
198 if (!dxvadevicehandle)
\r
200 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
\r
202 IDirect3DDevice9 *temp;
\r
203 d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE);
\r
207 void OsdWin::UnlockDevice()
\r
209 if (!evrsupported) return;
\r
210 if (!initted) return;
\r
211 d3ddevman->UnlockDevice(dxvadevicehandle,TRUE);
\r
212 if (dxvadevicehandle)
\r
214 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
\r
215 dxvadevicehandle=NULL;
\r
220 DWORD OsdWin::getFilterCaps()
\r
222 if (!initted) return NULL;
\r
224 d3ddevice->GetDeviceCaps(&caps);
\r
225 return caps.StretchRectFilterCaps;
\r
228 LPDIRECT3DVERTEXBUFFER9 OsdWin::InitVertexBuffer(DWORD width, DWORD height)
\r
230 LPDIRECT3DVERTEXBUFFER9 ret =NULL;
\r
231 Video* video=Video::getInstance();
\r
232 FLOAT texx=((float)video->getScreenWidth())/1024.f;
\r
233 FLOAT texy=((float)video->getScreenHeight())/1024.f;
\r
234 D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255);
\r
235 osdvertices[0].c=osdcolor;
\r
236 osdvertices[0].x=0.f-0.5f;
\r
237 osdvertices[0].y=0.f-0.5f;
\r
238 osdvertices[0].z=0.5f;
\r
239 osdvertices[0].rhw=1.f;
\r
240 osdvertices[0].u=0.f;
\r
241 osdvertices[0].v=0.f;
\r
242 osdvertices[1].c=osdcolor;
\r
243 osdvertices[1].x=((float)width)-0.5f;
\r
244 osdvertices[1].y=0.f-0.5f;
\r
245 osdvertices[1].z=0.5f;
\r
246 osdvertices[1].u=texx;
\r
247 osdvertices[1].v=0.f;
\r
248 osdvertices[1].rhw=1.f;
\r
249 osdvertices[2].c=osdcolor;
\r
250 osdvertices[2].x=((float)width)-0.5f;
\r
251 osdvertices[2].y=((float)height)-0.5f;
\r
252 osdvertices[2].z=0.5f;
\r
253 osdvertices[2].rhw=1.f;
\r
254 osdvertices[2].u=texx;
\r
255 osdvertices[2].v=texy;
\r
256 osdvertices[3].c=osdcolor;
\r
257 osdvertices[3].x=0.f-0.5f;
\r
258 osdvertices[3].y=((float)height)-0.5f;
\r
259 osdvertices[3].z=0.5f;
\r
260 osdvertices[3].rhw=1.f;
\r
261 osdvertices[3].u=0.f;
\r
262 osdvertices[3].v=texy;
\r
264 if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED,
\r
265 &ret,NULL)!=D3D_OK) {
\r
268 void *pvertex=NULL;
\r
269 if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) {
\r
272 memcpy(pvertex,osdvertices,sizeof(osdvertices));
\r
277 int OsdWin::shutdown()
\r
279 if (!initted) return 0;
\r
282 if (d3ddevman) d3ddevman->Release();
\r
283 d3drtsurf->Release();
\r
284 d3ddevice->Release();
\r
286 if (swapsurf) swapsurf->Release();
\r
287 if (swappy) swappy->Release();
\r
292 void OsdWin::screenShot(const char* fileName)
\r
294 screen->screenShot(fileName);
\r
297 void OsdWin::threadMethod()
\r
303 if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause)
\r
306 } else if (evrstate==EVR_pres_started)
\r
308 LPDIRECT3DSURFACE9 surf;
\r
309 if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime);
\r
318 if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
\r
323 if (waittime!=0) Sleep(min(10,waittime));
\r
329 void OsdWin::threadPostStopCleanup()
\r
336 // This function is called from the WinMain function in order to get Screen updates
\r
337 void OsdWin::Render()
\r
339 if (!initted) return ;
\r
340 if (external_driving) {
\r
341 DWORD time1=timeGetTime(); //Updates the Menue
\r
342 if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
\r
343 InternalRendering(NULL);
\r
344 lastrendertime=timeGetTime();
\r
346 //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
\r
349 DWORD time1=timeGetTime();
\r
350 if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
\r
351 InternalRendering(NULL);
\r
352 lastrendertime=timeGetTime();
\r
361 void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){
\r
362 if (!initted) return;
\r
363 if (external_driving) {
\r
364 InternalRendering(present);
\r
365 lastrendertime=timeGetTime();
\r
370 void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){
\r
372 HRESULT losty=d3ddevice->TestCooperativeLevel();
\r
373 if (losty==D3DERR_DEVICELOST) {
\r
376 return; //Device Lost
\r
378 if (losty==D3DERR_DEVICENOTRESET){
\r
383 WaitForSingleObject(event,INFINITE);
\r
388 LPDIRECT3DSURFACE9 targetsurf;
\r
391 targetsurf=swapsurf;
\r
392 d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target
\r
396 targetsurf=d3drtsurf;
\r
397 d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target
\r
399 D3DSURFACE_DESC targetdesc;
\r
400 targetsurf->GetDesc(&targetdesc);
\r
402 if (external_driving) {
\r
403 //Copy video to Backbuffer
\r
404 if (present!=NULL ) {
\r
405 VideoWin* video =(VideoWin*) Video::getInstance();
\r
406 /*calculating destination rect */
\r
407 RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width,
\r
408 /*video->getScreenHeight()*/targetdesc.Height};
\r
409 UCHAR mode=video->getMode();
\r
411 case Video::EIGHTH:
\r
412 destrect.right=destrect.right/2;
\r
413 destrect.bottom=destrect.bottom/2;
\r
414 case Video::QUARTER:
\r
415 destrect.right=destrect.right/2+video->getPosx()*2;
\r
416 destrect.bottom=destrect.bottom/2+video->getPosy()*2;
\r
417 destrect.left=video->getPosx()*2;
\r
418 destrect.top=video->getPosy()*2;
\r
419 d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
\r
422 D3DSURFACE_DESC surf_desc;
\r
423 present->GetDesc(&surf_desc);//for chop sides
\r
424 RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height};
\r
425 if (video->getPseudoTVsize()==Video::ASPECT4X3
\r
426 && video->getMode()==Video::NORMAL
\r
427 && video->getAspectRatio()==Video::ASPECT16X9) {
\r
428 unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.;
\r
429 sourcerect.left=(surf_desc.Width-correction)/2;
\r
430 sourcerect.right=sourcerect.left+correction;
\r
432 d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type);
\r
436 VideoWin* video =(VideoWin*) Video::getInstance();
\r
438 if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
\r
440 LPDIRECT3DVERTEXBUFFER9 vb=NULL;
\r
441 vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height);
\r
444 if (d3ddevice->BeginScene()==D3D_OK) {
\r
445 d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX));
\r
446 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
\r
447 d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture());
\r
448 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
\r
449 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE);
\r
451 d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
\r
452 d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
\r
455 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
\r
456 d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
\r
457 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
\r
458 d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE);
\r
461 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);
\r
462 d3ddevice->EndScene();
\r
463 //Show it to the user!
\r
467 if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){
\r
469 if (!external_driving) DoLost();
\r
474 if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){
\r
476 if (!external_driving) DoLost();
\r
486 // if (!external_driving) {
\r
487 // Sleep(4);//The User can wait for 4 milliseconds to see his changes
\r
491 bool OsdWin::DoLost(){
\r
492 Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
\r
494 if (external_driving && dsallocator!=NULL) {
\r
495 dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS
\r
497 //First we free up all resources
\r
498 Video* video = Video::getInstance();
\r
499 ((SurfaceWin*)screen)->ReleaseSurface();
\r
500 if (d3drtsurf) d3drtsurf->Release();
\r
502 D3DPRESENT_PARAMETERS d3dparas;
\r
503 ZeroMemory(&d3dparas,sizeof(d3dparas));
\r
504 d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;
\r
505 d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;
\r
506 d3dparas.Windowed=TRUE;
\r
507 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
\r
509 if (swapsurf) {swapsurf->Release();swapsurf=NULL;};
\r
510 if (swappy) {swappy->Release();swappy=NULL;};
\r
512 if (d3ddevice->Reset(&d3dparas)!=D3D_OK){
\r
515 d3ddevice->GetRenderTarget(0,&d3drtsurf);
\r
516 if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken);
\r
517 //InitVertexBuffer();
\r
518 //Redraw Views, Chris could you add a member function to BoxStack, so that
\r
519 // I can cause it to completely redraw the Views?
\r
520 // Otherwise the OSD would be distorted after Device Lost
\r
526 screen->create(video->getScreenWidth(), video->getScreenHeight());
\r
532 LPDIRECT3DDEVICE9 OsdWin::getD3dDev() {
\r
533 WaitForSingleObject(event,INFINITE);//We will only return if we are initted
\r
537 LPDIRECT3D9 OsdWin::getD3d() {
\r
538 WaitForSingleObject(event,INFINITE);//We will only return if we are initted
\r
542 void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads
\r
543 WaitForSingleObject(d3dmutex,INFINITE);
\r
547 void OsdWin::EndPainting() {
\r
549 ReleaseMutex(d3dmutex);
\r
552 void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) {
\r
557 d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type);
\r
558 LPDIRECT3DSWAPCHAIN9 temp=swappy;
\r
559 LPDIRECT3DSURFACE9 tempsurf=swapsurf;
\r
563 tempsurf->Release();
\r
568 external_driving=false;
\r
572 WaitForSingleObject(event,INFINITE);//We will only return if we are initted
\r
575 if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
\r
577 D3DPRESENT_PARAMETERS d3dparas;
\r
578 ZeroMemory(&d3dparas,sizeof(d3dparas));
\r
579 d3dparas.BackBufferWidth=width;
\r
580 d3dparas.BackBufferHeight=height;
\r
581 d3dparas.Windowed=TRUE;
\r
582 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
\r
583 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){
\r
584 Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
\r
587 swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf);
\r
589 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height);
\r
593 external_driving=true;
\r
598 void OsdWin::Blank() {
\r
599 WaitForSingleObject(event,INFINITE);
\r
601 d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
\r