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