]> git.vomp.tv Git - vompclient-marten.git/blob - playerradio.cc
Mouse support (part 1)
[vompclient-marten.git] / playerradio.cc
1 /*
2     Copyright 2004-2006 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 "playerradio.h"
22
23 // ----------------------------------- Called from outside, one offs or info funcs
24
25 PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, bool tIsRecording)
26 : afeed(this)
27 {
28   messageQueue = tmessageQueue;
29   messageReceiver = tmessageReceiver;
30   audio = Audio::getInstance();
31   logger = Log::getInstance();
32   vdr = VDR::getInstance();
33   initted = false;
34   lengthBytes = 0;
35   lengthPackets = 0;
36   streamPos = 0;
37   state = S_STOP;
38
39   startPTS = 0;
40   lengthSeconds = 0;
41
42   isRecording = tIsRecording;
43
44   threadBuffer = NULL;
45
46   blockSize = 10000;
47   startupBlockSize = 20000;
48   preBufferSize = 20000;
49
50   Video::getInstance()->turnVideoOff();
51 }
52
53 PlayerRadio::~PlayerRadio()
54 {
55   if (initted) shutdown();
56 }
57
58 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets)
59 {
60   if (initted) return 0;
61 #ifndef WIN32
62   pthread_mutex_init(&mutex, NULL);
63 #else
64   mutex=CreateMutex(NULL,FALSE,NULL);
65 #endif
66
67   if (isRecording)
68     demuxer = new DemuxerVDR();
69   else
70     demuxer = new DemuxerTS();
71   if (!demuxer) return 0;
72
73   if (!demuxer->init(this, audio, NULL, 0, 40000))
74   {
75     logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
76     shutdown();
77     return 0;
78   }
79
80   afeed.init();
81   audio->stop();
82
83   lengthBytes = tlengthBytes;
84   lengthPackets = tlengthPackets;
85
86   logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
87
88   UINT thisRead = 0;
89   int success;
90
91   if (isRecording)
92   {
93     UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
94     if (!buffer)
95     {
96       logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
97       shutdown();
98       return 0;
99     }
100
101     success = demuxer->findPTS(buffer, thisRead, &startPTS);
102     if (!success)
103     {
104       logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
105       free(buffer);
106       shutdown();
107       return 0;
108     }
109
110     free(buffer);
111
112     if (!setLengthSeconds())
113     {
114       logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
115       shutdown();
116       return 0;
117     }
118   }
119
120   initted = true;
121   return 1;
122 }
123
124 bool PlayerRadio::setLengthSeconds()
125 {
126   int success;
127   ULLONG endPTS = 0;
128   UINT thisRead = 0;
129   UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
130   if (!buffer)
131   {
132     logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
133     return false;
134   }
135
136   success = demuxer->findPTS(buffer, thisRead, &endPTS);
137   if (!success)
138   {
139     logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
140     free(buffer);
141     return false;
142   }
143
144   if (startPTS < endPTS)
145   {
146     lengthSeconds = (endPTS - startPTS) / 90000;
147   }
148   else
149   {
150     lengthSeconds = (startPTS - endPTS) / 90000;
151   }
152
153   return true;
154 }
155
156 int PlayerRadio::shutdown()
157 {
158   if (!initted) return 0;
159   switchState(S_STOP);
160   initted = false;
161
162   delete demuxer;
163   demuxer = NULL;
164
165 #ifdef WIN32
166   CloseHandle(mutex);
167 #endif
168
169   return 1;
170 }
171
172
173 void PlayerRadio::setStartBytes(ULLONG startBytes)
174 {
175   streamPos = startBytes;
176 }
177
178 ULONG PlayerRadio::getLengthSeconds()
179 {
180   return lengthSeconds;
181 }
182
183 ULONG PlayerRadio::getCurrentSeconds()
184 {
185   if (startup) return 0;
186
187   long long currentPTS = demuxer->getAudioPTS();
188   currentPTS -= startPTS;
189   if (currentPTS < 0) currentPTS += 8589934592ULL;
190   ULONG ret = currentPTS / 90000;
191   return ret;
192 }
193
194 // ----------------------------------- Externally called events
195
196 void PlayerRadio::play(ULONG Apid)
197 {
198   // TS mode
199   DemuxerTS* dts = (DemuxerTS*)demuxer;
200   dts->setAID(Apid);
201   play();
202 }
203
204 void PlayerRadio::play()
205 {
206   if (!initted) return;
207   if (state == S_PLAY) return;
208   lock();
209   switchState(S_PLAY);
210   unLock();
211 }
212
213 void PlayerRadio::stop()
214 {
215   if (!initted) return;
216   if (state == S_STOP) return;
217   lock();
218   logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
219   switchState(S_STOP);
220   unLock();
221 }
222
223 void PlayerRadio::pause()
224 {
225   if (!initted) return;
226   lock();
227
228   if (state == S_PAUSE_P)
229   {
230     switchState(S_PLAY);
231   }
232   else
233   {
234     switchState(S_PAUSE_P);
235   }
236
237   unLock();
238 }
239
240 void PlayerRadio::jumpToPercent(int percent)
241 {
242   lock();
243   logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
244   ULONG newPacket = percent * lengthPackets / 100;
245   switchState(S_JUMP, newPacket);
246   unLock();
247 }
248
249 void PlayerRadio::skipForward(int seconds)
250 {
251   lock();
252   logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
253   ULONG currentSeconds = getCurrentSeconds();
254   ULONG currentPacket = demuxer->getPacketNum();
255
256   if (currentSeconds == 0) { unLock(); return; } // div by zero
257   if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
258
259   ULONG newPacket = demuxer->getPacketNum() / currentSeconds * (currentSeconds + seconds);
260   if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
261   else switchState(S_JUMP, newPacket);
262   unLock();
263 }
264
265 void PlayerRadio::skipBackward(int seconds) // FIXME why are these signed?!
266 {
267   lock();
268   logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
269
270   ULONG currentSeconds = getCurrentSeconds();
271   ULONG currentPacket = demuxer->getPacketNum();
272
273   if (currentSeconds == 0) { unLock(); return; } // div by zero
274   if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
275
276   ULONG newPacket;
277   if ((UINT)seconds > currentSeconds)
278     newPacket = 0;
279   else
280     newPacket = demuxer->getPacketNum() / currentSeconds * (currentSeconds - seconds);
281
282   switchState(S_JUMP, newPacket);
283   unLock();
284 }
285
286 // ----------------------------------- Implementations called events
287
288 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
289 {
290   if (!initted) return;
291
292   logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
293
294   switch(state) // current state selector
295   {
296     case S_PLAY: // from S_PLAY -----------------------------------
297     {
298       switch(toState)
299       {
300         case S_PLAY: // to S_PLAY
301         {
302           return;
303         }
304         case S_PAUSE_P: // to S_PAUSE_P
305         {
306           audio->pause();
307           state = S_PAUSE_P;
308           return;
309         }
310         case S_STOP: // to S_STOP
311         {
312           afeed.stop();
313           threadStop();
314           audio->stop();
315           audio->unPause();
316           demuxer->reset();
317           state = S_STOP;
318           return;
319         }
320         case S_JUMP: // to S_JUMP
321         {
322           restartAtPacket(jumpPacket);
323           return;
324         }
325       }
326     }
327     case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
328     {
329       switch(toState)
330       {
331         case S_PLAY: // to S_PLAY
332         {
333           audio->unPause();
334           state = S_PLAY;
335           return;
336         }
337         case S_PAUSE_P: // to S_PAUSE_P
338         {
339           return;
340         }
341         case S_STOP: // to S_STOP
342         {
343           afeed.stop();
344           threadStop();
345           audio->stop();
346           audio->unPause();
347           demuxer->reset();
348           audio->systemMuteOff();
349           state = S_STOP;
350           return;
351         }
352         case S_JUMP: // to S_JUMP
353         {
354           state = S_PLAY;
355           audio->unPause();
356           restartAtPacket(jumpPacket);
357           return;
358         }
359       }
360     }
361     case S_STOP: // from S_STOP -----------------------------------
362     {
363       switch(toState)
364       {
365         case S_PLAY: // to S_PLAY
366         {
367           startup = true;
368
369           audio->reset();
370           audio->systemMuteOff();
371           demuxer->reset();
372           if (isRecording)
373           {
374             // FIXME use restartAtPacket here?
375             if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
376             demuxer->setPacketNum(currentPacketNumber);
377           }
378
379           state = S_PLAY;
380           threadStart();
381
382           if (isRecording)
383           {
384             logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
385             afeed.start();
386             audio->play();
387           }
388           else // do prebuffering
389           {
390             logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
391             preBuffering = true;
392           }
393           return;
394         }
395         case S_PAUSE_P: // to S_PAUSE_P
396         {
397           return;
398         }
399         case S_STOP: // to S_STOP
400         {
401           return;
402         }
403         case S_JUMP: // to S_JUMP
404         {
405           return;
406         }
407       }
408     }
409     // case S_JUMP cannot be selected as a start state because it auto flips to play
410   }
411 }
412
413 // ----------------------------------- Internal functions
414
415 void PlayerRadio::lock()
416 {
417 #ifndef WIN32
418   pthread_mutex_lock(&mutex);
419   logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
420
421 #else
422    WaitForSingleObject(mutex, INFINITE);
423 #endif
424 }
425
426 void PlayerRadio::unLock()
427 {
428 #ifndef WIN32
429   logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
430   pthread_mutex_unlock(&mutex);
431 #else
432    ReleaseMutex(mutex);
433 #endif
434 }
435
436 void PlayerRadio::restartAtPacket(ULONG newPacket)
437 {
438   afeed.stop();
439   threadStop();
440   audio->reset();
441   demuxer->flush();
442   currentPacketNumber = newPacket;
443   demuxer->setPacketNum(newPacket);
444   afeed.start();
445   threadStart();
446   audio->play();
447   audio->systemMuteOff();
448   audio->doMuting();
449 }
450
451 void PlayerRadio::doConnectionLost()
452 {
453   logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
454   Message* m = new Message();
455   m->to = messageReceiver;
456   m->from = this;
457   m->message = Message::PLAYER_EVENT;
458   m->parameter = PlayerRadio::CONNECTION_LOST;
459   messageQueue->postMessage(m);
460 }
461
462 // ----------------------------------- Callback
463
464 void PlayerRadio::call(void* caller)
465 {
466   threadSignalNoLock();
467 }
468
469 // ----------------------------------- Feed thread
470
471 void PlayerRadio::threadMethod()
472 {
473   if (isRecording)
474   {
475     if (state == S_PLAY) threadFeedPlay();
476   }
477   else
478   {
479     threadFeedLive();
480   }
481 }
482
483 void PlayerRadio::threadFeedLive()
484 {
485   UINT thisRead;
486   UINT writeLength;
487   UINT thisWrite;
488   UINT preBufferTotal = 0;
489
490   UINT askFor;
491   while(1)
492   {
493     thisRead = 0;
494     writeLength = 0;
495     thisWrite = 0;
496
497     threadCheckExit();
498
499     if (startup)
500       askFor = startupBlockSize; // find audio streams sized block
501     else
502       askFor = blockSize; // normal
503
504     threadBuffer = vdr->getBlock(0, askFor, &thisRead);
505
506     if (!vdr->isConnected())
507     {
508       doConnectionLost();
509       return;
510     }
511
512     if (!threadBuffer) break;
513
514     if (startup)
515     {
516       int a_stream = demuxer->scan(threadBuffer, thisRead);
517       demuxer->setAudioStream(a_stream);
518       logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
519       startup = false;
520     }
521
522     if (preBuffering)
523     {
524       preBufferTotal += thisRead;
525       if (preBufferTotal >= preBufferSize)
526       {
527         logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
528
529         preBuffering = false;
530         preBufferTotal = 0;
531
532         audio->play();
533         afeed.start();
534 //        unLock(); // thread will be locked by play until here
535         // FIXME - see if this can segfault because it is starting threads out of the master mutex
536       }
537     }
538
539     threadCheckExit();
540
541     while(writeLength < thisRead)
542     {
543       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
544       writeLength += thisWrite;
545
546       if (!thisWrite)
547       {
548         // demuxer is full and can't take anymore
549         threadLock();
550         threadWaitForSignal();
551         threadUnlock();
552       }
553
554       threadCheckExit();
555     }
556
557     free(threadBuffer);
558     threadBuffer = NULL;
559
560   }
561
562   logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
563
564   threadCheckExit();
565
566   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
567   m->to = messageReceiver;
568   m->from = this;
569   m->message = Message::PLAYER_EVENT;
570   m->parameter = PlayerRadio::STREAM_END;
571   logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
572   messageQueue->postMessage(m);
573   logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
574 }
575
576 void PlayerRadio::threadFeedPlay()
577 {
578   ULLONG feedPosition;
579   UINT thisRead, writeLength, thisWrite, askFor;
580   time_t lastRescan = time(NULL);
581
582   feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
583   if (!vdr->isConnected()) { doConnectionLost(); return; }
584   logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
585
586
587   while(1)
588   {
589     thisRead = 0;
590     writeLength = 0;
591     thisWrite = 0;
592
593     threadCheckExit();
594
595     // If we havn't rescanned for a while..
596     if ((lastRescan + 60) < time(NULL))
597     {
598       lengthBytes = vdr->rescanRecording(&lengthPackets);
599       if (!vdr->isConnected()) { doConnectionLost(); return; }
600       logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
601       lastRescan = time(NULL);
602
603       if (!setLengthSeconds())
604       {
605         logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
606         return;
607       }
608     }
609
610     if (feedPosition >= lengthBytes) break;  // finished playback
611
612     if (startup)
613     {
614       if (startupBlockSize > lengthBytes)
615         askFor = lengthBytes; // is a very small recording!
616       else
617         askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
618     }
619     else
620     {
621       if ((feedPosition + blockSize) > lengthBytes) // last block of recording
622         askFor = lengthBytes - feedPosition;
623       else // normal
624         askFor = blockSize;
625     }
626
627     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
628     feedPosition += thisRead;
629
630     if (!vdr->isConnected())
631     {
632       doConnectionLost();
633       return;
634     }
635
636     if (!threadBuffer) break;
637
638     if (startup)
639     {
640       int a_stream = demuxer->scan(threadBuffer, thisRead);
641       demuxer->setAudioStream(a_stream);
642       logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
643       startup = false;
644     }
645
646     threadCheckExit();
647
648     while(writeLength < thisRead)
649     {
650       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
651       writeLength += thisWrite;
652
653       if (!thisWrite)
654       {
655         // demuxer is full and can't take anymore
656         threadLock();
657         threadWaitForSignal();
658         threadUnlock();
659       }
660
661       threadCheckExit();
662     }
663
664     free(threadBuffer);
665     threadBuffer = NULL;
666
667   }
668
669   // end of recording
670   logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
671
672   threadCheckExit();
673
674
675   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
676   m->to = messageReceiver;
677   m->from = this;
678   m->message = Message::PLAYER_EVENT;
679   m->parameter = PlayerRadio::STOP_PLAYBACK;
680   logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
681   messageQueue->postMessage(m);
682 }
683
684 void PlayerRadio::threadPostStopCleanup()
685 {
686   if (threadBuffer)
687   {
688     free(threadBuffer);
689     threadBuffer = NULL;
690   }
691 }