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