TechNazgul RSS

Friday, March 25, 2011

Switching between XBMC and MythTV via remote control



Assuming you’ve tackled the annoying problem of properly auto-starting and resuming irexec (explained in part 1 and part 2), the next thing you might want to do is to be able to switch between XBMC and MythTV on the same machine.  I do this mainly so that I can watch LiveTV on MythTV, and use XBMC for everything else.

 

As it turns out, much of the hard work has been done for us and is available on Google Code, though I’ve modified the code very slightly below to work better in my setup.

 

So, let’s begin:

 

1. Create your appswitch.pl script

I put mine in ~/home/scripts/appswitch.pl.

 

Be sure to replace any curly quotes ‘’ with straight quotes '' when creating your script.  Also check all variables and paths in the “Global vars, paths…” section to ensure they work in your setup.

 

What this script does:

This script checks for what processes are running.  If neither xbmc or mythtv is running, it starts xbmc.  If one or the other is running, it kills the running process and starts the opposite (i.e. kills xbmc and starts myth, or vice-versa).

 

#!/usr/bin/perl -w
##############################################################
## Name: appswitch.pl
##
## Purpose: Executed by irexec this script will cycle between mythtv and xbmc using a single button.
##     
## (C)opyright 2008 Arksoft.
##                                                                                                
## Author: Arkay
##
## Ver 1.0: 4-08-2008.  Initial version.
##

## Modified slightly in March of 2011 by TechNazgul
##############################################################
# Require and Use Clauses.
##############################################################

use strict;                                     #Keeps code neat.
use Getopt::Std;                            #Getopt module for option preprocessing.
use vars qw/ $opt_d $opt_h /;     #Option Processing vars.
use POSIX qw(strftime);               #Time routine we need.

##############################################################
# Prototype definitions
##############################################################

sub logmsg(@);                  #Message logger so we can track what's been going on.
sub process_opts();         #Option processing.. Nothing exiting for this script.
sub do_command($);      #Execute a shell command for lazy perl programmers :)
sub check_proc($);          #Check if a process is running.
sub startproc($);

##############################################################
# Constant Definitions.
##############################################################

my ($TRUE) = 1;
my ($FALSE) = 0;

##############################################################
# Global vars, paths, commands to call.
##############################################################

my ($LOG) = "/home/xbmc/scripts/appswitch.log";         #Log location.
my ($LOGSIZE) = 1024;                                               #Maximum log size in kbytes, self pruning.
my ($DEBUG) = $FALSE;                                               #Debugging default is off.
my ($BASENAME) = $0;                                                #How was the program called?

my ($MYTHTV)="/usr/bin/mythfrontend";                      #Process name to start
my ($XBMC)="/usr/bin/xbmc";                                    #Process name to start
my ($KMYTHTV)="mythfrontend.real";                         #Process name to use with killall
my ($KXBMC)="xbmc.bin";                                             #Process name to use with killall
my ($KMPLAYER)="mplayer";                                       #Also kill any external players
my ($KILLALL)="/usr/bin/killall";                                   #kill command.
my ($AUDIOHACK)="/usr/bin/iecset audio on";             #Shitty Alsa hack with intel HDA

##############################################################
# The Mainline.
##############################################################
MAIN:
{
        my ($command);
        process_opts();

        logmsg "$BASENAME started : PID($$)";

        my ($myth)=check_proc($MYTHTV);
        my ($xbmc)=check_proc($XBMC);

        SWITCH:
        {
                if (($myth == $TRUE ) && ($xbmc == $TRUE))
                {
                        logmsg "Killing $XBMC and $MYTHTV";
                        $command="$KILLALL $KMYTHTV"; do_command($command);
                        $command="$KILLALL $KMPLAYER"; do_command($command);
                        $command="$KILLALL $KXBMC"; do_command($command);
                        sleep(2);

                        $myth=check_proc($MYTHTV);
                        if ($myth == $TRUE)
                        {
                                logmsg "Killing $MYTHTV (forced)";
                                $command="$KILLALL -9 $KMYTHTV"; do_command($command);
                                $command="$KILLALL -9 $KMPLAYER"; do_command($command);
                        }

                        $xbmc=check_proc($XBMC);
                        if ($xbmc == $TRUE)
                        {
                                logmsg "Killing $XBMC (forced)";
                                $command="$KILLALL -9 $KXBMC"; do_command($command);
                        }
                        do_command($AUDIOHACK);
                        startproc("export XBMC_PLATFORM_MODE=1 ; $XBMC");
                        last SWITCH;
                }
                               
                if ($myth == $TRUE)
                {
                        logmsg "Killing $MYTHTV";
                        $command="$KILLALL $KMYTHTV"; do_command($command);
                        $command="$KILLALL $KMPLAYER"; do_command($command);
                        sleep(2);
                        $myth=check_proc($MYTHTV);
                        if ($myth == $TRUE)
                        {
                                logmsg "Killing $MYTHTV (forced)";
                                $command="$KILLALL -9 $KMYTHTV"; do_command($command);
                                $command="$KILLALL -9 $KMPLAYER"; do_command($command);
                        }
                        do_command($AUDIOHACK);
                        startproc("export XBMC_PLATFORM_MODE=1 ; $XBMC");
                        last SWITCH;
                }

                if ($xbmc)
                {
                        logmsg "Killing $XBMC";
                        $command="$KILLALL $KXBMC"; do_command($command);
                        sleep(2);
                        $xbmc=check_proc($XBMC);
                        if ($xbmc == $TRUE)
                        {
                                logmsg "Killing $XBMC (forced)";
                                $command="$KILLALL -9 $KXBMC"; do_command($command);
                        }
                        do_command($AUDIOHACK);
                        startproc($MYTHTV);
                        last SWITCH;
                }
               do_command($AUDIOHACK);
               startproc("export XBMC_PLATFORM_MODE=1 ; $XBMC");
        }

        logmsg "$BASENAME Completed.";
}

##############################################################
# startproc()
# Execute a given command.
##############################################################
sub startproc($)
{
        my ($proc)=@_;
        logmsg "Starting $proc";
        exec("export DISPLAY=:0; $proc &");
}

##############################################################
# check_proc($)
# Check if processes are running that should stop shutdown from occuring.
##############################################################
sub check_proc($)
{
        my ($proc)=@_;
        my (@output);
        my ($command);
        my ($count)=0;
        my ($running)=$FALSE;

        logmsg "Checking for active process $proc.";

        $command="ps -ef | grep \"$proc\" | grep -v grep |wc -l";      
        @output=do_command($command);

        if (@output)
        {
                $count=$output[0];
                chomp ($count);
        }

        if ($count > 0)
        {
                logmsg "Found active process : $proc ($count running).";
                $running=$TRUE;
        }
        return($running);
}

##############################################################
# logmsg
# Little routine to write to the log file.
# Rotates around $LOGSIZE bytes.
##############################################################
sub logmsg(@)
{
        my ($string)=@_;
        my $time=scalar localtime;
        my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
        my (@lines,$line);

        ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)=stat("$LOG");

        if (defined($size))
        {
                $size=$size/1024;                               #size in kbyte

                if ($size >= $LOGSIZE)
                {
                        unlink ("$LOG.old") if (-e("$LOG.old"));
                        rename ($LOG,"$LOG.old");
                }
        }

        print "$time : $string\n" if ($DEBUG==$TRUE);

        if (open (LOG,">>$LOG"))
        {
                if ($string =~ /\n/)
                {
                        @lines=split(/\n/,$string);
                        foreach $line (@lines)
                        {
                                print LOG "$time : $line\n";
                        }
                }
                else
                {
                                print LOG "$time : $string\n";
                }
                close (LOG);
        }
        else
        {
                print "Unable to open LOG $LOG : $!";
        }
}

##############################################################
# process_opts()
# Set Global option flags dependant on command line input.
##############################################################
sub process_opts()
{
        getopts('dh');

        $DEBUG=$TRUE if ($opt_d);      
        exit(usage(1)) if ($opt_h);
}

##############################################################
# usage()
# Output Relevant Usage strings if incorrect opts are given.
##############################################################
sub usage()
{
        my($ucode)=@_;

        if ($ucode == 1)
        {
                print "Usage: $BASENAME [-dh]\n";
                return(0);
        }
}

##############################################################
# sub do_command($)
# use system call to execute command. Returns output of command in array.
##############################################################
sub do_command($)
{
    my ($command)=@_;
    my (@output);
    my ($exit_value)=0;

    logmsg "Executing $command" if ($DEBUG == $TRUE);

    @output=`$command`;

    $exit_value = $? >> 8;

    if ($exit_value != 0)
    {
        logmsg "Error executing $command : $!";
    }
    return(@output);
}

 

 

2. Configure irexec to call the script via remote button press

In your home directory, you should already have a .lircrc file.  You need to add to that the following code, either directly in that file, or by placing it in your ~/.lirc directory and calling it via ‘include’ in your .lircrc file.  In my case, I’m doing it via an inclusion.

 

In ~/.lircrc, add the following:

 

include ~/.lirc/irexec

 

Then, create ~/.lirc/irexec as (replace /home/xbmc with /home/USERNAME)

 

begin
     prog = irexec
     button = LiveTV
     config = /home/xbmc/scripts/appswitch.pl &
end

 

I used the remote button LiveTV as it wasn’t used for anything else by either XBMC or MythTV by default.

 

3. Restart lirc to load the new configuration

sudo /etc/init.d/lirc restart

 

4. Ensure irexec is running and test

 

Check for irexec by typing “ps –ef|grep irexec”  If it isn’t, fire it up manually with:

irexec –d

Then, press the LiveTV button on your remote and wait… If it doesn’t work as planned, kill all running irexec processes and restart irexec without the –d flag.  This will keep it running in the foreground and allow you to watch the script output as it progresses through it. If you see nothing, something is wrong with your irexec script.  If you see script output in your terminal window, then the irexec is being successfully called via remote. You can also experiment with different options of calling irexec as root or as your user, which sometimes yields differing results with the app restart script.

 

Technorati Tags: ,,

4 comments:

  1. Hi there, I'm having trouble with mythtv forcing itself to restart, so the scrypt is working but mythtv is still on top. Any ideas

    ReplyDelete
  2. Thanks for the good work of explaining lirc/irexec and tuning with xbmc/mythtv.

    You advocate running irexec as root but you also added that "You can also experiment with different options of calling irexec as root or as your user, which sometimes yields differing results with the app restart script."

    After much of my own experimentation, I found that running irexec as root in mythbuntu can lead to some unexpected (non-)results. Mythfrontend will not start from irexec running as root but will when running as a non-privileged user. irexec probably should not run as root by default. sudoers can be configured to allow root operations such as shutdown or reboot when necessary.

    ReplyDelete
  3. Would like to point out that these instructions, as detailed and appreciated as they are, are missing on critical step:
    $ chmod +x /path/to/appswitch.pl

    ReplyDelete
  4. Hi,

    I modified the script to use su -m "user" -c "command" for xbmc and mythtv, so I can execute as a normal user. irexec still runs as root.

    This assumes that xbmc user is whats running the desktop...

    jason@jason-GA-78LMT-S2P:~/scripts$ diff -w old.pl appswitch.pl
    37c37
    < my ($LOG) = "/home/xbmc/scripts/appswitch.log"; #Log location.
    ---
    > my ($LOG) = "/home/xbmc/scripts/appswitch.log"; #Log location.
    40a41
    > my ($SMYTHTV)="su -m \"xbmc\" -c \"/usr/bin/mythfrontend\""; #Command to start
    41a43
    > my ($SXBMC)="su -m \"xbmc\" -c \"/usr/bin/xbmc\""; #Command to start
    81c83
    < startproc("export XBMC_PLATFORM_MODE=1 ; $XBMC");
    ---
    > startproc("export XBMC_PLATFORM_MODE=1 ; $SXBMC");
    99c101
    < startproc("export XBMC_PLATFORM_MODE=1 ; $XBMC");
    ---
    > startproc("export XBMC_PLATFORM_MODE=1 ; $SXBMC");
    114c116
    < startproc($MYTHTV);
    ---
    > startproc($SMYTHTV);
    118c120
    < startproc("export XBMC_PLATFORM_MODE=1 ; $XBMC");
    ---
    > startproc("export XBMC_PLATFORM_MODE=1 ; $SXBMC");

    ReplyDelete

Followers

Facebook