]> git.vomp.tv Git - vompclient-marten.git/blob - playerliveradio.cc
Live radio half way there
[vompclient-marten.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
32 // ----------------------------------- Called from outside, one offs or info funcs
33
34 PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
35 : afeed(this)
36 {
37   messageQueue = tmessageQueue;
38   messageReceiver = tmessageReceiver;
39   chanList = tchanList;
40   
41   audio = Audio::getInstance();
42   logger = Log::getInstance();
43   vdr = VDR::getInstance();
44   initted = false;
45
46   stopNow = false;
47   state = S_STOP;
48 }
49
50 PlayerLiveRadio::~PlayerLiveRadio()
51 {
52   if (initted) shutdown();
53 }
54
55 int PlayerLiveRadio::init()
56 {
57   if (initted) return 0;
58
59   demuxer = new DemuxerTS();
60   if (!demuxer) return 0;
61  
62   if (!demuxer->init(this, audio, NULL, 0, 40000))
63   {
64     logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");
65     shutdown();
66     return 0;
67   }
68
69   afeed.init();
70   audio->stop();
71
72   initted = true;
73   return 1;
74 }
75
76 int PlayerLiveRadio::shutdown()
77 {
78   if (!initted) return 0;
79   stop();
80   initted = false;
81
82   delete demuxer;
83
84 #ifdef WIN32
85   CloseHandle(mutex);
86 #endif
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 void PlayerLiveRadio::setAudioChannel(int newChannel)
107 {
108   return demuxer->setAID(newChannel);
109 }
110
111 // ----------------------------------- Externally called events
112
113 void PlayerLiveRadio::go(ULONG index)
114 {
115   struct PLInstruction i;
116   i.instruction = I_SETCHANNEL;
117   i.channelIndex = index;
118   instructions.push(i);
119   threadStart();
120 }
121
122 void PlayerLiveRadio::setChannel(ULONG index)
123 {
124   logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");
125   struct PLInstruction i;
126   i.instruction = I_SETCHANNEL;
127   i.channelIndex = index;
128   instructions.push(i);  
129   threadSignalNoLock();
130 }
131
132 void PlayerLiveRadio::stop()
133 {
134   logger->log("PlayerLiveRadio", Log::DEBUG, "stop");
135   struct PLInstruction i;
136   i.instruction = I_STOP;
137   instructions.push(i);
138   threadSignal();
139   threadStop();
140 }
141
142 // ----------------------------------- Callback
143
144 void PlayerLiveRadio::call(void* caller)
145 {
146 }
147
148 // -----------------------------------
149
150 void PlayerLiveRadio::streamReceive(void* data, ULONG len)
151 {
152   if (streamChunks.size() < 11)
153   {
154     StreamChunk s;
155     s.data = data;
156     s.len = len;
157     streamChunks.push(s);
158     threadSignalNoLock();
159   }
160   else
161   {
162     // Too many chunks in streamChunks, drop this chunk
163     free(data);
164     logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");
165   }
166 }
167
168 void PlayerLiveRadio::clearStreamChunks()
169 {
170   while(streamChunks.size())
171   {
172     logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");
173     struct StreamChunk s = streamChunks.front();
174     streamChunks.pop();
175     free(s.data);
176   }
177 }
178
179 void PlayerLiveRadio::chunkToDemuxer()
180 {
181   StreamChunk s = streamChunks.front();
182   streamChunks.pop();
183   logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
184   int a = demuxer->put((UCHAR*)s.data, s.len);
185   logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);
186   free(s.data);  
187 }
188
189 void PlayerLiveRadio::switchState(UCHAR newState)
190 {
191   logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);
192
193   switch(state)
194   {
195     case S_STOP:   // FROM S_STOP
196     {
197       switch(newState)
198       {
199         case S_PREBUFFERING:
200         {
201           audio->stop();
202           audio->unPause();
203           audio->reset();
204           audio->setStreamType(Audio::MPEG2_PES);
205           audio->systemMuteOff();      
206           audio->doMuting();              
207 //          audio->sync();
208           audio->play();
209           audio->pause();
210
211           demuxer->reset();
212 //          demuxer->seek();
213
214           afeed.start();
215           
216           state = newState;
217           preBufferCount = 0;
218           return;
219         }
220         default:
221         {
222           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
223           abort();
224           break;
225         }
226       }
227     }
228
229     case S_PREBUFFERING:    // FROM S_PREBUFFERING
230     {
231       switch(newState)
232       {
233         case S_PLAY:
234         {
235           audio->unPause();
236           state = newState;
237           return;
238         }
239         case S_STOP:
240         {
241           vdr->stopStreaming();
242           clearStreamChunks();
243           afeed.stop();
244           audio->stop();
245           audio->reset();
246           state = newState;
247           return;        
248         }
249         default:
250         {
251           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
252           abort();
253           break;
254         }        
255       }
256     }
257     
258     case S_PLAY:     // FROM S_PLAY
259     {
260       switch(newState)
261       {
262         case S_STOP:
263         { 
264           vdr->stopStreaming();
265           clearStreamChunks();
266           afeed.stop();
267           audio->stop();
268           audio->reset();
269           state = newState;
270           return;
271         }
272         case S_PREBUFFERING: // IS THIS HOW IT WORKS?
273         {
274         /*
275           vdr->stopStreaming();
276           clearStreamChunks();
277           vfeed.stop();
278           afeed.stop();
279           video->stop();
280           video->blank();
281           audio->stop();
282           audio->unPause();
283           audio->reset();
284
285           video->reset();
286           video->sync();
287           video->play();
288           video->pause();
289
290           audio->setStreamType(Audio::MPEG2_PES);
291           audio->sync();
292           audio->play();
293           audio->pause();
294
295           demuxer->reset();
296           demuxer->seek();
297
298           afeed.start();
299           vfeed.start();
300
301           state = newState;
302                     preBufferCount = 0;
303           return;
304           */
305         }
306         default:
307         {
308           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
309           abort();
310           break;
311         }        
312       }
313     }    
314   }  
315 }
316
317 void PlayerLiveRadio::optimizeInstructionQueue()
318 {
319   // Walk the list
320   
321   // Currently there are only 2 instruction types, so this is a bit overkill...
322
323   struct PLInstruction i;
324   while(instructions.size() > 1)
325   {
326     i = instructions.front();
327     if (i.instruction == I_SETCHANNEL)
328     {
329       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant
330     }
331     else if (i.instruction == I_STOP)
332     {
333       return; // return here and ensure the next instruction will be stop
334     }
335   }
336 }
337
338 void PlayerLiveRadio::threadMethod()
339 {
340   while(1)
341   {
342     while(!instructions.empty())
343     {
344       if (instructions.size() > 1) optimizeInstructionQueue();
345
346       struct PLInstruction i = instructions.front();
347       instructions.pop();
348     
349       if (i.instruction == I_SETCHANNEL)
350       {
351         logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");
352
353         switchState(S_PREBUFFERING);
354
355         Channel* chan = (*chanList)[i.channelIndex];
356         chan->loadPids();
357
358         if (chan->numAPids > 0) 
359         {
360           demuxer->setAID(chan->apids[0].pid);
361           logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
362         }
363         else 
364         {
365           logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
366         }
367
368         vdr->streamChannel(chan->number, this);
369       }
370       else if (i.instruction == I_STOP)
371       {
372         logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
373         switchState(S_STOP);
374
375         stopNow = true;
376         break;
377       }
378     }
379
380     if (stopNow) break;
381
382     while(streamChunks.size())
383     {
384       chunkToDemuxer();
385
386       if (state == S_PREBUFFERING)
387       {        
388         if (++preBufferCount == preBufferAmount)
389         {
390           switchState(S_PLAY);
391         }
392       }
393     }
394     
395     threadLock();
396     threadWaitForSignal(); // unlocks and waits for signal
397     threadUnlock();
398   }
399
400   logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");
401 }
402