]> git.vomp.tv Git - vompclient.git/blob - playerlivetv.cc
Live TV updates
[vompclient.git] / playerlivetv.cc
1 /*
2     Copyright 2007 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "playerlivetv.h"
22
23 #include "log.h"
24 #include "audio.h"
25 #include "video.h"
26 #include "demuxerts.h"
27 #include "vdr.h"
28 #include "messagequeue.h"
29 #include "remote.h"
30 #include "message.h"
31 #include "channel.h"
32
33 // ----------------------------------- Called from outside, one offs or info funcs
34
35 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
36 : vfeed(this), afeed(this)
37 {
38   messageQueue = tmessageQueue;
39   messageReceiver = tmessageReceiver;
40   chanList = tchanList;
41   
42   audio = Audio::getInstance();
43   video = Video::getInstance();
44   logger = Log::getInstance();
45   vdr = VDR::getInstance();
46   initted = false;
47
48   videoStartup = false;
49
50   stopNow = false;
51   state = 1;
52
53   video->turnVideoOn();
54 }
55
56 PlayerLiveTV::~PlayerLiveTV()
57 {
58   if (initted) shutdown();
59 }
60
61 int PlayerLiveTV::init()
62 {
63   if (initted) return 0;
64
65   demuxer = new DemuxerTS();
66   if (!demuxer) return 0;
67  
68   if (!demuxer->init(this, audio, video, 2097152, 524288))
69   {
70     logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
71     shutdown();
72     return 0;
73   }
74
75   vfeed.init();
76   afeed.init();
77
78   video->stop();
79   video->blank();
80   audio->stop();
81
82   initted = true;
83   return 1;
84 }
85
86 int PlayerLiveTV::shutdown()
87 {
88   if (!initted) return 0;
89   stop();
90   initted = false;
91
92   delete demuxer;
93
94 #ifdef WIN32
95   CloseHandle(mutex);
96 #endif
97
98   return 1;
99 }
100
101 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
102 {
103   return demuxer->getmpAudioChannels();
104 }
105
106 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
107 {
108   return demuxer->getac3AudioChannels();
109 }
110
111 int PlayerLiveTV::getCurrentAudioChannel()
112 {
113   return demuxer->getAID();
114 }
115
116 void PlayerLiveTV::setAudioChannel(int newChannel)
117 {
118   return demuxer->setAID(newChannel);
119 }
120
121 // ----------------------------------- Externally called events
122
123 void PlayerLiveTV::go(ULONG index)
124 {
125   struct PLTVInstruction i;
126   i.instruction = 1;
127   i.channelIndex = index;
128   instructions.push(i);
129   threadStart();
130 }
131
132 void PlayerLiveTV::setChannel(ULONG index)
133 {
134   logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
135   struct PLTVInstruction i;
136   i.instruction = 1;
137   i.channelIndex = index;
138   instructions.push(i);  
139   threadSignalNoLock();
140 }
141
142 void PlayerLiveTV::stop()
143 {
144   logger->log("PlayerLiveTV", Log::DEBUG, "stop");
145   struct PLTVInstruction i;
146   i.instruction = 2;
147   instructions.push(i);
148   threadSignal();
149   threadStop();
150 }
151
152 // ----------------------------------- Callback
153
154 void PlayerLiveTV::call(void* caller)
155 {
156   if (caller == demuxer)
157   {
158     logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
159
160     if (video->getTVsize() == Video::ASPECT4X3)
161     {
162       logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
163       return;
164     }
165
166     int dxCurrentAspect = demuxer->getAspectRatio();
167     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
168     {
169       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
170       video->setAspectRatio(Video::ASPECT4X3);
171
172       Message* m = new Message();
173       m->from = this;
174       m->to = messageReceiver;
175       m->message = Message::PLAYER_EVENT;
176       m->parameter = PlayerLiveTV::ASPECT43;
177       messageQueue->postMessageFromOuterSpace(m);
178     }
179     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
180     {
181       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
182       video->setAspectRatio(Video::ASPECT16X9);
183
184       Message* m = new Message();
185       m->from = this;
186       m->to = messageReceiver;
187       m->message = Message::PLAYER_EVENT;
188       m->parameter = PlayerLiveTV::ASPECT169;
189       messageQueue->postMessageFromOuterSpace(m);
190     }
191     else
192     {
193       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
194     }
195   }
196   else if (caller == &afeed)
197   {
198     if (state == S_VIDEOSTARTUP)
199     {
200       logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
201       videoStartup = true;
202       threadSignalNoLock();
203     }
204   }
205 }
206
207 // -----------------------------------
208
209 void PlayerLiveTV::streamReceive(void* data, ULONG len)
210 {
211   if (streamChunks.size() < 11)
212   {
213     StreamChunk s;
214     s.data = data;
215     s.len = len;
216     streamChunks.push(s);
217     threadSignalNoLock();
218   }
219   else
220   {
221     // Too many chunks in streamChunks, drop this chunk
222     free(data);
223     logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
224   }
225 }
226
227 void PlayerLiveTV::clearStreamChunks()
228 {
229   while(streamChunks.size())
230   {
231     logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
232     struct StreamChunk s = streamChunks.front();
233     streamChunks.pop();
234     free(s.data);
235   }
236 }
237
238 void PlayerLiveTV::chunkToDemuxer()
239 {
240   StreamChunk s = streamChunks.front();
241   streamChunks.pop();
242   //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
243   /*int a = */demuxer->put((UCHAR*)s.data, s.len);
244   //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
245   free(s.data);  
246 }
247
248 void PlayerLiveTV::switchState(UCHAR newState)
249 {
250   logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
251
252   switch(state)
253   {
254     case S_STOP:   // FROM S_STOP
255     {
256       switch(newState)
257       {
258         case S_VIDEOSTARTUP:
259         {
260           video->blank();
261           video->reset();
262           video->sync();
263           video->play();
264           video->pause();
265
266           audio->stop();
267           audio->unPause();
268           audio->reset();
269           audio->setStreamType(Audio::MPEG2_PES);
270           audio->sync();
271           audio->play();
272           audio->pause();
273
274           demuxer->reset();
275           demuxer->seek();
276
277           afeed.start();
278           vfeed.start();
279           
280           state = newState;
281           return;
282         }
283         default:
284         {
285           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
286           abort();
287           break;
288         }
289       }
290     }
291
292     case S_VIDEOSTARTUP:     // FROM S_VIDEOSTARTUP
293     {
294       switch(newState)
295       {
296         case S_PREBUFFERING:
297         {
298           vfeed.release();
299           state = newState;
300           return;
301         }
302         
303         case S_VIDEOSTARTUP:
304         {
305           vdr->stopStreaming();
306           clearStreamChunks(); 
307           vfeed.stop();
308           afeed.stop();
309                            
310           video->blank();
311           video->reset();
312           video->sync();
313           video->play();
314           video->pause();
315
316           audio->stop();
317           audio->unPause();
318           audio->reset();
319           audio->setStreamType(Audio::MPEG2_PES);
320           audio->sync();
321           audio->play();
322           audio->pause();
323
324           demuxer->reset();
325           demuxer->seek();
326
327           afeed.start();
328           vfeed.start();
329           
330           state = newState;
331           return;
332         }        
333         case S_STOP:
334         { 
335           vdr->stopStreaming();
336           clearStreamChunks();
337           vfeed.stop();
338           afeed.stop();
339           video->stop();
340           video->blank();
341           audio->stop();
342           audio->reset();
343           video->reset();
344           state = newState;
345           return;
346         }
347         default:
348         {
349           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
350           abort();
351           break;
352         }        
353       }
354     }
355     
356     case S_PREBUFFERING:    // FROM S_PREBUFFERING
357     {
358       switch(newState)
359       {
360         case S_PLAY:
361         {
362           audio->unPause();
363           video->unPause();
364           state = newState;
365           return;
366         }
367         case S_VIDEOSTARTUP:
368         {
369           vdr->stopStreaming();
370           clearStreamChunks();
371           vfeed.stop();
372           afeed.stop();
373           video->stop();
374           video->blank();
375           audio->stop();
376           audio->unPause();
377           audio->reset();
378
379           video->reset();
380           video->sync();
381           video->play();
382           video->pause();
383
384           audio->setStreamType(Audio::MPEG2_PES);
385           audio->sync();
386           audio->play();
387           audio->pause();
388
389           demuxer->reset();
390           demuxer->seek();
391
392           afeed.start();
393           vfeed.start();
394
395           state = newState;
396           return;
397         }
398         case S_STOP:
399         {
400           vdr->stopStreaming();
401           clearStreamChunks();
402           vfeed.stop();
403           afeed.stop();
404           video->stop();
405           video->blank();
406           audio->stop();
407           audio->reset();
408           video->reset();
409           state = newState;
410           return;        
411         }
412         default:
413         {
414           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
415           abort();
416           break;
417         }        
418       }
419     }
420     
421     case S_PLAY:     // FROM S_PLAY
422     {
423       switch(newState)
424       {
425         case S_STOP:
426         { 
427           vdr->stopStreaming();
428           clearStreamChunks();
429           vfeed.stop();
430           afeed.stop();
431           video->stop();
432           video->blank();
433           audio->stop();
434           audio->reset();
435           video->reset();
436           state = newState;
437           return;
438         }
439         case S_VIDEOSTARTUP:
440         {
441           vdr->stopStreaming();
442           clearStreamChunks();
443           vfeed.stop();
444           afeed.stop();
445           video->stop();
446           video->blank();
447           audio->stop();
448           audio->unPause();
449           audio->reset();
450
451           video->reset();
452           video->sync();
453           video->play();
454           video->pause();
455
456           audio->setStreamType(Audio::MPEG2_PES);
457           audio->sync();
458           audio->play();
459           audio->pause();
460
461           demuxer->reset();
462           demuxer->seek();
463
464           afeed.start();
465           vfeed.start();
466
467           state = newState;
468           return;
469         }
470         default:
471         {
472           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
473           abort();
474           break;
475         }        
476       }
477     }    
478   }  
479 }
480
481 void PlayerLiveTV::threadMethod()
482 {
483   while(1)
484   {
485     if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
486     {
487       switchState(S_PREBUFFERING);
488       videoStartup = false;
489       videoStartup2Counter = 0;
490     }  
491   
492     while(!instructions.empty())
493     {
494       struct PLTVInstruction i = instructions.front();
495       instructions.pop();
496     
497       logger->log("PlayerLiveTV", Log::DEBUG, "%u %lu", i.instruction, i.channelIndex);
498       
499
500       if (i.instruction == 1)
501       {
502         logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
503
504         switchState(S_VIDEOSTARTUP);
505
506         Channel* chan = (*chanList)[i.channelIndex];
507         chan->loadPids();
508         demuxer->setVID(chan->vpid);
509         demuxer->setAID(chan->apids[0].pid);
510         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
511         vdr->streamChannel(chan->number, this);
512         
513       }
514       else if (i.instruction == 2)
515       {
516         logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
517         switchState(S_STOP);
518
519         stopNow = true;
520         break;
521       }
522     }
523
524     if (stopNow) break;
525
526     while(streamChunks.size())
527     {
528       chunkToDemuxer();
529
530       if (state == S_PREBUFFERING)
531       {        
532         if (++videoStartup2Counter == 3)
533         {
534           switchState(S_PLAY);
535         }
536       }
537     }
538     
539     threadLock();
540     threadWaitForSignal(); // unlocks and waits for signal
541     
542     threadUnlock();
543   }
544
545   logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
546 }
547