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