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