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