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