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