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