]> git.vomp.tv Git - vompclient.git/blob - playervideo.cc
Widescreen support, segfault fix on livetv interrupt
[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)
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   threadStop();
220   video->stop();
221   video->blank();
222   audio->stop();
223   audio->unPause();
224   vfeed.stop();
225   afeed.stop();
226   video->reset();
227   demuxer.reset();
228
229   feedPosition = 0;
230 }
231
232 void PlayerVideo::togglePause()
233 {
234   if (!initted) return;
235   if (!playing) return;
236
237   if (ffwd) toggleFastForward();
238   if (fbwd) toggleFastBackward();
239
240   if (paused)
241   {
242     video->unPause();
243     audio->unPause();
244     paused = 0;
245   }
246   else
247   {
248     video->pause();
249     audio->pause();
250     paused = 1;
251   }
252 }
253
254 #ifdef DEV
255 void PlayerVideo::test()
256 {
257   Log::getInstance()->log("Player", Log::DEBUG, "PLAYER TEST");
258
259 /*
260
261 //  video->test();
262 */
263
264 /*
265   static int flipflop = 0;
266
267   int a;
268   if (flipflop) a = video->setAspectRatio(Video::ASPECT16X9);
269   else a = video->setAspectRatio(Video::ASPECT4X3);
270
271   flipflop = !flipflop;
272
273   printf("A = %i\n", a);
274 */
275 }
276
277 void PlayerVideo::test2()
278 {
279   Log::getInstance()->log("Player", Log::DEBUG, "PLAYER TEST");
280
281 //  video->test2();
282 }
283 #endif
284
285 void PlayerVideo::setPosition(ULLONG position)
286 {
287   feedPosition = position;
288 }
289
290 void PlayerVideo::setLength(ULLONG length)
291 {
292   lastRescan = time(NULL);
293   streamLength = length;
294   Log::getInstance()->log("PlayerVideo", Log::DEBUG, "Player has received length of %llu", streamLength);
295 }
296
297 void PlayerVideo::skipForward(int seconds)
298 {
299   // skip forward 1 minute
300   Log::getInstance()->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
301
302   if (paused) togglePause();
303
304   ULLONG moveBy = seconds * 500000;
305
306   threadStop();
307   vfeed.stop();
308   afeed.stop();
309   video->stop();
310   video->reset();
311   audio->reset();
312   audio->doMuting();  // ???
313   demuxer.flush();
314   feedPosition += moveBy;
315
316 //  printf("Audio test %i\n", audio->test());
317
318   vfeed.start();
319   afeed.start();
320   threadStart();
321   audio->play();
322   video->play();
323   video->sync();
324   audio->sync();
325
326   video->pause();
327       usleep(500000); // SYNC
328   video->sync();
329   video->unPause();
330   video->sync();
331
332 }
333
334 void PlayerVideo::skipBackward(int seconds)
335 {
336   // skip forward 1 minute
337   Log::getInstance()->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
338
339   if (paused) togglePause();
340
341   ULLONG moveBy = seconds * 500000;
342
343   threadStop();
344   vfeed.stop();
345   afeed.stop();
346   video->stop();
347   audio->stop();
348   video->reset();
349   audio->reset();
350   audio->doMuting(); // ???
351   demuxer.flush();
352   if (feedPosition > moveBy) feedPosition -= moveBy;
353   vfeed.start();
354   afeed.start();
355   threadStart();
356   audio->play();
357   video->play();
358   video->sync();
359   audio->sync();
360
361   video->pause();
362       usleep(500000); // SYNC
363   video->sync();
364   video->unPause();
365   video->sync();
366
367 }
368
369 void PlayerVideo::toggleFastForward()
370 {
371   if (!initted) return;
372   if (!playing) return;
373
374   if (paused) togglePause();
375   if (fbwd) toggleFastBackward();
376
377   if (ffwd)
378   {
379     ffwd = 0;
380 //    video->unFastForward();
381
382
383     threadStop();
384     vfeed.stop();
385     afeed.stop();
386     video->stop();
387     audio->stop();
388     video->reset();
389     audio->reset();
390     demuxer.flush();
391 //    demuxer.seek();
392     vfeed.start();
393     afeed.enable();
394     afeed.start();
395     threadStart();
396     video->play();
397     audio->play();
398     video->sync();
399     audio->sync();
400
401     audio->systemMuteOff();
402
403     video->pause();
404         usleep(500000); // SYNC
405     video->sync();
406     video->unPause();
407     video->sync();
408
409 /*
410     demuxer.flushAudio();
411     audio->reset();
412     afeed.enable();
413     //video->reset();
414     audio->play();
415     video->play();
416     video->sync();
417     audio->sync();
418     audio->systemMuteOff();
419 */
420   }
421   else
422   {
423     ffwd = 1;
424     afeed.disable();
425     audio->systemMuteOn();
426     video->fastForward();
427   }
428 }
429
430 void PlayerVideo::toggleFastBackward()
431 {
432   if (!initted) return;
433   if (!playing) return;
434
435   if (paused) togglePause();
436   if (ffwd) toggleFastForward();
437
438   if (fbwd)
439   {
440     fbwd = 0;
441     afeed.enable();
442     audio->systemMuteOff();
443
444 //    threadStop();
445     feedMode = MODE_NORMAL;
446 //    threadStart();
447   }
448   else
449   {
450     fbwd = 1;
451     afeed.disable();
452     audio->systemMuteOn();
453
454     threadStop();
455     feedMode = MODE_BACKWARDS;
456     video->reset();
457     video->play();
458     demuxer.flush();
459     threadStart();
460   }
461 }
462
463 void PlayerVideo::jumpToPercent(int percent)
464 {
465   threadStop();
466   vfeed.stop();
467   afeed.stop();
468   video->stop();
469   audio->stop();
470   video->reset();
471   audio->reset();
472   demuxer.flush();
473   demuxer.seek();
474   feedPosition = streamLength * percent / 100;
475   vfeed.start();
476   afeed.start();
477   threadStart();
478   audio->play();
479   video->play();
480   video->sync();
481   audio->sync();
482
483   video->pause();
484       usleep(500000); // SYNC
485   video->sync();
486   video->unPause();
487   video->sync();
488 }
489
490
491 void PlayerVideo::call(void* caller)
492 {
493   if (caller == &demuxer)
494   {
495     Log* temp = Log::getInstance();
496     temp->log("Player", Log::DEBUG, "Callback from demuxer");
497     if (video->getAspectRatio() == Video::ASPECT16X9)
498     {
499       temp->log("Player", Log::DEBUG, "TV is 16:9, so will try to do a switch");
500
501       int dxCurrentAspect = demuxer.getAspectRatio();
502       if (dxCurrentAspect == Demuxer::ASPECT_4_3)
503       {
504         temp->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
505         video->setMode(Video::LETTERBOX);   // swap these lines over
506       }
507       else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
508       {
509         temp->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
510         video->setMode(Video::NORMAL);      // if these are the wrong way around
511       }
512       else
513       {
514         temp->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
515       }
516     }
517   }
518   else
519   {
520     threadSignalNoLock();
521   }
522 }
523
524 // Feed thread
525
526 void PlayerVideo::threadMethod()
527 {
528   UINT thisRead;
529   UINT writeLength;
530   UINT thisWrite;
531
532   VDR* vdr = VDR::getInstance();
533
534   UINT askFor;
535   while(1)
536   {
537     thisRead = 0;
538     writeLength = 0;
539     thisWrite = 0;
540
541     threadCheckExit();
542
543     // If we havn't rescanned for a while..
544     if (isRecording && ((lastRescan + 60) < time(NULL)))
545     {
546       streamLength = vdr->rescanRecording();
547       Log::getInstance()->log("PlayerVideo", Log::DEBUG, "Rescanned and reset length: %llu", streamLength);
548       lastRescan = time(NULL);
549     }
550
551     if (streamLength) // is playing a recording
552     {
553       if (feedPosition >= streamLength) break;  // finished playback
554
555       if (startup)
556       {
557         if (startupBlockSize > streamLength)
558           askFor = streamLength; // is a very small recording!
559         else
560           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
561       }
562       else
563       {
564         if ((feedPosition + blockSize) > streamLength) // last block of recording
565           askFor = streamLength - feedPosition;
566         else // normal
567           askFor = blockSize;
568       }
569     }
570     else // is playing live
571     {
572       if (startup)
573         askFor = startupBlockSize; // find audio streams sized block
574       else
575         askFor = blockSize; // normal
576     }
577
578     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
579     if (!threadBuffer) break;
580
581     if (startup)
582     {
583       int a_stream = demuxer.scan(threadBuffer, thisRead);
584       demuxer.setAudioStream(a_stream);
585       Log::getInstance()->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
586       startup = 0;
587     }
588
589     if (feedMode == MODE_NORMAL)
590     {
591       feedPosition += thisRead;
592     }
593     else if (feedMode == MODE_BACKWARDS)
594     {
595       if (feedPosition >= blockSize)
596       {
597         feedPosition -= blockSize;
598         demuxer.seek();
599       }
600       else
601       {
602         // got to the start of the recording.. revert to play mode? how?
603         feedPosition += thisRead;
604       }
605     }
606
607     threadCheckExit();
608
609     while(writeLength < thisRead)
610     {
611       thisWrite = demuxer.put(threadBuffer + writeLength, thisRead - writeLength);
612       writeLength += thisWrite;
613
614       if (!thisWrite)
615       {
616 //        Log::getInstance()->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
617         // demuxer is full and cant take anymore
618         threadWaitForSignal();
619 //        Log::getInstance()->log("Player", Log::DEBUG, "BACK FROM WAIT");
620       }
621
622       threadCheckExit();
623     }
624
625     free(threadBuffer);
626     threadBuffer = NULL;
627
628   }
629
630   // end of recording
631   Log::getInstance()->log("Player", Log::DEBUG, "Recording playback ends");
632   Message* m = new Message();
633   if (streamLength) m->message = Message::STOP_PLAYBACK;  // recording
634   else m->message = Message::STREAM_END;                  // live
635   Log::getInstance()->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
636   commandMessageQueue->postMessage(m);
637   Log::getInstance()->log("Player", Log::DEBUG, "Message posted...");
638 }
639
640 void PlayerVideo::threadPostStopCleanup()
641 {
642   Log::getInstance()->log("Player", Log::DEBUG, "Post stop cleanup 1");
643   if (threadBuffer)
644   {
645     Log::getInstance()->log("Player", Log::DEBUG, "Post stop cleanup 2");
646     free(threadBuffer);
647     threadBuffer = NULL;
648   }
649 }