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