]> git.vomp.tv Git - vompclient.git/blob - player.cc
Major switch to frame number navigation
[vompclient.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   initted = false;
33   paused = false;
34   playing = false;
35   ffwd = false;
36   fbwd = false;
37   lengthBytes = 0;
38   lengthFrames = 0;
39   feedPosition = 0;
40   feedMode = MODE_NORMAL;
41   lastRescan = 0;
42 //  startTS = 0;
43 //  endTS = 0;
44
45   videoStartup = false;
46   preBuffering = false;
47   isRecording = tIsRecording;
48   isRadio = tIsRadio;
49
50   threadBuffer = NULL;
51
52   if (isRadio)
53   {
54     blockSize = 20000;
55     startupBlockSize = 60000;
56     video->turnVideoOff();
57   }
58   else
59   {
60     blockSize = 100000;
61     startupBlockSize = 250000;
62     video->turnVideoOn();
63   }
64 }
65
66 Player::~Player()
67 {
68   if (initted) shutdown();
69 }
70
71 int Player::init()
72 {
73   if (initted) return 0;
74 #ifndef WIN32
75   pthread_mutex_init(&mutex, NULL);
76 #else
77   mutex=CreateMutex(NULL,FALSE,NULL);
78 #endif
79
80   demuxer = new DemuxerVDR();
81   if (!demuxer) return 0;
82
83   if (!demuxer->init(this, audio, video))
84   {
85     logger->log("Player", Log::ERR, "Demuxer failed to init");
86     shutdown();
87     return 0;
88   }
89
90   vfeed.init();
91   afeed.init();
92
93   video->stop();
94   video->blank();
95   audio->stop();
96
97   initted = true;
98   return 1;
99 }
100
101 int Player::shutdown()
102 {
103   if (!initted) return 0;
104   initted = false;
105
106  // copy of stop
107   if (playing)
108   {
109     playing = false;
110     threadStop();
111     video->stop();
112     video->blank();
113     audio->stop();
114     vfeed.stop();
115
116     afeed.stop();
117     video->reset();
118     demuxer->reset();
119     feedPosition = 0;
120   }
121
122   delete demuxer;
123   demuxer = NULL;
124 #ifdef WIN32
125   CloseHandle(mutex);
126 #endif
127
128   return 1;
129 }
130
131 void Player::setPosition(ULLONG position)
132 {
133   feedPosition = position;
134 }
135
136 void Player::setLengthBytes(ULLONG length)
137 {
138   lastRescan = time(NULL);
139   lengthBytes = length;
140   logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
141 }
142
143 void Player::setLengthFrames(ULONG length)
144 {
145   lengthFrames = length;
146   logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
147 }
148
149 /*
150 ULLONG Player::getEndTS() // FIXME delme - used only by bar clocks until current frame code is done
151 {
152   long long rendTS = endTS - startTS;
153   if (rendTS < 0) rendTS += 8589934592ULL;
154   return (ULLONG)rendTS;
155 }
156 */
157
158 /*
159 hmsf Player::getEndHMSF()
160 {
161   return video->framesToHMSF(lengthFrames);
162 }
163 */
164
165 ULONG Player::getLengthFrames()
166 {
167   return lengthFrames;
168 }
169
170 ULONG Player::getCurrentFrameNum()
171 {
172   if (startup) return 0;
173   return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
174 }
175
176 // ----------------------------------- Externally called events
177
178 int Player::play()
179 {
180   lock();
181   bool doUnlock = false;
182   int result = playInt(&doUnlock);
183   if (doUnlock) unLock();
184   return result;
185 }
186
187 void Player::stop()
188 {
189   lock();
190   stopInt();
191   unLock();
192 }
193
194 void Player::togglePause()
195 {
196   lock();
197   togglePauseInt();
198   unLock();
199 }
200
201 void Player::toggleFastForward()
202 {
203   lock();
204   toggleFastForwardInt();
205   unLock();
206 }
207
208 void Player::toggleFastBackward()
209 {
210   lock();
211   toggleFastBackwardInt();
212   unLock();
213 }
214
215 void Player::jumpToPercent(int percent)
216 {
217   lock();
218   jumpToPercentInt(percent);
219 //  unLock(); - let thread unlock this
220 }
221
222 void Player::skipForward(int seconds)
223 {
224   lock();
225   skipForwardInt(seconds);
226 //  unLock(); - let thread unlock this
227 }
228
229 void Player::skipBackward(int seconds)
230 {
231   lock();
232   skipBackwardInt(seconds);
233 //  unLock(); - let thread unlock this
234 }
235
236 // ----------------------------------- Implementations called events
237
238
239 int Player::playInt(bool* doUnlock)
240 {
241   if (!initted) return 0;
242
243   // If we are just paused, unpause!
244   if (paused)
245   {
246     togglePauseInt();
247     *doUnlock = true;
248     return 1;
249   }
250
251   // If we are fast forwarding, set to normal
252   if (ffwd)
253   {
254     toggleFastForwardInt();
255     return 1;
256   }
257
258   // If we are fast backwarding, set to normal
259   if (fbwd)
260   {
261     toggleFastBackwardInt();
262     return 1;
263   }
264
265   // If we are already playing, ignore. no resyncing to do now!
266   if (playing)
267   {
268     *doUnlock = true;
269     return 1;
270   }
271
272   // Standard play start
273   logger->log("Player", Log::DEBUG, "Standard play start");
274
275   startup = true;
276
277   audio->reset();
278   video->reset();
279   demuxer->reset();
280   demuxer->setFrameNum(0);
281   if (!isRadio) demuxer->seek();
282
283   videoStartup = true;
284   threadStart();
285
286   if (isRecording)
287   {
288     logger->log("Player", Log::DEBUG, "Immediate play");
289     afeed.start();
290     vfeed.start();
291     video->sync();
292     audio->sync();
293     audio->play();
294     video->pause();
295   }
296   else // do prebuffering
297   {
298     logger->log("Player", Log::DEBUG, "Prebuffering...");
299     preBuffering = true;
300   }
301
302   playing = true;
303   return 1;
304 }
305
306 void Player::stopInt()
307 {
308   if (!initted) return;
309   if (!playing) return;
310
311   if (ffwd || fbwd)
312   {
313     ffwd = false;
314     fbwd = false;
315     afeed.enable();
316     video->unFastForward();
317     audio->systemMuteOff();
318     feedMode = MODE_NORMAL;
319   }
320
321   playing = false;
322   paused = false;
323
324   vfeed.stop();
325   afeed.stop();
326   threadStop();
327   video->stop();
328   video->blank();
329   audio->stop();
330   audio->unPause();
331   video->reset();
332   demuxer->reset();
333
334   feedPosition = 0;
335 }
336
337 void Player::togglePauseInt()
338 {
339   if (!initted) return;
340   if (!playing) return;
341
342   if (ffwd) toggleFastForwardInt();
343   if (fbwd) toggleFastBackwardInt();
344
345   if (paused)
346   {
347     video->unPause();
348     audio->unPause();
349     paused = false;
350   }
351   else
352   {
353     video->pause();
354     audio->pause();
355     paused = true;
356   }
357 }
358
359 void Player::toggleFastForwardInt()
360 {
361   if (!initted) return;
362   if (!playing) return;
363
364   if (paused) togglePauseInt();
365   if (fbwd) toggleFastBackwardInt();
366
367   if (ffwd)
368   {
369     ffwd = false;
370     threadStop();
371     vfeed.stop();
372     afeed.stop();
373     video->stop();
374     audio->stop();
375     video->reset();
376     audio->reset();
377     demuxer->flush();
378     if (!isRadio) demuxer->seek();
379
380     videoStartup = true;
381     afeed.enable();
382     afeed.start();
383     vfeed.start();
384     threadStart();
385     audio->play();
386     video->sync();
387     audio->sync();
388     audio->systemMuteOff();
389     audio->doMuting();
390     fbwd = false;
391   }
392   else
393   {
394     ffwd = true;
395 #ifndef WIN32
396     afeed.disable();
397 #endif
398     audio->systemMuteOn();
399     video->fastForward();
400   }
401 }
402
403 void Player::toggleFastBackwardInt()
404 {
405   if (!initted) return;
406   if (!playing) return;
407
408   if (paused) togglePauseInt();
409   if (ffwd) toggleFastForwardInt();
410
411   if (fbwd)
412   {
413     fbwd = false;
414     afeed.enable();
415     audio->systemMuteOff();
416
417 //    threadStop();
418     feedMode = MODE_NORMAL;
419 //    threadStart();
420   }
421   else
422   {
423     fbwd = false;
424 #ifndef WIN32
425     afeed.disable();
426 #endif
427     audio->systemMuteOn();
428
429     threadStop();
430     feedMode = MODE_BACKWARDS;
431     video->reset();
432     video->play();
433     demuxer->flush();
434     if (!isRadio) demuxer->seek();
435
436     threadStart();
437   }
438 }
439
440 void Player::skipForwardInt(int seconds)
441 {
442   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
443   restartAtFrame(getCurrentFrameNum() + (seconds * video->getFPS()));
444 }
445
446 void Player::skipBackwardInt(int seconds)
447 {
448   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
449   long newFrameNum = getCurrentFrameNum() + (seconds * video->getFPS());
450   if (newFrameNum < 0) newFrameNum = 0;
451   restartAt(newFrameNum);
452 }
453
454 void Player::jumpToPercentInt(int percent)
455 {
456   logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
457   ULONG newFrame = percent * lengthFrames / 100;
458   restartAtFrame(newFrame);
459 }
460
461
462 // ----------------------------------- Internal functions
463
464 void Player::lock()
465 {
466 #ifndef WIN32
467   pthread_mutex_lock(&mutex);
468   logger->log("Player", Log::DEBUG, "LOCKED");
469
470 #else
471    WaitForSingleObject(mutex, INFINITE );
472 #endif
473 }
474
475 void Player::unLock()
476 {
477 #ifndef WIN32
478   logger->log("Player", Log::DEBUG, "UNLOCKING");
479   pthread_mutex_unlock(&mutex);
480 #else
481    ReleaseMutex(mutex);
482 #endif
483 }
484
485 void Player::restartAt(ULLONG timecode)
486 {
487   if (paused) togglePauseInt();
488   if (ffwd) toggleFastForwardInt();
489
490   ULONG wantedFrameNumber = video->timecodeToFrameNumber(timecode);
491   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
492   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
493   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
494
495   vfeed.stop();
496   afeed.stop();
497   threadStop();
498   video->stop();
499   video->reset();
500   audio->reset();
501   demuxer->flush();
502   if (!isRadio) demuxer->seek();
503   feedPosition = newPosition;
504   demuxer->setFrameNum(wantedFrameNumber);
505   videoStartup = true;
506   afeed.start();
507   vfeed.start();
508   threadStart();
509   audio->play();
510   video->sync();
511   audio->sync();
512   audio->systemMuteOff();
513   audio->doMuting();
514   fbwd = false;
515 }
516
517 void Player::restartAtFrame(ULONG newFrame)
518 {
519   if (paused) togglePauseInt();
520   if (ffwd) toggleFastForwardInt();
521
522   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame);
523   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
524   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition);
525
526   vfeed.stop();
527   afeed.stop();
528   threadStop();
529   video->stop();
530   video->reset();
531   audio->reset();
532   demuxer->flush();
533   if (!isRadio) demuxer->seek();
534   feedPosition = newPosition;
535   demuxer->setFrameNum(newFrame);
536   videoStartup = true;
537   afeed.start();
538   vfeed.start();
539   threadStart();
540   audio->play();
541   video->sync();
542   audio->sync();
543   audio->systemMuteOff();
544   audio->doMuting();
545   fbwd = false;
546 }
547
548 /*
549 void Player::setStartTS(UINT dataInBuffer)
550 {
551 #ifndef NEW_DEMUXER
552   if (isRecording && feedPosition) // (feedPosition != 0)
553   {
554     // FIXME find out how much data need to get to find a TS
555     // Need to get the actual start of the recording
556
557     UINT thisRead;
558     UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
559     if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
560     if (!tempBuffer) return;
561     if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &startTS);
562     free(tempBuffer);
563   }
564   else
565   {
566     demuxer->findVideoPTS(threadBuffer, dataInBuffer, &startTS);
567   }
568 #else
569   startTS=0;
570 #endif
571 }
572 */
573
574 /*
575 void Player::setEndTS()
576 {
577   logger->log("Player", Log::DEBUG, "Setting end TS");
578 #ifndef NEW_DEMUXER
579   UINT thisRead;
580   UCHAR* tempBuffer = VDR::getInstance()->getBlock((lengthBytes - 100000), 100000, &thisRead);
581   if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
582   if (!tempBuffer) return;
583   if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &endTS);
584   free(tempBuffer);
585  #else //The replacement code relias completely on VDRs timecode and not the pts
586    endTS=video->frameNumberToTimecode(
587     VDR::getInstance()->frameNumberFromPosition((lengthBytes - 100000)));
588  #endif
589   logger->log("Player", Log::DEBUG, "Set end TS");
590 }
591 */
592
593 void Player::doConnectionLost()
594 {
595   Message* m = new Message();
596   m->message = Message::CONNECTION_LOST;
597   m->to = this;
598   commandMessageQueue->postMessage(m);
599 }
600
601 // ----------------------------------- Callback
602
603 void Player::call(void* caller)
604 {
605   if (caller == demuxer)
606   {
607     logger->log("Player", Log::DEBUG, "Callback from demuxer");
608
609     if (video->getTVsize() == Video::ASPECT4X3)
610     {
611       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
612       return;
613     }
614
615     int dxCurrentAspect = demuxer->getAspectRatio();
616     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
617     {
618       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
619       video->setAspectRatio(Video::ASPECT4X3);
620     }
621     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
622     {
623       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
624       video->setAspectRatio(Video::ASPECT16X9);
625     }
626     else
627     {
628       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
629     }
630
631   }
632   else
633   {
634     if (videoStartup)
635     {
636       videoStartup = false;
637       video->reset();
638       video->play();
639       video->sync();
640       vfeed.release();
641       unLock();
642     }
643
644     threadSignalNoLock();
645   }
646 }
647
648 // ----------------------------------- Feed thread
649
650 void Player::threadMethod()
651 {
652   UINT thisRead;
653   UINT writeLength;
654   UINT thisWrite;
655   UINT preBufferTotal = 0;
656 #ifdef NEW_DEMUXER
657   ULLONG currentposition;
658 #endif
659
660   VDR* vdr = VDR::getInstance();
661
662   UINT askFor;
663   while(1)
664   {
665     thisRead = 0;
666     writeLength = 0;
667     thisWrite = 0;
668
669     threadCheckExit();
670
671     // If we havn't rescanned for a while..
672     if (isRecording && ((lastRescan + 60) < time(NULL)))
673     {
674       lengthBytes = vdr->rescanRecording(&lengthFrames);
675       if (!vdr->isConnected()) { doConnectionLost(); return; }
676       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
677       lastRescan = time(NULL);
678 //      setEndTS();
679     }
680
681     if (lengthBytes) // is playing a recording
682     {
683       if (feedPosition >= lengthBytes) break;  // finished playback
684
685       if (startup)
686       {
687         if (startupBlockSize > lengthBytes)
688           askFor = lengthBytes; // is a very small recording!
689         else
690           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
691       }
692       else
693       {
694         if ((feedPosition + blockSize) > lengthBytes) // last block of recording
695           askFor = lengthBytes - feedPosition;
696         else // normal
697           askFor = blockSize;
698       }
699     }
700     else // is playing live
701     {
702       if (startup)
703         askFor = startupBlockSize; // find audio streams sized block
704       else
705         askFor = blockSize; // normal
706     }
707
708     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
709 #ifdef NEW_DEMUXER
710   currentposition=feedPosition;
711  #endif
712     if (!vdr->isConnected())
713     {
714       doConnectionLost();
715       return;
716     }
717
718     if (!threadBuffer) break;
719
720     if (startup)
721     {
722       int a_stream = demuxer->scan(threadBuffer, thisRead);
723       demuxer->setAudioStream(a_stream);
724       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
725
726 //      setStartTS(thisRead);
727
728 //      if (isRecording) setEndTS();
729
730       startup = false;
731     }
732
733     if (preBuffering)
734     {
735       preBufferTotal += thisRead;
736       if (preBufferTotal > 500000)
737       {
738         logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
739
740         preBuffering = false;
741         preBufferTotal = 0;
742
743         audio->sync();
744         audio->play();
745         video->sync();
746         video->pause();
747         afeed.start();
748         vfeed.start();
749 //        unLock(); // thread will be locked by play until here
750         // FIXME - see if this can segfault because it is starting threads out of the master mutex
751       }
752     }
753
754     if (feedMode == MODE_NORMAL)
755     {
756       feedPosition += thisRead;
757     }
758     else if (feedMode == MODE_BACKWARDS)
759     {
760       if (feedPosition >= blockSize)
761       {
762         feedPosition -= blockSize;
763         if (!isRadio) demuxer->seek();
764       }
765       else
766       {
767         // got to the start of the recording.. revert to play mode? how?
768         feedPosition += thisRead;
769       }
770     }
771
772     threadCheckExit();
773
774     while(writeLength < thisRead)
775     {
776 #ifndef NEW_DEMUXER
777       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
778 #else
779       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength,
780       currentposition+(ULLONG)writeLength);
781 #endif
782       writeLength += thisWrite;
783 //      logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
784
785       if (!thisWrite)
786       {
787 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
788         // demuxer is full and can't take anymore
789         threadLock();
790         threadWaitForSignal();
791         threadUnlock();
792 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
793       }
794
795       threadCheckExit();
796     }
797
798     free(threadBuffer);
799     threadBuffer = NULL;
800
801   }
802
803   // end of recording
804   logger->log("Player", Log::DEBUG, "Recording playback ends");
805
806   threadCheckExit();
807
808   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
809   if (lengthBytes) m->message = Message::STOP_PLAYBACK;  // recording
810   else m->message = Message::STREAM_END;                  // live
811   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
812   commandMessageQueue->postMessage(m);
813   logger->log("Player", Log::DEBUG, "Message posted...");
814 }
815
816 void Player::threadPostStopCleanup()
817 {
818   if (threadBuffer)
819   {
820     free(threadBuffer);
821     threadBuffer = NULL;
822   }
823 }
824
825 // ----------------------------------- Dev
826
827 #ifdef DEV
828 void Player::test1()
829 {
830   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
831   video->play();
832 //  video->setAspectRatio(Video::ASPECT4X3);
833 }
834
835 void Player::test2()
836 {
837   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
838   video->setAspectRatio(Video::ASPECT16X9);
839 }
840 #endif