]> git.vomp.tv Git - vompclient.git/blob - playerlivetv.cc
Teletext
[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 #include "dvbsubtitles.h"
33 #include "osdreceiver.h"
34
35 // ----------------------------------- Called from outside, one offs or info funcs
36
37 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
38 : vfeed(this), afeed(this), tfeed(this)
39 {
40   messageQueue = tmessageQueue;
41   messageReceiver = tmessageReceiver;
42   osdReceiver = tosdReceiver;
43   chanList = tchanList;
44   
45   audio = Audio::getInstance();
46   video = Video::getInstance();
47   logger = Log::getInstance();
48   vdr = VDR::getInstance();
49   initted = false;
50
51   subtitlesShowing = false;
52   videoStartup = false;
53
54   stopNow = false;
55   state = S_STOP;
56
57   video->turnVideoOn();
58 }
59
60 PlayerLiveTV::~PlayerLiveTV()
61 {
62   if (initted) shutdown();
63 }
64
65 int PlayerLiveTV::init()
66 {
67   if (initted) return 0;
68
69   demuxer = new DemuxerTS();
70   if (!demuxer) return 0;
71   subtitles = new DVBSubtitles(osdReceiver);
72   if (!subtitles) return 0;
73
74   teletext = new TeletextDecoderVBIEBU();
75  
76   if (!demuxer->init(this, audio, video, teletext, 2097152, 524288, 65536))
77   {
78     logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
79     shutdown();
80     return 0;
81   }
82
83   vfeed.init();
84   afeed.init();
85   tfeed.init();
86
87   video->stop();
88   video->blank();
89   audio->stop();
90
91   initted = true;
92   return 1;
93 }
94
95 int PlayerLiveTV::shutdown()
96 {
97   if (!initted) return 0;
98   stop();
99   initted = false;
100
101   delete demuxer;
102   delete subtitles;
103   delete teletext;
104   teletext = NULL;
105   return 1;
106 }
107
108 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
109 {
110   return demuxer->getmpAudioChannels();
111 }
112
113 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
114 {
115   return demuxer->getac3AudioChannels();
116 }
117
118 int PlayerLiveTV::getCurrentAudioChannel()
119 {
120   return demuxer->getAID();
121 }
122
123 void PlayerLiveTV::setAudioChannel(int newChannel, int type)
124 {
125   return demuxer->setAID(newChannel,type);
126 }
127
128 void PlayerLiveTV::setSubtitleChannel(int newChannel)
129 {
130   return demuxer->setSubID(newChannel);
131 }
132
133 int *PlayerLiveTV::getTeletxtSubtitlePages()
134 {
135     return teletext->getSubtitlePages();
136 }
137
138 int PlayerLiveTV::getCurrentSubtitleChannel(){
139     return demuxer->getSubID();
140 }
141
142 bool PlayerLiveTV::toggleSubtitles()
143 {
144   if (!subtitlesShowing)
145   {
146     subtitlesShowing = true;
147     subtitles->show();
148   }
149   else
150   {
151     subtitlesShowing = false;
152     subtitles->hide();
153   }
154   return subtitlesShowing;
155 }
156
157
158 void  PlayerLiveTV::turnSubtitlesOn(bool ison) {
159  if (ison)
160   {
161     subtitlesShowing = true;
162     subtitles->show();
163   }
164   else
165   {
166     subtitlesShowing = false;
167     subtitles->hide();
168   }
169
170 }
171 // ----------------------------------- Externally called events
172
173 void PlayerLiveTV::go(ULONG index)
174 {
175   struct PLInstruction i;
176   i.instruction = I_SETCHANNEL;
177   i.channelIndex = index;
178   instructions.push(i);
179   threadStart();
180 }
181
182 void PlayerLiveTV::setChannel(ULONG index)
183 {
184   logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
185   struct PLInstruction i;
186   i.instruction = I_SETCHANNEL;
187   i.channelIndex = index;
188   instructions.push(i);  
189   threadSignalNoLock();
190 }
191
192 void PlayerLiveTV::stop()
193 {
194   logger->log("PlayerLiveTV", Log::DEBUG, "stop");
195   struct PLInstruction i;
196   i.instruction = I_STOP;
197   instructions.push(i);
198   threadSignal();
199   threadStop();
200 }
201
202 // ----------------------------------- Callback
203
204 void PlayerLiveTV::call(void* caller)
205 {
206   if (caller == demuxer)
207   {
208     logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
209
210     int dxCurrentAspect = demuxer->getAspectRatio();
211     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
212     {
213       if (video->getTVsize() == Video::ASPECT16X9)
214       {
215         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
216         video->setAspectRatio(Video::ASPECT4X3);
217       }
218       else
219       {
220         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
221       }
222
223       Message* m = new Message();
224       m->from = this;
225       m->to = messageReceiver;
226       m->message = Message::PLAYER_EVENT;
227       m->parameter = PlayerLiveTV::ASPECT43;
228       messageQueue->postMessageFromOuterSpace(m);
229     }
230     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
231     {
232       if (video->getTVsize() == Video::ASPECT16X9)
233       {
234         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
235         video->setAspectRatio(Video::ASPECT16X9);
236       }
237       else
238       {
239         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
240       }    
241
242       Message* m = new Message();
243       m->from = this;
244       m->to = messageReceiver;
245       m->message = Message::PLAYER_EVENT;
246       m->parameter = PlayerLiveTV::ASPECT169;
247       messageQueue->postMessageFromOuterSpace(m);
248     }
249     else
250     {
251       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
252     }
253   }
254   else if (caller == &afeed)
255   {
256     if (state == S_VIDEOSTARTUP)
257     {
258       logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
259       videoStartup = true;
260       threadSignalNoLock();
261     }
262   }
263 }
264
265 // -----------------------------------
266
267 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
268 {
269   // Flag:
270   // 0 = normal stream packet
271   // 1 = stream end
272   // 2 = connection lost
273
274 //  logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
275
276   if (flag == 1)
277   {
278     if (data) abort();
279     
280     Message* m = new Message();
281     m->from = this;
282     m->to = messageReceiver;
283     m->message = Message::PLAYER_EVENT;
284     m->parameter = PlayerLiveTV::STREAM_END;
285     messageQueue->postMessageFromOuterSpace(m);
286   }
287         
288   if (streamChunks.size() < 11)
289   {
290     StreamChunk s;
291     s.data = data;
292     s.len = len;
293     streamChunks.push(s);
294     threadSignalNoLock();
295   }
296   else
297   {
298     // Too many chunks in streamChunks, drop this chunk
299     free(data);
300     logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
301   }
302 }
303
304 void PlayerLiveTV::clearStreamChunks()
305 {
306   while(streamChunks.size())
307   {
308     logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
309     struct StreamChunk s = streamChunks.front();
310     streamChunks.pop();
311     free(s.data);
312   }
313 }
314
315 void PlayerLiveTV::chunkToDemuxer()
316 {
317   StreamChunk s = streamChunks.front();
318   streamChunks.pop();
319   //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
320   /*int a = */demuxer->put((UCHAR*)s.data, s.len);
321   //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
322   free(s.data);  
323 }
324
325 void PlayerLiveTV::switchState(UCHAR newState)
326 {
327   logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
328
329   switch(state)
330   {
331     case S_STOP:   // FROM S_STOP
332     {
333       switch(newState)
334       {
335         case S_VIDEOSTARTUP:
336         {
337           video->blank();
338           video->reset();
339           video->sync();
340           video->play();
341           video->pause();
342
343           audio->stop();
344           audio->unPause();
345           audio->reset();
346           audio->setStreamType(Audio::MPEG2_PES);
347           audio->sync();
348           audio->play();
349           audio->pause();
350
351           demuxer->reset();
352           demuxer->seek();
353
354           afeed.start();
355           vfeed.start();
356           subtitles->start();
357           tfeed.start();
358           
359           state = newState;
360           return;
361         }
362         default:
363         {
364           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
365           abort();
366           break;
367         }
368       }
369     }
370
371     case S_VIDEOSTARTUP:     // FROM S_VIDEOSTARTUP
372     {
373       switch(newState)
374       {
375         case S_PREBUFFERING:
376         {
377           vfeed.release();
378           state = newState;
379           return;
380         }
381         
382         case S_VIDEOSTARTUP:
383         {
384           vdr->stopStreaming();
385           clearStreamChunks(); 
386           vfeed.stop();
387           afeed.stop();
388           subtitles->stop();
389           tfeed.stop();
390                            
391           video->blank();
392           video->reset();
393           video->sync();
394           video->play();
395           video->pause();
396
397           audio->stop();
398           audio->unPause();
399           audio->reset();
400           audio->setStreamType(Audio::MPEG2_PES);
401           audio->sync();
402           audio->play();
403           audio->pause();
404
405           demuxer->reset();
406           demuxer->seek();
407
408           afeed.start();
409           vfeed.start();
410           subtitles->start();     
411           tfeed.start();
412           state = newState;
413           return;
414         }        
415         case S_STOP:
416         { 
417           vdr->stopStreaming();
418           clearStreamChunks();
419           vfeed.stop();
420           afeed.stop();
421 subtitles->stop();        
422  tfeed.stop();         
423  video->stop();          video->blank();
424           audio->stop();
425           audio->reset();
426           video->reset();
427           state = newState;
428           return;
429         }
430         default:
431         {
432           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
433           abort();
434           break;
435         }        
436       }
437     }
438     
439     case S_PREBUFFERING:    // FROM S_PREBUFFERING
440     {
441       switch(newState)
442       {
443         case S_PLAY:
444         {
445           audio->unPause();
446           video->unPause();
447           state = newState;
448           return;
449         }
450         case S_VIDEOSTARTUP:
451         {
452           vdr->stopStreaming();
453           clearStreamChunks();
454           vfeed.stop();
455           afeed.stop();
456           subtitles->stop();
457           tfeed.stop();
458           video->stop();
459           video->blank();
460           audio->stop();
461           audio->unPause();
462           audio->reset();
463
464           video->reset();
465           video->sync();
466           video->play();
467           video->pause();
468
469           audio->setStreamType(Audio::MPEG2_PES);
470           audio->sync();
471           audio->play();
472           audio->pause();
473
474           demuxer->reset();
475           demuxer->seek();
476
477           afeed.start();
478           vfeed.start();
479           subtitles->start();
480           tfeed.start();
481
482           state = newState;
483           return;
484         }
485         case S_STOP:
486         {
487           vdr->stopStreaming();
488           clearStreamChunks();
489           vfeed.stop();
490           afeed.stop();
491           subtitles->stop();
492           tfeed.stop();
493           video->stop();
494           video->blank();
495           audio->stop();
496           audio->reset();
497           video->reset();
498           state = newState;
499           return;        
500         }
501         default:
502         {
503           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
504           abort();
505           break;
506         }        
507       }
508     }
509     
510     case S_PLAY:     // FROM S_PLAY
511     {
512       switch(newState)
513       {
514         case S_STOP:
515         { 
516           vdr->stopStreaming();
517           clearStreamChunks();
518           vfeed.stop();
519           afeed.stop();
520           subtitles->stop();
521           tfeed.stop();
522           video->stop();
523           video->blank();
524           audio->stop();
525           audio->reset();
526           video->reset();
527           state = newState;
528           return;
529         }
530         case S_VIDEOSTARTUP:
531         {
532           vdr->stopStreaming();
533           clearStreamChunks();
534           vfeed.stop();
535           afeed.stop();
536           subtitles->stop();
537           tfeed.stop();
538           video->stop();
539           video->blank();
540           audio->stop();
541           audio->unPause();
542           audio->reset();
543
544           video->reset();
545           video->sync();
546           video->play();
547           video->pause();
548
549           audio->setStreamType(Audio::MPEG2_PES);
550           audio->sync();
551           audio->play();
552           audio->pause();
553
554           demuxer->reset();
555           demuxer->seek();
556
557           afeed.start();
558           vfeed.start();
559           subtitles->start();
560           tfeed.start();
561
562           state = newState;
563           return;
564         }
565         default:
566         {
567           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
568           abort();
569           break;
570         }        
571       }
572     }    
573   }  
574 }
575
576 bool PlayerLiveTV::checkError()
577 {
578   if (!vdr->isConnected())
579   {
580     if (state != S_STOP) switchState(S_STOP);
581     
582     Message* m = new Message();
583     m->from = this;
584     m->to = messageReceiver;
585     m->message = Message::PLAYER_EVENT;
586     m->parameter = PlayerLiveTV::CONNECTION_LOST;
587     messageQueue->postMessageFromOuterSpace(m);
588     
589     return true;
590   }   
591   return false;
592 }
593
594 void PlayerLiveTV::optimizeInstructionQueue()
595 {
596   // Walk the list
597   
598   // Currently there are only 2 instruction types, so this is a bit overkill...
599
600   struct PLInstruction i;
601   while(instructions.size() > 1)
602   {
603     i = instructions.front();
604     if (i.instruction == I_SETCHANNEL)
605     {
606       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant
607     }
608     else if (i.instruction == I_STOP)
609     {
610       return; // return here and ensure the next instruction will be stop
611     }
612   }
613 }
614
615 void PlayerLiveTV::threadMethod()
616 {
617   while(1)
618   {
619     if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
620     {
621       switchState(S_PREBUFFERING);
622       videoStartup = false;
623       preBufferCount = 0;
624       
625       checkError();
626     }  
627   
628     while(!instructions.empty())
629     {
630       if (instructions.size() > 1) optimizeInstructionQueue();
631
632       struct PLInstruction i = instructions.front();
633       instructions.pop();
634     
635       if (i.instruction == I_SETCHANNEL)
636       {
637         logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
638
639         switchState(S_VIDEOSTARTUP);
640         
641         if (!checkError())
642         {
643           Channel* chan = (*chanList)[i.channelIndex];
644           chan->loadPids();
645           demuxer->setVID(chan->vpid);
646           if (chan->numAPids > 0) 
647           {
648             demuxer->setAID(chan->apids[0].pid,0);
649             logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
650           }
651           else 
652           {
653             logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
654           }
655           if (chan->numSPids > 0)
656             demuxer->setSubID(chan->spids[0].pid);
657           demuxer->setTID(chan->tpid);
658           teletext->ResetDecoder();
659           int streamSuccess = vdr->streamChannel(chan->number, this);
660           if (!checkError() && !streamSuccess)
661           {      
662             Message* m = new Message();
663             m->from = this;
664             m->to = messageReceiver;
665             m->message = Message::PLAYER_EVENT;
666             m->parameter = PlayerLiveTV::STREAM_END;
667             messageQueue->postMessageFromOuterSpace(m);
668           }
669         }
670       }
671       else if (i.instruction == I_STOP)
672       {
673         logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
674         switchState(S_STOP);
675         checkError();
676
677         stopNow = true;
678         break;
679       }
680     }
681
682     if (stopNow) break;
683
684     while(streamChunks.size())
685     {
686       chunkToDemuxer();
687
688       if (state == S_PREBUFFERING)
689       {
690         ++preBufferCount;
691         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
692         logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
693         
694         Message* m = new Message();
695         m->from = this;
696         m->to = messageReceiver;
697         m->message = Message::PLAYER_EVENT;
698         m->parameter = PlayerLiveTV::PREBUFFERING;
699         m->tag = percentDone;
700         messageQueue->postMessageFromOuterSpace(m);
701
702         if (preBufferCount == preBufferAmount)
703         {
704           switchState(S_PLAY);
705           checkError();
706         }
707       }
708     }
709     
710     threadLock();
711     threadWaitForSignal(); // unlocks and waits for signal
712     threadUnlock();
713   }
714
715   logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
716 }
717