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