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