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