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