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