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