]> git.vomp.tv Git - vompclient-marten.git/blob - player.cc
Jump to percent now uses frames
[vompclient-marten.git] / player.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "player.h"
22
23 // ----------------------------------- Called from outside, one offs or info funcs
24
25 Player::Player(MessageQueue* messageQueue, bool tIsRecording, bool tIsRadio)
26 : vfeed(this), afeed(this)
27 {
28   commandMessageQueue = messageQueue;
29   audio = Audio::getInstance();
30   video = Video::getInstance();
31   logger = Log::getInstance();
32   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 ULLONG Player::getEndTS() // used internally (jump to percent)
150 {
151   long long rendTS = endTS - startTS;
152   if (rendTS < 0) rendTS += 8589934592ULL;
153   return (ULLONG)rendTS;
154 }
155
156 hmsf Player::getEndHMSF()
157 {
158   return video->framesToHMSF(lengthFrames);
159 }
160
161 ULLONG Player::getPositionTS() // used internall (skip fw/bw)
162 {
163   if (startup) return 0ULL;
164   long long currentTS = video->getCurrentTimestamp() - startTS;
165   if (currentTS < 0) currentTS += 8589934592ULL;
166   return (ULLONG)currentTS;
167 }
168
169 // ----------------------------------- Externally called events
170
171 int Player::play()
172 {
173   lock();
174   bool doUnlock = false;
175   int result = playInt(&doUnlock);
176   if (doUnlock) unLock();
177   return result;
178 }
179
180 void Player::stop()
181 {
182   lock();
183   stopInt();
184   unLock();
185 }
186
187 void Player::togglePause()
188 {
189   lock();
190   togglePauseInt();
191   unLock();
192 }
193
194 void Player::toggleFastForward()
195 {
196   lock();
197   toggleFastForwardInt();
198   unLock();
199 }
200
201 void Player::toggleFastBackward()
202 {
203   lock();
204   toggleFastBackwardInt();
205   unLock();
206 }
207
208 void Player::jumpToPercent(int percent)
209 {
210   lock();
211   jumpToPercentInt(percent);
212 //  unLock(); - let thread unlock this
213 }
214
215 void Player::skipForward(int seconds)
216 {
217   lock();
218   skipForwardInt(seconds);
219 //  unLock(); - let thread unlock this
220 }
221
222 void Player::skipBackward(int seconds)
223 {
224   lock();
225   skipBackwardInt(seconds);
226 //  unLock(); - let thread unlock this
227 }
228
229 // ----------------------------------- Implementations called events
230
231
232 int Player::playInt(bool* doUnlock)
233 {
234   if (!initted) return 0;
235
236   // If we are just paused, unpause!
237   if (paused)
238   {
239     togglePauseInt();
240     *doUnlock = true;
241     return 1;
242   }
243
244   // If we are fast forwarding, set to normal
245   if (ffwd)
246   {
247     toggleFastForwardInt();
248     return 1;
249   }
250
251   // If we are fast backwarding, set to normal
252   if (fbwd)
253   {
254     toggleFastBackwardInt();
255     return 1;
256   }
257
258   // If we are already playing, ignore. no resyncing to do now!
259   if (playing)
260   {
261     *doUnlock = true;
262     return 1;
263   }
264
265   // Standard play start
266   logger->log("Player", Log::DEBUG, "Standard play start");
267
268   startup = true;
269
270   audio->reset();
271   video->reset();
272   demuxer->reset();
273   if (!isRadio) demuxer->seek();
274
275   videoStartup = true;
276   threadStart();
277
278   if (isRecording)
279   {
280     logger->log("Player", Log::DEBUG, "Immediate play");
281     afeed.start();
282     vfeed.start();
283     video->sync();
284     audio->sync();
285     audio->play();
286     video->pause();
287   }
288   else // do prebuffering
289   {
290     logger->log("Player", Log::DEBUG, "Prebuffering...");
291     preBuffering = true;
292   }
293
294   playing = true;
295   return 1;
296 }
297
298 void Player::stopInt()
299 {
300   if (!initted) return;
301   if (!playing) return;
302
303   if (ffwd || fbwd)
304   {
305     ffwd = false;
306     fbwd = false;
307     afeed.enable();
308     video->unFastForward();
309     audio->systemMuteOff();
310     feedMode = MODE_NORMAL;
311   }
312
313   playing = false;
314   paused = false;
315
316   vfeed.stop();
317   afeed.stop();
318   threadStop();
319   video->stop();
320   video->blank();
321   audio->stop();
322   audio->unPause();
323   video->reset();
324   demuxer->reset();
325
326   feedPosition = 0;
327 }
328
329 void Player::togglePauseInt()
330 {
331   if (!initted) return;
332   if (!playing) return;
333
334   if (ffwd) toggleFastForwardInt();
335   if (fbwd) toggleFastBackwardInt();
336
337   if (paused)
338   {
339     video->unPause();
340     audio->unPause();
341     paused = false;
342   }
343   else
344   {
345     video->pause();
346     audio->pause();
347     paused = true;
348   }
349 }
350
351 void Player::toggleFastForwardInt()
352 {
353   if (!initted) return;
354   if (!playing) return;
355
356   if (paused) togglePauseInt();
357   if (fbwd) toggleFastBackwardInt();
358
359   if (ffwd)
360   {
361     ffwd = false;
362     threadStop();
363     vfeed.stop();
364     afeed.stop();
365     video->stop();
366     audio->stop();
367     video->reset();
368     audio->reset();
369     demuxer->flush();
370     if (!isRadio) demuxer->seek();
371
372     videoStartup = true;
373     afeed.enable();
374     afeed.start();
375     vfeed.start();
376     threadStart();
377     audio->play();
378     video->sync();
379     audio->sync();
380     audio->systemMuteOff();
381     audio->doMuting();
382     fbwd = false;
383   }
384   else
385   {
386     ffwd = true;
387 #ifndef WIN32
388     afeed.disable();
389 #endif
390     audio->systemMuteOn();
391     video->fastForward();
392   }
393 }
394
395 void Player::toggleFastBackwardInt()
396 {
397   if (!initted) return;
398   if (!playing) return;
399
400   if (paused) togglePauseInt();
401   if (ffwd) toggleFastForwardInt();
402
403   if (fbwd)
404   {
405     fbwd = false;
406     afeed.enable();
407     audio->systemMuteOff();
408
409 //    threadStop();
410     feedMode = MODE_NORMAL;
411 //    threadStart();
412   }
413   else
414   {
415     fbwd = false;
416 #ifndef WIN32
417     afeed.disable();
418 #endif
419     audio->systemMuteOn();
420
421     threadStop();
422     feedMode = MODE_BACKWARDS;
423     video->reset();
424     video->play();
425     demuxer->flush();
426     if (!isRadio) demuxer->seek();
427
428     threadStart();
429   }
430 }
431
432 void Player::skipForwardInt(int seconds)
433 {
434   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
435   restartAt(getPositionTS() + (seconds * 90000));
436 }
437
438 void Player::skipBackwardInt(int seconds)
439 {
440   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
441   long long newTimeCode = getPositionTS() - (seconds * 90000);
442   if (newTimeCode < 0) newTimeCode = 0;
443   restartAt(newTimeCode);
444 }
445
446 void Player::jumpToPercentInt(int percent)
447 {
448   logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
449   ULONG newFrame = (ULONG)(((float)percent / 100) * lengthFrames);
450   restartAtFrame(newFrame);
451 }
452
453
454 // ----------------------------------- Internal functions
455
456 void Player::lock()
457 {
458 #ifndef WIN32
459   pthread_mutex_lock(&mutex);
460   logger->log("Player", Log::DEBUG, "LOCKED");
461
462 #else
463    WaitForSingleObject(mutex, INFINITE );
464 #endif
465 }
466
467 void Player::unLock()
468 {
469 #ifndef WIN32
470   logger->log("Player", Log::DEBUG, "UNLOCKING");
471   pthread_mutex_unlock(&mutex);
472 #else
473    ReleaseMutex(mutex);
474 #endif
475 }
476
477 void Player::restartAt(ULLONG timecode)
478 {
479   if (paused) togglePauseInt();
480   if (ffwd) toggleFastForwardInt();
481
482   ULONG wantedFrameNumber = video->timecodeToFrameNumber(timecode);
483   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
484   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
485   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
486
487   vfeed.stop();
488   afeed.stop();
489   threadStop();
490   video->stop();
491   video->reset();
492   audio->reset();
493   demuxer->flush();
494   if (!isRadio) demuxer->seek();
495   feedPosition = newPosition;
496   videoStartup = true;
497   afeed.start();
498   vfeed.start();
499   threadStart();
500   audio->play();
501   video->sync();
502   audio->sync();
503   audio->systemMuteOff();
504   audio->doMuting();
505   fbwd = false;
506 }
507
508 void Player::restartAtFrame(ULONG newFrame)
509 {
510   if (paused) togglePauseInt();
511   if (ffwd) toggleFastForwardInt();
512
513   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame);
514   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
515   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition);
516
517   vfeed.stop();
518   afeed.stop();
519   threadStop();
520   video->stop();
521   video->reset();
522   audio->reset();
523   demuxer->flush();
524   if (!isRadio) demuxer->seek();
525   feedPosition = newPosition;
526   videoStartup = true;
527   afeed.start();
528   vfeed.start();
529   threadStart();
530   audio->play();
531   video->sync();
532   audio->sync();
533   audio->systemMuteOff();
534   audio->doMuting();
535   fbwd = false;
536 }
537
538 void Player::setStartTS(UINT dataInBuffer)
539 {
540 #ifndef NEW_DEMUXER
541   if (isRecording && feedPosition) // (feedPosition != 0)
542   {
543     // FIXME find out how much data need to get to find a TS
544     // Need to get the actual start of the recording
545
546     UINT thisRead;
547     UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
548     if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
549     if (!tempBuffer) return;
550     if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &startTS);
551     free(tempBuffer);
552   }
553   else
554   {
555     demuxer->findVideoPTS(threadBuffer, dataInBuffer, &startTS);
556   }
557 #else
558   startTS=0;
559 #endif
560 }
561
562 void Player::setEndTS()
563 {
564   logger->log("Player", Log::DEBUG, "Setting end TS");
565 #ifndef NEW_DEMUXER
566   UINT thisRead;
567   UCHAR* tempBuffer = VDR::getInstance()->getBlock((lengthBytes - 100000), 100000, &thisRead);
568   if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
569   if (!tempBuffer) return;
570   if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &endTS);
571   free(tempBuffer);
572  #else //The replacement code relias completely on VDRs timecode and not the pts
573    endTS=video->frameNumberToTimecode(
574     VDR::getInstance()->frameNumberFromPosition((lengthBytes - 100000)));
575  #endif
576   logger->log("Player", Log::DEBUG, "Set end TS");
577 }
578
579 void Player::doConnectionLost()
580 {
581   Message* m = new Message();
582   m->message = Message::CONNECTION_LOST;
583   m->to = this;
584   commandMessageQueue->postMessage(m);
585 }
586
587 // ----------------------------------- Callback
588
589 void Player::call(void* caller)
590 {
591   if (caller == demuxer)
592   {
593     logger->log("Player", Log::DEBUG, "Callback from demuxer");
594
595     if (video->getTVsize() == Video::ASPECT4X3)
596     {
597       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
598       return;
599     }
600
601     int dxCurrentAspect = demuxer->getAspectRatio();
602     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
603     {
604       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
605       video->setAspectRatio(Video::ASPECT4X3);
606     }
607     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
608     {
609       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
610       video->setAspectRatio(Video::ASPECT16X9);
611     }
612     else
613     {
614       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
615     }
616
617   }
618   else
619   {
620     if (videoStartup)
621     {
622       videoStartup = false;
623       video->reset();
624       video->play();
625       video->sync();
626       vfeed.release();
627       unLock();
628     }
629
630     threadSignalNoLock();
631   }
632 }
633
634 // ----------------------------------- Feed thread
635
636 void Player::threadMethod()
637 {
638   UINT thisRead;
639   UINT writeLength;
640   UINT thisWrite;
641   UINT preBufferTotal = 0;
642 #ifdef NEW_DEMUXER
643   ULLONG currentposition;
644 #endif
645
646   VDR* vdr = VDR::getInstance();
647
648   UINT askFor;
649   while(1)
650   {
651     thisRead = 0;
652     writeLength = 0;
653     thisWrite = 0;
654
655     threadCheckExit();
656
657     // If we havn't rescanned for a while..
658     if (isRecording && ((lastRescan + 60) < time(NULL)))
659     {
660       lengthBytes = vdr->rescanRecording(&lengthFrames);
661       if (!vdr->isConnected()) { doConnectionLost(); return; }
662       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
663       lastRescan = time(NULL);
664       setEndTS();
665     }
666
667     if (lengthBytes) // is playing a recording
668     {
669       if (feedPosition >= lengthBytes) break;  // finished playback
670
671       if (startup)
672       {
673         if (startupBlockSize > lengthBytes)
674           askFor = lengthBytes; // is a very small recording!
675         else
676           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
677       }
678       else
679       {
680         if ((feedPosition + blockSize) > lengthBytes) // last block of recording
681           askFor = lengthBytes - feedPosition;
682         else // normal
683           askFor = blockSize;
684       }
685     }
686     else // is playing live
687     {
688       if (startup)
689         askFor = startupBlockSize; // find audio streams sized block
690       else
691         askFor = blockSize; // normal
692     }
693
694     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
695 #ifdef NEW_DEMUXER
696   currentposition=feedPosition;
697  #endif
698     if (!vdr->isConnected())
699     {
700       doConnectionLost();
701       return;
702     }
703
704     if (!threadBuffer) break;
705
706     if (startup)
707     {
708       int a_stream = demuxer->scan(threadBuffer, thisRead);
709       demuxer->setAudioStream(a_stream);
710       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
711
712       setStartTS(thisRead);
713
714       if (isRecording) setEndTS();
715
716       startup = false;
717     }
718
719     if (preBuffering)
720     {
721       preBufferTotal += thisRead;
722       if (preBufferTotal > 500000)
723       {
724         logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
725
726         preBuffering = false;
727         preBufferTotal = 0;
728
729         audio->sync();
730         audio->play();
731         video->sync();
732         video->pause();
733         afeed.start();
734         vfeed.start();
735 //        unLock(); // thread will be locked by play until here
736         // FIXME - see if this can segfault because it is starting threads out of the master mutex
737       }
738     }
739
740     if (feedMode == MODE_NORMAL)
741     {
742       feedPosition += thisRead;
743     }
744     else if (feedMode == MODE_BACKWARDS)
745     {
746       if (feedPosition >= blockSize)
747       {
748         feedPosition -= blockSize;
749         if (!isRadio) demuxer->seek();
750       }
751       else
752       {
753         // got to the start of the recording.. revert to play mode? how?
754         feedPosition += thisRead;
755       }
756     }
757
758     threadCheckExit();
759
760     while(writeLength < thisRead)
761     {
762 #ifndef NEW_DEMUXER
763       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
764 #else
765       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength,
766       currentposition+(ULLONG)writeLength);
767 #endif
768       writeLength += thisWrite;
769 //      logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
770
771       if (!thisWrite)
772       {
773 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
774         // demuxer is full and can't take anymore
775         threadLock();
776         threadWaitForSignal();
777         threadUnlock();
778 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
779       }
780
781       threadCheckExit();
782     }
783
784     free(threadBuffer);
785     threadBuffer = NULL;
786
787   }
788
789   // end of recording
790   logger->log("Player", Log::DEBUG, "Recording playback ends");
791
792   threadCheckExit();
793
794   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
795   if (lengthBytes) m->message = Message::STOP_PLAYBACK;  // recording
796   else m->message = Message::STREAM_END;                  // live
797   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
798   commandMessageQueue->postMessage(m);
799   logger->log("Player", Log::DEBUG, "Message posted...");
800 }
801
802 void Player::threadPostStopCleanup()
803 {
804   if (threadBuffer)
805   {
806     free(threadBuffer);
807     threadBuffer = NULL;
808   }
809 }
810
811 // ----------------------------------- Dev
812
813 #ifdef DEV
814 void Player::test1()
815 {
816   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
817   video->play();
818 //  video->setAspectRatio(Video::ASPECT4X3);
819 }
820
821 void Player::test2()
822 {
823   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
824   video->setAspectRatio(Video::ASPECT16X9);
825 }
826 #endif