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