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