]> git.vomp.tv Git - vompclient.git/blob - playerlivetv.cc
Live radio prebuffering display - code upgrades for connection lost handling
[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(ULONG flag, void* data, ULONG len)
214 {
215   // Flag:
216   // 0 = normal stream packet
217   // 1 = stream end
218
219 //  logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
220
221   if (flag == 1)
222   {
223     if (data) abort();
224     
225     Message* m = new Message();
226     m->from = this;
227     m->to = messageReceiver;
228     m->message = Message::PLAYER_EVENT;
229     m->parameter = PlayerLiveTV::STREAM_END;
230     messageQueue->postMessageFromOuterSpace(m);
231   }
232         
233   if (streamChunks.size() < 11)
234   {
235     StreamChunk s;
236     s.data = data;
237     s.len = len;
238     streamChunks.push(s);
239     threadSignalNoLock();
240   }
241   else
242   {
243     // Too many chunks in streamChunks, drop this chunk
244     free(data);
245     logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
246   }
247 }
248
249 void PlayerLiveTV::clearStreamChunks()
250 {
251   while(streamChunks.size())
252   {
253     logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
254     struct StreamChunk s = streamChunks.front();
255     streamChunks.pop();
256     free(s.data);
257   }
258 }
259
260 void PlayerLiveTV::chunkToDemuxer()
261 {
262   StreamChunk s = streamChunks.front();
263   streamChunks.pop();
264   //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
265   /*int a = */demuxer->put((UCHAR*)s.data, s.len);
266   //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
267   free(s.data);  
268 }
269
270 void PlayerLiveTV::switchState(UCHAR newState)
271 {
272   logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
273
274   switch(state)
275   {
276     case S_STOP:   // FROM S_STOP
277     {
278       switch(newState)
279       {
280         case S_VIDEOSTARTUP:
281         {
282           video->blank();
283           video->reset();
284           video->sync();
285           video->play();
286           video->pause();
287
288           audio->stop();
289           audio->unPause();
290           audio->reset();
291           audio->setStreamType(Audio::MPEG2_PES);
292           audio->sync();
293           audio->play();
294           audio->pause();
295
296           demuxer->reset();
297           demuxer->seek();
298
299           afeed.start();
300           vfeed.start();
301           
302           state = newState;
303           return;
304         }
305         default:
306         {
307           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
308           abort();
309           break;
310         }
311       }
312     }
313
314     case S_VIDEOSTARTUP:     // FROM S_VIDEOSTARTUP
315     {
316       switch(newState)
317       {
318         case S_PREBUFFERING:
319         {
320           vfeed.release();
321           state = newState;
322           return;
323         }
324         
325         case S_VIDEOSTARTUP:
326         {
327           vdr->stopStreaming();
328           clearStreamChunks(); 
329           vfeed.stop();
330           afeed.stop();
331                            
332           video->blank();
333           video->reset();
334           video->sync();
335           video->play();
336           video->pause();
337
338           audio->stop();
339           audio->unPause();
340           audio->reset();
341           audio->setStreamType(Audio::MPEG2_PES);
342           audio->sync();
343           audio->play();
344           audio->pause();
345
346           demuxer->reset();
347           demuxer->seek();
348
349           afeed.start();
350           vfeed.start();
351           
352           state = newState;
353           return;
354         }        
355         case S_STOP:
356         { 
357           vdr->stopStreaming();
358           clearStreamChunks();
359           vfeed.stop();
360           afeed.stop();
361           video->stop();
362           video->blank();
363           audio->stop();
364           audio->reset();
365           video->reset();
366           state = newState;
367           return;
368         }
369         default:
370         {
371           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
372           abort();
373           break;
374         }        
375       }
376     }
377     
378     case S_PREBUFFERING:    // FROM S_PREBUFFERING
379     {
380       switch(newState)
381       {
382         case S_PLAY:
383         {
384           audio->unPause();
385           video->unPause();
386           state = newState;
387           return;
388         }
389         case S_VIDEOSTARTUP:
390         {
391           vdr->stopStreaming();
392           clearStreamChunks();
393           vfeed.stop();
394           afeed.stop();
395           video->stop();
396           video->blank();
397           audio->stop();
398           audio->unPause();
399           audio->reset();
400
401           video->reset();
402           video->sync();
403           video->play();
404           video->pause();
405
406           audio->setStreamType(Audio::MPEG2_PES);
407           audio->sync();
408           audio->play();
409           audio->pause();
410
411           demuxer->reset();
412           demuxer->seek();
413
414           afeed.start();
415           vfeed.start();
416
417           state = newState;
418           return;
419         }
420         case S_STOP:
421         {
422           vdr->stopStreaming();
423           clearStreamChunks();
424           vfeed.stop();
425           afeed.stop();
426           video->stop();
427           video->blank();
428           audio->stop();
429           audio->reset();
430           video->reset();
431           state = newState;
432           return;        
433         }
434         default:
435         {
436           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
437           abort();
438           break;
439         }        
440       }
441     }
442     
443     case S_PLAY:     // FROM S_PLAY
444     {
445       switch(newState)
446       {
447         case S_STOP:
448         { 
449           vdr->stopStreaming();
450           clearStreamChunks();
451           vfeed.stop();
452           afeed.stop();
453           video->stop();
454           video->blank();
455           audio->stop();
456           audio->reset();
457           video->reset();
458           state = newState;
459           return;
460         }
461         case S_VIDEOSTARTUP:
462         {
463           vdr->stopStreaming();
464           clearStreamChunks();
465           vfeed.stop();
466           afeed.stop();
467           video->stop();
468           video->blank();
469           audio->stop();
470           audio->unPause();
471           audio->reset();
472
473           video->reset();
474           video->sync();
475           video->play();
476           video->pause();
477
478           audio->setStreamType(Audio::MPEG2_PES);
479           audio->sync();
480           audio->play();
481           audio->pause();
482
483           demuxer->reset();
484           demuxer->seek();
485
486           afeed.start();
487           vfeed.start();
488
489           state = newState;
490           return;
491         }
492         default:
493         {
494           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
495           abort();
496           break;
497         }        
498       }
499     }    
500   }  
501 }
502
503 bool PlayerLiveTV::checkError()
504 {
505   if (!vdr->isConnected())
506   {
507     switchState(S_STOP);
508     
509     Message* m = new Message();
510     m->from = this;
511     m->to = messageReceiver;
512     m->message = Message::PLAYER_EVENT;
513     m->parameter = PlayerLiveTV::CONNECTION_LOST;
514     messageQueue->postMessageFromOuterSpace(m);
515     
516     return true;
517   }   
518   return false;
519 }
520
521 void PlayerLiveTV::optimizeInstructionQueue()
522 {
523   // Walk the list
524   
525   // Currently there are only 2 instruction types, so this is a bit overkill...
526
527   struct PLInstruction i;
528   while(instructions.size() > 1)
529   {
530     i = instructions.front();
531     if (i.instruction == I_SETCHANNEL)
532     {
533       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant
534     }
535     else if (i.instruction == I_STOP)
536     {
537       return; // return here and ensure the next instruction will be stop
538     }
539   }
540 }
541
542 void PlayerLiveTV::threadMethod()
543 {
544   while(1)
545   {
546     if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
547     {
548       switchState(S_PREBUFFERING);
549       videoStartup = false;
550       preBufferCount = 0;
551       
552       checkError();
553     }  
554   
555     while(!instructions.empty())
556     {
557       if (instructions.size() > 1) optimizeInstructionQueue();
558
559       struct PLInstruction i = instructions.front();
560       instructions.pop();
561     
562       if (i.instruction == I_SETCHANNEL)
563       {
564         logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
565
566         switchState(S_VIDEOSTARTUP);
567         
568         if (!checkError())
569         {
570           Channel* chan = (*chanList)[i.channelIndex];
571           chan->loadPids();
572           demuxer->setVID(chan->vpid);
573           if (chan->numAPids > 0) 
574           {
575             demuxer->setAID(chan->apids[0].pid,0);
576             logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
577           }
578           else 
579           {
580             logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
581           }
582
583           int streamSuccess = vdr->streamChannel(chan->number, this);
584           if (!checkError() && !streamSuccess)
585           {      
586             Message* m = new Message();
587             m->from = this;
588             m->to = messageReceiver;
589             m->message = Message::PLAYER_EVENT;
590             m->parameter = PlayerLiveTV::STREAM_END;
591             messageQueue->postMessageFromOuterSpace(m);
592           }
593         }
594       }
595       else if (i.instruction == I_STOP)
596       {
597         logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
598         switchState(S_STOP);
599         checkError();
600
601         stopNow = true;
602         break;
603       }
604     }
605
606     if (stopNow) break;
607
608     while(streamChunks.size())
609     {
610       chunkToDemuxer();
611
612       if (state == S_PREBUFFERING)
613       {
614         ++preBufferCount;
615         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
616         logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
617         
618         Message* m = new Message();
619         m->from = this;
620         m->to = messageReceiver;
621         m->message = Message::PLAYER_EVENT;
622         m->parameter = PlayerLiveTV::PREBUFFERING;
623         m->tag = percentDone;
624         messageQueue->postMessageFromOuterSpace(m);
625
626         if (preBufferCount == preBufferAmount)
627         {
628           switchState(S_PLAY);
629           checkError();
630         }
631       }
632     }
633     
634     threadLock();
635     threadWaitForSignal(); // unlocks and waits for signal
636     threadUnlock();
637   }
638
639   logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
640 }
641