]> git.vomp.tv Git - vompclient.git/blob - playerradio.cc
Media Player From Andreas Vogel
[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., 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 = 40000;
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   free(buffer);
138   if (!success)
139   {
140     logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
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(double percent)
241 {
242   lock();
243   logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
244   ULONG newPacket = (ULONG)(percent * lengthPackets / 100);
245   switchState(S_JUMP, newPacket);
246   unLock();
247 }
248
249 void PlayerRadio::skipForward(UINT 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 = currentPacket + (currentPacket * seconds / currentSeconds);
260   if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
261   else switchState(S_JUMP, newPacket);
262   unLock();
263 }
264
265 void PlayerRadio::skipBackward(UINT seconds)
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 = currentPacket - (currentPacket * seconds / currentSeconds);
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->setStreamType(Audio::MPEG2_PES);
371           audio->systemMuteOff();
372           demuxer->reset();
373           if (isRecording)
374           {
375             // FIXME use restartAtPacket here?
376             if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
377             demuxer->setPacketNum(currentPacketNumber);
378           }
379
380           state = S_PLAY;
381           threadStart();
382
383           if (isRecording)
384           {
385             logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
386             afeed.start();
387             audio->play();
388           }
389           else // do prebuffering
390           {
391             logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
392             preBuffering = true;
393           }
394           return;
395         }
396         case S_PAUSE_P: // to S_PAUSE_P
397         {
398           return;
399         }
400         case S_STOP: // to S_STOP
401         {
402           return;
403         }
404         case S_JUMP: // to S_JUMP
405         {
406           return;
407         }
408       }
409     }
410     // case S_JUMP cannot be selected as a start state because it auto flips to play
411   }
412 }
413
414 // ----------------------------------- Internal functions
415
416 void PlayerRadio::lock()
417 {
418 #ifndef WIN32
419   pthread_mutex_lock(&mutex);
420   logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
421
422 #else
423    WaitForSingleObject(mutex, INFINITE);
424 #endif
425 }
426
427 void PlayerRadio::unLock()
428 {
429 #ifndef WIN32
430   logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
431   pthread_mutex_unlock(&mutex);
432 #else
433    ReleaseMutex(mutex);
434 #endif
435 }
436
437 void PlayerRadio::restartAtPacket(ULONG newPacket)
438 {
439   afeed.stop();
440   threadStop();
441   audio->reset();
442         audio->setStreamType(Audio::MPEG2_PES);
443   demuxer->flush();
444   currentPacketNumber = newPacket;
445   demuxer->setPacketNum(newPacket);
446   afeed.start();
447   threadStart();
448   audio->play();
449   audio->systemMuteOff();
450   audio->doMuting();
451 }
452
453 void PlayerRadio::doConnectionLost()
454 {
455   logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
456   Message* m = new Message();
457   m->to = messageReceiver;
458   m->from = this;
459   m->message = Message::PLAYER_EVENT;
460   m->parameter = PlayerRadio::CONNECTION_LOST;
461   messageQueue->postMessage(m);
462 }
463
464 // ----------------------------------- Callback
465
466 void PlayerRadio::call(void* caller)
467 {
468   threadSignalNoLock();
469 }
470
471 // ----------------------------------- Feed thread
472
473 void PlayerRadio::threadMethod()
474 {
475   if (isRecording)
476   {
477     if (state == S_PLAY) threadFeedPlay();
478   }
479   else
480   {
481     threadFeedLive();
482   }
483 }
484
485 void PlayerRadio::threadFeedLive()
486 {
487   UINT thisRead;
488   UINT writeLength;
489   UINT thisWrite;
490   UINT preBufferTotal = 0;
491
492   UINT askFor;
493   while(1)
494   {
495     thisRead = 0;
496     writeLength = 0;
497     thisWrite = 0;
498
499     threadCheckExit();
500
501     if (startup)
502       askFor = startupBlockSize; // find audio streams sized block
503     else
504       askFor = blockSize; // normal
505
506     threadBuffer = vdr->getBlock(0, askFor, &thisRead);
507
508     if (!vdr->isConnected())
509     {
510       doConnectionLost();
511       return;
512     }
513
514     if (!threadBuffer) break;
515
516     if (startup)
517     {
518       int a_stream = demuxer->scan(threadBuffer, thisRead);
519       demuxer->setAudioStream(a_stream);
520       logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
521       startup = false;
522     }
523
524     if (preBuffering)
525     {
526       preBufferTotal += thisRead;
527       if (preBufferTotal >= preBufferSize)
528       {
529         logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
530
531         preBuffering = false;
532         preBufferTotal = 0;
533
534         audio->play();
535         afeed.start();
536 //        unLock(); // thread will be locked by play until here
537         // FIXME - see if this can segfault because it is starting threads out of the master mutex
538       }
539     }
540
541     threadCheckExit();
542
543     while(writeLength < thisRead)
544     {
545       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
546       writeLength += thisWrite;
547
548       if (!thisWrite)
549       {
550         // demuxer is full and can't take anymore
551         threadLock();
552         threadWaitForSignal();
553         threadUnlock();
554       }
555
556       threadCheckExit();
557     }
558
559     free(threadBuffer);
560     threadBuffer = NULL;
561
562   }
563
564   logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
565
566   threadCheckExit();
567
568   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
569   m->to = messageReceiver;
570   m->from = this;
571   m->message = Message::PLAYER_EVENT;
572   m->parameter = PlayerRadio::STREAM_END;
573   logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
574   messageQueue->postMessage(m);
575   logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
576 }
577
578 void PlayerRadio::threadFeedPlay()
579 {
580   ULLONG feedPosition;
581   UINT thisRead, writeLength, thisWrite, askFor;
582   time_t lastRescan = time(NULL);
583
584   feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
585   if (!vdr->isConnected()) { doConnectionLost(); return; }
586   logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
587
588
589   while(1)
590   {
591     thisRead = 0;
592     writeLength = 0;
593     thisWrite = 0;
594
595     threadCheckExit();
596
597     // If we havn't rescanned for a while..
598     if ((lastRescan + 60) < time(NULL))
599     {
600       lengthBytes = vdr->rescanRecording(&lengthPackets);
601       if (!vdr->isConnected()) { doConnectionLost(); return; }
602       logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
603       lastRescan = time(NULL);
604
605       if (!setLengthSeconds())
606       {
607         logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
608         return;
609       }
610     }
611
612     if (feedPosition >= lengthBytes) break;  // finished playback
613
614     if (startup)
615     {
616       if (startupBlockSize > lengthBytes)
617         askFor = lengthBytes; // is a very small recording!
618       else
619         askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
620     }
621     else
622     {
623       if ((feedPosition + blockSize) > lengthBytes) // last block of recording
624         askFor = lengthBytes - feedPosition;
625       else // normal
626         askFor = blockSize;
627     }
628
629     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
630     feedPosition += thisRead;
631
632     if (!vdr->isConnected())
633     {
634       doConnectionLost();
635       return;
636     }
637
638     if (!threadBuffer) break;
639
640     if (startup)
641     {
642       int a_stream = demuxer->scan(threadBuffer, thisRead);
643       demuxer->setAudioStream(a_stream);
644       logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
645       startup = false;
646     }
647
648     threadCheckExit();
649
650     while(writeLength < thisRead)
651     {
652       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
653       writeLength += thisWrite;
654
655       if (!thisWrite)
656       {
657         // demuxer is full and can't take anymore
658         threadLock();
659         threadWaitForSignal();
660         threadUnlock();
661       }
662
663       threadCheckExit();
664     }
665
666     free(threadBuffer);
667     threadBuffer = NULL;
668
669   }
670
671   // end of recording
672   logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
673
674   threadCheckExit();
675
676
677   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
678   m->to = messageReceiver;
679   m->from = this;
680   m->message = Message::PLAYER_EVENT;
681   m->parameter = PlayerRadio::STOP_PLAYBACK;
682   logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
683   messageQueue->postMessage(m);
684 }
685
686 void PlayerRadio::threadPostStopCleanup()
687 {
688   if (threadBuffer)
689   {
690     free(threadBuffer);
691     threadBuffer = NULL;
692   }
693 }