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