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