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