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