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