]> git.vomp.tv Git - vompclient.git/blob - player.cc
Windows port
[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 #ifndef WIN32
376     afeed.disable();
377 #endif
378     audio->systemMuteOn();
379     video->fastForward();
380   }
381 }
382
383 void Player::toggleFastBackwardInt()
384 {
385   if (!initted) return;
386   if (!playing) return;
387
388   if (paused) togglePauseInt();
389   if (ffwd) toggleFastForwardInt();
390
391   if (fbwd)
392   {
393     fbwd = false;
394     afeed.enable();
395     audio->systemMuteOff();
396
397 //    threadStop();
398     feedMode = MODE_NORMAL;
399 //    threadStart();
400   }
401   else
402   {
403     fbwd = false;
404 #ifndef WIN32
405     afeed.disable();
406 #endif
407     audio->systemMuteOn();
408
409     threadStop();
410     feedMode = MODE_BACKWARDS;
411     video->reset();
412     video->play();
413     demuxer->flush();
414     if (!isRadio) demuxer->seek();
415
416     threadStart();
417   }
418 }
419
420 void Player::skipForwardInt(int seconds)
421 {
422   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
423   restartAt(getPositionTS() + (seconds * 90000));
424 }
425
426 void Player::skipBackwardInt(int seconds)
427 {
428   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
429   long long newTimeCode = getPositionTS() - (seconds * 90000);
430   if (newTimeCode < 0) newTimeCode = 0;
431   restartAt(newTimeCode);
432 }
433
434 void Player::jumpToPercentInt(int percent)
435 {
436   logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
437   ULLONG newTimeCode = (ULLONG)(getEndTS() * ((float)percent / 100));
438   restartAt(newTimeCode);
439 }
440
441
442 // ----------------------------------- Internal functions
443
444 void Player::lock()
445 {
446 #ifndef WIN32
447   pthread_mutex_lock(&mutex);
448   logger->log("Player", Log::DEBUG, "LOCKED");
449
450 #else
451    WaitForSingleObject(mutex, INFINITE );
452 #endif
453 }
454
455 void Player::unLock()
456 {
457 #ifndef WIN32
458   logger->log("Player", Log::DEBUG, "UNLOCKING");
459   pthread_mutex_unlock(&mutex);
460 #else
461    ReleaseMutex(mutex);
462 #endif
463 }
464
465 void Player::restartAt(ULLONG timecode)
466 {
467   if (paused) togglePauseInt();
468   if (ffwd) toggleFastForwardInt();
469
470   ULONG wantedFrameNumber = video->timecodeToFrameNumber(timecode);
471   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
472   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
473   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
474
475   vfeed.stop();
476   afeed.stop();
477   threadStop();
478   video->stop();
479   video->reset();
480   audio->reset();
481   demuxer->flush();
482   if (!isRadio) demuxer->seek();
483   feedPosition = newPosition;
484   videoStartup = true;
485   afeed.start();
486   vfeed.start();
487   threadStart();
488   audio->play();
489   video->sync();
490   audio->sync();
491   audio->systemMuteOff();
492   audio->doMuting();
493   fbwd = false;
494 }
495
496 void Player::setStartTS(UINT dataInBuffer)
497 {
498 #ifndef NEW_DEMUXER
499   if (isRecording && feedPosition) // (feedPosition != 0)
500   {
501     // FIXME find out how much data need to get to find a TS
502     // Need to get the actual start of the recording
503
504     UINT thisRead;
505     UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
506     if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
507     if (!tempBuffer) return;
508     if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &startTS);
509     free(tempBuffer);
510   }
511   else
512   {
513     demuxer->findVideoPTS(threadBuffer, dataInBuffer, &startTS);
514   }
515 #else
516   startTS=0;
517 #endif
518 }
519
520 void Player::setEndTS()
521 {
522   logger->log("Player", Log::DEBUG, "Setting end TS");
523 #ifndef NEW_DEMUXER
524   UINT thisRead;
525   UCHAR* tempBuffer = VDR::getInstance()->getBlock((streamLength - 100000), 100000, &thisRead);
526   if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
527   if (!tempBuffer) return;
528   if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &endTS);
529   free(tempBuffer);
530  #else //The replacement code relias completely on VDRs timecode and not the pts
531    endTS=video->frameNumberToTimecode(
532     VDR::getInstance()->frameNumberFromPosition((streamLength - 100000)));
533  #endif
534   logger->log("Player", Log::DEBUG, "Set end TS");
535 }
536
537 void Player::doConnectionLost()
538 {
539   Message* m = new Message();
540   m->message = Message::CONNECTION_LOST;
541   m->to = this;
542   commandMessageQueue->postMessage(m);
543 }
544
545 // ----------------------------------- Callback
546
547 void Player::call(void* caller)
548 {
549   if (caller == demuxer)
550   {
551     logger->log("Player", Log::DEBUG, "Callback from demuxer");
552
553     if (video->getTVsize() == Video::ASPECT4X3)
554     {
555       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
556       return;
557     }
558
559     int dxCurrentAspect = demuxer->getAspectRatio();
560     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
561     {
562       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
563       video->setAspectRatio(Video::ASPECT4X3);
564     }
565     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
566     {
567       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
568       video->setAspectRatio(Video::ASPECT16X9);
569     }
570     else
571     {
572       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
573     }
574
575   }
576   else
577   {
578     if (videoStartup)
579     {
580       videoStartup = false;
581       video->reset();
582       video->play();
583       video->sync();
584       vfeed.release();
585       unLock();
586     }
587
588     threadSignalNoLock();
589   }
590 }
591
592 // ----------------------------------- Feed thread
593
594 void Player::threadMethod()
595 {
596   UINT thisRead;
597   UINT writeLength;
598   UINT thisWrite;
599   UINT preBufferTotal = 0;
600 #ifdef NEW_DEMUXER
601   ULLONG currentposition;
602 #endif
603
604   VDR* vdr = VDR::getInstance();
605
606   UINT askFor;
607   while(1)
608   {
609     thisRead = 0;
610     writeLength = 0;
611     thisWrite = 0;
612
613     threadCheckExit();
614
615     // If we havn't rescanned for a while..
616     if (isRecording && ((lastRescan + 60) < time(NULL)))
617     {
618       streamLength = vdr->rescanRecording();
619       if (!vdr->isConnected()) { doConnectionLost(); return; }
620       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", streamLength);
621       lastRescan = time(NULL);
622       setEndTS();
623     }
624
625     if (streamLength) // is playing a recording
626     {
627       if (feedPosition >= streamLength) break;  // finished playback
628
629       if (startup)
630       {
631         if (startupBlockSize > streamLength)
632           askFor = streamLength; // is a very small recording!
633         else
634           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
635       }
636       else
637       {
638         if ((feedPosition + blockSize) > streamLength) // last block of recording
639           askFor = streamLength - feedPosition;
640         else // normal
641           askFor = blockSize;
642       }
643     }
644     else // is playing live
645     {
646       if (startup)
647         askFor = startupBlockSize; // find audio streams sized block
648       else
649         askFor = blockSize; // normal
650     }
651
652     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
653 #ifdef NEW_DEMUXER
654   currentposition=feedPosition;
655  #endif
656     if (!vdr->isConnected())
657     {
658       doConnectionLost();
659       return;
660     }
661
662     if (!threadBuffer) break;
663
664     if (startup)
665     {
666       int a_stream = demuxer->scan(threadBuffer, thisRead);
667       demuxer->setAudioStream(a_stream);
668       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
669
670       setStartTS(thisRead);
671
672       if (isRecording) setEndTS();
673
674       startup = false;
675     }
676
677     if (preBuffering)
678     {
679       preBufferTotal += thisRead;
680       if (preBufferTotal > 500000)
681       {
682         logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
683
684         preBuffering = false;
685         preBufferTotal = 0;
686
687         audio->sync();
688         audio->play();
689         video->sync();
690         video->pause();
691         afeed.start();
692         vfeed.start();
693 //        unLock(); // thread will be locked by play until here
694         // FIXME - see if this can segfault because it is starting threads out of the master mutex
695       }
696     }
697
698     if (feedMode == MODE_NORMAL)
699     {
700       feedPosition += thisRead;
701     }
702     else if (feedMode == MODE_BACKWARDS)
703     {
704       if (feedPosition >= blockSize)
705       {
706         feedPosition -= blockSize;
707         if (!isRadio) demuxer->seek();
708       }
709       else
710       {
711         // got to the start of the recording.. revert to play mode? how?
712         feedPosition += thisRead;
713       }
714     }
715
716     threadCheckExit();
717
718     while(writeLength < thisRead)
719     {
720 #ifndef NEW_DEMUXER
721       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
722 #else
723       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength,
724       currentposition+(ULLONG)writeLength);
725 #endif
726       writeLength += thisWrite;
727 //      logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
728
729       if (!thisWrite)
730       {
731 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
732         // demuxer is full and can't take anymore
733         threadLock();
734         threadWaitForSignal();
735         threadUnlock();
736 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
737       }
738
739       threadCheckExit();
740     }
741
742     free(threadBuffer);
743     threadBuffer = NULL;
744
745   }
746
747   // end of recording
748   logger->log("Player", Log::DEBUG, "Recording playback ends");
749
750   threadCheckExit();
751
752   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
753   if (streamLength) m->message = Message::STOP_PLAYBACK;  // recording
754   else m->message = Message::STREAM_END;                  // live
755   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
756   commandMessageQueue->postMessage(m);
757   logger->log("Player", Log::DEBUG, "Message posted...");
758 }
759
760 void Player::threadPostStopCleanup()
761 {
762   if (threadBuffer)
763   {
764     free(threadBuffer);
765     threadBuffer = NULL;
766   }
767 }
768
769 // ----------------------------------- Dev
770
771 #ifdef DEV
772 void Player::test1()
773 {
774   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
775   video->play();
776 //  video->setAspectRatio(Video::ASPECT4X3);
777 }
778
779 void Player::test2()
780 {
781   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
782   video->setAspectRatio(Video::ASPECT16X9);
783 }
784 #endif