Caller ID from Asterisk to all phones, MythTV’s and SqueezeBoxes

Well, I had some spare time and finally got round to getting CallerID sorted on my phones. I’ve had it enabled on the line for a while now, and even had it doing database lookups to display a name on the phone handsets as well as the number, tho’ that broke when I upgraded Asterisk. I have, however, sorted out that problem and expanded everything a little.

Now, when the phone rings, the number is looked up in a database of known numbers, then sent out to all the phone handsets as well as any active MythTV frontend or Squeezebox on the network! If we are watching the TV a box pops up with the time, date, number, and if found, name of the incoming call. Likewise if we are in the library where we can’t actually hear the phone, then the Squeezebox display will change to the name and number and we can decide wether to answer it or not!

All this is done using a feature in Asterisk call AGI, or Asterisk Gateway Interface. This is very similar to CGI scripts found on web pages. AGI scripts are run from the Asterisk dialplan to do various things. In this case the AGI perl script is run and the incoming call number is passed to it. This number is looked up in a database to see if its a known number. Wether it is known or not, the script sets the full CID info in Asterisk.

It then procedes to use a utility call cidbcast that is in the contrib directory of MythTV. This cidbcast sends out a UDP broadcast over the network to any listening machine, in this case the MythTV backend server which has the mythudprelay program running, another one from the MythTV contrib directory. This picks up the broadcast and sends the message to all MythTV frontends using mythtvosd.

Lastly the AGI script connects to the SlimServer which controls all the Squeezeboxes. Talking over the CLI port (9090) on the server it gets a list of all current players and proceeds to send a simple two line display message consisting of the number and name. The majority of this part of the script is a simple cut and paste of the relevant parts of the Squeezebox plugin written by Max Spicer.

All in all this is a fairly simple thing to implement, but is very useful for those as lazy as me!

Script

#!/usr/bin/perl

use DBI;
use IO::Socket;

# MySQL settings for asterisk
my $dbhost = 'localhost';
my $dbuser = 'asterisk';
my $dbpass = 'asterisk';
my $dbname = 'asterisk';

# Slimserver Settings
my $serverAddress = 'mythbe-1';
my $serverPort = 9090;
my $maxVolume = 75;
my $displayTime = 30;
my $debug = 0;

# Mythtv Setting
my $mythxmlfile = '/usr/lib/asterisk/cidbcast.xml';



################
# Get name from mysql for the incoming number
#
$|=1;

#Get the initial data
     my %input;
     while() {
         chomp;
         last unless length($_);
         if (/^agi_(\w+)\:\s+(.*)$/) {
         $input{$1} = $2;
     }
}

my $dbh = DBI->connect ("dbi:mysql:host=$dbhost:database=$dbname","$dbuser","$dbpass") or die "Can't connect to database: $DBI::errstr\n";
my $sth = $dbh->prepare( "SELECT cid FROM known_numbers WHERE source='$input{callerid}'" );
$sth->execute();
@row = $sth->fetchrow_array();
$cidname = @row[0];
print "SET CALLERID \"@row\"<$input{callerid}>";

###############
# Broadcast info to MythTV
my $command;
$command = "cidbcast --once --file=$mythxmlfile 6947 $input{callerid} \"$cidname\" line";
system("$command &> /dev/null");

################
# Send out to squeezeboxes
# Code ripped from Squeezebox CID plugin by Max Spicer
# http://www.thespicers.net/cid.html
#

my $socket = IO::Socket::INET->new (PeerAddr => $serverAddress,
                                    PeerPort => $serverPort,
                                    Proto    => 'tcp',
                                    Type     => SOCK_STREAM)
or die 'Couldn\'t connect to server';

# Get the number of players
my $playerCount = sendAndReceive('player count ?');
$debug && print "$playerCount players found\n";

# Display message on each player and adjust volume if necessary
for (my $i = 0; $i < $playerCount; $i++) { my $playerId = sendAndReceive("player id $i ?"); # Put the players display at max brightness if it's currently 0 my $powerState = sendAndReceive("$playerId power ?"); my $brightnessPref = $powerState ? 'powerOnBrightness' : 'powerOffBrightness'; my $brightness = sendAndReceive("$playerId playerpref $brightnessPref ?"); $debug && print "brightness: $brightness\n"; if ($brightness == 0) { sendAndReceive("$playerId playerpref $brightnessPref 4"); } $cidname =~ s/\s/%20/g; $debug && print("Sending: $playerId display Incoming%20call:%20$input{callerid} $cidname $displayTime\n"); sendAndReceive("$playerId display Incoming%20call:%20$input{callerid} $cidname $displayTime"); # Drop the volume if necessary my $playerMode = sendAndReceive("$playerId mode ?"); $debug && print "playerMode: $playerMode\n"; if ($playerMode eq "play" && sendAndReceive("$playerId mixer volume ?") > $maxVolume) {
    $debug && print "Decreasing volume\n";
    sendAndReceive("$playerId mixer volume $maxVolume");
  }
}
$debug && print "\n";
exit 1;

# Send given cmd to $socket and return answer with original command removed from
# front if present.  Routine nicked from code by Felix Mueller. :-)
sub sendAndReceive {
  my $cmd = shift;
  return if( $cmd eq "");

  print $socket "$cmd\n";
  $debug > 1 && print "Sent $cmd to server\n";
  my $answer = <$socket>;
  $debug > 1 && print "Server replied: $answer\n";
  $answer =~ s/$cmd //i;
  $answer =~ s/\n//;

  return $answer;
}

Database table structure

mysql> describe known_numbers;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| source | varchar(13) |      | PRI |         |       |
| cid    | varchar(26) |      |     |         |       |
+--------+-------------+------+-----+---------+-------+

MythTV and a Sony PSP

I’m not really much of a gamer, but after having a go on someone’s PSP, I had to get myself one. They are a wonder of modern technology. A small package that crams just about everything you could want to play games, and more besides. It is sold as a games machine, but it makes a pretty good all round portable multimedia system, playing movies, MP3s and holding your holiday snaps on there too!

Most of the functionality is easy to get to work with linux. Basically it is seen as a mass storage device and files can be copied to and from it with the usb cable. The one area that is problematic under linux is movies. Sony have decided to only support an MP4 media format, and even then it has some strange headers so that only certain MP4 files will play. There’s plenty of information out on the net for converting these files, but it can be hidden in various different sources. I’ve written this article to draw together all those sources into one place. I’ve tried to credit the original supply of the information wherever possible.

First step is to get a working copy of ffmpeg that supports the PSP format. This isn’t as easy as it sounds. I ran into problems with the version of compiler I was using. Most up to date distributions ship with gcc4, which will not let you compile the ffmpeg that you need. On Mandrake and Debian, they also ship a version of gcc3, but unfortunately this has a bug in it which means that you will get halfway through the compile and it will crash. In the end I downloaded the source for the latest version of gcc3 (3.4.5) and compiled that. On Mandrake you change the symlink in /etc/alternatives/gcc to the version you want to use, in Debian the symlink is in /usr/bin/gcc

Feel free to try with the version of compiler you have, but if you have any problems then just get the latest version of gcc3. It may be worth trying seeing as it takes a rather long time to compile gcc!

A few other libraries are needed to get this working, but I found the default ones in Mandrake 2006 to work fine, YYMV. The libraries needed are the libfaad, libfaac, libogg and libvorbis. Don’t forget to get the -devel packages for these (-dev under debian).

Once you are ready to compile, I found the best instructions on the linux PS2 site, in a post by “cronocloud“. Here is the basic gist of the post.

Get a snapshot of the ffmpeg CVS from the 10th March at: http://www.nurs.or.jp/~calcium/3gpp/sources/ffmpeg-050310-0.30.tar.gz, along with a patch from: http://www.nurs.or.jp/~calcium/3gpp/sources/ffmpeg_patched_diff_031.tar.gz.

Uncompress the ffmpeg and patch tarballs into the same directory. Next move the file libavformat/movenc.c from the patch into the ffmpeg/libavformat directory, overwriting the existing one. Then, open the file in an editor and find the section (near the top) that looks like:

#ifdef WIN32_PSP 
 #include "wchar.h" 
 #include "locale.h" 
 #include "windows.h" 
 #endif

Replace this with:

#ifdef WIN32_PSP 
#include "wchar.h" 
#include "locale.h" 
//#include "windows.h" 
 
#define CP_ACP 0 
#define MB_PRECOMPOSED 0 
size_t MultiByteToWideChar(int a, int b, char *src, size_t c, wchar_t *dest, size_t n) { return mbstowcs(dest, src, n) + 1; } 
#define _timezone 0 
#endif

Edit the file ffmpeg/libavformat/Makefile and remove the line:

OBJS+=avisynth.o

Edit the file ffmpeg/libavformat/allformats.c and remove the line:

avs_init(); // [MobileHackerz]

Lastly, edit the file ffmpeg/ffmpeg.c and remove the lines:

setvbuf(stdout, (char *)NULL, _IONBF, 0); // [MobileHackerz] 
setvbuf(stderr, (char *)NULL, _IONBF, 0); // [MobileHackerz] 
_setmode(_fileno(stdout), _O_BINARY); // [MobileHackerz]

Once all this is done, run a configure and make in the main ffmpeg directory. This should go away and compile ffmpeg (hopefully). The configure line to use is:

./configure --enable-gpl --enable-faac --enable-faad --enable-vorbis --enable-ogg

By now, you should hopefully have a ffmpeg binary that will convert just about any movie format into one that can be played on your PSP. Unfortunately, ffmpeg is not the simplest of programs to use, and needs 101 command line options to be specified. I’m not exactly an expert when it comes to video formats, so a lot of these options I specify below come from the “monkey see, monkey do” school of thought! The following is an example of how to convert a NUV video (the kind used in MythTV) to a PSP MP4 format.

./ffmpeg -y -i /home/mythtv/Recordings/1010_20051213180000_20051213183000.nuv -title "The Simpsons - Brothers Little Helper" -bitexact -f psp -s 320x240 -r 29.97 -b 768 -acodec aac -ac 2 -ar 24000 -ab 32 -map 0:0 -map 0:2 ~/M4V00070.MP4

This will take the file and encode it to an output file ~/M4V00070.MP4. The filename must be in this format for the PSP to pick it up. (M4V followed by 5 numbers, ending with .MP4 – All characters must be in upper case). Some options are straight forward (-title!) some others can be tweaked. The frame rate (-r) should be either 29.97 or 14.985. I’ve not been able to get any other rates working. The bitrate (-b) should be either 384 or 768, depending on what type of quality you want. -f specifies the output format is psp (duh!), -acodec is the audio codec to use (aac) and the -map options at the end are so that the audio works with NUV videos (probably not needed for other movies).

The last step I did was to take a script that I found on the MythTV website and extend it a little. Thanks to james at mauibay for the initial script. Extensions on mine include, automatic tracking of movie number, to always create unique movies, and creation of a thumbnail. Also it will produce a log file so you can check on why things went wrong. This script can then be added as a user job in MythTV so that it can be fired off on any recording, or automatically on all.

#!/bin/bash

FILE=$1
TITLE=$2

FFMPEGBIN=/home/mythtv/bin/ffmpeg
OUTDIR=/home/mythtv/PSP
declare -i FILENUM=`cat $OUTDIR/.filenumber`

OUTFILE="M4V`cat $OUTDIR/.filenumber`.MP4"
OUTTHM="M4V`cat $OUTDIR/.filenumber`.THM"

echo "Encoding" > $OUTDIR/.$OUTFILE.log
echo "Input file:  $FILE" >> $OUTDIR/.$OUTFILE.log
echo "Output file: $OUTFILE" >> $OUTDIR/.$OUTFILE.log
echo "Title:       $TITLE" >> $OUTDIR/.$OUTFILE.log

let FILENUM=$FILENUM+1
printf "%05d" $FILENUM > $OUTDIR/.filenumber


$FFMPEGBIN -y -i $FILE -title "$TITLE" -bitexact -f psp -s 320x240 -r 29.97 -b 768 -acodec aac -ac 2 -ar 24000 -ab 32 -map 0:0 -map 0:2 $OUTDIR/$OUTFILE >> $OUTDIR/.$OUTFILE.log

$FFMPEGBIN -y -i $FILE -title "$TITLE" -s 160x90 -padtop 16 -padbottom 14 -r 1 -t 1 -ss 3:00.00 -an -f singlejpeg $OUTDIR/$OUTTHM >> $OUTDIR/.$OUTFILE.log

When setting the MythTV job, don’t forget to put quotes round the title setting!

Hopefully this is easy to follow and has all the information in one place. If you need any more help or know of anything missing from my explanation, feel free to drop me a line.