]> git.vomp.tv Git - vompclient.git/blob - player.cc
Preliminray OMX h264 playback stuff not finished yet
[vompclient.git] / player.cc
1 /*\r
2     Copyright 2004-2008 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
19 */\r
20 \r
21 #include "player.h"\r
22 \r
23 #include "log.h"\r
24 #include "audio.h"\r
25 #include "video.h"\r
26 #include "demuxervdr.h"\r
27 #include "demuxerts.h"\r
28 #include "vdr.h"\r
29 #include "messagequeue.h"\r
30 #include "remote.h"\r
31 #include "message.h"\r
32 #include "dvbsubtitles.h"\r
33 #include "osdreceiver.h"\r
34 \r
35 #define USER_RESPONSE_TIME 500 // Milliseconds\r
36 \r
37 // ----------------------------------- Called from outside, one offs or info funcs\r
38 \r
39 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)\r
40 : vfeed(this), afeed(this), tfeed(this)\r
41 {\r
42   messageQueue = tmessageQueue;\r
43   messageReceiver = tmessageReceiver;\r
44   osdReceiver = tosdReceiver;\r
45   audio = Audio::getInstance();\r
46   video = Video::getInstance();\r
47   logger = Log::getInstance();\r
48   vdr = VDR::getInstance();\r
49   initted = false;\r
50   lengthBytes = 0;\r
51   lengthFrames = 0;\r
52   currentFrameNumber = 0;\r
53   state = S_STOP;\r
54   ifactor = 4;\r
55   is_pesrecording=true;\r
56 \r
57   subtitlesShowing = false;\r
58 \r
59   videoStartup = false;\r
60   threadBuffer = NULL;\r
61  \r
62   blockSize = 100000;\r
63   startupBlockSize = 250000;\r
64   video->turnVideoOn();\r
65 }\r
66 \r
67 Player::~Player()\r
68 {\r
69   if (initted) shutdown();\r
70 }\r
71 \r
72 int Player::init(bool p_isPesRecording,double framespersecond)\r
73 {\r
74   if (initted) return 0;\r
75 #ifndef WIN32\r
76   pthread_mutex_init(&mutex, NULL);\r
77 #else\r
78   mutex=CreateMutex(NULL,FALSE,NULL);\r
79 #endif\r
80   is_pesrecording = p_isPesRecording;\r
81   fps=framespersecond;\r
82   if (is_pesrecording)\r
83     demuxer = new DemuxerVDR();\r
84   else\r
85     demuxer = new DemuxerTS();\r
86   if (!demuxer) return 0;\r
87   subtitles = new DVBSubtitles(osdReceiver);\r
88   if (!subtitles) return 0;\r
89 \r
90   teletext = new TeletextDecoderVBIEBU();\r
91   if (!teletext) return 0;\r
92   teletext->setRecordigMode(true);\r
93   unsigned int demux_video_size=2097152;\r
94    if (video->supportsh264()) demux_video_size*=5;\r
95  \r
96   if (!demuxer->init(this, audio, video,teletext, demux_video_size,524288,65536, framespersecond, subtitles))\r
97   {\r
98     logger->log("Player", Log::ERR, "Demuxer failed to init");\r
99     shutdown();\r
100     return 0;\r
101   }\r
102 \r
103   vfeed.init();\r
104   afeed.init();\r
105   tfeed.init();\r
106 \r
107   video->stop();\r
108   video->blank();\r
109   audio->stop();\r
110 \r
111   initted = true;\r
112   return 1;\r
113 }\r
114 \r
115 int Player::shutdown()\r
116 {\r
117   if (!initted) return 0;\r
118   switchState(S_STOP);\r
119   initted = false;\r
120 \r
121   delete demuxer;\r
122   demuxer = NULL;\r
123   delete subtitles;\r
124   subtitles = NULL;\r
125   delete teletext;\r
126   teletext = NULL;\r
127 \r
128 #ifdef WIN32\r
129   CloseHandle(mutex);\r
130 #endif\r
131 \r
132   return 1;\r
133 }\r
134 \r
135 void Player::setStartFrame(ULONG startFrame)\r
136 {\r
137   currentFrameNumber = startFrame;\r
138 }\r
139 \r
140 void Player::setLengthBytes(ULLONG length)\r
141 {\r
142   lengthBytes = length;\r
143   logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);\r
144 }\r
145 \r
146 void Player::setLengthFrames(ULONG length)\r
147 {\r
148   lengthFrames = length;\r
149   logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);\r
150 }\r
151 \r
152 ULONG Player::getLengthFrames()\r
153 {\r
154   return lengthFrames;\r
155 }\r
156 \r
157 ULONG Player::getCurrentFrameNum()\r
158 {\r
159   if (startup) return 0;\r
160   switch(state)\r
161   {\r
162     case S_PLAY:\r
163     case S_PAUSE_P:\r
164       return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());\r
165     case S_PAUSE_I:\r
166     case S_FFWD:\r
167     case S_FBWD:\r
168       return currentFrameNumber;\r
169     default:\r
170       return 0; // shouldn't happen\r
171   }\r
172 }\r
173 \r
174 bool* Player::getDemuxerMpegAudioChannels()\r
175 {\r
176   return demuxer->getmpAudioChannels();\r
177 }\r
178 \r
179 bool* Player::getDemuxerAc3AudioChannels()\r
180 {\r
181   return demuxer->getac3AudioChannels();\r
182 }\r
183 \r
184 bool* Player::getDemuxerSubtitleChannels()\r
185 {\r
186   return demuxer->getSubtitleChannels();\r
187 }\r
188 \r
189 int Player::getCurrentAudioChannel()\r
190 {\r
191     if (is_pesrecording) {\r
192         return demuxer->getselAudioChannel();\r
193     } else {\r
194         return ((DemuxerTS*)demuxer)->getAID();\r
195     }\r
196 }\r
197 \r
198 int Player::getCurrentSubtitleChannel()\r
199 {\r
200     if (is_pesrecording) {\r
201         return demuxer->getselSubtitleChannel();\r
202     } else {\r
203         return ((DemuxerTS*)demuxer)->getSubID();\r
204     }\r
205 }\r
206 \r
207 void Player::setSubtitleChannel(int newChannel)\r
208 {\r
209     if (is_pesrecording) {\r
210          demuxer->setDVBSubtitleStream(newChannel);\r
211     } else {\r
212         ((DemuxerTS*)demuxer)->setSubID(newChannel);\r
213     }\r
214 }\r
215 \r
216 int *Player::getTeletxtSubtitlePages()\r
217 {\r
218     return teletext->getSubtitlePages();\r
219 }\r
220 \r
221 void Player::setAudioChannel(int newChannel, int type)\r
222 {\r
223     if (is_pesrecording) {\r
224         demuxer->setAudioStream(newChannel);\r
225         return;\r
226     } else {\r
227         ((DemuxerTS*)demuxer)->setAID(newChannel,type);\r
228         return;\r
229     }\r
230 }\r
231 \r
232 bool Player::toggleSubtitles()\r
233 {\r
234   if (!subtitlesShowing)\r
235   {\r
236     subtitlesShowing = true;\r
237     subtitles->show();\r
238   }\r
239   else\r
240   {\r
241     subtitlesShowing = false;\r
242     subtitles->hide();\r
243   }\r
244   return subtitlesShowing;\r
245 }\r
246 \r
247 void  Player::turnSubtitlesOn(bool ison) {\r
248  if (ison)\r
249   {\r
250     subtitlesShowing = true;\r
251     subtitles->show();\r
252   }\r
253   else\r
254   {\r
255     subtitlesShowing = false;\r
256     subtitles->hide();\r
257   }\r
258 \r
259 }\r
260 \r
261 Channel Player::getDemuxerChannel() {\r
262     if (!is_pesrecording) {\r
263         return ((DemuxerTS*) demuxer)->getChannelInfo();\r
264     } \r
265     return Channel(); //Should not happen!\r
266 }\r
267 \r
268 \r
269 // ----------------------------------- Externally called events\r
270 \r
271 void Player::play()\r
272 {\r
273   if (!initted) return;\r
274   if (state == S_PLAY) return;\r
275   lock();\r
276 \r
277   bool doUnlock = false;\r
278   if (state == S_PAUSE_P) doUnlock = true;\r
279   switchState(S_PLAY);\r
280   if (doUnlock) unLock();\r
281 }\r
282 \r
283 void Player::playpause()\r
284 {\r
285   if (!initted) return;\r
286   lock();\r
287 \r
288   bool doUnlock = false;\r
289   if (state==S_PLAY) {\r
290           doUnlock=true;\r
291           switchState(S_PAUSE_P);\r
292   } else {\r
293           if (state == S_PAUSE_P) doUnlock = true;\r
294           switchState(S_PLAY);\r
295   }\r
296   if (doUnlock) unLock();\r
297 \r
298 }\r
299 \r
300 \r
301 \r
302 \r
303 void Player::stop()\r
304 {\r
305   if (!initted) return;\r
306   if (state == S_STOP) return;\r
307   lock();\r
308   logger->log("Player", Log::DEBUG, "Stop called lock");\r
309   switchState(S_STOP);\r
310   unLock();\r
311 }\r
312 \r
313 void Player::pause()\r
314 {\r
315   if (!initted) return;\r
316   lock();\r
317 \r
318   if ((state == S_FFWD) || (state == S_FBWD))\r
319   {\r
320     switchState(S_PAUSE_I);\r
321   }\r
322   else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))\r
323   {\r
324     switchState(S_PLAY);\r
325   }\r
326   else\r
327   {\r
328     switchState(S_PAUSE_P);\r
329   }\r
330 \r
331   unLock();\r
332 }\r
333 \r
334 void Player::fastForward()\r
335 {\r
336   if (!initted) return;\r
337   lock();\r
338 \r
339   if (state == S_FFWD)\r
340   {\r
341     // change the rate\r
342     switch(ifactor)\r
343     {\r
344       case 4: ifactor = 8; break;\r
345       case 8: ifactor = 16; break;\r
346       case 16: ifactor = 32; break;\r
347       case 32: ifactor = 4; break;\r
348     }\r
349   }\r
350   else\r
351   {\r
352     ifactor = 4;\r
353     switchState(S_FFWD);\r
354   }\r
355   unLock();\r
356 }\r
357 \r
358 void Player::fastBackward()\r
359 {\r
360   if (!initted) return;\r
361   lock();\r
362 \r
363   if (state == S_FBWD)\r
364   {\r
365     // change the rate\r
366     switch(ifactor)\r
367     {\r
368       case 4: ifactor = 8; break;\r
369       case 8: ifactor = 16; break;\r
370       case 16: ifactor = 32; break;\r
371       case 32: ifactor = 4; break;\r
372     }\r
373   }\r
374   else\r
375   {\r
376     ifactor = 4;\r
377     switchState(S_FBWD);\r
378   }\r
379   unLock();\r
380 }\r
381 \r
382 void Player::jumpToPercent(double percent)\r
383 {\r
384   lock();\r
385   logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);\r
386   ULONG newFrame = (ULONG)(percent * lengthFrames / 100);\r
387   switchState(S_JUMP, newFrame);\r
388 //  unLock(); - let thread unlock this\r
389 }\r
390 \r
391 void Player::jumpToMark(int mark)\r
392 {\r
393   lock();\r
394   logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);\r
395   switchState(S_JUMP, mark);\r
396 //  unLock(); - let thread unlock this\r
397 }\r
398 \r
399 void Player::jumpToFrameP(int newFrame)\r
400 {\r
401   lock();\r
402   logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);\r
403   switchState(S_JUMP_PI, newFrame);\r
404   unLock();\r
405 }\r
406 \r
407 void Player::skipForward(int seconds)\r
408 {\r
409   lock();\r
410   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);\r
411   ULONG newFrame = getCurrentFrameNum();\r
412   if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid\r
413   newFrame +=(ULONG) (((double)seconds) * fps);\r
414   if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }\r
415   else switchState(S_JUMP, newFrame);\r
416 //  unLock(); - let thread unlock this\r
417 }\r
418 \r
419 void Player::skipBackward(int seconds)\r
420 {\r
421   lock();\r
422   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);\r
423   long newFrame = getCurrentFrameNum();\r
424   if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid\r
425   newFrame -= (ULONG) (((double)seconds) * fps);\r
426   if (newFrame < 0) newFrame = 0;\r
427   switchState(S_JUMP, newFrame);\r
428 //  unLock(); - let thread unlock this\r
429 }\r
430 \r
431 // ----------------------------------- Implementations called events\r
432 \r
433 void Player::switchState(UCHAR toState, ULONG jumpFrame)\r
434 {\r
435   if (!initted) return;\r
436 \r
437   logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);\r
438 \r
439   switch(state) // current state selector\r
440   {\r
441     case S_PLAY: // from S_PLAY -----------------------------------\r
442     {\r
443       switch(toState)\r
444       {\r
445         case S_PLAY: // to S_PLAY\r
446         {\r
447           return;\r
448         }\r
449         case S_PAUSE_P: // to S_PAUSE_P\r
450         {\r
451           video->pause();\r
452           audio->pause();\r
453           state = S_PAUSE_P;\r
454           return;\r
455         }\r
456         case S_PAUSE_I: // to S_PAUSE_I\r
457         {\r
458           // can't occur\r
459           return;\r
460         }\r
461         case S_FFWD: // to S_FFWD\r
462         {\r
463           currentFrameNumber = getCurrentFrameNum();\r
464           audio->systemMuteOn();\r
465           threadStop();\r
466           vfeed.stop();\r
467           afeed.stop();\r
468           tfeed.stop();\r
469           subtitles->stop();\r
470           demuxer->flush();\r
471           state = S_FFWD;\r
472           threadStart();\r
473           return;\r
474         }\r
475         case S_FBWD: // to S_FBWD\r
476         {\r
477           currentFrameNumber = getCurrentFrameNum();\r
478           audio->systemMuteOn();\r
479           threadStop();\r
480           vfeed.stop();\r
481           afeed.stop();\r
482           tfeed.stop();\r
483           subtitles->stop();\r
484           demuxer->flush();\r
485           state = S_FBWD;\r
486           threadStart();\r
487           return;\r
488         }\r
489         case S_STOP: // to S_STOP\r
490         {\r
491           vfeed.stop();\r
492           afeed.stop();\r
493           tfeed.stop();\r
494           subtitles->stop();\r
495           threadStop();\r
496           video->stop();\r
497           video->blank();\r
498           audio->stop();\r
499           audio->unPause();\r
500           video->reset();\r
501           demuxer->reset();\r
502           state = S_STOP;\r
503           return;\r
504         }\r
505         case S_JUMP: // to S_JUMP\r
506         {\r
507           restartAtFrame(jumpFrame);\r
508           return;\r
509         }\r
510         case S_JUMP_PI: // to S_JUMP_PI\r
511         {\r
512           audio->systemMuteOn();\r
513           threadStop();\r
514           vfeed.stop();\r
515           afeed.stop();\r
516           tfeed.stop();\r
517           subtitles->stop();\r
518           demuxer->flush();\r
519           state = S_PAUSE_I;\r
520           video->reset();\r
521           video->play();\r
522           restartAtFramePI(jumpFrame);\r
523           return;\r
524         }\r
525       }\r
526     }\r
527     case S_PAUSE_P: // from S_PAUSE_P -----------------------------------\r
528     {\r
529       switch(toState)\r
530       {\r
531         case S_PLAY: // to S_PLAY\r
532         {\r
533           video->unPause();\r
534           audio->unPause();\r
535           state = S_PLAY;\r
536           return;\r
537         }\r
538         case S_PAUSE_P: // to S_PAUSE_P\r
539         {\r
540           return;\r
541         }\r
542         case S_PAUSE_I: // to S_PAUSE_I\r
543         {\r
544           return;\r
545         }\r
546         case S_FFWD: // to S_FFWD\r
547         {\r
548           currentFrameNumber = getCurrentFrameNum();\r
549           audio->systemMuteOn();\r
550           vfeed.stop();\r
551           afeed.stop();\r
552           tfeed.stop();\r
553           subtitles->stop();\r
554           threadStop();\r
555           video->unPause();\r
556           audio->unPause();\r
557           state = S_FFWD;\r
558           threadStart();\r
559           return;\r
560         }\r
561         case S_FBWD: // to S_FBWD\r
562         {\r
563           currentFrameNumber = getCurrentFrameNum();\r
564           audio->systemMuteOn();\r
565           vfeed.stop();\r
566           afeed.stop();\r
567           tfeed.stop();\r
568           subtitles->stop();\r
569           threadStop();\r
570           video->unPause();\r
571           audio->unPause();\r
572           state = S_FBWD;\r
573           threadStart();\r
574           return;\r
575         }\r
576         case S_STOP: // to S_STOP\r
577         {\r
578           vfeed.stop();\r
579           afeed.stop();\r
580           tfeed.stop();\r
581           subtitles->stop();\r
582           threadStop();\r
583           video->stop();\r
584           video->blank();\r
585           audio->stop();\r
586           video->reset();\r
587           audio->unPause();\r
588           demuxer->reset();\r
589           audio->systemMuteOff();\r
590           state = S_STOP;\r
591           return;\r
592         }\r
593         case S_JUMP: // to S_JUMP\r
594         {\r
595           state = S_PLAY;\r
596           audio->systemMuteOn();\r
597           audio->unPause();\r
598           restartAtFrame(jumpFrame);\r
599           return;\r
600         }\r
601         case S_JUMP_PI: // to S_JUMP_PI\r
602         {\r
603           audio->systemMuteOn();\r
604           audio->unPause();\r
605           threadStop();\r
606           vfeed.stop();\r
607           afeed.stop();\r
608           tfeed.stop();\r
609           subtitles->stop();\r
610           demuxer->flush();\r
611           state = S_PAUSE_I;\r
612           video->reset();\r
613           video->play();\r
614           restartAtFramePI(jumpFrame);\r
615           return;\r
616         }\r
617       }\r
618     }\r
619     case S_PAUSE_I: // from S_PAUSE_I -----------------------------------\r
620     {\r
621       switch(toState)\r
622       {\r
623         case S_PLAY: // to S_PLAY\r
624         {\r
625           state = S_PLAY;\r
626           restartAtFrame(currentFrameNumber);\r
627           return;\r
628         }\r
629         case S_PAUSE_P: // to S_PAUSE_P\r
630         {\r
631           return;\r
632         }\r
633         case S_PAUSE_I: // to S_PAUSE_I\r
634         {\r
635           return;\r
636         }\r
637         case S_FFWD: // to S_FFWD\r
638         {\r
639           state = S_FFWD;\r
640           threadStart();\r
641           return;\r
642         }\r
643         case S_FBWD: // to S_FBWD\r
644         {\r
645           state = S_FBWD;\r
646           threadStart();\r
647           return;\r
648         }\r
649         case S_STOP: // to S_STOP\r
650         {\r
651           video->stop();\r
652           video->blank();\r
653           audio->stop();\r
654           video->reset();\r
655           demuxer->reset();\r
656           audio->systemMuteOff();\r
657           state = S_STOP;\r
658           return;\r
659         }\r
660         case S_JUMP: // to S_JUMP\r
661         {\r
662           state = S_PLAY;\r
663           restartAtFrame(jumpFrame);\r
664           return;\r
665         }\r
666         case S_JUMP_PI: // to S_JUMP_PI\r
667         {\r
668           restartAtFramePI(jumpFrame);\r
669           return;\r
670         }\r
671       }\r
672     }\r
673     case S_FFWD: // from S_FFWD -----------------------------------\r
674     {\r
675       switch(toState)\r
676       {\r
677         case S_PLAY: // to S_PLAY\r
678         {\r
679           state = S_PLAY;\r
680           ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);\r
681           if (stepback < currentFrameNumber)\r
682             currentFrameNumber -= stepback;\r
683           else\r
684             currentFrameNumber = 0;\r
685           restartAtFrame(currentFrameNumber);\r
686           return;\r
687         }\r
688         case S_PAUSE_P: // to S_PAUSE_P\r
689         {\r
690           // can't occur\r
691           return;\r
692         }\r
693         case S_PAUSE_I: // to S_PAUSE_I\r
694         {\r
695           threadStop();\r
696           state = S_PAUSE_I;\r
697           return;\r
698         }\r
699         case S_FFWD: // to S_FFWD\r
700         {\r
701           return;\r
702         }\r
703         case S_FBWD: // to S_FBWD\r
704         {\r
705           threadStop();\r
706           state = S_FBWD;\r
707           threadStart();\r
708           return;\r
709         }\r
710         case S_STOP: // to S_STOP\r
711         {\r
712           threadStop();\r
713           video->stop();\r
714           video->blank();\r
715           audio->stop();\r
716           video->reset();\r
717           demuxer->reset();\r
718           state = S_STOP;\r
719           return;\r
720         }\r
721         case S_JUMP: // to S_JUMP\r
722         {\r
723           state = S_PLAY;\r
724           restartAtFrame(jumpFrame);\r
725           return;\r
726         }\r
727         case S_JUMP_PI: // to S_JUMP_PI\r
728         {\r
729           threadStop();\r
730           state = S_PAUSE_I;\r
731           restartAtFramePI(jumpFrame);\r
732           return;\r
733         }\r
734       }\r
735     }\r
736     case S_FBWD: // from S_FBWD -----------------------------------\r
737     {\r
738       switch(toState)\r
739       {\r
740         case S_PLAY: // to S_PLAY\r
741         {\r
742           state = S_PLAY;\r
743           restartAtFrame(currentFrameNumber);\r
744           return;\r
745         }\r
746         case S_PAUSE_P: // to S_PAUSE_P\r
747         {\r
748           // can't occur\r
749           return;\r
750         }\r
751         case S_PAUSE_I: // to S_PAUSE_I\r
752         {\r
753           threadStop();\r
754           state = S_PAUSE_I;\r
755           return;\r
756         }\r
757         case S_FFWD: // to S_FFWD\r
758         {\r
759           threadStop();\r
760           state = S_FFWD;\r
761           threadStart();\r
762           return;\r
763         }\r
764         case S_FBWD: // to S_FBWD\r
765         {\r
766           return;\r
767         }\r
768         case S_STOP: // to S_STOP\r
769         {\r
770           threadStop();\r
771           video->stop();\r
772           video->blank();\r
773           audio->stop();\r
774           video->reset();\r
775           demuxer->reset();\r
776           state = S_STOP;\r
777           return;\r
778         }\r
779         case S_JUMP: // to S_JUMP\r
780         {\r
781           state = S_PLAY;\r
782           restartAtFrame(jumpFrame);\r
783           return;\r
784         }\r
785         case S_JUMP_PI: // to S_JUMP_PI\r
786         {\r
787           threadStop();\r
788           state = S_PAUSE_I;\r
789           restartAtFramePI(jumpFrame);\r
790           return;\r
791         }\r
792       }\r
793     }\r
794     case S_STOP: // from S_STOP -----------------------------------\r
795     {\r
796       switch(toState)\r
797       {\r
798         case S_PLAY: // to S_PLAY\r
799         {\r
800           startup = true;\r
801 \r
802           audio->reset();\r
803           audio->setStreamType(Audio::MPEG2_PES);\r
804           audio->systemMuteOff();\r
805           video->reset();\r
806           demuxer->reset();\r
807           // FIXME use restartAtFrame here?\r
808           if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;\r
809           demuxer->setFrameNum(currentFrameNumber);\r
810           demuxer->seek();\r
811           videoStartup = true;\r
812           state = S_PLAY;\r
813           threadStart();\r
814           logger->log("Player", Log::DEBUG, "Immediate play");\r
815           afeed.start();\r
816           vfeed.start();\r
817           tfeed.start();\r
818           subtitles->start();\r
819           video->sync();\r
820           audio->sync();\r
821           audio->play();\r
822           video->pause();\r
823           return;\r
824         }\r
825         case S_PAUSE_P: // to S_PAUSE_P\r
826         {\r
827           return;\r
828         }\r
829         case S_PAUSE_I: // to S_PAUSE_I\r
830         {\r
831           return;\r
832         }\r
833         case S_FFWD: // to S_FFWD\r
834         {\r
835           return;\r
836         }\r
837         case S_FBWD: // to S_FBWD\r
838         {\r
839           return;\r
840         }\r
841         case S_STOP: // to S_STOP\r
842         {\r
843           return;\r
844         }\r
845         case S_JUMP: // to S_JUMP\r
846         {\r
847           return;\r
848         }\r
849         case S_JUMP_PI: // to S_JUMP_PI\r
850         {\r
851           return;\r
852         }\r
853       }\r
854     }\r
855     // case S_JUMP cannot be a start state because it auto flips to play\r
856     // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I\r
857   }\r
858 }\r
859 \r
860 // ----------------------------------- Internal functions\r
861 \r
862 void Player::lock()\r
863 {\r
864 #ifndef WIN32\r
865   pthread_mutex_lock(&mutex);\r
866   logger->log("Player", Log::DEBUG, "LOCKED");\r
867 \r
868 #else\r
869    WaitForSingleObject(mutex, INFINITE);\r
870 #endif\r
871 }\r
872 \r
873 void Player::unLock()\r
874 {\r
875 #ifndef WIN32\r
876   logger->log("Player", Log::DEBUG, "UNLOCKING");\r
877   pthread_mutex_unlock(&mutex);\r
878 #else\r
879    ReleaseMutex(mutex);\r
880 #endif\r
881 }\r
882 \r
883 void Player::restartAtFrame(ULONG newFrame)\r
884 {\r
885   vfeed.stop();\r
886   afeed.stop();\r
887   tfeed.stop();\r
888   subtitles->stop();\r
889   threadStop();\r
890   video->stop();\r
891   video->reset();\r
892   audio->reset();\r
893   audio->setStreamType(Audio::MPEG2_PES);\r
894   demuxer->flush();\r
895   demuxer->seek();\r
896   currentFrameNumber = newFrame;\r
897   demuxer->setFrameNum(newFrame);\r
898   videoStartup = true;\r
899   afeed.start();\r
900   tfeed.start();\r
901   vfeed.start();\r
902   subtitles->start();\r
903   threadStart();\r
904   audio->play();\r
905   video->sync();\r
906   audio->sync();\r
907   audio->systemMuteOff();\r
908   audio->doMuting();\r
909 }\r
910 \r
911 \r
912 void Player::restartAtFramePI(ULONG newFrame)\r
913 {\r
914   ULLONG filePos;\r
915   ULONG nextiframeNumber;\r
916   ULONG iframeLength;\r
917   ULONG iframeNumber;\r
918 \r
919   UCHAR* buffer;\r
920   UINT amountReceived;\r
921   UINT videoLength;\r
922 \r
923   // newFrame could be anywhere, go forwards to next I-Frame\r
924   if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;\r
925 \r
926   // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame\r
927   vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);\r
928 \r
929   buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);\r
930   if (!vdr->isConnected())\r
931   {\r
932     if (buffer) free(buffer);\r
933     doConnectionLost();\r
934   }\r
935   else\r
936   {\r
937     videoLength = demuxer->stripAudio(buffer, amountReceived);\r
938     video->displayIFrame(buffer, videoLength);\r
939     video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)\r
940     free(buffer);\r
941     currentFrameNumber = iframeNumber;\r
942   }\r
943 }\r
944 \r
945 void Player::doConnectionLost()\r
946 {\r
947   logger->log("Player", Log::DEBUG, "Connection lost, sending message");\r
948   Message* m = new Message();\r
949   m->to = messageReceiver;\r
950   m->from = this;\r
951   m->message = Message::PLAYER_EVENT;\r
952   m->parameter = Player::CONNECTION_LOST;\r
953   messageQueue->postMessage(m);\r
954 }\r
955 \r
956 // ----------------------------------- Callback\r
957 \r
958 void Player::call(void* caller)\r
959 {\r
960   if (caller == demuxer)\r
961   {\r
962     logger->log("Player", Log::DEBUG, "Callback from demuxer");\r
963 \r
964     if (video->getTVsize() == Video::ASPECT4X3)\r
965     {\r
966       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
967       return;\r
968     }\r
969 \r
970     int dxCurrentAspect = demuxer->getAspectRatio();\r
971     if (dxCurrentAspect == Demuxer::ASPECT_4_3)\r
972     {\r
973       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");\r
974       video->setAspectRatio(Video::ASPECT4X3);\r
975 \r
976       Message* m = new Message();\r
977       m->from = this;\r
978       m->to = messageReceiver;\r
979       m->message = Message::PLAYER_EVENT;\r
980       m->parameter = Player::ASPECT43;\r
981       messageQueue->postMessageFromOuterSpace(m);\r
982     }\r
983     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)\r
984     {\r
985       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");\r
986       video->setAspectRatio(Video::ASPECT16X9);\r
987 \r
988       Message* m = new Message();\r
989       m->from = this;\r
990       m->to = messageReceiver;\r
991       m->message = Message::PLAYER_EVENT;\r
992       m->parameter = Player::ASPECT169;\r
993       messageQueue->postMessageFromOuterSpace(m);\r
994     }\r
995     else\r
996     {\r
997       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");\r
998     }\r
999 \r
1000   }\r
1001   else\r
1002   {\r
1003     if (videoStartup)\r
1004     {\r
1005       videoStartup = false;\r
1006       video->reset();\r
1007       video->play();\r
1008       video->sync();\r
1009       vfeed.release();\r
1010       unLock();\r
1011     }\r
1012 \r
1013     threadSignalNoLock();\r
1014   }\r
1015 }\r
1016 \r
1017 // ----------------------------------- Feed thread\r
1018 \r
1019 void Player::threadMethod()\r
1020 {\r
1021   // this method used to be simple, the only thing it does\r
1022   // is farm out to threadFeed Live/Play/Scan\r
1023   // All the guff is to support scan hitting one end\r
1024 \r
1025   if ((state == S_FFWD) || (state == S_FBWD))\r
1026   {\r
1027     if (video->PTSIFramePlayback()) threadPTSFeedScan();\r
1028     else threadFeedScan();\r
1029     // if this returns then scan hit one end\r
1030     if (state == S_FFWD) // scan hit the end. stop\r
1031     {\r
1032       threadCheckExit();\r
1033       Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex\r
1034       m->to = messageReceiver;\r
1035       m->from = this;\r
1036       m->message = Message::PLAYER_EVENT;\r
1037       m->parameter = STOP_PLAYBACK;\r
1038       logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);\r
1039       messageQueue->postMessage(m);\r
1040       logger->log("Player", Log::DEBUG, "Message posted...");\r
1041       return;\r
1042     }\r
1043     // if execution gets to here, threadFeedScan hit the start, go to play mode\r
1044     state = S_PLAY;\r
1045     audio->reset();\r
1046     audio->setStreamType(Audio::MPEG2_PES);\r
1047     demuxer->flush();\r
1048     demuxer->seek();\r
1049     demuxer->setFrameNum(currentFrameNumber);\r
1050     videoStartup = true;\r
1051     afeed.start();\r
1052     tfeed.start();\r
1053     vfeed.start();\r
1054     subtitles->start();\r
1055     audio->play();\r
1056     audio->sync();\r
1057     audio->systemMuteOff();\r
1058     audio->doMuting();\r
1059   }\r
1060 \r
1061   if (state == S_PLAY) threadFeedPlay();\r
1062 }\r
1063 \r
1064 void Player::threadFeedPlay()\r
1065 {\r
1066   ULLONG feedPosition;\r
1067   UINT thisRead, writeLength, thisWrite, askFor;\r
1068   time_t lastRescan = time(NULL);\r
1069 \r
1070   feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);\r
1071   if (!vdr->isConnected()) { doConnectionLost(); return; }\r
1072   logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);\r
1073 \r
1074 \r
1075   while(1)\r
1076   {\r
1077     thisRead = 0;\r
1078     writeLength = 0;\r
1079     thisWrite = 0;\r
1080 \r
1081     threadCheckExit();\r
1082 \r
1083     // If we havn't rescanned for a while..\r
1084     if ((lastRescan + 60) < time(NULL))\r
1085     {\r
1086       lengthBytes = vdr->rescanRecording(&lengthFrames);\r
1087       if (!vdr->isConnected()) { doConnectionLost(); return; }\r
1088       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);\r
1089       lastRescan = time(NULL);\r
1090     }\r
1091 \r
1092     if (feedPosition >= lengthBytes) break;  // finished playback\r
1093 \r
1094     if (startup)\r
1095     {\r
1096       if (startupBlockSize > lengthBytes)\r
1097         askFor = lengthBytes; // is a very small recording!\r
1098       else\r
1099         askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams\r
1100     }\r
1101     else\r
1102     {\r
1103       if ((feedPosition + blockSize) > lengthBytes) // last block of recording\r
1104         askFor = lengthBytes - feedPosition;\r
1105       else // normal\r
1106         askFor = blockSize;\r
1107     }\r
1108     //logger->log("Player", Log::DEBUG, "Get Block in");\r
1109 \r
1110     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);\r
1111     //logger->log("Player", Log::DEBUG, "Get Block out");\r
1112 \r
1113     feedPosition += thisRead;\r
1114 \r
1115     if (!vdr->isConnected())\r
1116     {\r
1117       doConnectionLost();\r
1118       return;\r
1119     }\r
1120 \r
1121     if (!threadBuffer) break;\r
1122 \r
1123     if (startup)\r
1124     {\r
1125       int a_stream = demuxer->scan(threadBuffer, thisRead);\r
1126       demuxer->setAudioStream(a_stream);\r
1127       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);\r
1128       startup = false;\r
1129     }\r
1130 \r
1131     threadCheckExit();\r
1132 \r
1133     while(writeLength < thisRead)\r
1134     {\r
1135       //logger->log("Player", Log::DEBUG, "Put in");\r
1136       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);\r
1137       //logger->log("Player", Log::DEBUG, "Put out");\r
1138       writeLength += thisWrite;\r
1139 \r
1140       if (!thisWrite)\r
1141       {\r
1142         // demuxer is full and can't take anymore\r
1143         threadLock();\r
1144         threadWaitForSignal();\r
1145         threadUnlock();\r
1146       }\r
1147 \r
1148       threadCheckExit();\r
1149     }\r
1150 \r
1151     free(threadBuffer);\r
1152     threadBuffer = NULL;\r
1153 \r
1154   }\r
1155 \r
1156   // end of recording\r
1157   logger->log("Player", Log::DEBUG, "Recording playback ends");\r
1158 \r
1159   if (videoStartup) // oh woe. there never was a stream, I was conned!\r
1160   {\r
1161     videoStartup = false;\r
1162     unLock();\r
1163     MILLISLEEP(500); // I think this will solve a race\r
1164   }\r
1165 \r
1166   threadCheckExit();\r
1167 \r
1168 \r
1169   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex\r
1170   m->to = messageReceiver;\r
1171   m->from = this;\r
1172   m->message = Message::PLAYER_EVENT;\r
1173   m->parameter = Player::STOP_PLAYBACK;\r
1174   logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);\r
1175   messageQueue->postMessage(m);\r
1176 }\r
1177 \r
1178 \r
1179 void Player::threadPTSFeedScan()\r
1180 {\r
1181   // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?\r
1182 \r
1183   ULONG direction = 0;\r
1184   int dir_fac=-1;\r
1185   ULONG baseFrameNumber = 0;\r
1186   ULONG iframeNumber = 0;\r
1187   ULONG iframeLength = 0;\r
1188   ULONG currentfeedFrameNumber=currentFrameNumber;\r
1189   ULONG firstFrameNumber=currentFrameNumber;\r
1190   ULLONG filePos;\r
1191   UINT amountReceived;\r
1192   UINT videoLength;\r
1193 \r
1194   UINT playtime=0;\r
1195 \r
1196 #ifndef WIN32\r
1197   struct timeval clock0 = {0,0};  // Time stamp after fetching I-frame info\r
1198   struct timeval clock1 = {0,0};  // Time stamp after fetching I-frame data\r
1199   struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame\r
1200 #else\r
1201   DWORD clock0 = 0, clock1 = 0, clock2 = 0;\r
1202 #endif\r
1203 \r
1204   int frameTimeOffset = 0; // Time in msec between frames\r
1205   int disp_msec = 0;  // Time taken to display data\r
1206   int total_msec = 0; // Time taken to fetch data and display it\r
1207   int sleepTime = 0;\r
1208 \r
1209   if (state == S_FFWD) {\r
1210           direction = 1; // and 0 for backward\r
1211           dir_fac=1;\r
1212   }\r
1213   video->EnterIframePlayback();\r
1214 \r
1215   while(1)\r
1216   {\r
1217     // Fetch I-frames until we get one that can be displayed in good time\r
1218     // Repeat while clock0 + total_msec > clock2 + frameTimeOffset\r
1219 \r
1220     baseFrameNumber = currentfeedFrameNumber;\r
1221 \r
1222     threadCheckExit();\r
1223     if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))\r
1224        return;\r
1225 \r
1226     if (iframeNumber >= lengthFrames) return;\r
1227         // scan has got to the end of what we knew to be there before we started scanning\r
1228 \r
1229     baseFrameNumber = iframeNumber;\r
1230 \r
1231     frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));\r
1232 \r
1233     logger->log("Player", Log::DEBUG, "XXX Got frame");\r
1234 \r
1235     threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);\r
1236 \r
1237     if (!vdr->isConnected())\r
1238     {\r
1239       if (threadBuffer) free(threadBuffer);\r
1240       doConnectionLost();\r
1241       break;\r
1242     }\r
1243 \r
1244 \r
1245     threadCheckExit();\r
1246 \r
1247 \r
1248     videoLength = demuxer->stripAudio(threadBuffer, amountReceived);\r
1249     demuxer->changeTimes(threadBuffer,videoLength,playtime);\r
1250     int count=0;\r
1251     while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block\r
1252     {\r
1253         MILLISLEEP(20);\r
1254         threadCheckExit();\r
1255         count++;\r
1256         if (count%300==0) {\r
1257                 ULLONG cur_time=video->getCurrentTimestamp();\r
1258         //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",\r
1259                 //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);\r
1260                 if (cur_time!=0) {\r
1261                     currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));\r
1262                 }\r
1263         }\r
1264 \r
1265     }\r
1266     playtime +=frameTimeOffset;\r
1267     currentfeedFrameNumber = iframeNumber;\r
1268     {\r
1269                 ULLONG cur_time=video->getCurrentTimestamp();\r
1270         //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",\r
1271                 //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);\r
1272                 if (cur_time!=0) {\r
1273                     currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));\r
1274                 }\r
1275         }\r
1276 \r
1277     free(threadBuffer);\r
1278     threadBuffer = NULL;\r
1279   }\r
1280 }\r
1281 \r
1282 \r
1283 \r
1284 void Player::threadFeedScan()\r
1285 {\r
1286   // This method is actually really simple - get frame from vdr,\r
1287   // spit it at the video chip, wait for a time. Most of the code here\r
1288   // is to get the wait right so that the scan occurs at the correct rate.\r
1289 \r
1290   ULONG direction = 0;\r
1291   ULONG baseFrameNumber = 0;\r
1292   ULONG iframeNumber = 0;\r
1293   ULONG iframeLength = 0;\r
1294   ULLONG filePos;\r
1295   UINT amountReceived;\r
1296   UINT videoLength;\r
1297 \r
1298 #ifndef WIN32\r
1299   struct timeval clock0 = {0,0};  // Time stamp after fetching I-frame info\r
1300   struct timeval clock1 = {0,0};  // Time stamp after fetching I-frame data\r
1301   struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame\r
1302 #else\r
1303   DWORD clock0 = 0, clock1 = 0, clock2 = 0;\r
1304 #endif\r
1305 \r
1306   int frameTimeOffset = 0; // Time in msec between frames\r
1307   int disp_msec = 0;  // Time taken to display data\r
1308   int total_msec = 0; // Time taken to fetch data and display it\r
1309   int sleepTime = 0;\r
1310 \r
1311   if (state == S_FFWD) direction = 1; // and 0 for backward\r
1312 \r
1313   while(1)\r
1314   {\r
1315     // Fetch I-frames until we get one that can be displayed in good time\r
1316     // Repeat while clock0 + total_msec > clock2 + frameTimeOffset\r
1317 \r
1318     baseFrameNumber = currentFrameNumber;\r
1319     do\r
1320     {\r
1321       threadCheckExit();\r
1322       if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))\r
1323         return;\r
1324 \r
1325       if (iframeNumber >= lengthFrames) return;\r
1326         // scan has got to the end of what we knew to be there before we started scanning\r
1327 \r
1328       baseFrameNumber = iframeNumber;\r
1329       frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));\r
1330 #ifndef WIN32\r
1331       gettimeofday(&clock0, NULL);\r
1332 #else\r
1333       clock0 = timeGetTime();\r
1334 #endif\r
1335     }\r
1336 #ifndef WIN32\r
1337     while (clock2.tv_sec != 0 &&\r
1338           (clock0.tv_sec - clock2.tv_sec) * 1000 +\r
1339           (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);\r
1340 #else\r
1341     while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);\r
1342 #endif\r
1343     logger->log("Player", Log::DEBUG, "XXX Got frame");\r
1344 \r
1345     threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);\r
1346     \r
1347     if (!vdr->isConnected())\r
1348     {\r
1349       if (threadBuffer) free(threadBuffer);\r
1350       doConnectionLost();\r
1351       break;\r
1352     }\r
1353     \r
1354 #ifndef WIN32\r
1355     gettimeofday(&clock1, NULL);\r
1356     if (clock2.tv_sec != 0)\r
1357       sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000\r
1358                 + (clock2.tv_usec - clock1.tv_usec) / 1000\r
1359                 + frameTimeOffset - disp_msec;\r
1360 #else\r
1361     clock1 = timeGetTime();\r
1362     if (clock2 != 0)\r
1363       sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;\r
1364 #endif\r
1365     if (sleepTime < 0) sleepTime = 0;\r
1366     threadCheckExit();\r
1367     MILLISLEEP(sleepTime);\r
1368    logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);\r
1369 \r
1370     videoLength = demuxer->stripAudio(threadBuffer, amountReceived);\r
1371     video->displayIFrame(threadBuffer, videoLength);\r
1372     currentFrameNumber = iframeNumber;\r
1373     free(threadBuffer);\r
1374     threadBuffer = NULL;\r
1375 \r
1376 #ifndef WIN32\r
1377     gettimeofday(&clock2, NULL);\r
1378     total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000\r
1379                + (clock2.tv_usec - clock0.tv_usec) / 1000\r
1380                - sleepTime;\r
1381     disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000\r
1382               + (clock2.tv_usec - clock1.tv_usec) / 1000\r
1383               - sleepTime;\r
1384 #else\r
1385     clock2 = timeGetTime();\r
1386     total_msec = clock2 - clock0 - sleepTime;\r
1387     disp_msec = clock2 - clock1 - sleepTime;\r
1388 #endif\r
1389     logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);\r
1390   }\r
1391 }\r
1392 \r
1393 void Player::threadPostStopCleanup()\r
1394 {\r
1395   if (threadBuffer)\r
1396   {\r
1397     free(threadBuffer);\r
1398     threadBuffer = NULL;\r
1399   }\r
1400 }\r
1401 \r
1402 // ----------------------------------- Dev\r
1403 \r
1404 #ifdef DEV\r
1405 void Player::test1()\r
1406 {\r
1407   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");\r
1408 }\r
1409 \r
1410 void Player::test2()\r
1411 {\r
1412   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");\r
1413 }\r
1414 #endif\r