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