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