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