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