]> git.vomp.tv Git - vompclient.git/blob - player.cc
Portability
[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   MILLISLEEP(500);
116   audio->unPause();
117 }
118
119 void Player::resyncVideo()
120 {
121   // Temp hopefully
122   if (!initted) return;
123   video->pause();
124   MILLISLEEP(500);
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 //  MILLISLEEP(1000);
183
184   vfeed.start();
185   afeed.start();
186   video->play();
187   audio->play();
188   audio->sync();
189   video->sync();
190   resyncVideo();
191 */
192
193 // ------------------------------------------------------------------------------------------------
194
195   playing = true;
196   return 1;
197 }
198
199 void Player::stop()
200 {
201   if (!initted) return;
202   if (!playing) return;
203
204   logger->log("Player", Log::DEBUG, "Stop called", streamLength);
205
206   if (ffwd || fbwd)
207   {
208     ffwd = false;
209     fbwd = false;
210     afeed.enable();
211     video->unFastForward();
212     audio->systemMuteOff();
213     feedMode = MODE_NORMAL;
214   }
215
216   playing = false;
217   paused = false;
218
219   vfeed.stop();
220   afeed.stop();
221   threadStop();
222   video->stop();
223   video->blank();
224   audio->stop();
225   audio->unPause();
226   video->reset();
227   demuxer.reset();
228
229   feedPosition = 0;
230 }
231
232 void Player::togglePause()
233 {
234   if (!initted) return;
235   if (!playing) return;
236
237   if (ffwd) toggleFastForward();
238   if (fbwd) toggleFastBackward();
239
240   if (paused)
241   {
242     video->unPause();
243     audio->unPause();
244     paused = false;
245   }
246   else
247   {
248     video->pause();
249     audio->pause();
250     paused = true;
251   }
252 }
253
254 void Player::setPosition(ULLONG position)
255 {
256   feedPosition = position;
257 }
258
259 void Player::setLength(ULLONG length)
260 {
261   lastRescan = time(NULL);
262   streamLength = length;
263   logger->log("Player", Log::DEBUG, "Player has received length of %llu", streamLength);
264 }
265
266 void Player::skipForward(int seconds)
267 {
268   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
269
270   if (paused) togglePause();
271
272   ULONG wantedFrameNumber = video->timecodeToFrameNumber(getPositionTS() + (seconds * 90000));
273   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
274   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
275   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
276
277   vfeed.stop();
278   afeed.stop();
279   threadStop();
280   video->stop();
281   video->reset();
282   audio->reset();
283   audio->doMuting();  // ???
284   demuxer.flush();
285   feedPosition = newPosition;
286   vfeed.start();
287   afeed.start();
288   threadStart();
289   audio->play();
290   video->play();
291   video->sync();
292   audio->sync();
293
294   resyncVideo();
295 }
296
297 void Player::skipBackward(int seconds)
298 {
299   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
300
301   if (paused) togglePause();
302
303   ULLONG newPosition = 0;
304
305   long long newTimeCode = getPositionTS() - (seconds * 90000);
306   if (newTimeCode > 0)
307   {
308     ULONG wantedFrameNumber = video->timecodeToFrameNumber((ULLONG)newTimeCode);
309     newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
310     if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
311     logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
312   }
313
314   vfeed.stop();
315   afeed.stop();
316   threadStop();
317   video->stop();
318   audio->stop();
319   video->reset();
320   audio->reset();
321   audio->doMuting(); // ???
322   demuxer.flush();
323   feedPosition = newPosition;
324   vfeed.start();
325   afeed.start();
326   threadStart();
327   audio->play();
328   video->play();
329   video->sync();
330   audio->sync();
331
332   resyncVideo();
333 }
334
335 void Player::toggleFastForward()
336 {
337   if (!initted) return;
338   if (!playing) return;
339
340   if (paused) togglePause();
341   if (fbwd) toggleFastBackward();
342
343   if (ffwd)
344   {
345     ffwd = false;
346 //    video->unFastForward();
347
348
349     vfeed.stop();
350     afeed.stop();
351     threadStop();
352     video->stop();
353     audio->stop();
354     video->reset();
355     audio->reset();
356     demuxer.flush();
357 //    demuxer.seek();
358     vfeed.start();
359     afeed.enable();
360     afeed.start();
361     threadStart();
362     video->play();
363     audio->play();
364     video->sync();
365     audio->sync();
366
367     audio->systemMuteOff();
368
369     resyncVideo();
370 /*
371     demuxer.flushAudio();
372     audio->reset();
373     afeed.enable();
374     //video->reset();
375     audio->play();
376     video->play();
377     video->sync();
378     audio->sync();
379     audio->systemMuteOff();
380 */
381   }
382   else
383   {
384     ffwd = true;
385     afeed.disable();
386     audio->systemMuteOn();
387     video->fastForward();
388   }
389 }
390
391 void Player::toggleFastBackward()
392 {
393   if (!initted) return;
394   if (!playing) return;
395
396   if (paused) togglePause();
397   if (ffwd) toggleFastForward();
398
399   if (fbwd)
400   {
401     fbwd = false;
402     afeed.enable();
403     audio->systemMuteOff();
404
405 //    threadStop();
406     feedMode = MODE_NORMAL;
407 //    threadStart();
408   }
409   else
410   {
411     fbwd = false;
412     afeed.disable();
413     audio->systemMuteOn();
414
415     threadStop();
416     feedMode = MODE_BACKWARDS;
417     video->reset();
418     video->play();
419     demuxer.flush();
420     threadStart();
421   }
422 }
423
424 void Player::jumpToPercent(int percent)
425 {
426   if (paused) togglePause();
427   if (ffwd) toggleFastForward();
428
429   vfeed.stop();
430   afeed.stop();
431   threadStop();
432   video->stop();
433   audio->stop();
434   video->reset();
435   audio->reset();
436   demuxer.flush();
437   demuxer.seek();
438   feedPosition = streamLength * percent / 100;
439   vfeed.start();
440   afeed.start();
441   threadStart();
442   audio->play();
443   video->play();
444   video->sync();
445   audio->sync();
446
447   resyncVideo();
448 }
449
450 ULLONG Player::getPositionTS()
451 {
452   if (startup) return 0ULL;
453   long long currentTS = video->getCurrentTimestamp() - startTS;
454   if (currentTS < 0) currentTS += 8589934592ULL;
455   return (ULLONG)currentTS;
456 }
457
458 ULLONG Player::getEndTS()
459 {
460   long long rendTS = endTS - startTS;
461   if (rendTS < 0) rendTS += 8589934592ULL;
462   return (ULLONG)rendTS;
463 }
464
465 void Player::call(void* caller)
466 {
467   if (caller == &demuxer)
468   {
469     logger->log("Player", Log::DEBUG, "Callback from demuxer");
470
471     if (video->getTVsize() == Video::ASPECT4X3)
472     {
473       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
474       return;
475     }
476
477     int dxCurrentAspect = demuxer.getAspectRatio();
478     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
479     {
480       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
481       video->setAspectRatio(Video::ASPECT4X3);
482     }
483     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
484     {
485       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
486       video->setAspectRatio(Video::ASPECT16X9);
487     }
488     else
489     {
490       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
491     }
492
493   }
494   else
495   {
496     threadSignalNoLock();
497   }
498 }
499
500 void Player::doConnectionLost()
501 {
502   Message* m = new Message();
503   m->message = Message::CONNECTION_LOST;
504   m->to = this;
505   commandMessageQueue->postMessage(m);
506 }
507
508 // Feed thread
509
510 void Player::threadMethod()
511 {
512   UINT thisRead;
513   UINT writeLength;
514   UINT thisWrite;
515
516   VDR* vdr = VDR::getInstance();
517
518   UINT askFor;
519   while(1)
520   {
521     thisRead = 0;
522     writeLength = 0;
523     thisWrite = 0;
524
525     threadCheckExit();
526
527     // If we havn't rescanned for a while..
528     if (isRecording && ((lastRescan + 60) < time(NULL)))
529     {
530       streamLength = vdr->rescanRecording();
531       if (!vdr->isConnected()) { doConnectionLost(); return; }
532       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", streamLength);
533       lastRescan = time(NULL);
534       setEndTS();
535     }
536
537     if (streamLength) // is playing a recording
538     {
539       if (feedPosition >= streamLength) break;  // finished playback
540
541       if (startup)
542       {
543         if (startupBlockSize > streamLength)
544           askFor = streamLength; // is a very small recording!
545         else
546           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
547       }
548       else
549       {
550         if ((feedPosition + blockSize) > streamLength) // last block of recording
551           askFor = streamLength - feedPosition;
552         else // normal
553           askFor = blockSize;
554       }
555     }
556     else // is playing live
557     {
558       if (startup)
559         askFor = startupBlockSize; // find audio streams sized block
560       else
561         askFor = blockSize; // normal
562     }
563
564     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
565     if (!vdr->isConnected())
566     {
567       doConnectionLost();
568       return;
569     }
570
571     if (!threadBuffer) break;
572
573     if (startup)
574     {
575       int a_stream = demuxer.scan(threadBuffer, thisRead);
576       demuxer.setAudioStream(a_stream);
577       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
578
579       setStartTS(thisRead);
580
581       if (isRecording) setEndTS();
582
583       startup = 0;
584     }
585
586     if (feedMode == MODE_NORMAL)
587     {
588       feedPosition += thisRead;
589     }
590     else if (feedMode == MODE_BACKWARDS)
591     {
592       if (feedPosition >= blockSize)
593       {
594         feedPosition -= blockSize;
595         demuxer.seek();
596       }
597       else
598       {
599         // got to the start of the recording.. revert to play mode? how?
600         feedPosition += thisRead;
601       }
602     }
603
604     threadCheckExit();
605
606     while(writeLength < thisRead)
607     {
608       thisWrite = demuxer.put(threadBuffer + writeLength, thisRead - writeLength);
609       writeLength += thisWrite;
610
611       if (!thisWrite)
612       {
613 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
614         // demuxer is full and cant take anymore
615         threadLock();
616         threadWaitForSignal();
617         threadUnlock();
618 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
619       }
620
621       threadCheckExit();
622     }
623
624     free(threadBuffer);
625     threadBuffer = NULL;
626
627   }
628
629   // end of recording
630   logger->log("Player", Log::DEBUG, "Recording playback ends");
631
632   threadCheckExit();
633
634   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
635   if (streamLength) m->message = Message::STOP_PLAYBACK;  // recording
636   else m->message = Message::STREAM_END;                  // live
637   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
638   commandMessageQueue->postMessage(m);
639   logger->log("Player", Log::DEBUG, "Message posted...");
640 }
641
642 void Player::threadPostStopCleanup()
643 {
644   logger->log("Player", Log::DEBUG, "Post stop cleanup 1");
645   if (threadBuffer)
646   {
647     logger->log("Player", Log::DEBUG, "Post stop cleanup 2");
648     free(threadBuffer);
649     threadBuffer = NULL;
650   }
651   logger->log("Player", Log::DEBUG, "Post stop cleanup 3");
652 }
653
654 void Player::setStartTS(UINT dataInBuffer)
655 {
656   if (isRecording && feedPosition) // (feedPosition != 0)
657   {
658     // FIXME find out how much data need to get to find a TS
659     // Need to get the actual start of the recording
660
661     UINT thisRead;
662     UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
663     if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
664     if (!tempBuffer) return;
665     if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &startTS);
666     free(tempBuffer);
667   }
668   else
669   {
670     demuxer.findVideoPTS(threadBuffer, dataInBuffer, &startTS);
671   }
672 }
673
674 void Player::setEndTS()
675 {
676   logger->log("Player", Log::DEBUG, "Setting end TS");
677
678   UINT thisRead;
679   UCHAR* tempBuffer = VDR::getInstance()->getBlock((streamLength - 100000), 100000, &thisRead);
680   if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
681   if (!tempBuffer) return;
682   if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &endTS);
683   free(tempBuffer);
684   logger->log("Player", Log::DEBUG, "Set end TS");
685 }
686
687 #ifdef DEV
688 void Player::test1()
689 {
690   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
691 }
692
693 void Player::test2()
694 {
695   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
696 }
697 #endif