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