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