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