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