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