]> git.vomp.tv Git - vompclient.git/blob - osdwin.cc
Rewritten vomp discovery protocol
[vompclient.git] / osdwin.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21
22 #include "osdwin.h"
23 #include "mtd.h"
24 #include "videowin.h"
25 #include "surfacewin.h"
26
27 #include "dsallocator.h"
28 #include "message.h"
29 #include "command.h"
30
31 #define  BACKBUFFER_WIDTH 1280
32 #define  BACKBUFFER_HEIGHT 720
33
34
35
36 typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
37 typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
38
39 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL;
40 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL;
41
42 //This is stuff for rendering the OSD
43
44
45 OsdWin::OsdWin()
46 {
47   d3d=NULL;
48   d3ddevice=NULL;
49   d3drtsurf=NULL;
50   swappy=NULL;
51   swapsurf=NULL;
52   evrstate=EVR_pres_off;
53   window=NULL;
54
55   external_driving=false;
56   dsallocator=NULL;
57   filter_type=D3DTEXF_FORCE_DWORD;
58   lastrendertime=timeGetTime();
59   event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL);
60   d3dmutex = CreateMutex(NULL,FALSE,NULL);
61   /*EVR stuff*/
62   dxvadevicehandle=NULL;
63   evrsupported=true;
64   HMODULE hlib=NULL;
65   hlib=LoadLibrary("dxva2.dll");
66   if (!hlib) {
67           evrsupported=false;
68           return;
69   }
70   ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
71   if (!ptrDXVA2CreateDirect3DDeviceManager9){
72           evrsupported=false;
73           return;
74   }
75
76   hlib=LoadLibrary("evr.dll");
77   if (!hlib) {
78           evrsupported=false;
79           return;
80   }
81  
82   ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface");
83   if (!ptrMFCreateVideoSampleFromSurface){
84           evrsupported=false;
85           return;
86   }
87   
88 }
89
90 OsdWin::~OsdWin()
91 {
92
93   if (initted) 
94   {
95           threadStop();
96                 shutdown();
97   }
98   CloseHandle(event);
99   CloseHandle(d3dmutex);
100 }
101
102 int OsdWin::getFD()
103 {
104   if (!initted) return 0;
105   return fdOsd;
106 }
107
108 int OsdWin::init(void* device)
109 {
110   if (initted) return 0;
111    Video* video = Video::getInstance();
112    window=*((HWND*)device);
113   //First Create Direct 3D Object
114   d3d=Direct3DCreate9(D3D_SDK_VERSION);
115   if (!d3d) 
116   {
117     Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
118     return 0;
119   }
120   // then create the Device
121   D3DPRESENT_PARAMETERS d3dparas;
122   ZeroMemory(&d3dparas,sizeof(d3dparas));
123   d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;
124   d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;
125   d3dparas.Windowed=TRUE;
126   d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
127   if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device),
128           D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) {
129            Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
130        return 0;
131   }
132   d3ddevice->GetRenderTarget(0,&d3drtsurf);
133
134   /*
135   if (!InitVertexBuffer()) {
136            Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
137                   return 0;
138   }*/
139   /* We have to determine which kind of filtering is supported*/
140   D3DCAPS9 caps;
141   d3ddevice->GetDeviceCaps(&caps);
142   if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0)
143           && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) {
144           if (filter_type==D3DTEXF_FORCE_DWORD) {
145                         filter_type=D3DTEXF_LINEAR;
146           }
147   } else {
148       if (filter_type==D3DTEXF_LINEAR)
149       {
150           filter_type=D3DTEXF_POINT;
151       }
152   }
153   
154   if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0)
155           && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) {
156        if (filter_type==D3DTEXF_FORCE_DWORD) 
157        {
158                   filter_type=D3DTEXF_POINT;
159    }
160    } else {
161        if (filter_type==D3DTEXF_POINT) {
162            filter_type=D3DTEXF_NONE;
163    }
164    }
165    if (filter_type==D3DTEXF_FORCE_DWORD) {
166        filter_type=D3DTEXF_NONE;
167    }
168
169    if (evrsupported)
170    {
171            if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false;
172            else {
173                    d3ddevman->ResetDevice(d3ddevice,dxvatoken);
174            }
175    }
176
177
178
179
180   //Now we will create the Screen
181   screen = new SurfaceWin(Surface::SCREEN);
182   SetEvent(event);//Now all devices are ready
183   screen->create(video->getScreenWidth(), video->getScreenHeight());
184   screen->display();
185   initted = 1; // must set this here or create surface won't work
186   threadStart(); 
187
188   return 1;
189 }
190
191 void OsdWin::LockDevice()
192 {
193         if (!evrsupported) return;
194         if (!dxvadevicehandle) 
195         {
196                 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
197         }
198         IDirect3DDevice9 *temp;
199         d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE);
200
201 }
202
203 void OsdWin::UnlockDevice()
204 {
205         if (!evrsupported) return;
206         if (!initted) return;
207         d3ddevman->UnlockDevice(dxvadevicehandle,TRUE);
208         if (dxvadevicehandle) 
209         {
210                 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
211                 dxvadevicehandle=NULL;
212         }
213
214 }
215
216 DWORD OsdWin::getFilterCaps()
217 {
218     if (!initted) return NULL;
219     D3DCAPS9 caps;
220     d3ddevice->GetDeviceCaps(&caps);
221     return caps.StretchRectFilterCaps;
222 }
223         
224 LPDIRECT3DVERTEXBUFFER9  OsdWin::InitVertexBuffer(DWORD width, DWORD height)
225 {
226   LPDIRECT3DVERTEXBUFFER9 ret =NULL;
227   Video* video=Video::getInstance();
228   FLOAT texx=((float)video->getScreenWidth())/1024.f;
229   FLOAT texy=((float)video->getScreenHeight())/1024.f;
230   D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255);
231   osdvertices[0].c=osdcolor;
232   osdvertices[0].x=0.f-0.5f;
233   osdvertices[0].y=0.f-0.5f;
234   osdvertices[0].z=0.5f;
235   osdvertices[0].rhw=1.f;
236   osdvertices[0].u=0.f;
237   osdvertices[0].v=0.f;
238   osdvertices[1].c=osdcolor;
239   osdvertices[1].x=((float)width)-0.5f;-0.5f;
240   osdvertices[1].y=0.f-0.5f;
241   osdvertices[1].z=0.5f;
242   osdvertices[1].u=texx;
243   osdvertices[1].v=0.f;
244   osdvertices[1].rhw=1.f;
245   osdvertices[2].c=osdcolor;
246   osdvertices[2].x=((float)width)-0.5f;
247   osdvertices[2].y=((float)height)-0.5f;
248   osdvertices[2].z=0.5f;
249   osdvertices[2].rhw=1.f;
250   osdvertices[2].u=texx;
251   osdvertices[2].v=texy;
252   osdvertices[3].c=osdcolor;
253   osdvertices[3].x=0.f-0.5f;
254   osdvertices[3].y=((float)height)-0.5f;
255   osdvertices[3].z=0.5f;
256   osdvertices[3].rhw=1.f;
257   osdvertices[3].u=0.f;
258   osdvertices[3].v=texy;
259   
260   if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED,
261           &ret,NULL)!=D3D_OK) {
262           return NULL;
263   }
264   void *pvertex=NULL;
265   if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) {
266           return NULL;
267   }
268   memcpy(pvertex,osdvertices,sizeof(osdvertices));
269   ret->Unlock();
270   return ret;
271 }
272
273 int OsdWin::shutdown()
274 {
275   if (!initted) return 0;
276   initted = 0;
277   evrsupported=0;
278   if (d3ddevman) d3ddevman->Release();
279   d3drtsurf->Release();
280   d3ddevice->Release();
281   d3d->Release();
282   if (swapsurf) swapsurf->Release();
283   if (swappy) swappy->Release();
284
285   return 1;
286 }
287
288 void OsdWin::screenShot(char* fileName)
289 {
290   screen->screenShot(fileName);
291 }
292
293 void OsdWin::threadMethod()
294 {
295         while (true)
296         {
297                 DWORD waittime=10;
298                 if (initted){
299                         if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause) 
300                         {
301                                 Render();
302                         } else if (evrstate==EVR_pres_started)
303                         {
304                                 LPDIRECT3DSURFACE9 surf;
305                                 if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime);
306                                 if (surf==NULL)
307                                 {
308                                         Render();
309                                 }
310                                 else
311                                 {
312                                         RenderDS(surf);
313                                         surf->Release();
314                                         if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
315                                 }
316                         }
317                 }
318                 threadCheckExit();
319                 if (waittime!=0) Sleep(min(10,waittime));
320                 //Sleep(1);
321         }
322 }
323
324
325 void OsdWin::threadPostStopCleanup()
326 {
327         //Doing nothing
328         //goo;
329 }
330
331
332 // This function is called from the WinMain function in order to get Screen updates
333 void OsdWin::Render()
334 {
335         if (!initted) return ;
336         if (external_driving) {
337         DWORD time1=timeGetTime(); //Updates the Menue
338                 if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
339                         InternalRendering(NULL);
340                         lastrendertime=timeGetTime();
341         } else {
342                    //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
343         }
344         } else {
345                 DWORD time1=timeGetTime();
346                 if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
347                         InternalRendering(NULL);
348                         lastrendertime=timeGetTime();
349                 } else {
350                         //Sleep(5);
351                 
352                 }
353                 
354         }
355 }
356
357 void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){
358         if (!initted) return; 
359         if (external_driving) {
360                 InternalRendering(present);
361         lastrendertime=timeGetTime();
362         }
363 }
364
365
366 void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){
367     BeginPainting();
368     HRESULT losty=d3ddevice->TestCooperativeLevel();
369     if (losty==D3DERR_DEVICELOST) {
370         //Sleep(10);
371                 EndPainting();
372         return; //Device Lost
373     }
374     if (losty==D3DERR_DEVICENOTRESET){
375            EndPainting();
376        DoLost();
377        return;
378     }
379         WaitForSingleObject(event,INFINITE);
380         
381    
382         
383     
384     LPDIRECT3DSURFACE9 targetsurf;
385         if (swappy)
386         {
387                 targetsurf=swapsurf;
388                 d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target
389         } 
390         else
391         {
392                 targetsurf=d3drtsurf;
393                 d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target
394         }
395         D3DSURFACE_DESC targetdesc;
396         targetsurf->GetDesc(&targetdesc);
397
398         if (external_driving) {
399                 //Copy video to Backbuffer
400                 if (present!=NULL ) {
401                         VideoWin* video =(VideoWin*) Video::getInstance();
402                         /*calculating destination rect */
403                         RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width,
404                                 /*video->getScreenHeight()*/targetdesc.Height};
405                         UCHAR mode=video->getMode();
406                         switch (mode) {
407                         case Video::EIGHTH:
408                         destrect.right=destrect.right/2;
409                         destrect.bottom=destrect.bottom/2;
410                         case Video::QUARTER:
411                         destrect.right=destrect.right/2+video->getPosx()*2;
412                         destrect.bottom=destrect.bottom/2+video->getPosy()*2;
413                         destrect.left=video->getPosx()*2;
414                         destrect.top=video->getPosy()*2;
415                         d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
416                         break;
417                         };
418                         D3DSURFACE_DESC surf_desc;
419                         present->GetDesc(&surf_desc);//for chop sides
420                         RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height};
421                         if (video->getPseudoTVsize()==Video::ASPECT4X3 
422                                 && video->getMode()==Video::NORMAL 
423                                 && video->getAspectRatio()==Video::ASPECT16X9) {
424                                         unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.;
425                                         sourcerect.left=(surf_desc.Width-correction)/2;
426                                         sourcerect.right=sourcerect.left+correction;
427                         }
428                         d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type);
429
430                 }
431         } else {
432                 VideoWin* video =(VideoWin*) Video::getInstance();
433                 //Clear Background
434                 if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
435         }
436         LPDIRECT3DVERTEXBUFFER9 vb=NULL;
437         vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height);
438
439         //Drawing the OSD
440         if (d3ddevice->BeginScene()==D3D_OK) {
441                 d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX));
442                 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
443                 d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture());
444                 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
445                 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE);
446
447         d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
448         d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
449
450
451                 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
452                 d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
453                 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
454                 d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE);
455
456
457                 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);
458                 d3ddevice->EndScene();
459                 //Show it to the user!
460         HRESULT hres;
461                 if (swappy)
462                 {
463                         if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){
464                                 //EndPainting();
465                                 if (!external_driving) DoLost();
466                         }
467                 } 
468                 else
469                 {
470                         if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){
471                                 //EndPainting();
472                                 if (!external_driving) DoLost();
473                         }
474                 }
475                 
476         }
477         
478         vb->Release();
479         EndPainting();
480
481         
482 //      if (!external_driving) {
483 //              Sleep(4);//The User can wait for 4 milliseconds to see his changes
484 //      }
485 }
486
487 bool OsdWin::DoLost(){
488         Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
489         ResetEvent(event);
490         if (external_driving && dsallocator!=NULL) {
491                 dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS
492         }
493         //First we free up all resources
494         Video* video = Video::getInstance();
495         ((SurfaceWin*)screen)->ReleaseSurface();
496         if (d3drtsurf) d3drtsurf->Release();
497     d3drtsurf=NULL;
498         D3DPRESENT_PARAMETERS d3dparas;
499         ZeroMemory(&d3dparas,sizeof(d3dparas));
500         d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;
501         d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;
502         d3dparas.Windowed=TRUE;
503         d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
504
505         if (swapsurf) {swapsurf->Release();swapsurf=NULL;};
506         if (swappy) {swappy->Release();swappy=NULL;};
507
508         if (d3ddevice->Reset(&d3dparas)!=D3D_OK){
509                 return false;
510         }
511     d3ddevice->GetRenderTarget(0,&d3drtsurf);
512         if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken);
513         //InitVertexBuffer();
514     //Redraw Views, Chris could you add a member function to BoxStack, so that
515         // I can cause it to completely redraw the Views?
516         // Otherwise the OSD would be distorted after Device Lost
517         // FIXME
518         
519         SetEvent(event);
520
521
522         screen->create(video->getScreenWidth(), video->getScreenHeight());
523         screen->display();
524         
525         return true;
526
527 }
528 LPDIRECT3DDEVICE9 OsdWin::getD3dDev() {
529         WaitForSingleObject(event,INFINITE);//We will only return if we are initted
530         return d3ddevice;
531 }
532
533 LPDIRECT3D9 OsdWin::getD3d() {
534         WaitForSingleObject(event,INFINITE);//We will only return if we are initted
535         return d3d;
536 }
537
538 void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads
539         WaitForSingleObject(d3dmutex,INFINITE);
540         LockDevice();
541 }
542
543 void OsdWin::EndPainting() {
544         UnlockDevice();
545         ReleaseMutex(d3dmutex);
546 }
547
548 void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) {
549         
550         if (swappy)
551         {
552                 BeginPainting();
553                 d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type);
554                 LPDIRECT3DSWAPCHAIN9 temp=swappy;
555                 LPDIRECT3DSURFACE9 tempsurf=swapsurf;
556                 swappy=NULL;
557                 swapsurf=NULL;
558                 EndPainting();
559                 tempsurf->Release();
560                 temp->Release();
561         }
562
563         if (dsall==NULL) {
564                 external_driving=false;
565                 dsallocator=NULL;       
566                 return;
567         }
568         WaitForSingleObject(event,INFINITE);//We will only return if we are initted
569         BeginPainting();
570
571         if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT) 
572         {
573                 D3DPRESENT_PARAMETERS d3dparas;
574                 ZeroMemory(&d3dparas,sizeof(d3dparas));
575                 d3dparas.BackBufferWidth=width;
576                 d3dparas.BackBufferHeight=height;
577                 d3dparas.Windowed=TRUE;
578                 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
579                 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){
580                         Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
581                         //return 0;
582                 } else {
583                         swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf);
584                 }
585         Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height);
586         }
587
588         dsallocator=dsall;
589         external_driving=true;
590         
591         EndPainting();
592 }
593
594 void OsdWin::Blank() {
595         WaitForSingleObject(event,INFINITE);
596         BeginPainting();
597         d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
598         EndPainting();
599 }