]> git.vomp.tv Git - vompclient.git/blob - playerlivetv.cc
Buffering progress bar
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 = S_STOP;
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   return 1;
95 }
96
97 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
98 {
99   return demuxer->getmpAudioChannels();
100 }
101
102 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
103 {
104   return demuxer->getac3AudioChannels();
105 }
106
107 int PlayerLiveTV::getCurrentAudioChannel()
108 {
109   return demuxer->getAID();
110 }
111
112 void PlayerLiveTV::setAudioChannel(int newChannel, int type)
113 {
114   return demuxer->setAID(newChannel,type);
115 }
116
117 // ----------------------------------- Externally called events
118
119 void PlayerLiveTV::go(ULONG index)
120 {
121   struct PLInstruction i;
122   i.instruction = I_SETCHANNEL;
123   i.channelIndex = index;
124   instructions.push(i);
125   threadStart();
126 }
127
128 void PlayerLiveTV::setChannel(ULONG index)
129 {
130   logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
131   struct PLInstruction i;
132   i.instruction = I_SETCHANNEL;
133   i.channelIndex = index;
134   instructions.push(i);  
135   threadSignalNoLock();
136 }
137
138 void PlayerLiveTV::stop()
139 {
140   logger->log("PlayerLiveTV", Log::DEBUG, "stop");
141   struct PLInstruction i;
142   i.instruction = I_STOP;
143   instructions.push(i);
144   threadSignal();
145   threadStop();
146 }
147
148 // ----------------------------------- Callback
149
150 void PlayerLiveTV::call(void* caller)
151 {
152   if (caller == demuxer)
153   {
154     logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
155
156     int dxCurrentAspect = demuxer->getAspectRatio();
157     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
158     {
159       if (video->getTVsize() == Video::ASPECT16X9)
160       {
161         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
162         video->setAspectRatio(Video::ASPECT4X3);
163       }
164       else
165       {
166         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
167       }
168
169       Message* m = new Message();
170       m->from = this;
171       m->to = messageReceiver;
172       m->message = Message::PLAYER_EVENT;
173       m->parameter = PlayerLiveTV::ASPECT43;
174       messageQueue->postMessageFromOuterSpace(m);
175     }
176     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
177     {
178       if (video->getTVsize() == Video::ASPECT16X9)
179       {
180         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
181         video->setAspectRatio(Video::ASPECT16X9);
182       }
183       else
184       {
185         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
186       }    
187
188       Message* m = new Message();
189       m->from = this;
190       m->to = messageReceiver;
191       m->message = Message::PLAYER_EVENT;
192       m->parameter = PlayerLiveTV::ASPECT169;
193       messageQueue->postMessageFromOuterSpace(m);
194     }
195     else
196     {
197       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
198     }
199   }
200   else if (caller == &afeed)
201   {
202     if (state == S_VIDEOSTARTUP)
203     {
204       logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
205       videoStartup = true;
206       threadSignalNoLock();
207     }
208   }
209 }
210
211 // -----------------------------------
212
213 void PlayerLiveTV::streamReceive(void* data, ULONG len)
214 {
215   if (streamChunks.size() < 11)
216   {
217     StreamChunk s;
218     s.data = data;
219     s.len = len;
220     streamChunks.push(s);
221     threadSignalNoLock();
222   }
223   else
224   {
225     // Too many chunks in streamChunks, drop this chunk
226     free(data);
227     logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
228   }
229 }
230
231 void PlayerLiveTV::clearStreamChunks()
232 {
233   while(streamChunks.size())
234   {
235     logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
236     struct StreamChunk s = streamChunks.front();
237     streamChunks.pop();
238     free(s.data);
239   }
240 }
241
242 void PlayerLiveTV::chunkToDemuxer()
243 {
244   StreamChunk s = streamChunks.front();
245   streamChunks.pop();
246   //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
247   /*int a = */demuxer->put((UCHAR*)s.data, s.len);
248   //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
249   free(s.data);  
250 }
251
252 void PlayerLiveTV::switchState(UCHAR newState)
253 {
254   logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
255
256   switch(state)
257   {
258     case S_STOP:   // FROM S_STOP
259     {
260       switch(newState)
261       {
262         case S_VIDEOSTARTUP:
263         {
264           video->blank();
265           video->reset();
266           video->sync();
267           video->play();
268           video->pause();
269
270           audio->stop();
271           audio->unPause();
272           audio->reset();
273           audio->setStreamType(Audio::MPEG2_PES);
274           audio->sync();
275           audio->play();
276           audio->pause();
277
278           demuxer->reset();
279           demuxer->seek();
280
281           afeed.start();
282           vfeed.start();
283           
284           state = newState;
285           return;
286         }
287         default:
288         {
289           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
290           abort();
291           break;
292         }
293       }
294     }
295
296     case S_VIDEOSTARTUP:     // FROM S_VIDEOSTARTUP
297     {
298       switch(newState)
299       {
300         case S_PREBUFFERING:
301         {
302           vfeed.release();
303           state = newState;
304           return;
305         }
306         
307         case S_VIDEOSTARTUP:
308         {
309           vdr->stopStreaming();
310           clearStreamChunks(); 
311           vfeed.stop();
312           afeed.stop();
313                            
314           video->blank();
315           video->reset();
316           video->sync();
317           video->play();
318           video->pause();
319
320           audio->stop();
321           audio->unPause();
322           audio->reset();
323           audio->setStreamType(Audio::MPEG2_PES);
324           audio->sync();
325           audio->play();
326           audio->pause();
327
328           demuxer->reset();
329           demuxer->seek();
330
331           afeed.start();
332           vfeed.start();
333           
334           state = newState;
335           return;
336         }        
337         case S_STOP:
338         { 
339           vdr->stopStreaming();
340           clearStreamChunks();
341           vfeed.stop();
342           afeed.stop();
343           video->stop();
344           video->blank();
345           audio->stop();
346           audio->reset();
347           video->reset();
348           state = newState;
349           return;
350         }
351         default:
352         {
353           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
354           abort();
355           break;
356         }        
357       }
358     }
359     
360     case S_PREBUFFERING:    // FROM S_PREBUFFERING
361     {
362       switch(newState)
363       {
364         case S_PLAY:
365         {
366           audio->unPause();
367           video->unPause();
368           state = newState;
369           return;
370         }
371         case S_VIDEOSTARTUP:
372         {
373           vdr->stopStreaming();
374           clearStreamChunks();
375           vfeed.stop();
376           afeed.stop();
377           video->stop();
378           video->blank();
379           audio->stop();
380           audio->unPause();
381           audio->reset();
382
383           video->reset();
384           video->sync();
385           video->play();
386           video->pause();
387
388           audio->setStreamType(Audio::MPEG2_PES);
389           audio->sync();
390           audio->play();
391           audio->pause();
392
393           demuxer->reset();
394           demuxer->seek();
395
396           afeed.start();
397           vfeed.start();
398
399           state = newState;
400           return;
401         }
402         case S_STOP:
403         {
404           vdr->stopStreaming();
405           clearStreamChunks();
406           vfeed.stop();
407           afeed.stop();
408           video->stop();
409           video->blank();
410           audio->stop();
411           audio->reset();
412           video->reset();
413           state = newState;
414           return;        
415         }
416         default:
417         {
418           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
419           abort();
420           break;
421         }        
422       }
423     }
424     
425     case S_PLAY:     // FROM S_PLAY
426     {
427       switch(newState)
428       {
429         case S_STOP:
430         { 
431           vdr->stopStreaming();
432           clearStreamChunks();
433           vfeed.stop();
434           afeed.stop();
435           video->stop();
436           video->blank();
437           audio->stop();
438           audio->reset();
439           video->reset();
440           state = newState;
441           return;
442         }
443         case S_VIDEOSTARTUP:
444         {
445           vdr->stopStreaming();
446           clearStreamChunks();
447           vfeed.stop();
448           afeed.stop();
449           video->stop();
450           video->blank();
451           audio->stop();
452           audio->unPause();
453           audio->reset();
454
455           video->reset();
456           video->sync();
457           video->play();
458           video->pause();
459
460           audio->setStreamType(Audio::MPEG2_PES);
461           audio->sync();
462           audio->play();
463           audio->pause();
464
465           demuxer->reset();
466           demuxer->seek();
467
468           afeed.start();
469           vfeed.start();
470
471           state = newState;
472           return;
473         }
474         default:
475         {
476           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
477           abort();
478           break;
479         }        
480       }
481     }    
482   }  
483 }
484
485 void PlayerLiveTV::optimizeInstructionQueue()
486 {
487   // Walk the list
488   
489   // Currently there are only 2 instruction types, so this is a bit overkill...
490
491   struct PLInstruction i;
492   while(instructions.size() > 1)
493   {
494     i = instructions.front();
495     if (i.instruction == I_SETCHANNEL)
496     {
497       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant
498     }
499     else if (i.instruction == I_STOP)
500     {
501       return; // return here and ensure the next instruction will be stop
502     }
503   }
504 }
505
506 void PlayerLiveTV::threadMethod()
507 {
508   while(1)
509   {
510     if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
511     {
512       switchState(S_PREBUFFERING);
513       videoStartup = false;
514       preBufferCount = 0;
515     }  
516   
517     while(!instructions.empty())
518     {
519       if (instructions.size() > 1) optimizeInstructionQueue();
520
521       struct PLInstruction i = instructions.front();
522       instructions.pop();
523     
524       if (i.instruction == I_SETCHANNEL)
525       {
526         logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
527
528         switchState(S_VIDEOSTARTUP);
529
530         Channel* chan = (*chanList)[i.channelIndex];
531         chan->loadPids();
532         demuxer->setVID(chan->vpid);
533         if (chan->numAPids > 0) 
534         {
535           demuxer->setAID(chan->apids[0].pid,0);
536           logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
537         }
538         else 
539         {
540           logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
541         }
542
543         vdr->streamChannel(chan->number, this);
544       }
545       else if (i.instruction == I_STOP)
546       {
547         logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
548         switchState(S_STOP);
549
550         stopNow = true;
551         break;
552       }
553     }
554
555     if (stopNow) break;
556
557     while(streamChunks.size())
558     {
559       chunkToDemuxer();
560
561       if (state == S_PREBUFFERING)
562       {
563         ++preBufferCount;
564         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
565         logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
566         
567         Message* m = new Message();
568         m->from = this;
569         m->to = messageReceiver;
570         m->message = Message::PLAYER_EVENT;
571         m->parameter = PlayerLiveTV::PREBUFFERING;
572         m->tag = percentDone;
573         messageQueue->postMessageFromOuterSpace(m);
574
575         if (preBufferCount == preBufferAmount)
576         {
577           switchState(S_PLAY);
578         }
579       }
580     }
581     
582     threadLock();
583     threadWaitForSignal(); // unlocks and waits for signal
584     threadUnlock();
585   }
586
587   logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
588 }
589