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