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