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