]> git.vomp.tv Git - vompclient.git/blob - playerliveradio.cc
Compile fix for MVP re: location change of hmsf
[vompclient.git] / playerliveradio.cc
1 /*
2     Copyright 2008 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 "playerliveradio.h"
22
23 #include "defines.h"
24 #include "log.h"
25 #include "audio.h"
26 #include "demuxerts.h"
27 #include "vdr.h"
28 #include "messagequeue.h"
29 #include "remote.h"
30 #include "message.h"
31 #include "channel.h"
32 #include "video.h"
33
34 // ----------------------------------- Called from outside, one offs or info funcs
35
36 PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
37 : afeed(this)
38 {
39   messageQueue = tmessageQueue;
40   messageReceiver = tmessageReceiver;
41   chanList = tchanList;
42   
43   audio = Audio::getInstance();
44   logger = Log::getInstance();
45   vdr = VDR::getInstance();
46   initted = false;
47
48   stopNow = false;
49   state = S_STOP;
50   Video::getInstance()->turnVideoOff();
51 }
52
53 PlayerLiveRadio::~PlayerLiveRadio()
54 {
55   if (initted) shutdown();
56 }
57
58 int PlayerLiveRadio::init()
59 {
60   if (initted) return 0;
61
62   demuxer = new DemuxerTS();
63   if (!demuxer) return 0;
64  
65   if (!demuxer->init(this, audio, NULL, NULL, 0, 200000, 0))
66   {
67     logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");
68     shutdown();
69     return 0;
70   }
71
72   afeed.init();
73   audio->stop();
74
75   initted = true;
76   return 1;
77 }
78
79 int PlayerLiveRadio::shutdown()
80 {
81   if (!initted) return 0;
82   stop();
83   initted = false;
84   delete demuxer;
85   return 1;
86 }
87
88 bool* PlayerLiveRadio::getDemuxerMpegAudioChannels()
89 {
90   return demuxer->getmpAudioChannels();
91 }
92
93 bool* PlayerLiveRadio::getDemuxerAc3AudioChannels()
94 {
95   return demuxer->getac3AudioChannels();
96 }
97
98 int PlayerLiveRadio::getCurrentAudioChannel()
99 {
100   return demuxer->getAID();
101 }
102
103 int* PlayerLiveRadio::getTeletxtSubtitlePages()
104 {
105   return NULL;
106 }
107
108 int PlayerLiveRadio::getCurrentSubtitleChannel()
109 {
110   return demuxer->getSubID();
111 }
112
113 void PlayerLiveRadio::setAudioChannel(int newChannel, int type, int streamtype)
114 {
115   demuxer->setAID(newChannel, type, streamtype, true);
116 }
117
118 void PlayerLiveRadio::setSubtitleChannel(int newChannel)
119 {
120   demuxer->setSubID(newChannel);
121 }
122
123 // ----------------------------------- Externally called events
124
125 void PlayerLiveRadio::go(ULONG index)
126 {
127   struct PLInstruction i;
128   i.instruction = I_SETCHANNEL;
129   i.channelIndex = index;
130   instructions.push(i);
131   threadStart();
132 }
133
134 void PlayerLiveRadio::setChannel(ULONG index)
135 {
136   logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");
137   struct PLInstruction i;
138   i.instruction = I_SETCHANNEL;
139   i.channelIndex = index;
140   instructions.push(i);  
141   logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());
142   threadSignalNoLock();
143 }
144
145 void PlayerLiveRadio::stop()
146 {
147   logger->log("PlayerLiveRadio", Log::DEBUG, "stop");
148   struct PLInstruction i;
149   i.instruction = I_STOP;
150   instructions.push(i);
151   threadSignal();
152   threadStop();
153 }
154
155 // ----------------------------------- Callback
156
157 void PlayerLiveRadio::call(void* caller)
158 {
159 }
160
161 // -----------------------------------
162
163 void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)
164 {
165   // Flag:
166   // 0 = normal stream packet
167   // 1 = stream end
168   // 2 = connection lost
169
170   if (flag == 1)
171   {
172     if (data) abort();
173     
174     Message* m = new Message();
175     m->from = this;
176     m->to = messageReceiver;
177     m->message = Message::PLAYER_EVENT;
178     m->parameter.num = PlayerLiveRadio::STREAM_END;
179     messageQueue->postMessageFromOuterSpace(m);
180   }
181   
182   if (streamChunks.size() < 11)
183   {
184     StreamChunk s;
185     s.data = data;
186     s.len = len;
187     streamChunks.push(s);
188     threadSignalNoLock();
189   }
190   else
191   {
192     // Too many chunks in streamChunks, drop this chunk
193     free(data);
194     logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");
195   }
196 }
197
198 void PlayerLiveRadio::clearStreamChunks()
199 {
200   while(streamChunks.size())
201   {
202     logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");
203     struct StreamChunk s = streamChunks.front();
204     streamChunks.pop();
205     free(s.data);
206   }
207 }
208
209 void PlayerLiveRadio::chunkToDemuxer()
210 {
211   StreamChunk s = streamChunks.front();
212   streamChunks.pop();
213   //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
214   /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);
215   //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);
216   free(s.data);  
217 }
218
219 void PlayerLiveRadio::switchState(UCHAR newState)
220 {
221   logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);
222
223   switch(state)
224   {
225     case S_STOP:   // FROM S_STOP
226     {
227       switch(newState)
228       {
229         case S_PREBUFFERING:
230         {
231           audio->stop();
232           audio->unPause();
233           audio->reset();
234           audio->setStreamType(Audio::MPEG2_PES);
235           audio->systemMuteOff();      
236           audio->doMuting();              
237           audio->play();
238           audio->pause();
239           demuxer->reset();
240           afeed.start();
241           
242           state = newState;
243           preBufferCount = 0;
244           return;
245         }
246         default:
247         {
248           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
249           abort();
250           break;
251         }
252       }
253     }
254
255     case S_PREBUFFERING:    // FROM S_PREBUFFERING
256     {
257       switch(newState)
258       {
259         case S_PLAY:
260         {
261           audio->unPause();
262           state = newState;
263           return;
264         }
265         case S_STOP:
266         {
267           vdr->stopStreaming();
268           clearStreamChunks();
269           afeed.stop();
270           audio->stop();
271           audio->reset();
272           state = newState;
273           return;        
274         }
275         case S_PREBUFFERING:
276         {
277           vdr->stopStreaming();
278           clearStreamChunks();
279           afeed.stop();
280           audio->stop();
281           audio->reset();
282           audio->play();
283           audio->pause();
284           demuxer->reset();
285           afeed.start();
286
287           state = newState;
288           preBufferCount = 0;
289           return;        
290         }
291         default:
292         {
293           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
294           abort();
295           break;
296         }        
297       }
298     }
299     
300     case S_PLAY:     // FROM S_PLAY
301     {
302       switch(newState)
303       {
304         case S_STOP:
305         { 
306           vdr->stopStreaming();
307           clearStreamChunks();
308           afeed.stop();
309           audio->stop();
310           audio->reset();
311           state = newState;
312           return;
313         }
314         case S_PREBUFFERING: // IS THIS HOW IT WORKS?
315         {
316           vdr->stopStreaming();
317           clearStreamChunks();
318           afeed.stop();
319           audio->stop();
320           audio->reset();
321           audio->play();
322           audio->pause();
323           demuxer->reset();
324           afeed.start();
325
326           state = newState;
327           preBufferCount = 0;
328           return;
329         }
330         default:
331         {
332           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
333           abort();
334           break;
335         }        
336       }
337     }    
338   }  
339 }
340
341 bool PlayerLiveRadio::checkError()
342 {
343   if (!vdr->isConnected())
344   {
345     switchState(S_STOP);
346     
347     Message* m = new Message();
348     m->from = this;
349     m->to = messageReceiver;
350     m->message = Message::PLAYER_EVENT;
351     m->parameter.num = PlayerLiveRadio::CONNECTION_LOST;
352     messageQueue->postMessageFromOuterSpace(m);
353     
354     return true;
355   }   
356   return false;
357 }
358
359 void PlayerLiveRadio::optimizeInstructionQueue()
360 {
361   // Walk the list
362   
363   // Currently there are only 2 instruction types, so this is a bit overkill...
364
365   struct PLInstruction i;
366   while(instructions.size() > 1)
367   {
368     i = instructions.front();
369     if (i.instruction == I_SETCHANNEL)
370     {
371       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant
372     }
373     else if (i.instruction == I_STOP)
374     {
375       return; // return here and ensure the next instruction will be stop
376     }
377   }
378 }
379
380 void PlayerLiveRadio::threadMethod()
381 {
382   while(1)
383   {
384     while(!instructions.empty())
385     {
386       if (instructions.size() > 1)
387       {
388         logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");
389         optimizeInstructionQueue();
390       }
391
392       struct PLInstruction i = instructions.front();
393       instructions.pop();
394     
395       if (i.instruction == I_SETCHANNEL)
396       {
397         logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");
398
399         switchState(S_PREBUFFERING);
400
401         if (!checkError())
402         {
403           Channel* chan = (*chanList)[i.channelIndex];
404           chan->loadPids();
405
406           bool found=false;
407
408           if (chan->numAPids > 0) 
409           {
410             ULONG j = 0;
411             while (j < chan->numAPids && !found)
412             {
413               if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type))
414               {
415                 demuxer->setAID(chan->apids[j].pid, 0, chan->apids[j].type, true);
416                 audio->setStreamType(Audio::MPEG2_PES);
417                 logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid, chan->apids[j].type);
418                 found = true;
419               }
420               j++;
421             }
422           }
423
424           if (!found)
425           {
426             if (chan->numDPids > 0 && audio->maysupportAc3())
427             {
428               ULONG j = 0;
429               while (j < chan->numDPids && !found)
430               {
431                 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type))
432                 {
433                   demuxer->setAID(chan->dpids[j].pid, 1, chan->dpids[j].type, true);
434                   audio->setStreamType(Audio::MPEG2_PES);
435                   logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid, chan->dpids[j].type);
436                   found=true;
437                 }
438                 j++;
439               }
440             }
441             else
442             {
443               logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
444             }
445           }
446
447           int streamSuccess = vdr->streamChannel(chan->number, this);
448           if (!checkError() && !streamSuccess)
449           {      
450             Message* m = new Message();
451             m->from = this;
452             m->to = messageReceiver;
453             m->message = Message::PLAYER_EVENT;
454             m->parameter.num = PlayerLiveRadio::STREAM_END;
455             messageQueue->postMessageFromOuterSpace(m);
456           }
457         }
458       }
459       else if (i.instruction == I_STOP)
460       {
461         logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
462         switchState(S_STOP);
463         checkError();
464
465         stopNow = true;
466         break;
467       }
468     }
469
470     if (stopNow) break;
471
472     while(streamChunks.size())
473     {
474       chunkToDemuxer();
475
476       if (state == S_PREBUFFERING)
477       {
478         ++preBufferCount;
479         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
480         logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);
481         
482         Message* m = new Message();
483         m->from = this;
484         m->to = messageReceiver;
485         m->message = Message::PLAYER_EVENT;
486         m->parameter.num = PlayerLiveRadio::PREBUFFERING;
487         m->tag = percentDone;
488         messageQueue->postMessageFromOuterSpace(m);
489
490         if (preBufferCount == preBufferAmount)
491         {
492           switchState(S_PLAY);
493           checkError();
494         }
495       }
496     }
497     
498     threadLock();
499     threadWaitForSignal(); // unlocks and waits for signal
500     threadUnlock();
501   }
502
503   logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");
504 }