]> git.vomp.tv Git - vompclient.git/blob - player.cc
Change WSelectList option data to void*. About 65 CWFs
[vompclient.git] / player.cc
1 /*
2     Copyright 2004-2008 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, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "player.h"
21
22 #include "log.h"
23 #include "audio.h"
24 #include "video.h"
25 #include "demuxervdr.h"
26 #include "demuxerts.h"
27 #include "vdr.h"
28 #include "messagequeue.h"
29 #include "input.h"
30 #include "message.h"
31 #include "dvbsubtitles.h"
32 #include "osdreceiver.h"
33
34 #define USER_RESPONSE_TIME 500 // Milliseconds
35
36 // ----------------------------------- Called from outside, one offs or info funcs
37
38 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
39 : vfeed(this), afeed(this), tfeed(this)
40 {
41   messageQueue = tmessageQueue;
42   messageReceiver = tmessageReceiver;
43   osdReceiver = tosdReceiver;
44   audio = Audio::getInstance();
45   video = Video::getInstance();
46   logger = Log::getInstance();
47   vdr = VDR::getInstance();
48
49   video->turnVideoOn();
50 }
51
52 Player::~Player()
53 {
54   if (initted) shutdown();
55 }
56
57 int Player::init(bool p_isPesRecording, double framespersecond)
58 {
59   if (initted) return 0;
60 #ifndef WIN32
61   pthread_mutex_init(&mutex, NULL);
62 #else
63   mutex=CreateMutex(NULL,FALSE,NULL);
64 #endif
65   is_pesrecording = p_isPesRecording;
66   fps = framespersecond;
67   if (is_pesrecording)
68     demuxer = new DemuxerVDR();
69   else
70     demuxer = new DemuxerTS();
71   if (!demuxer) return 0;
72   subtitles = new DVBSubtitles(osdReceiver);
73   if (!subtitles) return 0;
74
75   teletext = new TeletextDecoderVBIEBU();
76   if (!teletext) return 0;
77   teletext->setRecordigMode(true);
78   unsigned int demux_video_size = 2097152;
79   unsigned int demux_audio_size = 524288;
80   if (video->supportsh264())
81   {
82     demux_video_size *= 5 * 2;
83   }
84   if (audio->maysupportAc3())
85   {
86     //demux_audio_size*=2;
87   }
88  
89   if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
90   {
91     logger->log("Player", Log::ERR, "Demuxer failed to init");
92     shutdown();
93     return 0;
94   }
95
96   vfeed.init();
97   afeed.init();
98   tfeed.init();
99
100   video->stop();
101   video->blank();
102   audio->stop();
103
104   if (Command::getInstance()->getSubDefault())
105     turnSubtitlesOn(true);
106   else
107     turnSubtitlesOn(false);
108
109   initted = true;
110   return 1;
111 }
112
113 int Player::shutdown()
114 {
115   if (!initted) return 0;
116   switchState(S_STOP);
117   initted = false;
118
119   delete demuxer;
120   demuxer = NULL;
121   delete subtitles;
122   subtitles = NULL;
123   delete teletext;
124   teletext = NULL;
125
126 #ifdef WIN32
127   CloseHandle(mutex);
128 #endif
129
130   return 1;
131 }
132
133 void Player::setStartFrame(ULONG startFrame)
134 {
135   ULONG nextiframeNumber;
136   ULONG iframeLength;
137   ULONG iframeNumber;
138   ULLONG filePos;
139
140   // newFrame could be anywhere, go forwards to next I-Frame
141   if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
142
143   // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
144   vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
145
146   logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
147   currentFrameNumber = iframeNumber;
148 }
149
150 void Player::setLengthBytes(ULLONG length)
151 {
152   lengthBytes = length;
153   logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
154 }
155
156 void Player::setLengthFrames(ULONG length)
157 {
158   lengthFrames = length;
159   logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
160 }
161
162 ULONG Player::getLengthFrames()
163 {
164   return lengthFrames;
165 }
166
167 ULONG Player::getCurrentFrameNum()
168 {
169   if (startup) return 0;
170   switch(state)
171   {
172     case S_PLAY:
173     case S_PAUSE_P:
174       return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
175     case S_PAUSE_I:
176     case S_FFWD:
177     case S_FBWD:
178       return currentFrameNumber;
179     default:
180       return 0; // shouldn't happen
181   }
182 }
183
184 bool* Player::getDemuxerMpegAudioChannels()
185 {
186   return demuxer->getmpAudioChannels();
187 }
188
189 bool* Player::getDemuxerAc3AudioChannels()
190 {
191   return demuxer->getac3AudioChannels();
192 }
193
194 bool* Player::getDemuxerSubtitleChannels()
195 {
196   return demuxer->getSubtitleChannels();
197 }
198
199 int Player::getCurrentAudioChannel()
200 {
201   if (is_pesrecording)
202     return demuxer->getselAudioChannel();
203   else
204     return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
205 }
206
207 int Player::getCurrentSubtitleChannel()
208 {
209   if (is_pesrecording)
210     return demuxer->getselSubtitleChannel();
211   else
212     return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
213 }
214
215 void Player::setSubtitleChannel(int newChannel)
216 {
217   if (is_pesrecording)
218     demuxer->setDVBSubtitleStream(newChannel);
219   else
220     dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
221 }
222
223 int *Player::getTeletxtSubtitlePages()
224 {
225   return teletext->getSubtitlePages();
226 }
227
228 void Player::setAudioChannel(int newChannel, int type, int streamtype)
229 {
230   if (is_pesrecording)
231   {
232     demuxer->setAudioStream(newChannel);
233     return;
234   }
235   else
236   {
237     dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
238     return;
239   }
240 }
241
242 bool Player::toggleSubtitles()
243 {
244   if (!subtitlesShowing)
245   {
246     subtitlesShowing = true;
247     subtitles->show();
248   }
249   else
250   {
251     subtitlesShowing = false;
252     subtitles->hide();
253   }
254   return subtitlesShowing;
255 }
256
257 void  Player::turnSubtitlesOn(bool ison)
258 {
259  if (ison)
260   {
261     subtitlesShowing = true;
262     subtitles->show();
263   }
264   else
265   {
266     subtitlesShowing = false;
267     subtitles->hide();
268   }
269 }
270
271 void Player::tellSubtitlesOSDVisible(bool visible)
272 {
273   subtitles->setOSDMenuVisibility(visible);
274 }
275
276 Channel * Player::getDemuxerChannel()
277 {
278   if (!is_pesrecording)
279   {
280     return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
281   }
282   return NULL; //Should not happen!
283 }
284
285
286 // ----------------------------------- Externally called events
287
288 void Player::play()
289 {
290   if (!initted) return;
291   if (state == S_PLAY) return;
292   lock();
293
294   bool doUnlock = false;
295   if (state == S_PAUSE_P) doUnlock = true;
296   switchState(S_PLAY);
297   if (doUnlock) unLock();
298 }
299
300 void Player::playpause()
301 {
302   if (!initted) return;
303   lock();
304
305   bool doUnlock = false;
306   if (state==S_PLAY)
307   {
308     doUnlock=true;
309     switchState(S_PAUSE_P);
310   }
311   else
312   {
313     if (state == S_PAUSE_P) doUnlock = true;
314     switchState(S_PLAY);
315   }
316   if (doUnlock) unLock();
317 }
318
319 void Player::stop()
320 {
321   if (!initted) return;
322   if (state == S_STOP) return;
323   lock();
324   logger->log("Player", Log::DEBUG, "Stop called lock");
325   switchState(S_STOP);
326   unLock();
327 }
328
329 void Player::pause()
330 {
331   if (!initted) return;
332   lock();
333
334   if ((state == S_FFWD) || (state == S_FBWD))
335   {
336     switchState(S_PAUSE_I);
337   }
338   else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
339   {
340     switchState(S_PLAY);
341   }
342   else
343   {
344     switchState(S_PAUSE_P);
345   }
346
347   unLock();
348 }
349
350 void Player::fastForward()
351 {
352   if (!initted) return;
353   lock();
354
355   if (state == S_FFWD)
356   {
357     // change the rate
358     switch(ifactor)
359     {
360       case 4: ifactor = 8; break;
361       case 8: ifactor = 16; break;
362       case 16: ifactor = 32; break;
363       case 32: ifactor = 4; break;
364     }
365   }
366   else
367   {
368     ifactor = 4;
369     switchState(S_FFWD);
370   }
371   unLock();
372 }
373
374 void Player::fastBackward()
375 {
376   if (!initted) return;
377   lock();
378
379   if (state == S_FBWD)
380   {
381     // change the rate
382     switch(ifactor)
383     {
384       case 4: ifactor = 8; break;
385       case 8: ifactor = 16; break;
386       case 16: ifactor = 32; break;
387       case 32: ifactor = 4; break;
388     }
389   }
390   else
391   {
392     ifactor = 4;
393     switchState(S_FBWD);
394   }
395   unLock();
396 }
397
398 void Player::jumpToPercent(double percent)
399 {
400   lock();
401   logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
402   ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
403   switchState(S_JUMP, newFrame);
404 //  unLock(); - let thread unlock this
405 }
406
407 void Player::jumpToMark(int mark)
408 {
409   lock();
410   logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
411   switchState(S_JUMP, mark);
412 //  unLock(); - let thread unlock this
413 }
414
415 void Player::jumpToFrameP(int newFrame)
416 {
417   lock();
418   logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
419   switchState(S_JUMP_PI, newFrame);
420   unLock();
421 }
422
423 void Player::skipForward(int seconds)
424 {
425   lock();
426   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
427   ULONG newFrame = getCurrentFrameNum();
428   if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
429   newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
430   if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
431   else switchState(S_JUMP, newFrame);
432 //  unLock(); - let thread unlock this
433 }
434
435 void Player::skipBackward(int seconds)
436 {
437   lock();
438   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
439   long newFrame = getCurrentFrameNum();
440   if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
441   newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
442   if (newFrame < 0) newFrame = 0;
443   switchState(S_JUMP, newFrame);
444 //  unLock(); - let thread unlock this
445 }
446
447 // ----------------------------------- Implementations called events
448
449 void Player::switchState(UCHAR toState, ULONG jumpFrame)
450 {
451   if (!initted) return;
452
453   logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
454
455   switch(state) // current state selector
456   {
457     case S_PLAY: // from S_PLAY -----------------------------------
458     {
459       switch(toState)
460       {
461         case S_PLAY: // to S_PLAY
462         {
463           return;
464         }
465         case S_PAUSE_P: // to S_PAUSE_P
466         {
467           #ifdef VOMP_PLATFORM_RASPBERRY
468           vfeed.stop(); // can't vfeed during pause
469           #endif
470
471           video->pause();
472           audio->pause();
473           state = S_PAUSE_P;
474           return;
475         }
476         case S_PAUSE_I: // to S_PAUSE_I
477         {
478           // can't occur
479           return;
480         }
481         case S_FFWD: // to S_FFWD
482         {
483           currentFrameNumber = getCurrentFrameNum();
484           audio->systemMuteOn();
485           threadStop();
486           vfeed.stop();
487           afeed.stop();
488           tfeed.stop();
489           subtitles->stop();
490           demuxer->flush();
491           state = S_FFWD;
492           threadStart();
493           return;
494         }
495         case S_FBWD: // to S_FBWD
496         {
497           currentFrameNumber = getCurrentFrameNum();
498           audio->systemMuteOn();
499           threadStop();
500           vfeed.stop();
501           afeed.stop();
502           tfeed.stop();
503           subtitles->stop();
504           demuxer->flush();
505           state = S_FBWD;
506           threadStart();
507           return;
508         }
509         case S_STOP: // to S_STOP
510         {
511           vfeed.stop();
512           afeed.stop();
513           tfeed.stop();
514           subtitles->stop();
515           threadStop();
516           video->stop();
517           video->blank();
518           audio->stop();
519           audio->unPause();
520           video->reset();
521           demuxer->reset();
522           state = S_STOP;
523           return;
524         }
525         case S_JUMP: // to S_JUMP
526         {
527           restartAtFrame(jumpFrame);
528           return;
529         }
530         case S_JUMP_PI: // to S_JUMP_PI
531         {
532           audio->systemMuteOn();
533           threadStop();
534           vfeed.stop();
535           afeed.stop();
536           tfeed.stop();
537           subtitles->stop();
538           demuxer->flush();
539           state = S_PAUSE_I;
540           video->reset();
541           video->play();
542           restartAtFramePI(jumpFrame);
543           return;
544         }
545       }
546     }
547     case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
548     {
549       switch(toState)
550       {
551         case S_PLAY: // to S_PLAY
552         {
553           video->unPause();
554           audio->unPause();
555
556           #ifdef VOMP_PLATFORM_RASPBERRY
557           vfeed.start(false);
558           #endif
559
560           state = S_PLAY;
561           return;
562         }
563         case S_PAUSE_P: // to S_PAUSE_P
564         {
565           return;
566         }
567         case S_PAUSE_I: // to S_PAUSE_I
568         {
569           return;
570         }
571         case S_FFWD: // to S_FFWD
572         {
573           currentFrameNumber = getCurrentFrameNum();
574           audio->systemMuteOn();
575           vfeed.stop();
576           afeed.stop();
577           tfeed.stop();
578           subtitles->stop();
579           threadStop();
580           video->unPause();
581           audio->unPause();
582           state = S_FFWD;
583           threadStart();
584           return;
585         }
586         case S_FBWD: // to S_FBWD
587         {
588           currentFrameNumber = getCurrentFrameNum();
589           audio->systemMuteOn();
590           vfeed.stop();
591           afeed.stop();
592           tfeed.stop();
593           subtitles->stop();
594           threadStop();
595           video->unPause();
596           audio->unPause();
597           state = S_FBWD;
598           threadStart();
599           return;
600         }
601         case S_STOP: // to S_STOP
602         {
603           vfeed.stop();
604           afeed.stop();
605           tfeed.stop();
606           subtitles->stop();
607           threadStop();
608           video->stop();
609           video->blank();
610           audio->stop();
611           video->reset();
612           audio->unPause();
613           demuxer->reset();
614           audio->systemMuteOff();
615           state = S_STOP;
616           return;
617         }
618         case S_JUMP: // to S_JUMP
619         {
620           state = S_PLAY;
621           audio->systemMuteOn();
622           audio->unPause();
623           restartAtFrame(jumpFrame);
624           return;
625         }
626         case S_JUMP_PI: // to S_JUMP_PI
627         {
628           audio->systemMuteOn();
629           audio->unPause();
630           threadStop();
631           vfeed.stop();
632           afeed.stop();
633           tfeed.stop();
634           subtitles->stop();
635           demuxer->flush();
636           state = S_PAUSE_I;
637           video->reset();
638           video->play();
639           restartAtFramePI(jumpFrame);
640           return;
641         }
642       }
643     }
644     case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
645     {
646       switch(toState)
647       {
648         case S_PLAY: // to S_PLAY
649         {
650           state = S_PLAY;
651           restartAtFrame(currentFrameNumber);
652           return;
653         }
654         case S_PAUSE_P: // to S_PAUSE_P
655         {
656           return;
657         }
658         case S_PAUSE_I: // to S_PAUSE_I
659         {
660           return;
661         }
662         case S_FFWD: // to S_FFWD
663         {
664           state = S_FFWD;
665           threadStart();
666           return;
667         }
668         case S_FBWD: // to S_FBWD
669         {
670           state = S_FBWD;
671           threadStart();
672           return;
673         }
674         case S_STOP: // to S_STOP
675         {
676           video->stop();
677           video->blank();
678           audio->stop();
679           video->reset();
680           demuxer->reset();
681           audio->systemMuteOff();
682           state = S_STOP;
683           return;
684         }
685         case S_JUMP: // to S_JUMP
686         {
687           state = S_PLAY;
688           restartAtFrame(jumpFrame);
689           return;
690         }
691         case S_JUMP_PI: // to S_JUMP_PI
692         {
693           restartAtFramePI(jumpFrame);
694           return;
695         }
696       }
697     }
698     case S_FFWD: // from S_FFWD -----------------------------------
699     {
700       switch(toState)
701       {
702         case S_PLAY: // to S_PLAY
703         {
704           state = S_PLAY;
705           ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
706           if (stepback < currentFrameNumber)
707             currentFrameNumber -= stepback;
708           else
709             currentFrameNumber = 0;
710           restartAtFrame(currentFrameNumber);
711           return;
712         }
713         case S_PAUSE_P: // to S_PAUSE_P
714         {
715           // can't occur
716           return;
717         }
718         case S_PAUSE_I: // to S_PAUSE_I
719         {
720           threadStop();
721           state = S_PAUSE_I;
722           return;
723         }
724         case S_FFWD: // to S_FFWD
725         {
726           return;
727         }
728         case S_FBWD: // to S_FBWD
729         {
730           threadStop();
731           state = S_FBWD;
732           threadStart();
733           return;
734         }
735         case S_STOP: // to S_STOP
736         {
737           threadStop();
738           video->stop();
739           video->blank();
740           audio->stop();
741           video->reset();
742           demuxer->reset();
743           state = S_STOP;
744           return;
745         }
746         case S_JUMP: // to S_JUMP
747         {
748           state = S_PLAY;
749           restartAtFrame(jumpFrame);
750           return;
751         }
752         case S_JUMP_PI: // to S_JUMP_PI
753         {
754           threadStop();
755           state = S_PAUSE_I;
756           restartAtFramePI(jumpFrame);
757           return;
758         }
759       }
760     }
761     case S_FBWD: // from S_FBWD -----------------------------------
762     {
763       switch(toState)
764       {
765         case S_PLAY: // to S_PLAY
766         {
767           state = S_PLAY;
768           restartAtFrame(currentFrameNumber);
769           return;
770         }
771         case S_PAUSE_P: // to S_PAUSE_P
772         {
773           // can't occur
774           return;
775         }
776         case S_PAUSE_I: // to S_PAUSE_I
777         {
778           threadStop();
779           state = S_PAUSE_I;
780           return;
781         }
782         case S_FFWD: // to S_FFWD
783         {
784           threadStop();
785           state = S_FFWD;
786           threadStart();
787           return;
788         }
789         case S_FBWD: // to S_FBWD
790         {
791           return;
792         }
793         case S_STOP: // to S_STOP
794         {
795           threadStop();
796           video->stop();
797           video->blank();
798           audio->stop();
799           video->reset();
800           demuxer->reset();
801           state = S_STOP;
802           return;
803         }
804         case S_JUMP: // to S_JUMP
805         {
806           state = S_PLAY;
807           restartAtFrame(jumpFrame);
808           return;
809         }
810         case S_JUMP_PI: // to S_JUMP_PI
811         {
812           threadStop();
813           state = S_PAUSE_I;
814           restartAtFramePI(jumpFrame);
815           return;
816         }
817       }
818     }
819     case S_STOP: // from S_STOP -----------------------------------
820     {
821       switch(toState)
822       {
823         case S_PLAY: // to S_PLAY
824         {
825           startup = true;
826
827           audio->reset();
828           audio->setStreamType(Audio::MPEG2_PES);
829           audio->systemMuteOff();
830           video->reset();
831           demuxer->reset();
832           // FIXME use restartAtFrame here?
833           if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
834           demuxer->setFrameNum(currentFrameNumber);
835           demuxer->seek();
836           videoStartup = true;
837           state = S_PLAY;
838           threadStart();
839           logger->log("Player", Log::DEBUG, "Immediate play");
840           afeed.start();
841           vfeed.start();
842           tfeed.start();
843           subtitles->start();
844           video->sync();
845           audio->sync();
846           audio->play();
847           video->pause();
848           return;
849         }
850         case S_PAUSE_P: // to S_PAUSE_P
851         {
852           return;
853         }
854         case S_PAUSE_I: // to S_PAUSE_I
855         {
856           return;
857         }
858         case S_FFWD: // to S_FFWD
859         {
860           return;
861         }
862         case S_FBWD: // to S_FBWD
863         {
864           return;
865         }
866         case S_STOP: // to S_STOP
867         {
868           return;
869         }
870         case S_JUMP: // to S_JUMP
871         {
872           return;
873         }
874         case S_JUMP_PI: // to S_JUMP_PI
875         {
876           return;
877         }
878       }
879     }
880     // case S_JUMP cannot be a start state because it auto flips to play
881     // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
882   }
883 }
884
885 // ----------------------------------- Internal functions
886
887 void Player::lock()
888 {
889 #ifndef WIN32
890   pthread_mutex_lock(&mutex);
891   logger->log("Player", Log::DEBUG, "LOCKED");
892
893 #else
894    WaitForSingleObject(mutex, INFINITE);
895 #endif
896 }
897
898 void Player::unLock()
899 {
900 #ifndef WIN32
901   logger->log("Player", Log::DEBUG, "UNLOCKING");
902   pthread_mutex_unlock(&mutex);
903 #else
904    ReleaseMutex(mutex);
905 #endif
906 }
907
908 void Player::restartAtFrame(ULONG newFrame)
909 {
910   vfeed.stop();
911   afeed.stop();
912   tfeed.stop();
913   subtitles->stop();
914   threadStop();
915   video->stop();
916   video->reset();
917   audio->reset();
918   audio->setStreamType(Audio::MPEG2_PES);
919   demuxer->flush();
920   demuxer->seek();
921   currentFrameNumber = newFrame;
922   demuxer->setFrameNum(newFrame);
923   videoStartup = true;
924   afeed.start();
925   tfeed.start();
926   vfeed.start();
927   subtitles->start();
928   threadStart();
929   audio->play();
930   video->sync();
931   audio->sync();
932   audio->systemMuteOff();
933   audio->doMuting();
934 }
935
936
937 void Player::restartAtFramePI(ULONG newFrame)
938 {
939   ULLONG filePos;
940   ULONG nextiframeNumber;
941   ULONG iframeLength;
942   ULONG iframeNumber;
943
944   UCHAR* buffer;
945   UINT amountReceived;
946   UINT videoLength;
947
948   // newFrame could be anywhere, go forwards to next I-Frame
949   if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
950
951   // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
952   vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
953
954   buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
955   if (!vdr->isConnected())
956   {
957     if (buffer) free(buffer);
958     doConnectionLost();
959   }
960   else
961   {
962     videoLength = demuxer->stripAudio(buffer, amountReceived);
963     video->displayIFrame(buffer, videoLength);
964     video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
965     free(buffer);
966     currentFrameNumber = iframeNumber;
967   }
968 }
969
970 void Player::doConnectionLost()
971 {
972   logger->log("Player", Log::DEBUG, "Connection lost, sending message");
973   Message* m = new Message();
974   m->to = messageReceiver;
975   m->from = this;
976   m->message = Message::PLAYER_EVENT;
977   m->parameter = Player::CONNECTION_LOST;
978   messageQueue->postMessage(m);
979 }
980
981 // ----------------------------------- Callback
982
983 void Player::call(void* caller)
984 {
985   if (caller == demuxer)
986   {
987     logger->log("Player", Log::DEBUG, "Callback from demuxer");
988
989     if (video->getTVsize() == Video::ASPECT4X3)
990     {
991       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
992       return;
993     }
994
995     int parx,pary;
996     int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
997     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
998     {
999       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
1000       video->setAspectRatio(Video::ASPECT4X3,parx,pary);
1001
1002       Message* m = new Message();
1003       m->from = this;
1004       m->to = messageReceiver;
1005       m->message = Message::PLAYER_EVENT;
1006       m->parameter = Player::ASPECT43;
1007       messageQueue->postMessage(m);
1008     }
1009     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1010     {
1011       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
1012       video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1013
1014       Message* m = new Message();
1015       m->from = this;
1016       m->to = messageReceiver;
1017       m->message = Message::PLAYER_EVENT;
1018       m->parameter = Player::ASPECT169;
1019       messageQueue->postMessage(m);
1020     }
1021     else
1022     {
1023       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1024           video->setAspectRatio(dxCurrentAspect,parx,pary);
1025     }
1026
1027   }
1028   else
1029   {
1030     if (videoStartup)
1031     {
1032       videoStartup = false;
1033       video->reset();
1034       video->play();
1035       video->sync();
1036       vfeed.release();
1037       unLock();
1038     }
1039
1040     threadSignalNoLock();
1041   }
1042 }
1043
1044 // ----------------------------------- Feed thread
1045
1046 void Player::threadMethod()
1047 {
1048   // this method used to be simple, the only thing it does
1049   // is farm out to threadFeed Live/Play/Scan
1050   // All the guff is to support scan hitting one end
1051
1052   if ((state == S_FFWD) || (state == S_FBWD))
1053   {
1054     if (video->PTSIFramePlayback()) threadPTSFeedScan();
1055     else threadFeedScan();
1056     // if this returns then scan hit one end
1057     if (state == S_FFWD) // scan hit the end. stop
1058     {
1059       threadCheckExit();
1060       Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1061       m->to = messageReceiver;
1062       m->from = this;
1063       m->message = Message::PLAYER_EVENT;
1064       m->parameter = STOP_PLAYBACK;
1065       logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1066       messageQueue->postMessage(m);
1067       logger->log("Player", Log::DEBUG, "Message posted...");
1068       return;
1069     }
1070     // if execution gets to here, threadFeedScan hit the start, go to play mode
1071     state = S_PLAY;
1072     audio->reset();
1073     audio->setStreamType(Audio::MPEG2_PES);
1074     demuxer->flush();
1075     demuxer->seek();
1076     demuxer->setFrameNum(currentFrameNumber);
1077     videoStartup = true;
1078     afeed.start();
1079     tfeed.start();
1080     vfeed.start();
1081     subtitles->start();
1082     audio->play();
1083     audio->sync();
1084     audio->systemMuteOff();
1085     audio->doMuting();
1086   }
1087
1088   if (state == S_PLAY) threadFeedPlay();
1089 }
1090
1091 void Player::threadFeedPlay()
1092 {
1093   ULLONG feedPosition;
1094   UINT thisRead, writeLength, thisWrite, askFor;
1095   time_t lastRescan = time(NULL);
1096
1097   feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1098   if (!vdr->isConnected()) { doConnectionLost(); return; }
1099   logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1100
1101
1102   while(1)
1103   {
1104     thisRead = 0;
1105     writeLength = 0;
1106     thisWrite = 0;
1107
1108     threadCheckExit();
1109
1110     // If we havn't rescanned for a while..
1111     if ((lastRescan + 60) < time(NULL))
1112     {
1113       lengthBytes = vdr->rescanRecording(&lengthFrames);
1114       if (!vdr->isConnected()) { doConnectionLost(); return; }
1115       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1116       lastRescan = time(NULL);
1117     }
1118
1119     if (feedPosition >= lengthBytes) break;  // finished playback
1120
1121     if (startup)
1122     {
1123       if (startupBlockSize > lengthBytes)
1124         askFor = lengthBytes; // is a very small recording!
1125       else
1126         askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1127     }
1128     else
1129     {
1130       if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1131         askFor = lengthBytes - feedPosition;
1132       else // normal
1133         askFor = blockSize;
1134     }
1135     //logger->log("Player", Log::DEBUG, "Get Block in");
1136
1137     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1138     //logger->log("Player", Log::DEBUG, "Get Block out");
1139
1140     feedPosition += thisRead;
1141
1142     if (!vdr->isConnected())
1143     {
1144       doConnectionLost();
1145       return;
1146     }
1147
1148     if (!threadBuffer) break;
1149
1150     if (startup)
1151     {
1152       int a_stream = demuxer->scan(threadBuffer, thisRead);
1153       demuxer->setAudioStream(a_stream);
1154       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1155       startup = false;
1156     }
1157
1158     threadCheckExit();
1159
1160     while(writeLength < thisRead)
1161     {
1162       //logger->log("Player", Log::DEBUG, "Put in");
1163       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1164       //logger->log("Player", Log::DEBUG, "Put out");
1165       writeLength += thisWrite;
1166
1167       if (!thisWrite)
1168       {
1169         // demuxer is full and can't take anymore
1170         threadLock();
1171         if (!threadActive) { threadUnlock(); threadCheckExit(); }
1172         threadWaitForSignal();
1173         threadUnlock();
1174       }
1175
1176       threadCheckExit();
1177     }
1178
1179     free(threadBuffer);
1180     threadBuffer = NULL;
1181
1182   }
1183
1184   // end of recording
1185   logger->log("Player", Log::DEBUG, "Recording playback ends");
1186
1187   if (videoStartup) // oh woe. there never was a stream, I was conned!
1188   {
1189     videoStartup = false;
1190     unLock();
1191     MILLISLEEP(500); // I think this will solve a race
1192   }
1193
1194   threadCheckExit();
1195
1196
1197   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1198   m->to = messageReceiver;
1199   m->from = this;
1200   m->message = Message::PLAYER_EVENT;
1201   m->parameter = Player::STOP_PLAYBACK;
1202   logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1203   messageQueue->postMessage(m);
1204 }
1205
1206
1207 void Player::threadPTSFeedScan()
1208 {
1209   // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1210
1211   ULONG direction = 0;
1212   int dir_fac=-1;
1213   ULONG baseFrameNumber = 0;
1214   ULONG iframeNumber = 0;
1215   ULONG iframeLength = 0;
1216   ULONG currentfeedFrameNumber=currentFrameNumber;
1217   ULONG firstFrameNumber=currentFrameNumber;
1218   ULLONG filePos;
1219   UINT amountReceived;
1220   UINT videoLength;
1221
1222   UINT playtime=0;
1223
1224   int frameTimeOffset = 0; // Time in msec between frames
1225
1226   if (state == S_FFWD)
1227   {
1228     direction = 1; // and 0 for backward
1229     dir_fac = 1;
1230   }
1231   video->EnterIframePlayback();
1232
1233   while(1)
1234   {
1235     baseFrameNumber = currentfeedFrameNumber;
1236
1237     threadCheckExit();
1238     if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1239        return;
1240
1241     if (iframeNumber >= lengthFrames) return;
1242         // scan has got to the end of what we knew to be there before we started scanning
1243
1244     baseFrameNumber = iframeNumber;
1245
1246     frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1247
1248     logger->log("Player", Log::DEBUG, "XXX Got frame");
1249
1250     threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1251
1252     if (!vdr->isConnected())
1253     {
1254       if (threadBuffer) free(threadBuffer);
1255       doConnectionLost();
1256       break;
1257     }
1258
1259
1260     threadCheckExit();
1261
1262
1263     videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1264     demuxer->changeTimes(threadBuffer,videoLength,playtime);
1265     int count=0;
1266     while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1267     {
1268         MILLISLEEP(20);
1269         threadCheckExit();
1270         count++;
1271         if (count%300==0) {
1272                 ULLONG cur_time=video->getCurrentTimestamp();
1273         //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1274                 //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1275                 if (cur_time!=0) {
1276                     currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1277                 }
1278         }
1279
1280     }
1281     playtime +=frameTimeOffset;
1282     currentfeedFrameNumber = iframeNumber;
1283     {
1284                 ULLONG cur_time=video->getCurrentTimestamp();
1285         //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1286                 //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1287                 if (cur_time!=0) {
1288                     currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1289                 }
1290         }
1291
1292     free(threadBuffer);
1293     threadBuffer = NULL;
1294   }
1295 }
1296
1297
1298
1299 void Player::threadFeedScan()
1300 {
1301   // This method is actually really simple - get frame from vdr,
1302   // spit it at the video chip, wait for a time. Most of the code here
1303   // is to get the wait right so that the scan occurs at the correct rate.
1304
1305   ULONG direction = 0;
1306   ULONG baseFrameNumber = 0;
1307   ULONG iframeNumber = 0;
1308   ULONG iframeLength = 0;
1309   ULLONG filePos;
1310   UINT amountReceived;
1311   UINT videoLength;
1312
1313 #ifndef WIN32
1314   struct timeval clock0 = {0,0};  // Time stamp after fetching I-frame info
1315   struct timeval clock1 = {0,0};  // Time stamp after fetching I-frame data
1316   struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1317 #else
1318   DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1319 #endif
1320
1321   int frameTimeOffset = 0; // Time in msec between frames
1322   int disp_msec = 0;  // Time taken to display data
1323   int total_msec = 0; // Time taken to fetch data and display it
1324   int sleepTime = 0;
1325
1326   if (state == S_FFWD) direction = 1; // and 0 for backward
1327
1328   while(1)
1329   {
1330     // Fetch I-frames until we get one that can be displayed in good time
1331     // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1332
1333     baseFrameNumber = currentFrameNumber;
1334     do
1335     {
1336       threadCheckExit();
1337       if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1338         return;
1339
1340       if (iframeNumber >= lengthFrames) return;
1341         // scan has got to the end of what we knew to be there before we started scanning
1342
1343       baseFrameNumber = iframeNumber;
1344       frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1345 #ifndef WIN32
1346       gettimeofday(&clock0, NULL);
1347 #else
1348       clock0 = timeGetTime();
1349 #endif
1350     }
1351 #ifndef WIN32
1352     while (clock2.tv_sec != 0 &&
1353           (clock0.tv_sec - clock2.tv_sec) * 1000 +
1354           (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1355 #else
1356     while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1357 #endif
1358     logger->log("Player", Log::DEBUG, "XXX Got frame");
1359
1360     threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1361
1362     if (!vdr->isConnected() || !amountReceived)
1363     {
1364       if (threadBuffer) free(threadBuffer);
1365       doConnectionLost();
1366       break;
1367     }
1368     
1369 #ifndef WIN32
1370     gettimeofday(&clock1, NULL);
1371     if (clock2.tv_sec != 0)
1372       sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1373                 + (clock2.tv_usec - clock1.tv_usec) / 1000
1374                 + frameTimeOffset - disp_msec;
1375 #else
1376     clock1 = timeGetTime();
1377     if (clock2 != 0)
1378       sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1379 #endif
1380     if (sleepTime < 0) sleepTime = 0;
1381     threadCheckExit();
1382     MILLISLEEP(sleepTime);
1383    logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1384
1385     videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1386     video->displayIFrame(threadBuffer, videoLength);
1387     currentFrameNumber = iframeNumber;
1388     free(threadBuffer);
1389     threadBuffer = NULL;
1390
1391 #ifndef WIN32
1392     gettimeofday(&clock2, NULL);
1393     total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1394                + (clock2.tv_usec - clock0.tv_usec) / 1000
1395                - sleepTime;
1396     disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1397               + (clock2.tv_usec - clock1.tv_usec) / 1000
1398               - sleepTime;
1399 #else
1400     clock2 = timeGetTime();
1401     total_msec = clock2 - clock0 - sleepTime;
1402     disp_msec = clock2 - clock1 - sleepTime;
1403 #endif
1404     logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1405   }
1406 }
1407
1408 void Player::threadPostStopCleanup()
1409 {
1410   if (threadBuffer)
1411   {
1412     free(threadBuffer);
1413     threadBuffer = NULL;
1414   }
1415 }
1416
1417 // ----------------------------------- Dev
1418
1419 #ifdef DEV
1420 void Player::test1()
1421 {
1422   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1423 }
1424
1425 void Player::test2()
1426 {
1427   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1428 }
1429 #endif