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