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