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