]> git.vomp.tv Git - vompclient.git/blob - videowin.cc
Windows port
[vompclient.git] / videowin.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "videowin.h"
22 #include "log.h"
23 #include "dssourcefilter.h"
24 #include "dsallocator.h"
25 #include "vdr.h"
26
27 void AdjustWindow();
28
29
30
31 VideoWin::VideoWin()
32 {
33   dsgraphbuilder=NULL;
34   dsmediacontrol=NULL;
35   dsvmrrenderer=NULL;
36   dsrefclock=NULL;
37   dsmediafilter=NULL;
38   sourcefilter=NULL;
39   allocatorvmr=NULL;
40   dsvmrsurfnotify=NULL;
41   filtermutex=CreateMutex(NULL,FALSE,NULL);
42   offsetnotset=true;
43   offsetvideonotset=true;
44   offsetaudionotset=true;
45   startoffset=0;
46   lastrefaudiotime=0;
47   lastrefvideotime=0;
48   lastreftimeBYTE=0;
49   lastreftimeRT=0;
50   firstsynched=false;
51   cur_audio_media_sample=NULL;
52   cur_video_media_sample=NULL;
53   videoon=true;
54   audioon=true;
55   pseudotvsize=0;
56   videoposx=0;
57   videoposy=0;
58  
59
60
61 }
62
63 VideoWin::~VideoWin()
64 {
65   CleanupDS();
66   CloseHandle(filtermutex);
67  
68
69
70   instance = NULL;
71 }
72
73 int VideoWin::init(UCHAR tformat)
74 {
75   if (initted) return 0;
76
77   initted = 1;
78   tvsize=Video::ASPECT16X9; //Internally Vomp should think we are a 16:9 TV
79   videoposx=0;
80   videoposy=0;
81
82   if (!setFormat(tformat)){ shutdown(); return 0; }
83   return 1;
84 }
85
86 int VideoWin::setTVsize(UCHAR ttvsize)
87 {
88   pseudotvsize=ttvsize;
89   return 1;
90 }
91
92 int VideoWin::setDefaultAspect()
93 {
94   return setAspectRatio(Video::ASPECT4X3);
95 }
96
97 int VideoWin::shutdown()
98 {
99   if (!initted) return 0;
100   initted = 0;
101   return 1;
102 }
103
104 int VideoWin::setFormat(UCHAR tformat)
105 {
106   if (!initted) return 0;
107   if ((tformat != PAL) && (tformat != NTSC)) return 0;
108   format = tformat;
109   if (format == NTSC)
110   {
111     screenWidth = 720;
112     screenHeight = 480;
113   }
114   if (format == PAL)
115   {
116     screenWidth = 720;
117     screenHeight = 576;
118   }
119
120   return 1;
121 }
122
123 int VideoWin::setConnection(UCHAR tconnection)
124 {
125   if (!initted) return 0;
126   if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0;
127   connection = tconnection;
128
129   return 1;
130 }
131
132 int VideoWin::setAspectRatio(UCHAR taspectRatio)
133 {
134   if (!initted) return 0;
135   if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0;
136   aspectRatio = taspectRatio;
137   AdjustWindow();
138   return 1;
139 }
140
141 int VideoWin::setMode(UCHAR tmode)
142 {
143   if (!initted) return 0;
144
145   //if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode
146
147   if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH)
148       && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0;
149   mode = tmode;
150   videoposx=0;
151   videoposy=0;
152   AdjustWindow();
153
154   return 1;
155 }
156
157 int VideoWin::signalOff()
158 {
159   return 1;
160 }
161
162 int VideoWin::signalOn()
163 {
164   return 1;
165 }
166
167 int VideoWin::setSource()
168 {
169   if (!initted) return 0;
170
171   return 1;
172 }
173
174 int VideoWin::setPosition(int x, int y)
175 {
176   if (!initted) return 0;
177   if (mode==QUARTER || mode==EIGHTH) {
178         videoposx=x;
179         videoposy=y;
180   }
181   return 1;
182 }
183
184 int VideoWin::sync()
185 {
186   if (!initted) return 0;
187
188   return 1;
189 }
190
191 #ifdef DS_DEBUG // This stuff would not included in vomp due to lincemse restrcitions
192 #include "dshelper.h"
193 #endif
194
195 #define DO_VIDEO
196
197 int VideoWin::play()
198 {
199   if (!initted) return 0;
200
201   //Build filter graph
202   HRESULT hres;
203  
204   if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,
205     IID_IGraphBuilder,(void**)&dsgraphbuilder)!=S_OK) {
206       return 0;
207    }
208    #ifdef DS_DEBUG
209    AddToRot(dsgraphbuilder,&graphidentifier);
210    #endif
211    //This is just a try to see if building the graph works
212 //   dsgraphbuilder->RenderFile(L"D:\\Projekte\\VTP Client\\test.mpa" ,NULL);
213    //So this is the real code, this prevents the feeder from calling noexisting objects!
214    WaitForSingleObject(filtermutex,INFINITE);
215    firstsynched=false;
216    sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data
217    // to DirectShow
218    if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter")!=S_OK) {
219          Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!");
220      CleanupDS();
221      ReleaseMutex(filtermutex);
222      return 0;
223    }
224    //if (audioon) {
225      if (hres=dsgraphbuilder->Render(sourcefilter->GetPin(0)/*audio*/)!=S_OK) {
226            Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!");
227        CleanupDS();
228        ReleaseMutex(filtermutex);
229        return 0;
230      }
231    //}
232 #ifdef DO_VIDEO
233     if (videoon) {
234                 //We alloc the vmr9 as next step
235                 if (hres=CoCreateInstance(CLSID_VideoMixingRenderer9,0,
236                         CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsvmrrenderer)!=S_OK) {
237                         Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating VMR9 renderer!");
238                         CleanupDS();
239                         ReleaseMutex(filtermutex);
240                 }
241                         /*VMR 9 stuff**/
242                 if (hres=dsgraphbuilder->AddFilter(dsvmrrenderer,L"VMR9")!=S_OK) {
243                         CleanupDS();
244                         Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding VMR9 renderer!");
245                         ReleaseMutex(filtermutex);
246                         return 0;
247                 }
248                 IVMRFilterConfig9* vmrfilconfig;
249                 if (dsvmrrenderer->QueryInterface(IID_IVMRFilterConfig9,(void**)&vmrfilconfig)!=S_OK) {
250                         CleanupDS();
251                         Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Filterconfig interface!");
252                         ReleaseMutex(filtermutex);
253                         return 0;
254                 }
255                 vmrfilconfig->SetRenderingMode(VMR9Mode_Renderless);
256                 vmrfilconfig->Release();
257                 
258                 if (dsvmrrenderer->QueryInterface(IID_IVMRSurfaceAllocatorNotify9,(void**)& dsvmrsurfnotify)!=S_OK) {
259                         CleanupDS();
260                         Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Surface Allocator interface!");
261                         ReleaseMutex(filtermutex);
262                         return 0;
263                 }
264                 allocatorvmr=new DsAllocator();
265                 dsvmrsurfnotify->AdviseSurfaceAllocator(NULL,allocatorvmr);
266                 allocatorvmr->AdviseNotify(dsvmrsurfnotify);
267
268
269
270                 /*VMR 9 stuff end */
271                 IFilterGraph2*fg2=NULL;
272                 if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!=S_OK) {
273                         Log::getInstance()->log("VideoWin", Log::WARN , "Failed querying for FilterGraph2 Interface!");
274                         CleanupDS();
275                         ReleaseMutex(filtermutex);
276                         return 0;
277                 }
278                 if (hres=fg2->RenderEx(sourcefilter->GetPin(1)/*video*/,
279                                 AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL)!=S_OK) {
280                         Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering Video!");
281                         CleanupDS();
282                         ReleaseMutex(filtermutex);
283                         return 0;
284                 }
285    }
286 #endif
287    if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER,
288     IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) {
289       return 0;
290    }
291    
292    dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter);
293    dsmediafilter->SetSyncSource(dsrefclock);
294
295    dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol);
296
297    dsmediacontrol->Run();
298    ReleaseMutex(filtermutex);
299   return 1;
300 }
301
302 int VideoWin::stop()
303 {
304   if (!initted) return 0;
305
306   CleanupDS();
307
308
309   return 1;
310 }
311
312 int VideoWin::reset()
313 {
314   if (!initted) return 0;
315   videoposx=0;
316   videoposy=0;
317
318   return 1;
319 }
320
321 int VideoWin::pause()
322 {
323   if (!initted) return 0;
324   if (dsmediacontrol) dsmediacontrol->Pause();
325   return 1;
326 }
327
328 int VideoWin::unPause() // FIXME get rid - same as play!!
329 {//No on windows this is not the same, I don't get rid of!
330   if (!initted) return 0;
331   if (dsmediacontrol) dsmediacontrol->Run();
332   return 1;
333 }
334
335 int VideoWin::fastForward()
336 {
337   if (!initted) return 0;
338   return 1;
339 }
340
341 int VideoWin::unFastForward()
342 {
343   if (!initted) return 0;
344   return 1;
345 }
346
347 int VideoWin::attachFrameBuffer()
348 {
349   if (!initted) return 0;
350   return 1;
351 }
352
353 int VideoWin::blank(void)
354 {
355   return 1;
356 }
357
358 int VideoWin::getFD()
359 {
360   if (!initted) return 0;
361
362   return fdVideo;
363 }
364
365 ULLONG VideoWin::getCurrentTimestamp()
366 {
367         REFERENCE_TIME cr_time,startoffset;
368         
369         if (!dsrefclock || !sourcefilter) return 0;
370         
371         dsrefclock->GetTime(&cr_time);
372         startoffset=sourcefilter->getStartOffset();
373         cr_time-=startoffset;
374         cr_time-=lastreftimeRT;
375         ULLONG result=frameNumberToTimecode(
376                 VDR::getInstance()->frameNumberFromPosition(lastreftimeBYTE));
377         result+=(ULLONG)(cr_time/10000LL*90LL);
378         return result;
379
380 }
381
382 ULONG VideoWin::timecodeToFrameNumber(ULLONG timecode)
383 {
384   if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25);
385   else               return (ULONG)(((double)timecode / (double)90000) * (double)30);
386 }
387
388 ULLONG VideoWin::frameNumberToTimecode(ULONG framenumber)
389 {
390   if (format == PAL) return (ULLONG)(((double)framenumber * (double)90000) / (double)25);
391   else               return (ULLONG)(((double)framenumber * (double)90000) / (double)30);
392 }
393
394 void VideoWin::CleanupDS()
395 {
396   WaitForSingleObject(filtermutex,INFINITE);
397   if (cur_audio_media_sample) {
398     cur_audio_media_sample->Release();
399     cur_audio_media_sample=NULL;
400   }
401   if (cur_video_media_sample) {
402     cur_video_media_sample->Release();
403     cur_video_media_sample=NULL;
404   }
405   if (dsvmrsurfnotify) {
406           dsvmrsurfnotify->Release();
407           dsvmrsurfnotify=NULL;
408   }
409   if (dsvmrrenderer) {
410           dsvmrrenderer->Release();
411           dsvmrrenderer=NULL;
412   }
413  
414   if (allocatorvmr) {
415           allocatorvmr->Release();
416           allocatorvmr=NULL;
417   }
418  
419   if (dsrefclock) {
420           dsrefclock->Release();
421           dsrefclock=NULL;
422   }
423   if (dsmediafilter) {
424           dsmediafilter->Release();
425           dsmediafilter=NULL;
426   }
427
428   if (dsmediacontrol) {
429     dsmediacontrol->Stop();
430     dsmediacontrol->Release();
431     dsmediacontrol=NULL;
432   }
433   if (dsgraphbuilder){
434 #ifdef DS_DEBUG
435     RemoveFromRot(graphidentifier);
436 #endif
437     dsgraphbuilder->Release();
438     dsgraphbuilder=NULL;
439     sourcefilter=NULL; //The Graph Builder destroys our SourceFilter
440   }
441   ReleaseMutex(filtermutex);
442
443 }
444
445
446 UINT VideoWin::DeliverMediaSample(MediaPacket packet,
447      UCHAR* buffer,
448      UINT *samplepos)
449 {
450   /*First Check, if we have an audio sample*/
451 #ifdef DO_VIDEO
452   /*First Check, if we have an audio sample*/
453
454   IMediaSample* ms=NULL;
455   REFERENCE_TIME reftime1=0;
456   REFERENCE_TIME reftime2=0;
457
458   UINT headerstrip=0;
459   if (packet.disconti) {
460     firstsynched=false;
461     DeliverVideoMediaSample();
462
463   }
464
465
466   /*Inspect PES-Header */
467
468   if (*samplepos==0) {//stripheader
469     headerstrip=buffer[packet.pos_buffer+8]+9/*is this right*/;
470     *samplepos+=headerstrip;
471     if ( packet.synched ) {
472       DeliverVideoMediaSample();//write out old data
473    /*   if (packet.presentation_time<0) { //Preroll?
474         *samplepos=packet.length;//if we have not processed at least one
475         return packet.length;//synched packet ignore it!
476       }*/
477
478       reftime1=packet.presentation_time;
479       reftime2=reftime1+1;
480       firstsynched=true;
481     } else {
482       if (!firstsynched) {//
483         *samplepos=packet.length;//if we have not processed at least one
484         return packet.length;//synched packet ignore it!
485       }
486     }
487   }
488   BYTE *ms_buf;
489   UINT ms_length;
490   UINT ms_pos;
491   UINT haveToCopy;
492   if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample
493     samplepos=0;
494     MILLISLEEP(10);
495     return 0;
496   }
497   ms_pos=ms->GetActualDataLength();
498   ms_length=ms->GetSize();
499   haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos);
500   if ((ms_length-ms_pos)<1) {
501     DeliverVideoMediaSample(); //we are full!
502     if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample
503       samplepos=0;
504       MILLISLEEP(10);
505       return 0;
506     }
507     ms_pos=ms->GetActualDataLength();
508     ms_length=ms->GetSize();
509     haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos);
510   }
511   ms->GetPointer(&ms_buf);
512
513
514   if (ms_pos==0) {//will only be changed on first packet
515     if (packet.disconti) {
516       ms->SetDiscontinuity(TRUE);
517     } else {
518       ms->SetDiscontinuity(FALSE);
519     }
520     if (packet.synched) {
521       ms->SetSyncPoint(TRUE);
522       ms->SetTime(&reftime1,&reftime2);
523       //ms->SetTime(NULL,NULL);
524       ms->SetMediaTime(NULL, NULL);
525           if (reftime1<0) ms->SetPreroll(TRUE);
526           else ms->SetPreroll(FALSE);
527           /*Timecode handling*/
528           lastreftimeRT=reftime1;
529           lastreftimeBYTE=packet.recording_byte_pos;
530
531     }else {
532       ms->SetSyncPoint(FALSE);
533       ms->SetTime(NULL,NULL);
534       ms->SetMediaTime(NULL, NULL);
535           ms->SetPreroll(FALSE);
536          
537     //  ms->SetSyncPoint(TRUE);
538     }
539   }
540
541
542   memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy);
543     ms->SetActualDataLength(haveToCopy+ms_pos);
544
545   *samplepos+=haveToCopy;
546
547   return haveToCopy+headerstrip;
548
549 #else
550
551        *samplepos+=packet.length;
552       MILLISLEEP(0); //yet not implemented//bad idea
553        return packet.length;
554 #endif
555 }
556
557 int VideoWin::getCurrentAudioMediaSample(IMediaSample** ms)
558 {
559   //WaitForSingleObject(filtermutex,INFINITE);
560   if (!sourcefilter){
561   //  ReleaseMutex(filtermutex);
562     return 0;
563   }
564   if (cur_audio_media_sample) {
565     *ms=cur_audio_media_sample;//already open
566     return 1;
567   }
568   if (!sourcefilter->getCurrentAudioMediaSample(ms)) {
569   //  ReleaseMutex(filtermutex);
570   }
571   if (*ms) (*ms)->SetActualDataLength(0);
572   cur_audio_media_sample=*ms;
573   //Don't release the mutex before deliver
574   return 1;
575 }
576
577 int VideoWin::getCurrentVideoMediaSample(IMediaSample** ms)
578 {
579   //WaitForSingleObject(filtermutex,INFINITE);
580   if (!sourcefilter){
581   //  ReleaseMutex(filtermutex);
582     return 0;
583   }
584   if (cur_video_media_sample) {
585     *ms=cur_video_media_sample;//already open
586     return 1;
587   }
588   if (!sourcefilter->getCurrentVideoMediaSample(ms)) {
589   //  ReleaseMutex(filtermutex);
590   }
591   if (*ms) (*ms)->SetActualDataLength(0);
592
593   cur_video_media_sample=*ms;
594   //Don't release the mutex before deliver
595   return 1;
596 }
597
598 int VideoWin::DeliverAudioMediaSample(){
599   if (cur_audio_media_sample) {
600     sourcefilter->DeliverAudioMediaSample(cur_audio_media_sample);
601     cur_audio_media_sample=NULL;
602   }
603   //ReleaseMutex(filtermutex);
604   return 1;
605 }
606
607 int VideoWin::DeliverVideoMediaSample(){
608   if (cur_video_media_sample) {
609     sourcefilter->DeliverVideoMediaSample(cur_video_media_sample);
610     cur_video_media_sample=NULL;
611   }
612   //ReleaseMutex(filtermutex);
613   return 1;
614 }
615
616 long long VideoWin::SetStartOffset(long long curreftime, bool *rsync)
617 {
618   *rsync=false;
619   if (offsetnotset) {
620     startoffset=curreftime;//offset is set for audio
621     offsetnotset=false;
622     offsetvideonotset=false;
623         
624         
625   } else {
626     if (offsetvideonotset) {
627       offsetvideonotset=false;
628       *rsync=true;
629     } else {
630       if ( (curreftime-lastrefvideotime)>10000000LL
631         || (curreftime-lastrefvideotime)<-10000000LL) {//if pts jumps to big resync
632         startoffset+=curreftime-lastrefvideotime;
633         lastrefaudiotime+=curreftime-lastrefvideotime;
634         //*rsync=true;
635         offsetaudionotset=true;
636
637       }
638     }
639
640   }
641   lastrefvideotime=curreftime;
642   return startoffset;
643
644 }
645
646 long long VideoWin::SetStartAudioOffset(long long curreftime, bool *rsync)
647 {
648   *rsync=false;
649   if (offsetnotset) {
650     startoffset=curreftime;
651     offsetnotset=false;
652     offsetaudionotset=false;
653   }else {
654     if (offsetaudionotset) {
655       offsetaudionotset=false;
656       *rsync=true;
657     } else {
658       if ( (curreftime-lastrefaudiotime)>10000000LL
659         || (curreftime-lastrefaudiotime)<-10000000LL) {//if pts jumps to big resync
660         startoffset+=curreftime-lastrefaudiotime;
661         lastrefvideotime+=curreftime-lastrefaudiotime;
662         //*rsync=true;
663         offsetvideonotset=true;
664
665       }
666     }
667
668   }
669   lastrefaudiotime=curreftime;
670   return startoffset;
671
672 }
673 void VideoWin::ResetTimeOffsets() {
674   offsetnotset=true; //called from demuxer
675   offsetvideonotset=true;
676   offsetaudionotset=true;
677   startoffset=0;
678   lastrefaudiotime=0;
679   lastrefvideotime=0;
680   lastreftimeBYTE=0;
681   lastreftimeRT=0;
682
683   
684 }
685
686
687 #ifdef DEV
688 int VideoWin::test()
689 {
690   return 0;
691 }
692
693 int VideoWin::test2()
694 {
695   return 0;
696 }
697 #endif
698 \r