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