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