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