]> git.vomp.tv Git - vompclient.git/blob - player.cc
MVP/Windows convergence: Stream; DrainTarget
[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   logger->log("Player", Log::DEBUG, "Stop called 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 (isRecording)
274   {
275     if (startFrameNum > lengthFrames) startFrameNum = 0;
276     demuxer->setFrameNum(startFrameNum);
277   }
278   if (!isRadio) demuxer->seek();
279
280   videoStartup = true;
281   threadStart();
282
283   if (isRecording)
284   {
285     logger->log("Player", Log::DEBUG, "Immediate play");
286     afeed.start();
287     vfeed.start();
288     video->sync();
289     audio->sync();
290     audio->play();
291     video->pause();
292   }
293   else // do prebuffering
294   {
295     logger->log("Player", Log::DEBUG, "Prebuffering...");
296 //    afeed.start();
297 //    vfeed.start();
298     preBuffering = true;
299   }
300
301   playing = true;
302   return 1;
303 }
304
305 void Player::stopInt()
306 {
307   if (!initted) return;
308   if (!playing) return;
309
310   if (ffwd || fbwd)
311   {
312     ffwd = false;
313     fbwd = false;
314     afeed.enable();
315     video->unFastForward();
316     audio->systemMuteOff();
317     feedMode = MODE_NORMAL;
318   }
319
320   playing = false;
321   paused = false;
322
323   vfeed.stop();
324   afeed.stop();
325   if (threadIsActive()) threadStop();
326   video->stop();
327   video->blank();
328   audio->stop();
329   audio->unPause();
330   video->reset();
331   demuxer->reset();
332
333   feedPosition = 0;
334 }
335
336 void Player::togglePauseInt()
337 {
338   if (!initted) return;
339   if (!playing) return;
340
341   if (ffwd) toggleFastForwardInt();
342   if (fbwd) toggleFastBackwardInt();
343
344   if (paused)
345   {
346     video->unPause();
347     audio->unPause();
348     paused = false;
349   }
350   else
351   {
352     video->pause();
353     audio->pause();
354     paused = true;
355   }
356 }
357
358 void Player::toggleFastForwardInt()
359 {
360   if (!initted) return;
361   if (!playing) return;
362
363   if (paused) togglePauseInt();
364   if (fbwd) toggleFastBackwardInt();
365
366   if (ffwd)
367   {
368     ffwd = false;
369     threadStop();
370     vfeed.stop();
371     afeed.stop();
372     video->stop();
373     audio->stop();
374     video->reset();
375     audio->reset();
376     demuxer->flush();
377     if (!isRadio) demuxer->seek();
378
379     videoStartup = true;
380     afeed.enable();
381     afeed.start();
382     vfeed.start();
383     threadStart();
384     audio->play();
385     video->sync();
386     audio->sync();
387     audio->systemMuteOff();
388     audio->doMuting();
389     fbwd = false;
390   }
391   else
392   {
393     ffwd = true;
394 #ifndef WIN32
395     afeed.disable();
396 #endif
397     audio->systemMuteOn();
398     video->fastForward();
399   }
400 }
401
402 void Player::toggleFastBackwardInt()
403 {
404   if (!initted) return;
405   if (!playing) return;
406
407   if (paused) togglePauseInt();
408   if (ffwd) toggleFastForwardInt();
409
410   if (fbwd)
411   {
412     fbwd = false;
413     afeed.enable();
414     audio->systemMuteOff();
415
416 //    threadStop();
417     feedMode = MODE_NORMAL;
418 //    threadStart();
419   }
420   else
421   {
422     fbwd = false;
423 #ifndef WIN32
424     afeed.disable();
425 #endif
426     audio->systemMuteOn();
427
428     threadStop();
429     feedMode = MODE_BACKWARDS;
430     video->reset();
431     video->play();
432     demuxer->flush();
433     if (!isRadio) demuxer->seek();
434
435     threadStart();
436   }
437 }
438
439 void Player::skipForwardInt(int seconds)
440 {
441   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
442   restartAtFrame(getCurrentFrameNum() + (seconds * video->getFPS()));
443 }
444
445 void Player::skipBackwardInt(int seconds)
446 {
447   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
448   long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS());
449   if (newFrameNum < 0) newFrameNum = 0;
450   restartAtFrame(newFrameNum);
451 }
452
453 void Player::jumpToPercentInt(int percent)
454 {
455   logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
456   ULONG newFrame = percent * lengthFrames / 100;
457   restartAtFrame(newFrame);
458 }
459
460
461 // ----------------------------------- Internal functions
462
463 void Player::lock()
464 {
465 #ifndef WIN32
466   pthread_mutex_lock(&mutex);
467   logger->log("Player", Log::DEBUG, "LOCKED");
468
469 #else
470    WaitForSingleObject(mutex, INFINITE );
471 #endif
472 }
473
474 void Player::unLock()
475 {
476 #ifndef WIN32
477   logger->log("Player", Log::DEBUG, "UNLOCKING");
478   pthread_mutex_unlock(&mutex);
479 #else
480    ReleaseMutex(mutex);
481 #endif
482 }
483
484 void Player::restartAtFrame(ULONG newFrame)
485 {
486   if (paused) togglePauseInt();
487   if (ffwd) toggleFastForwardInt();
488
489   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame);
490   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
491   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition);
492
493   vfeed.stop();
494   afeed.stop();
495   threadStop();
496   video->stop();
497   video->reset();
498   audio->reset();
499   demuxer->flush();
500   if (!isRadio) demuxer->seek();
501   feedPosition = newPosition;
502   demuxer->setFrameNum(newFrame);
503   videoStartup = true;
504   afeed.start();
505   vfeed.start();
506   threadStart();
507   audio->play();
508   video->sync();
509   audio->sync();
510   audio->systemMuteOff();
511   audio->doMuting();
512   fbwd = false;
513 }
514
515 void Player::doConnectionLost()
516 {
517   Message* m = new Message();
518   m->message = Message::CONNECTION_LOST;
519   m->to = this;
520   commandMessageQueue->postMessage(m);
521 }
522
523 // ----------------------------------- Callback
524
525 void Player::call(void* caller)
526 {
527   if (caller == demuxer)
528   {
529     logger->log("Player", Log::DEBUG, "Callback from demuxer");
530
531     if (video->getTVsize() == Video::ASPECT4X3)
532     {
533       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
534       return;
535     }
536
537     int dxCurrentAspect = demuxer->getAspectRatio();
538     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
539     {
540       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
541       video->setAspectRatio(Video::ASPECT4X3);
542     }
543     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
544     {
545       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
546       video->setAspectRatio(Video::ASPECT16X9);
547     }
548     else
549     {
550       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
551     }
552
553   }
554   else
555   {
556     if (videoStartup)
557     {
558       videoStartup = false;
559       video->reset();
560       video->play();
561       video->sync();
562       vfeed.release();
563       unLock();
564     }
565
566     threadSignalNoLock();
567   }
568 }
569
570 // ----------------------------------- Feed thread
571
572 void Player::threadMethod()
573 {
574   UINT thisRead;
575   UINT writeLength;
576   UINT thisWrite;
577   UINT preBufferTotal = 0;
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
629     if (!vdr->isConnected())
630     {
631       doConnectionLost();
632       return;
633     }
634
635     if (!threadBuffer) break;
636
637     if (startup)
638     {
639       int a_stream = demuxer->scan(threadBuffer, thisRead);
640       demuxer->setAudioStream(a_stream);
641       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
642
643 //      setStartTS(thisRead);
644
645 //      if (isRecording) setEndTS();
646
647       startup = false;
648     }
649
650     if (preBuffering)
651     {
652       preBufferTotal += thisRead;
653       if (preBufferTotal > 500000)
654       {
655         logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
656
657         preBuffering = false;
658         preBufferTotal = 0;
659
660         audio->sync();
661         audio->play();
662         video->sync();
663         video->pause();
664         afeed.start();
665         vfeed.start();
666 //        unLock(); // thread will be locked by play until here
667         // FIXME - see if this can segfault because it is starting threads out of the master mutex
668       }
669     }
670
671     if (feedMode == MODE_NORMAL)
672     {
673       feedPosition += thisRead;
674     }
675     else if (feedMode == MODE_BACKWARDS)
676     {
677       if (feedPosition >= blockSize)
678       {
679         feedPosition -= blockSize;
680         if (!isRadio) demuxer->seek();
681       }
682       else
683       {
684         // got to the start of the recording.. revert to play mode? how?
685         feedPosition += thisRead;
686       }
687     }
688
689     threadCheckExit();
690
691     while(writeLength < thisRead)
692     {
693       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
694       writeLength += thisWrite;
695 //      logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
696
697       if (!thisWrite)
698       {
699 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
700         // demuxer is full and can't take anymore
701         threadLock();
702         threadWaitForSignal();
703         threadUnlock();
704 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
705       }
706
707       threadCheckExit();
708     }
709
710     free(threadBuffer);
711     threadBuffer = NULL;
712
713   }
714
715   // end of recording
716   logger->log("Player", Log::DEBUG, "Recording playback ends");
717
718   if (videoStartup) // oh woe. there never was a stream, I was conned!
719   {
720     videoStartup = false;
721     unLock();
722     MILLISLEEP(500); // I think this will solve a race
723   }
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