]> git.vomp.tv Git - vompclient.git/blob - player.cc
Connection failed detection
[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 Player::Player(MessageQueue* messageQueue, UCHAR tIsRecording, UCHAR isRadio)
24 : vfeed(this), afeed(this)
25 {
26   commandMessageQueue = messageQueue;
27   audio = Audio::getInstance();
28   video = Video::getInstance();
29   logger = Log::getInstance();
30   initted = 0;
31   paused = false;
32   playing = false;
33   ffwd = false;
34   fbwd = false;
35   streamLength = 0;
36   feedPosition = 0;
37   feedMode = MODE_NORMAL;
38   isRecording = tIsRecording;
39   lastRescan = 0;
40   startTS = 0;
41   endTS = 0;
42   startup = 1;
43   threadBuffer = NULL;
44
45   if (isRadio)
46   {
47     blockSize = 20000;
48     startupBlockSize = 60000;
49   }
50   else
51   {
52     blockSize = 100000;
53     startupBlockSize = 250000;
54   }
55 }
56
57 Player::~Player()
58 {
59   if (initted) shutdown();
60 }
61
62 int Player::init()
63 {
64   if (initted) return 0;
65
66   if (!demuxer.init(this))
67   {
68     logger->log("Player", Log::ERR, "Demuxer failed to init");
69     shutdown();
70     return 0;
71   }
72
73   vfeed.init(video->getFD());
74   afeed.init(audio->getFD());
75
76   video->stop();
77   video->blank();
78   audio->stop();
79
80   initted = 1;
81   return 1;
82 }
83
84 int Player::shutdown()
85 {
86   if (!initted) return 0;
87   initted = 0;
88
89   logger->log("Player", Log::DEBUG, "Player shutdown...");
90
91   // copy of stop
92   if (playing)
93   {
94     playing = false;
95     threadStop();
96     video->stop();
97     video->blank();
98     audio->stop();
99     vfeed.stop();
100     afeed.stop();
101     video->reset();
102     demuxer.reset();
103     feedPosition = 0;
104   }
105   logger->log("Player", Log::DEBUG, "Player shutdown done");
106
107   return 1;
108 }
109
110 void Player::resyncAudio()
111 {
112   // Temp hopefully
113   if (!initted) return;
114   audio->pause();
115   usleep(500000);
116   audio->unPause();
117 }
118
119 void Player::resyncVideo()
120 {
121   // Temp hopefully
122   if (!initted) return;
123   video->pause();
124   usleep(500000);
125   video->unPause();
126 }
127
128 int Player::play()
129 {
130   if (!initted) return 0;
131
132   // If we are just paused, unpause!
133   if (paused)
134   {
135     togglePause();
136     return 1;
137   }
138
139   // If we are fast forwarding, set to normal
140   if (ffwd)
141   {
142     toggleFastForward();
143     return 1;
144   }
145
146   // If we are fast backwarding, set to normal
147   if (fbwd)
148   {
149     toggleFastBackward();
150     return 1;
151   }
152
153   // If we are already playing, bail // FIXME - resync?
154   if (playing)
155   {
156     logger->log("Player", Log::DEBUG, "DOING RESYNC");
157     resyncVideo();
158     return 1;
159   }
160
161   // Standard play start
162   logger->log("Player", Log::DEBUG, "Standard play start");
163
164   audio->reset();
165   video->reset();
166   demuxer.reset();
167
168 // ------------------------ This one works, but doesn't allow any pre-buffering.
169   threadStart();
170   vfeed.start();
171   afeed.start();
172   audio->play();
173   video->play();
174   video->sync();
175   audio->sync();
176
177   resyncVideo();
178 // ------------------------ This one doesn't work, but it should, and would allow for prebuffering.
179
180 /*
181   threadStart();
182 //  sleep(1);
183
184 //  struct timespec delay;
185 //  delay.tv_sec = 1;
186 //  delay.tv_nsec = 500000000;
187 //  nanosleep(&delay, NULL);
188
189   vfeed.start();
190   afeed.start();
191   video->play();
192   audio->play();
193   video->sync();
194   audio->sync();
195
196   video->pause();
197       usleep(500000); // SYNC
198   video->sync();
199   video->unPause();
200   video->sync();
201 */
202
203 // ------------------------------------------------------------------------------------------------
204
205   playing = true;
206   return 1;
207 }
208
209 void Player::stop()
210 {
211   if (!initted) return;
212   if (!playing) return;
213
214   if (ffwd || fbwd)
215   {
216     ffwd = false;
217     fbwd = false;
218     afeed.enable();
219     video->unFastForward();
220     audio->systemMuteOff();
221     feedMode = MODE_NORMAL;
222   }
223
224   playing = false;
225   paused = false;
226
227   vfeed.stop();
228   afeed.stop();
229   threadStop();
230   video->stop();
231   video->blank();
232   audio->stop();
233   audio->unPause();
234   video->reset();
235   demuxer.reset();
236
237   feedPosition = 0;
238 }
239
240 void Player::togglePause()
241 {
242   if (!initted) return;
243   if (!playing) return;
244
245   if (ffwd) toggleFastForward();
246   if (fbwd) toggleFastBackward();
247
248   if (paused)
249   {
250     video->unPause();
251     audio->unPause();
252     paused = false;
253   }
254   else
255   {
256     video->pause();
257     audio->pause();
258     paused = true;
259   }
260 }
261
262 void Player::setPosition(ULLONG position)
263 {
264   feedPosition = position;
265 }
266
267 void Player::setLength(ULLONG length)
268 {
269   lastRescan = time(NULL);
270   streamLength = length;
271   logger->log("Player", Log::DEBUG, "Player has received length of %llu", streamLength);
272 }
273
274 void Player::skipForward(int seconds)
275 {
276   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
277
278   if (paused) togglePause();
279
280   ULONG wantedFrameNumber = video->timecodeToFrameNumber(getPositionTS() + (seconds * 90000));
281   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
282   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
283
284   vfeed.stop();
285   afeed.stop();
286   threadStop();
287   video->stop();
288   video->reset();
289   audio->reset();
290   audio->doMuting();  // ???
291   demuxer.flush();
292   feedPosition = newPosition;
293   vfeed.start();
294   afeed.start();
295   threadStart();
296   audio->play();
297   video->play();
298   video->sync();
299   audio->sync();
300
301   resyncVideo();
302 }
303
304 void Player::skipBackward(int seconds)
305 {
306   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
307
308   if (paused) togglePause();
309
310   ULLONG newPosition = 0;
311
312   long long newTimeCode = getPositionTS() - (seconds * 90000);
313   if (newTimeCode > 0)
314   {
315     ULONG wantedFrameNumber = video->timecodeToFrameNumber((ULLONG)newTimeCode);
316     newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
317     logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
318   }
319
320   vfeed.stop();
321   afeed.stop();
322   threadStop();
323   video->stop();
324   audio->stop();
325   video->reset();
326   audio->reset();
327   audio->doMuting(); // ???
328   demuxer.flush();
329   feedPosition = newPosition;
330   vfeed.start();
331   afeed.start();
332   threadStart();
333   audio->play();
334   video->play();
335   video->sync();
336   audio->sync();
337
338   resyncVideo();
339 }
340
341 void Player::toggleFastForward()
342 {
343   if (!initted) return;
344   if (!playing) return;
345
346   if (paused) togglePause();
347   if (fbwd) toggleFastBackward();
348
349   if (ffwd)
350   {
351     ffwd = false;
352 //    video->unFastForward();
353
354
355     vfeed.stop();
356     afeed.stop();
357     threadStop();
358     video->stop();
359     audio->stop();
360     video->reset();
361     audio->reset();
362     demuxer.flush();
363 //    demuxer.seek();
364     vfeed.start();
365     afeed.enable();
366     afeed.start();
367     threadStart();
368     video->play();
369     audio->play();
370     video->sync();
371     audio->sync();
372
373     audio->systemMuteOff();
374
375     resyncVideo();
376 /*
377     demuxer.flushAudio();
378     audio->reset();
379     afeed.enable();
380     //video->reset();
381     audio->play();
382     video->play();
383     video->sync();
384     audio->sync();
385     audio->systemMuteOff();
386 */
387   }
388   else
389   {
390     ffwd = true;
391     afeed.disable();
392     audio->systemMuteOn();
393     video->fastForward();
394   }
395 }
396
397 void Player::toggleFastBackward()
398 {
399   if (!initted) return;
400   if (!playing) return;
401
402   if (paused) togglePause();
403   if (ffwd) toggleFastForward();
404
405   if (fbwd)
406   {
407     fbwd = false;
408     afeed.enable();
409     audio->systemMuteOff();
410
411 //    threadStop();
412     feedMode = MODE_NORMAL;
413 //    threadStart();
414   }
415   else
416   {
417     fbwd = false;
418     afeed.disable();
419     audio->systemMuteOn();
420
421     threadStop();
422     feedMode = MODE_BACKWARDS;
423     video->reset();
424     video->play();
425     demuxer.flush();
426     threadStart();
427   }
428 }
429
430 void Player::jumpToPercent(int percent)
431 {
432   if (paused) togglePause();
433   if (ffwd) toggleFastForward();
434
435   vfeed.stop();
436   afeed.stop();
437   threadStop();
438   video->stop();
439   audio->stop();
440   video->reset();
441   audio->reset();
442   demuxer.flush();
443   demuxer.seek();
444   feedPosition = streamLength * percent / 100;
445   vfeed.start();
446   afeed.start();
447   threadStart();
448   audio->play();
449   video->play();
450   video->sync();
451   audio->sync();
452
453   resyncVideo();
454 }
455
456 ULLONG Player::getPositionTS()
457 {
458   if (startup) return 0ULL;
459   long long currentTS = video->getCurrentTimestamp() - startTS;
460   if (currentTS < 0) currentTS += 8589934592ULL;
461   return (ULLONG)currentTS;
462 }
463
464 ULLONG Player::getEndTS()
465 {
466   long long rendTS = endTS - startTS;
467   if (rendTS < 0) rendTS += 8589934592ULL;
468   return (ULLONG)rendTS;
469 }
470
471 void Player::call(void* caller)
472 {
473   if (caller == &demuxer)
474   {
475     logger->log("Player", Log::DEBUG, "Callback from demuxer");
476
477     if (video->getTVsize() == Video::ASPECT4X3)
478     {
479       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
480       return;
481     }
482
483     int dxCurrentAspect = demuxer.getAspectRatio();
484     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
485     {
486       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
487       video->setAspectRatio(Video::ASPECT4X3);
488     }
489     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
490     {
491       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
492       video->setAspectRatio(Video::ASPECT16X9);
493     }
494     else
495     {
496       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
497     }
498
499   }
500   else
501   {
502     threadSignalNoLock();
503   }
504 }
505
506 // Feed thread
507
508 void Player::threadMethod()
509 {
510   UINT thisRead;
511   UINT writeLength;
512   UINT thisWrite;
513
514   VDR* vdr = VDR::getInstance();
515
516   UINT askFor;
517   while(1)
518   {
519     thisRead = 0;
520     writeLength = 0;
521     thisWrite = 0;
522
523     threadCheckExit();
524
525     // If we havn't rescanned for a while..
526     if (isRecording && ((lastRescan + 60) < time(NULL)))
527     {
528       streamLength = vdr->rescanRecording();
529       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", streamLength);
530       lastRescan = time(NULL);
531       setEndTS();
532     }
533
534     if (streamLength) // is playing a recording
535     {
536       if (feedPosition >= streamLength) break;  // finished playback
537
538       if (startup)
539       {
540         if (startupBlockSize > streamLength)
541           askFor = streamLength; // is a very small recording!
542         else
543           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
544       }
545       else
546       {
547         if ((feedPosition + blockSize) > streamLength) // last block of recording
548           askFor = streamLength - feedPosition;
549         else // normal
550           askFor = blockSize;
551       }
552     }
553     else // is playing live
554     {
555       if (startup)
556         askFor = startupBlockSize; // find audio streams sized block
557       else
558         askFor = blockSize; // normal
559     }
560
561     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
562     if (!threadBuffer) break;
563
564     if (startup)
565     {
566       int a_stream = demuxer.scan(threadBuffer, thisRead);
567       demuxer.setAudioStream(a_stream);
568       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
569
570       setStartTS(thisRead);
571
572       if (isRecording) setEndTS();
573
574       startup = 0;
575     }
576
577     if (feedMode == MODE_NORMAL)
578     {
579       feedPosition += thisRead;
580     }
581     else if (feedMode == MODE_BACKWARDS)
582     {
583       if (feedPosition >= blockSize)
584       {
585         feedPosition -= blockSize;
586         demuxer.seek();
587       }
588       else
589       {
590         // got to the start of the recording.. revert to play mode? how?
591         feedPosition += thisRead;
592       }
593     }
594
595     threadCheckExit();
596
597     while(writeLength < thisRead)
598     {
599       thisWrite = demuxer.put(threadBuffer + writeLength, thisRead - writeLength);
600       writeLength += thisWrite;
601
602       if (!thisWrite)
603       {
604 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
605         // demuxer is full and cant take anymore
606         threadLock();
607         threadWaitForSignal();
608         threadUnlock();
609 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
610       }
611
612       threadCheckExit();
613     }
614
615     free(threadBuffer);
616     threadBuffer = NULL;
617
618   }
619
620   // end of recording
621   logger->log("Player", Log::DEBUG, "Recording playback ends");
622   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
623   if (streamLength) m->message = Message::STOP_PLAYBACK;  // recording
624   else m->message = Message::STREAM_END;                  // live
625   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
626   commandMessageQueue->postMessage(m);
627   logger->log("Player", Log::DEBUG, "Message posted...");
628 }
629
630 void Player::threadPostStopCleanup()
631 {
632   logger->log("Player", Log::DEBUG, "Post stop cleanup 1");
633   if (threadBuffer)
634   {
635     logger->log("Player", Log::DEBUG, "Post stop cleanup 2");
636     free(threadBuffer);
637     threadBuffer = NULL;
638   }
639   logger->log("Player", Log::DEBUG, "Post stop cleanup 3");
640 }
641
642 void Player::setStartTS(UINT dataInBuffer)
643 {
644   if (isRecording && feedPosition) // (feedPosition != 0)
645   {
646     // FIXME find out how much data need to get to find a TS
647     // Need to get the actual start of the recording
648
649     UINT thisRead;
650     UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
651     if (!tempBuffer) return;
652     if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &startTS);
653     free(tempBuffer);
654   }
655   else
656   {
657     demuxer.findVideoPTS(threadBuffer, dataInBuffer, &startTS);
658   }
659 }
660
661 void Player::setEndTS()
662 {
663   logger->log("Player", Log::DEBUG, "Setting end TS");
664
665   UINT thisRead;
666   UCHAR* tempBuffer = VDR::getInstance()->getBlock((streamLength - 100000), 100000, &thisRead);
667   if (!tempBuffer) return;
668   if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &endTS);
669   free(tempBuffer);
670   logger->log("Player", Log::DEBUG, "Set end TS");
671 }
672
673 #ifdef DEV
674 void Player::test1()
675 {
676   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
677 }
678
679 void Player::test2()
680 {
681   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
682 }
683 #endif