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