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