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