]> git.vomp.tv Git - vompclient.git/blob - command.cc
Demuxer::scanForVideo()
[vompclient.git] / command.cc
1 /*
2     Copyright 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "command.h"
22 #ifdef WIN32
23 #include "remotewin.h"
24 #endif
25
26 Command* Command::instance = NULL;
27
28 Command::Command()
29 {
30   if (instance) return;
31   instance = this;
32   initted = 0;
33   isStandby = 0;
34   firstBoot = 1;
35   connLost = NULL;
36 }
37
38 Command::~Command()
39 {
40   instance = NULL;
41 }
42
43 Command* Command::getInstance()
44 {
45   return instance;
46 }
47
48 int Command::init()
49 {
50   if (initted) return 0;
51   initted = 1;
52
53   logger = Log::getInstance();
54   viewman = ViewMan::getInstance();
55   remote = Remote::getInstance();
56
57   if (!logger || !viewman || !remote)
58   {
59     initted = 0;
60     return 0;
61   }
62 #ifndef WIN32
63   pthread_mutex_init(&masterLock, NULL);
64 #else
65   masterLock=CreateMutex(NULL,FALSE,NULL);
66 #endif
67
68   return 1;
69 }
70
71 int Command::shutdown()
72 {
73   if (!initted) return 0;
74   initted = 0;
75   return 1;
76 }
77
78 void Command::stop()
79 {
80 //  VDR::getInstance()->cancelFindingServer();
81   udp.shutdown();
82   irun = 0;
83 }
84
85 void Command::doWallpaper()
86 {
87   Video* video = Video::getInstance();
88
89   // Blue background
90   View* v = new View();
91   v->create(video->getScreenWidth(), video->getScreenHeight());
92   v->setBackgroundColour(Colour::VIDEOBLUE);
93   v->draw();
94   viewman->add(v);
95   viewman->updateView(v);
96   viewman->removeView(v);
97
98   // Wallpaper
99   wallpaper = new VWallpaper();
100   if (video->getFormat() == Video::PAL)
101   {
102     logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
103     wallpaper->init("/wallpaperPAL.jpg");
104   }
105   else
106   {
107     logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
108     wallpaper->init("/wallpaperNTSC.jpg");
109   }
110   wallpaper->draw();
111   viewman->add(wallpaper);
112   viewman->updateView(wallpaper);
113 }
114
115 void Command::run()
116 {
117   if (!initted) return;
118   irun = 1;
119 #ifndef WIN32
120   mainPid = getpid();
121 #endif
122
123   // just in case
124   Video::getInstance()->signalOn();
125   Led::getInstance()->on();
126
127   doWallpaper();
128
129   // End of startup. Lock the mutex and put the first view up
130 //  logger->log("Command", Log::DEBUG, "WANT LOCK");
131 #ifndef WIN32
132   pthread_mutex_lock(&masterLock);
133 #else
134   WaitForSingleObject(masterLock, INFINITE );
135 #endif
136   //logger->log("Command", Log::DEBUG, "LOCKED");
137
138   VConnect* vconnect = new VConnect();
139   viewman->add(vconnect);
140   vconnect->run();
141
142   // Start method 2 of getting commands in...
143   udp.run(this);
144
145   UCHAR button = 0;
146   while(irun)
147   {
148     // unlock and wait
149     //logger->log("Command", Log::DEBUG, "UNLOCK");
150 #ifndef WIN32
151     pthread_mutex_unlock(&masterLock);
152 #else
153     ReleaseMutex(masterLock);
154 #endif
155     button = remote->getButtonPress(2);  // FIXME why is this set to 2 and not 0? so it can quit
156     // something happened, lock and process
157
158     //logger->log("Command", Log::DEBUG, "WANT LOCK");
159 #ifndef WIN32
160     pthread_mutex_lock(&masterLock);
161 #else
162     WaitForSingleObject(masterLock, INFINITE );
163 #endif
164     //logger->log("Command", Log::DEBUG, "LOCK");
165
166     if ((button == Remote::NA_NONE) || (button == Remote::NA_UNKNOWN)) continue;
167
168     if (button != Remote::NA_SIGNAL) handleCommand(button);
169     processMessageQueue();
170   }
171
172   //logger->log("Command", Log::DEBUG, "UNLOCK");
173 #ifndef WIN32
174   pthread_mutex_unlock(&masterLock);
175 #else
176   ReleaseMutex(masterLock);
177 #endif
178 }
179
180 void Command::postMessage(Message* m)
181 {
182   // This is locked here in case the main loop is not waiting for an event, but is processing one
183   // it could be killed but then not react to it because the signal wouldn't cause
184   // remote->getButtonPress to break
185   // locking the mutex ensures that the master thread is waiting on getButtonPress
186
187
188   //logger->log("Command", Log::DEBUG, "WANT LOCK");
189 #ifndef WIN32
190   pthread_mutex_lock(&masterLock);
191 #else
192   WaitForSingleObject(masterLock, INFINITE );
193 #endif
194   //logger->log("Command", Log::DEBUG, "LOCK");
195   MessageQueue::postMessage(m);
196
197 #ifndef WIN32
198   kill(mainPid, SIGURG);
199   pthread_mutex_unlock(&masterLock);
200 #else
201   ((RemoteWin*)Remote::getInstance())->Signal();
202   ReleaseMutex(masterLock);
203 #endif
204   //logger->log("Command", Log::DEBUG, "UNLOCK");
205 }
206
207 void Command::postMessageNoLock(Message* m)
208 {
209   // As above but use this one if this message is being posted because of a button press
210   // the mutex is already locked, locking around postMessage is not needed as the
211   // queue is guaranteed to be run when the button has been processed
212   MessageQueue::postMessage(m);
213 }
214
215 bool Command::postMessageIfNotBusy(Message* m)
216 {
217   // This is for the timers module
218   // If the masterlock is locked then the timers module wants to
219   // cancel delivery
220   //logger->log("Command", Log::DEBUG, "TRY LOCK");
221 #ifndef WIN32
222   if (pthread_mutex_trylock(&masterLock) != EBUSY)
223   {
224     //logger->log("Command", Log::DEBUG, "LOCK");
225     MessageQueue::postMessage(m);
226     kill(mainPid, SIGURG);
227     pthread_mutex_unlock(&masterLock);
228     //logger->log("Command", Log::DEBUG, "UNLOCK");
229     return true;
230   }
231   else
232   {
233     return false;
234   }
235 #else
236   switch (WaitForSingleObject(masterLock, 0 ))
237   { //FIXME this is not "if not busy" check
238     case WAIT_OBJECT_0: //but with proper argument 0 this did not work
239     // case WAIT_ABANDONED:
240     MessageQueue::postMessage(m);
241     ((RemoteWin*)Remote::getInstance())->Signal();
242     ReleaseMutex(masterLock);
243     return true;
244
245     case WAIT_ABANDONED: return false;
246     case WAIT_TIMEOUT: return false;
247   }
248     return false;
249 #endif
250 }
251
252 void Command::postMessageFromOuterSpace(Message* m)
253 {
254   /*
255   Yet another way of getting messages into Command. This one is for events that
256   are not standard button presses (or UDP generated buttons). It is also not for
257   events that are generated as a result of other events (events that can safely
258   call postMessageNoLock and be guaranteed that the message will be processed
259   because it is known that the queue is currently being processed).
260   This is for events that come from outer space and can occur when the master
261   mutex is locked or not, they need to be queued and executed but it doesn't
262   matter when.
263   Actually so far it is for events caused by the video stream - aspect ratio
264   changes. These can occur when the master mutex is locked and so postMessage
265   doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
266   locked at the time then the message could be sat around a while before
267   being noticed.
268   The whole message system was at first supposed to prevent the problem of
269   calling a function on an object that had just been deleted, by ordering
270   messages such that all calls are done before object deletion. However,
271   because of the new centralised messaging system and the fact that ViewMan
272   locates the destination object before calling it, the messaging system now
273   allows the kind of sloppy calls it was supposed to stop. Weird huh. This
274   is mentioned here because the video stream might generate an event just as
275   the user hits stop. The mutex is locked, and by the time the message
276   is examined the vvideorec/live has been deleted. This doesn't matter because
277   viewman will drop the message if it can't find the matching object to
278   deliver it to.
279   Finally, all this is fine and dandy, except that I'm not 100% sure that
280   this sloppy postMessage and hope a queued signal will force it to be processed
281   thingy will actually work. Hmmm.
282   Lastly <g>, I will consider making the naming system a little more sane
283   if this works.
284   */
285
286   logger->log("Command", Log::DEBUG, "PMFOS called");
287   MessageQueue::postMessage(m);
288
289 #ifndef WIN32
290   kill(mainPid, SIGURG);
291 #else
292   ((RemoteWin*)Remote::getInstance())->Signal();
293 #endif
294 }
295
296 void Command::processMessage(Message* m)
297 {
298   logger->log("Command", Log::DEBUG, "processing message %i", m->message);
299
300   // Timer handling is very weird at the mo. Take them out here and convert
301   if (m->message == Message::TIMER)
302   {
303     // FIXME - go to one message queue only - then instead of having
304     // objects deriving from messagequeues, make them derive from
305     // messagereceiver - then one messagequeue can deliver any message to anywhere
306
307     // FIXME - a slight modification - how if messagereceivers were to register
308     // themselves as receivers to avoid the calling-a-deleted-object problem
309     // then only deliver/register/unregister would have to be protected
310
311     // deliver timer
312
313     logger->log("Command", Log::DEBUG, "sending timer to %p with parameter %u", m->to, m->parameter);
314     ((TimerReceiver*)m->to)->timercall(m->parameter);
315 //    handleCommand(Remote::NA_NONE); // in case any timer has posted messages to viewman,
316 //                                    // run viewman message queue here. FIXME improve this!
317 //    break;
318   }
319   else if (m->to == this)
320   {
321     switch(m->message)
322     {
323       case Message::STANDBY:
324       {
325         doStandby();
326         break;
327       }
328
329
330       // << FIXME OBSELETE
331       case Message::STOP_PLAYBACK:
332       {
333         handleCommand(Remote::STOP); // an odd way of doing it, but so simple
334         break;
335       }
336       case Message::STREAM_END:
337       {
338         VVideoLive::getInstance()->streamEnd();
339         break;
340       }
341
342       // Also connection_lost comes from player - anywhere else?
343       // FIXME OBSELETE >>
344
345
346       case Message::VDR_CONNECTED:
347       {
348         doJustConnected((VConnect*)m->from);
349         break;
350       }
351       case Message::SCREENSHOT:
352       {
353         Osd::getInstance()->screenShot("/out.jpg");
354         break;
355       }
356       case Message::CONNECTION_LOST:
357       {
358         doFromTheTop(true);
359         break;
360       }
361       case Message::UDP_BUTTON:
362       {
363         handleCommand(m->parameter);
364         break;
365       }
366     }
367   }
368   else
369   {
370     logger->log("Command", Log::DEBUG, "Sending message to viewman");
371     viewman->processMessage(m);
372   }
373 }
374
375 void Command::handleCommand(int button)
376 {
377   if (isStandby && (button != Remote::POWER)) return;
378
379   if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
380
381   // command was not handled
382
383   switch(button)
384   {
385     case Remote::DF_LEFT:
386     case Remote::DF_RIGHT:
387     case Remote::VOLUMEUP:
388     case Remote::VOLUMEDOWN:
389     {
390       VVolume* v = new VVolume();
391       viewman->add(v);
392       v->handleCommand(button); // this will draw+show
393       return;
394     }
395     case Remote::MUTE:
396     {
397       VMute* v = new VMute();
398       v->draw();
399       viewman->add(v);
400       viewman->updateView(v);
401       return;
402     }
403     case Remote::POWER:
404     {
405       doStandby();
406       return;
407     }
408     case Remote::OK:
409     {
410       if (!connLost) return; // if connLost, handle Remote::OK
411       doFromTheTop(false);
412       return;
413     }
414   }
415 }
416
417 void Command::sig1()
418 {
419 #ifdef DEV
420   Message* m = new Message(); // break into master mutex
421   m->message = Message::SCREENSHOT;
422   m->to = this;
423   postMessage(m);
424 #endif
425 }
426
427 void Command::doStandby()
428 {
429   if (isStandby)
430   {
431     Video::getInstance()->signalOn();
432     Led::getInstance()->on();
433     isStandby = 0;
434
435
436     VConnect* vconnect = new VConnect();
437     viewman->add(vconnect);
438     vconnect->run();
439   }
440   else
441   {
442     viewman->removeAll();
443     Video::getInstance()->signalOff();
444     viewman->updateView(wallpaper);
445
446     VDR::getInstance()->configSave("General", "Last Power State", "Off");
447     VDR::getInstance()->disconnect();
448     Led::getInstance()->off();
449     isStandby = 1;
450 #ifdef WIN32
451     stop(); //different behavoiur on windows, we exit
452 #endif
453   }
454 }
455
456 void Command::doFromTheTop(bool which)
457 {
458   if (which)
459   {
460     connLost = new VInfo();
461     connLost->create(360, 200);
462     if (Video::getInstance()->getFormat() == Video::PAL)
463       connLost->setScreenPos(190, 170);
464     else
465       connLost->setScreenPos(180, 120);
466     connLost->setOneLiner(tr("Connection lost"));
467     connLost->setDropThrough();
468     connLost->setBorderOn(1);
469     connLost->setTitleBarColour(Colour::DANGER);
470     connLost->okButton();
471     connLost->draw();
472     viewman->add(connLost);
473     viewman->updateView(connLost);
474     remote->clearBuffer();
475   }
476   else
477   {
478     VDR::getInstance()->disconnect();
479     viewman->removeAll();
480     viewman->updateView(wallpaper);
481     connLost = NULL;
482     VConnect* vconnect = new VConnect();
483     viewman->add(vconnect);
484     vconnect->run();
485   }
486 }
487
488 void Command::doReboot()
489 {
490   VDR::getInstance()->disconnect();
491   // just kill it...
492   logger->log("Command", Log::NOTICE, "Reboot");
493 #ifndef WIN32
494   reboot(LINUX_REBOOT_CMD_RESTART);
495 #endif //Would we support this on windows?
496 }
497
498 void Command::connectionLost()
499 {
500   Message* m = new Message(); // break into master mutex
501   m->message = Message::CONNECTION_LOST;
502   m->to = this;
503   postMessageNoLock(m);
504 }
505
506 void Command::doJustConnected(VConnect* vconnect)
507 {
508   I18n::initialize();
509   Video* video = Video::getInstance();
510   viewman->removeView(vconnect);
511
512   VInfo* vi = new VInfo();
513   vi->create(400, 200);
514   if (video->getFormat() == Video::PAL)
515     vi->setScreenPos(170, 200);
516   else
517     vi->setScreenPos(160, 150);
518
519   vi->setOneLiner(tr("Connected, loading config"));
520   vi->draw();
521   viewman->add(vi);
522   viewman->updateView(vi);
523
524   VDR* vdr = VDR::getInstance();
525   char* config;
526
527   // See if config says to override video format (PAL/NTSC)
528   config = vdr->configLoad("General", "Override Video Format");
529   if (config)
530   {
531     logger->log("Command", Log::DEBUG, "Override Video Format is present");
532
533     if (   (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
534         || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL))  )
535     {
536       // Oh sheesh, need to switch format. Bye bye TV...
537
538       // Take everything down
539       viewman->removeAll();
540       viewman->removeView(wallpaper);
541       Osd* osd = Osd::getInstance();
542       osd->shutdown();
543       video->shutdown();
544
545       // Get video and osd back up with the new mode
546       if (!strcmp(config, "PAL"))
547       {
548         logger->log("Command", Log::DEBUG, "Switching to PAL");
549         video->init(Video::PAL);
550       }
551       else if (!strcmp(config, "NTSC"))
552       {
553         logger->log("Command", Log::DEBUG, "Switching to NTSC");
554         video->init(Video::NTSC);
555       }
556       osd->init((char*)("/dev/stbgfx"));
557
558       // Put the wallpaper back
559       doWallpaper();
560
561       // Re add the vinfo
562       vi = new VInfo();
563       vi->create(400, 200);
564       if (video->getFormat() == Video::PAL)
565         vi->setScreenPos(170, 200);
566       else
567         vi->setScreenPos(160, 150);
568
569       vi->setOneLiner(tr("Connected, loading config"));
570       vi->draw();
571       viewman->add(vi);
572       viewman->updateView(vi);
573     }
574     else
575     {
576       logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
577     }
578   }
579   else
580   {
581     logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
582   }
583
584   // Power off if first boot and config says so
585   if (firstBoot)
586   {
587     firstBoot = 0;
588
589     logger->log("Command", Log::DEBUG, "Load power after boot");
590
591     config = vdr->configLoad("General", "Power After Boot");
592
593     if (config)
594     {
595       if (!STRCASECMP(config, "On"))
596       {
597         logger->log("Command", Log::INFO, "Config says Power After Boot = On");
598       }
599       else if (!STRCASECMP(config, "Off"))
600       {
601         logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
602         doStandby();
603         delete[] config;
604         return; // quit here
605       }
606       else if (!STRCASECMP(config, "Last state"))
607       {
608         char* lastPowerState = vdr->configLoad("General", "Last Power State");
609         if (lastPowerState)
610         {
611           if (!STRCASECMP(lastPowerState, "On"))
612           {
613             logger->log("Command", Log::INFO, "Config says Last Power State = On");
614           }
615           else if (!STRCASECMP(lastPowerState, "Off"))
616           {
617             logger->log("Command", Log::INFO, "Config says Last Power State = Off");
618             doStandby();
619             delete[] config;
620             return; // quit here
621           }
622           else
623           {
624             logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
625           }
626         }
627         else
628         {
629           logger->log("Command", Log::INFO, "Config General/Last Power State not found");
630         }
631       }
632       else
633       {
634         logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
635       }
636       delete[] config;
637     }
638     else
639     {
640       logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
641     }
642   }
643
644
645   // Go S-Video if config says so
646
647   config = vdr->configLoad("TV", "Connection");
648
649   if (config)
650   {
651     if (!STRCASECMP(config, "S-Video"))
652     {
653       logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
654       video->setConnection(Video::SVIDEO);
655     }
656     else
657     {
658       logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
659       video->setConnection(Video::COMPOSITERGB);
660     }
661     delete[] config;
662   }
663   else
664   {
665     logger->log("Command", Log::INFO, "Config TV/S-Video not found");
666   }
667
668   // Set remote type
669
670   config = vdr->configLoad("General", "Remote type");
671
672   if (config)
673   {
674     if (!STRCASECMP(config, "New"))
675     {
676       logger->log("Command", Log::INFO, "Switching to New remote type");
677       remote->setRemoteType(Remote::NEWREMOTE);
678     }
679     else
680     {
681       logger->log("Command", Log::INFO, "Switching to Old remote type");
682       remote->setRemoteType(Remote::OLDREMOTE);
683     }
684     delete[] config;
685   }
686   else
687   {
688     logger->log("Command", Log::INFO, "Config General/Remote type not found");
689     remote->setRemoteType(Remote::OLDREMOTE);
690   }
691
692   // Get TV aspect ratio
693
694   config = vdr->configLoad("TV", "Aspect");
695   if (config)
696   {
697     if (!STRCASECMP(config, "16:9"))
698     {
699       logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
700       video->setTVsize(Video::ASPECT16X9);
701     }
702     else
703     {
704       logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
705       video->setTVsize(Video::ASPECT4X3);
706     }
707     delete[] config;
708   }
709   else
710   {
711     logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
712     video->setTVsize(Video::ASPECT4X3);
713   }
714
715   config = vdr->configLoad("TV", "Widemode");
716   if (config)
717   {
718     if (!STRCASECMP(config, "Letterbox"))
719     {
720       logger->log("Command", Log::INFO, "Setting letterbox mode");
721       video->setMode(Video::LETTERBOX);
722     }
723     else
724     {
725       logger->log("Command", Log::INFO, "Setting chop-sides mode");
726       video->setMode(Video::NORMAL);
727     }
728     delete[] config;
729   }
730   else
731   {
732     logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
733     video->setMode(Video::NORMAL);
734   }
735
736   config = vdr->configLoad("Advanced", "TCP receive window");
737   if (config)
738   {
739     size_t newTCPsize = atoi(config);
740     delete[] config;
741
742     logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
743     vdr->setReceiveWindow(newTCPsize);
744   }
745   else
746   {
747     logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
748     vdr->setReceiveWindow(2048); // Default
749   }
750
751
752   // config done
753
754   // Save power state = on
755
756   vdr->configSave("General", "Last Power State", "On");
757
758   // Make sure connection didn't die
759   if (!vdr->isConnected())
760   {
761     Command::getInstance()->connectionLost();
762   }
763   else
764   {
765     viewman->removeView(vi);
766
767     VWelcome* vw = new VWelcome();
768     vw->draw();
769     viewman->add(vw);
770     viewman->updateView(vw);
771
772     // Enter pre-keys here
773 //    handleCommand(Remote::THREE);
774 //    handleCommand(Remote::UP);
775 //    handleCommand(Remote::OK);
776 //    handleCommand(Remote::OK);
777 //    handleCommand(Remote::PLAY);
778 //    handleCommand(Remote::DOWN);
779 //    handleCommand(Remote::DOWN);
780 //    handleCommand(Remote::OK);
781   }
782 }