]> git.vomp.tv Git - vompclient-marten.git/blob - playerliveradio.cc
FFMPEG decoding in color at 7 fps
[vompclient-marten.git] / playerliveradio.cc
1 /*\r
2     Copyright 2008 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
19 */\r
20 \r
21 #include "playerliveradio.h"\r
22 \r
23 #include "log.h"\r
24 #include "audio.h"\r
25 #include "demuxerts.h"\r
26 #include "vdr.h"\r
27 #include "messagequeue.h"\r
28 #include "remote.h"\r
29 #include "message.h"\r
30 #include "channel.h"\r
31 #include "video.h"\r
32 \r
33 // ----------------------------------- Called from outside, one offs or info funcs\r
34 \r
35 PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)\r
36 : afeed(this)\r
37 {\r
38   messageQueue = tmessageQueue;\r
39   messageReceiver = tmessageReceiver;\r
40   chanList = tchanList;\r
41   \r
42   audio = Audio::getInstance();\r
43   logger = Log::getInstance();\r
44   vdr = VDR::getInstance();\r
45   initted = false;\r
46 \r
47   stopNow = false;\r
48   state = S_STOP;\r
49   Video::getInstance()->turnVideoOff();\r
50 }\r
51 \r
52 PlayerLiveRadio::~PlayerLiveRadio()\r
53 {\r
54   if (initted) shutdown();\r
55 }\r
56 \r
57 int PlayerLiveRadio::init()\r
58 {\r
59   if (initted) return 0;\r
60 \r
61   demuxer = new DemuxerTS();\r
62   if (!demuxer) return 0;\r
63  \r
64   if (!demuxer->init(this, audio, NULL, NULL, 0, 200000,0))\r
65   {\r
66     logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");\r
67     shutdown();\r
68     return 0;\r
69   }\r
70 \r
71   afeed.init();\r
72   audio->stop();\r
73 \r
74   initted = true;\r
75   return 1;\r
76 }\r
77 \r
78 int PlayerLiveRadio::shutdown()\r
79 {\r
80   if (!initted) return 0;\r
81   stop();\r
82   initted = false;\r
83 \r
84   delete demuxer;\r
85 \r
86 \r
87 \r
88   return 1;\r
89 }\r
90 \r
91 bool* PlayerLiveRadio::getDemuxerMpegAudioChannels()\r
92 {\r
93   return demuxer->getmpAudioChannels();\r
94 }\r
95 \r
96 bool* PlayerLiveRadio::getDemuxerAc3AudioChannels()\r
97 {\r
98   return demuxer->getac3AudioChannels();\r
99 }\r
100 \r
101 int PlayerLiveRadio::getCurrentAudioChannel()\r
102 {\r
103   return demuxer->getAID();\r
104 }\r
105 \r
106 int *PlayerLiveRadio::getTeletxtSubtitlePages(){\r
107     return NULL;\r
108 }\r
109 \r
110 int PlayerLiveRadio::getCurrentSubtitleChannel(){\r
111     return demuxer->getSubID();\r
112 }\r
113 \r
114 void PlayerLiveRadio::setAudioChannel(int newChannel, int type)\r
115 {\r
116   demuxer->setAID(newChannel, type);\r
117 }\r
118 \r
119 void PlayerLiveRadio::setSubtitleChannel(int newChannel)\r
120 {\r
121   demuxer->setSubID(newChannel);\r
122 }\r
123 \r
124 // ----------------------------------- Externally called events\r
125 \r
126 void PlayerLiveRadio::go(ULONG index)\r
127 {\r
128   struct PLInstruction i;\r
129   i.instruction = I_SETCHANNEL;\r
130   i.channelIndex = index;\r
131   instructions.push(i);\r
132   threadStart();\r
133 }\r
134 \r
135 void PlayerLiveRadio::setChannel(ULONG index)\r
136 {\r
137   logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");\r
138   struct PLInstruction i;\r
139   i.instruction = I_SETCHANNEL;\r
140   i.channelIndex = index;\r
141   instructions.push(i);  \r
142   logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());\r
143   threadSignalNoLock();\r
144 }\r
145 \r
146 void PlayerLiveRadio::stop()\r
147 {\r
148   logger->log("PlayerLiveRadio", Log::DEBUG, "stop");\r
149   struct PLInstruction i;\r
150   i.instruction = I_STOP;\r
151   instructions.push(i);\r
152   threadSignal();\r
153   threadStop();\r
154 }\r
155 \r
156 // ----------------------------------- Callback\r
157 \r
158 void PlayerLiveRadio::call(void* caller)\r
159 {\r
160 }\r
161 \r
162 // -----------------------------------\r
163 \r
164 void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)\r
165 {\r
166   // Flag:\r
167   // 0 = normal stream packet\r
168   // 1 = stream end\r
169   // 2 = connection lost\r
170 \r
171   if (flag == 1)\r
172   {\r
173     if (data) abort();\r
174     \r
175     Message* m = new Message();\r
176     m->from = this;\r
177     m->to = messageReceiver;\r
178     m->message = Message::PLAYER_EVENT;\r
179     m->parameter = PlayerLiveRadio::STREAM_END;\r
180     messageQueue->postMessageFromOuterSpace(m);\r
181   }\r
182   \r
183   if (streamChunks.size() < 11)\r
184   {\r
185     StreamChunk s;\r
186     s.data = data;\r
187     s.len = len;\r
188     streamChunks.push(s);\r
189     threadSignalNoLock();\r
190   }\r
191   else\r
192   {\r
193     // Too many chunks in streamChunks, drop this chunk\r
194     free(data);\r
195     logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");\r
196   }\r
197 }\r
198 \r
199 void PlayerLiveRadio::clearStreamChunks()\r
200 {\r
201   while(streamChunks.size())\r
202   {\r
203     logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");\r
204     struct StreamChunk s = streamChunks.front();\r
205     streamChunks.pop();\r
206     free(s.data);\r
207   }\r
208 }\r
209 \r
210 void PlayerLiveRadio::chunkToDemuxer()\r
211 {\r
212   StreamChunk s = streamChunks.front();\r
213   streamChunks.pop();\r
214   //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);\r
215   /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);\r
216   //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);\r
217   free(s.data);  \r
218 }\r
219 \r
220 void PlayerLiveRadio::switchState(UCHAR newState)\r
221 {\r
222   logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);\r
223 \r
224   switch(state)\r
225   {\r
226     case S_STOP:   // FROM S_STOP\r
227     {\r
228       switch(newState)\r
229       {\r
230         case S_PREBUFFERING:\r
231         {\r
232           audio->stop();\r
233           audio->unPause();\r
234           audio->reset();\r
235           audio->setStreamType(Audio::MPEG2_PES);\r
236           audio->systemMuteOff();      \r
237           audio->doMuting();              \r
238           audio->play();\r
239           audio->pause();\r
240           demuxer->reset();\r
241           afeed.start();\r
242           \r
243           state = newState;\r
244           preBufferCount = 0;\r
245           return;\r
246         }\r
247         default:\r
248         {\r
249           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
250           abort();\r
251           break;\r
252         }\r
253       }\r
254     }\r
255 \r
256     case S_PREBUFFERING:    // FROM S_PREBUFFERING\r
257     {\r
258       switch(newState)\r
259       {\r
260         case S_PLAY:\r
261         {\r
262           audio->unPause();\r
263           state = newState;\r
264           return;\r
265         }\r
266         case S_STOP:\r
267         {\r
268           vdr->stopStreaming();\r
269           clearStreamChunks();\r
270           afeed.stop();\r
271           audio->stop();\r
272           audio->reset();\r
273           state = newState;\r
274           return;        \r
275         }\r
276         case S_PREBUFFERING:\r
277         {\r
278           vdr->stopStreaming();\r
279           clearStreamChunks();\r
280           afeed.stop();\r
281           audio->stop();\r
282           audio->reset();\r
283           audio->play();\r
284           audio->pause();\r
285           demuxer->reset();\r
286           afeed.start();\r
287 \r
288           state = newState;\r
289           preBufferCount = 0;\r
290           return;        \r
291         }\r
292         default:\r
293         {\r
294           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
295           abort();\r
296           break;\r
297         }        \r
298       }\r
299     }\r
300     \r
301     case S_PLAY:     // FROM S_PLAY\r
302     {\r
303       switch(newState)\r
304       {\r
305         case S_STOP:\r
306         { \r
307           vdr->stopStreaming();\r
308           clearStreamChunks();\r
309           afeed.stop();\r
310           audio->stop();\r
311           audio->reset();\r
312           state = newState;\r
313           return;\r
314         }\r
315         case S_PREBUFFERING: // IS THIS HOW IT WORKS?\r
316         {\r
317           vdr->stopStreaming();\r
318           clearStreamChunks();\r
319           afeed.stop();\r
320           audio->stop();\r
321           audio->reset();\r
322           audio->play();\r
323           audio->pause();\r
324           demuxer->reset();\r
325           afeed.start();\r
326 \r
327           state = newState;\r
328           preBufferCount = 0;\r
329           return;\r
330         }\r
331         default:\r
332         {\r
333           logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
334           abort();\r
335           break;\r
336         }        \r
337       }\r
338     }    \r
339   }  \r
340 }\r
341 \r
342 bool PlayerLiveRadio::checkError()\r
343 {\r
344   if (!vdr->isConnected())\r
345   {\r
346     switchState(S_STOP);\r
347     \r
348     Message* m = new Message();\r
349     m->from = this;\r
350     m->to = messageReceiver;\r
351     m->message = Message::PLAYER_EVENT;\r
352     m->parameter = PlayerLiveRadio::CONNECTION_LOST;\r
353     messageQueue->postMessageFromOuterSpace(m);\r
354     \r
355     return true;\r
356   }   \r
357   return false;\r
358 }\r
359 \r
360 void PlayerLiveRadio::optimizeInstructionQueue()\r
361 {\r
362   // Walk the list\r
363   \r
364   // Currently there are only 2 instruction types, so this is a bit overkill...\r
365 \r
366   struct PLInstruction i;\r
367   while(instructions.size() > 1)\r
368   {\r
369     i = instructions.front();\r
370     if (i.instruction == I_SETCHANNEL)\r
371     {\r
372       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant\r
373     }\r
374     else if (i.instruction == I_STOP)\r
375     {\r
376       return; // return here and ensure the next instruction will be stop\r
377     }\r
378   }\r
379 }\r
380 \r
381 void PlayerLiveRadio::threadMethod()\r
382 {\r
383   while(1)\r
384   {\r
385     while(!instructions.empty())\r
386     {\r
387       if (instructions.size() > 1)\r
388       {\r
389         logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");\r
390         optimizeInstructionQueue();\r
391       }\r
392 \r
393       struct PLInstruction i = instructions.front();\r
394       instructions.pop();\r
395     \r
396       if (i.instruction == I_SETCHANNEL)\r
397       {\r
398         logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");\r
399 \r
400         switchState(S_PREBUFFERING);\r
401 \r
402         if (!checkError())\r
403         {\r
404           Channel* chan = (*chanList)[i.channelIndex];\r
405           chan->loadPids();\r
406 \r
407           if (chan->numAPids > 0) \r
408           {\r
409             demuxer->setAID(chan->apids[0].pid,0);\r
410             logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);\r
411           }\r
412           else \r
413           {\r
414             logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");\r
415           }\r
416 \r
417           int streamSuccess = vdr->streamChannel(chan->number, this);\r
418           if (!checkError() && !streamSuccess)\r
419           {      \r
420             Message* m = new Message();\r
421             m->from = this;\r
422             m->to = messageReceiver;\r
423             m->message = Message::PLAYER_EVENT;\r
424             m->parameter = PlayerLiveRadio::STREAM_END;\r
425             messageQueue->postMessageFromOuterSpace(m);\r
426           }\r
427         }\r
428       }\r
429       else if (i.instruction == I_STOP)\r
430       {\r
431         logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");\r
432         switchState(S_STOP);\r
433         checkError();\r
434 \r
435         stopNow = true;\r
436         break;\r
437       }\r
438     }\r
439 \r
440     if (stopNow) break;\r
441 \r
442     while(streamChunks.size())\r
443     {\r
444       chunkToDemuxer();\r
445 \r
446       if (state == S_PREBUFFERING)\r
447       {\r
448         ++preBufferCount;\r
449         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);\r
450         logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);\r
451         \r
452         Message* m = new Message();\r
453         m->from = this;\r
454         m->to = messageReceiver;\r
455         m->message = Message::PLAYER_EVENT;\r
456         m->parameter = PlayerLiveRadio::PREBUFFERING;\r
457         m->tag = percentDone;\r
458         messageQueue->postMessageFromOuterSpace(m);\r
459 \r
460         if (preBufferCount == preBufferAmount)\r
461         {\r
462           switchState(S_PLAY);\r
463           checkError();\r
464         }\r
465       }\r
466     }\r
467     \r
468     threadLock();\r
469     threadWaitForSignal(); // unlocks and waits for signal\r
470     threadUnlock();\r
471   }\r
472 \r
473   logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");\r
474 }\r
475 \r