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