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