]> git.vomp.tv Git - vompclient.git/blob - playerliveradio.cc
Live radio prebuffering display - code upgrades for connection lost handling
[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, 0, 200000))
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 void PlayerLiveRadio::setAudioChannel(int newChannel, int type)
107 {
108   return demuxer->setAID(newChannel, type);
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   logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());
130   threadSignalNoLock();
131 }
132
133 void PlayerLiveRadio::stop()
134 {
135   logger->log("PlayerLiveRadio", Log::DEBUG, "stop");
136   struct PLInstruction i;
137   i.instruction = I_STOP;
138   instructions.push(i);
139   threadSignal();
140   threadStop();
141 }
142
143 // ----------------------------------- Callback
144
145 void PlayerLiveRadio::call(void* caller)
146 {
147 }
148
149 // -----------------------------------
150
151 void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)
152 {
153   // Flag:
154   // 0 = normal stream packet
155   // 1 = stream end
156
157   if (flag == 1)
158   {
159     if (data) abort();
160     
161     Message* m = new Message();
162     m->from = this;
163     m->to = messageReceiver;
164     m->message = Message::PLAYER_EVENT;
165     m->parameter = PlayerLiveRadio::STREAM_END;
166     messageQueue->postMessageFromOuterSpace(m);
167   }
168   
169   if (streamChunks.size() < 11)
170   {
171     StreamChunk s;
172     s.data = data;
173     s.len = len;
174     streamChunks.push(s);
175     threadSignalNoLock();
176   }
177   else
178   {
179     // Too many chunks in streamChunks, drop this chunk
180     free(data);
181     logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");
182   }
183 }
184
185 void PlayerLiveRadio::clearStreamChunks()
186 {
187   while(streamChunks.size())
188   {
189     logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");
190     struct StreamChunk s = streamChunks.front();
191     streamChunks.pop();
192     free(s.data);
193   }
194 }
195
196 void PlayerLiveRadio::chunkToDemuxer()
197 {
198   StreamChunk s = streamChunks.front();
199   streamChunks.pop();
200   //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
201   /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);
202   //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);
203   free(s.data);  
204 }
205
206 void PlayerLiveRadio::switchState(UCHAR newState)
207 {
208   logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);
209
210   switch(state)
211   {
212     case S_STOP:   // FROM S_STOP
213     {
214       switch(newState)
215       {
216         case S_PREBUFFERING:
217         {
218           audio->stop();
219           audio->unPause();
220           audio->reset();
221           audio->setStreamType(Audio::MPEG2_PES);
222           audio->systemMuteOff();      
223           audio->doMuting();              
224           audio->play();
225           audio->pause();
226           demuxer->reset();
227           afeed.start();
228           
229           state = newState;
230           preBufferCount = 0;
231           return;
232         }
233         default:
234         {
235           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
236           abort();
237           break;
238         }
239       }
240     }
241
242     case S_PREBUFFERING:    // FROM S_PREBUFFERING
243     {
244       switch(newState)
245       {
246         case S_PLAY:
247         {
248           audio->unPause();
249           state = newState;
250           return;
251         }
252         case S_STOP:
253         {
254           vdr->stopStreaming();
255           clearStreamChunks();
256           afeed.stop();
257           audio->stop();
258           audio->reset();
259           state = newState;
260           return;        
261         }
262         case S_PREBUFFERING:
263         {
264           vdr->stopStreaming();
265           clearStreamChunks();
266           afeed.stop();
267           audio->stop();
268           audio->reset();
269           audio->play();
270           audio->pause();
271           demuxer->reset();
272           afeed.start();
273
274           state = newState;
275           preBufferCount = 0;
276           return;        
277         }
278         default:
279         {
280           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
281           abort();
282           break;
283         }        
284       }
285     }
286     
287     case S_PLAY:     // FROM S_PLAY
288     {
289       switch(newState)
290       {
291         case S_STOP:
292         { 
293           vdr->stopStreaming();
294           clearStreamChunks();
295           afeed.stop();
296           audio->stop();
297           audio->reset();
298           state = newState;
299           return;
300         }
301         case S_PREBUFFERING: // IS THIS HOW IT WORKS?
302         {
303           vdr->stopStreaming();
304           clearStreamChunks();
305           afeed.stop();
306           audio->stop();
307           audio->reset();
308           audio->play();
309           audio->pause();
310           demuxer->reset();
311           afeed.start();
312
313           state = newState;
314           preBufferCount = 0;
315           return;
316         }
317         default:
318         {
319           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
320           abort();
321           break;
322         }        
323       }
324     }    
325   }  
326 }
327
328 bool PlayerLiveRadio::checkError()
329 {
330   if (!vdr->isConnected())
331   {
332     switchState(S_STOP);
333     
334     Message* m = new Message();
335     m->from = this;
336     m->to = messageReceiver;
337     m->message = Message::PLAYER_EVENT;
338     m->parameter = PlayerLiveRadio::CONNECTION_LOST;
339     messageQueue->postMessageFromOuterSpace(m);
340     
341     return true;
342   }   
343   return false;
344 }
345
346 void PlayerLiveRadio::optimizeInstructionQueue()
347 {
348   // Walk the list
349   
350   // Currently there are only 2 instruction types, so this is a bit overkill...
351
352   struct PLInstruction i;
353   while(instructions.size() > 1)
354   {
355     i = instructions.front();
356     if (i.instruction == I_SETCHANNEL)
357     {
358       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant
359     }
360     else if (i.instruction == I_STOP)
361     {
362       return; // return here and ensure the next instruction will be stop
363     }
364   }
365 }
366
367 void PlayerLiveRadio::threadMethod()
368 {
369   while(1)
370   {
371     while(!instructions.empty())
372     {
373       if (instructions.size() > 1)
374       {
375         logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");
376         optimizeInstructionQueue();
377       }
378
379       struct PLInstruction i = instructions.front();
380       instructions.pop();
381     
382       if (i.instruction == I_SETCHANNEL)
383       {
384         logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");
385
386         switchState(S_PREBUFFERING);
387
388         if (!checkError())
389         {
390           Channel* chan = (*chanList)[i.channelIndex];
391           chan->loadPids();
392
393           if (chan->numAPids > 0) 
394           {
395             demuxer->setAID(chan->apids[0].pid,0);
396             logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
397           }
398           else 
399           {
400             logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
401           }
402
403           int streamSuccess = vdr->streamChannel(chan->number, this);
404           if (!checkError() && !streamSuccess)
405           {      
406             Message* m = new Message();
407             m->from = this;
408             m->to = messageReceiver;
409             m->message = Message::PLAYER_EVENT;
410             m->parameter = PlayerLiveRadio::STREAM_END;
411             messageQueue->postMessageFromOuterSpace(m);
412           }
413         }
414       }
415       else if (i.instruction == I_STOP)
416       {
417         logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
418         switchState(S_STOP);
419         checkError();
420
421         stopNow = true;
422         break;
423       }
424     }
425
426     if (stopNow) break;
427
428     while(streamChunks.size())
429     {
430       chunkToDemuxer();
431
432       if (state == S_PREBUFFERING)
433       {
434         ++preBufferCount;
435         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
436         logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);
437         
438         Message* m = new Message();
439         m->from = this;
440         m->to = messageReceiver;
441         m->message = Message::PLAYER_EVENT;
442         m->parameter = PlayerLiveRadio::PREBUFFERING;
443         m->tag = percentDone;
444         messageQueue->postMessageFromOuterSpace(m);
445
446         if (preBufferCount == preBufferAmount)
447         {
448           switchState(S_PLAY);
449           checkError();
450         }
451       }
452     }
453     
454     threadLock();
455     threadWaitForSignal(); // unlocks and waits for signal
456     threadUnlock();
457   }
458
459   logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");
460 }
461