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