]> git.vomp.tv Git - vompclient.git/blob - playerliveradio.cc
Rewritten vomp discovery protocol
[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)
115 {
116   return demuxer->setAID(newChannel, type);
117 }
118
119 void PlayerLiveRadio::setSubtitleChannel(int newChannel)
120 {
121   return 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           if (chan->numAPids > 0) 
408           {
409             demuxer->setAID(chan->apids[0].pid,0);
410             logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
411           }
412           else 
413           {
414             logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
415           }
416
417           int streamSuccess = vdr->streamChannel(chan->number, this);
418           if (!checkError() && !streamSuccess)
419           {      
420             Message* m = new Message();
421             m->from = this;
422             m->to = messageReceiver;
423             m->message = Message::PLAYER_EVENT;
424             m->parameter = PlayerLiveRadio::STREAM_END;
425             messageQueue->postMessageFromOuterSpace(m);
426           }
427         }
428       }
429       else if (i.instruction == I_STOP)
430       {
431         logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
432         switchState(S_STOP);
433         checkError();
434
435         stopNow = true;
436         break;
437       }
438     }
439
440     if (stopNow) break;
441
442     while(streamChunks.size())
443     {
444       chunkToDemuxer();
445
446       if (state == S_PREBUFFERING)
447       {
448         ++preBufferCount;
449         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
450         logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);
451         
452         Message* m = new Message();
453         m->from = this;
454         m->to = messageReceiver;
455         m->message = Message::PLAYER_EVENT;
456         m->parameter = PlayerLiveRadio::PREBUFFERING;
457         m->tag = percentDone;
458         messageQueue->postMessageFromOuterSpace(m);
459
460         if (preBufferCount == preBufferAmount)
461         {
462           switchState(S_PLAY);
463           checkError();
464         }
465       }
466     }
467     
468     threadLock();
469     threadWaitForSignal(); // unlocks and waits for signal
470     threadUnlock();
471   }
472
473   logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");
474 }
475