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