Streaming to iPod touch or iPhone

From MythTV Official Wiki
Revision as of 06:52, 10 April 2010 by JohnReid (talk | contribs) (Typo)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This page is a collection of what you can do to stream myth recordings to an iPod touch or iPhone via MythWeb. This makes the iPod a de facto front-end for myth with the exception of live TV. I actually bought an iPod just to see if I could do this, and I have to say, it is my favourite purchase in a LONG time

This guide is heavily based on Stream mythtv recordings from mythweb using flash video setup which has had tremendous success. I appreciate all the work the authors have done for that, and I hope someone can make use of these modifications.

If you are having problems, please post in the discussion and I'll try and help out.


  • MythTV
  • MythWeb
  • mplayer
  • ffmpeg with libfaac and libxvid


As with any new device there are formats it can handle, unfortunately most do not handle NUV. The iPhone and iPod Touch both handle a specific version of the MPEG-4 standard, as well as h264 video streams. These are documented on Apple's Developer Connection

The steps to making your iPod stream from your server involve:

  1. Encode the file to a friendly format
    1. Set up a script to generate iPod friendly format
    2. Set up a script to delete iPod friendly format files when the original is deleted
  2. Serve the file from your web-server
    1. Ensure your webserver has the correct set-up to see the new files
    2. Link it from the "recorded shows" page in MythWeb (HUGE thanks to Xris for all his work in MythWeb)

Transcoding and Scripts


First we have a script to convert the show to an iPod friendly format (if you're good with mencoder or ffmpeg your help in making this a bit better would be greatly appreciated)

In order to use this you will also need to ensure your webuser is in the myth users group, or in some other manner has permission to write to the myth video store.

Script.png /usr/local/bin/


#MythIPod v.1                                                 
#Copyright 2007 Chris Lorenzo et al                  
#This software is licensed under the CC-GNU LGPL <> 
#Additionally modified 2007 Doug Johnson

if [ ! $# == 2 ]; then
  echo "Usage: $0 directory file"


#Get the video information
video_info=`mplayer -vo null -ao null -frames 10 -identify "${directory}/${file}" 2>/dev/null`

#Command line grep sucks, but we make due...
aspect=`echo $video_info | egrep -oe "Movie-Aspect is [0-9:.]+" | egrep -o "[0-9:.]+"`
framerate=`echo $video_info | egrep -oe "[0-9.]+ fps" | egrep -o "[0-9.]+"`

# set resolution by aspect ratio

if [ "$aspect" == "1.78:1" ]


# Figure out which AAC we have
AAC=$(ffmpeg -v 2>&1|sed "s/--enable-/\n/g"|grep aac|awk '{print $1}')
if [ "x${AAC}" = "xlibfaac" ]
	ACODEC="-acodec libfaac"
	ACODEC="-acodec aac"

# Create the MP4

ffmpeg -i "${directory}/${file}" $ACODEC -ab ${abitrate} -ac 2 -s ${width}x${height} -vcodec mpeg4 -b ${vbitrate} -flags +aic+mv4+trell -mbd 2 -cmp 2 -subcmp 2 -g 250 -maxrate 512k -bufsize 2M -title "${file}" "${directory}/${file}.mp4" 

# UPDATED - For newer builds of ffmpeg, trell and title are deprecated - use this instead:
# ffmpeg -i "${directory}/${file}" $ACODEC -ab ${abitrate} -ac 2 -s ${width}x${height} -vcodec mpeg4 -b ${vbitrate} -flags +aic+mv4 -trellis 1 -mbd 2 -cmp 2 -subcmp 2 -g 250 -maxrate 512k -bufsize 2M -metadata title="${file}" "${directory}/${file}.mp4"
# UPDATED 2 - If you get an error like "libfaac doesn't support this output format" try resampling audio input to 44100Hz:
# ffmpeg -i "${directory}/${file}" $ACODEC -ab ${abitrate} -ac 2 -ar 44100 -s ${width}x${height} -vcodec mpeg4 -b ${vbitrate} -flags +aic+mv4 -trellis 1 -mbd 2 -cmp 2 -subcmp 2 -g 250 -maxrate 512k -bufsize 2M -metadata title="${file}" "${directory}/${file}.mp4"

The script attempts to detect which AAC you have installed. If you compiled ffmpeg with --enable-libfaac the script will properly use libfaac instead of aac for the acodec option. However, if ffmpeg doesn't have support for either installed, you may still get the following error
Unknown codec 'aac'

To continue troubleshooting, you can see what the name of the library is in your /usr/lib by doing

ls /usr/lib | grep aac

and then use the one found there (what's before the .so.X.X.X)

This can be set up as a user-job with the one of the following ways:

The proper, safe way to do this instead would be to add a user job in mythtv-setup. i.e. add the line
/usr/local/bin/ %DIR% %FILE%
to a spare user job slot. That way, if you already have a user job in whichever slot you choose for the following SQL it won't be overwritten.

Or by following SQL instructions in mythconverg (note, if you have another user job defined, 1 is probably not your best choice. Select an open user job. Note, it's not generally a good idea to do this by SQL if you are unfamiliar with what you are doing.

% mysql mythconverg
UPDATE settings SET data='/usr/local/bin/ %DIR% %FILE%' WHERE value='UserJob1';
UPDATE settings SET data='Convert to iPod' WHERE value='UserJobDesc1';
UPDATE settings SET data='1' WHERE value='JobAllowUserJob1';

Expire transcoded recordings

Deleting old files

From the Flash Streaming page we get this (very slightly modified) perl script which can autoexpire mp4 recordings. When ran it will check your myth recordings for mp4 files without a corresponding .mpg file and delete any it finds. The code is below, save it as /usr/local/bin/ and chmod it to 755. If you are also using mythflash then please just add another if block that looks exactly the same within the foreach loop, and change the extension to mp4 Also make sure you change the $directory variable to where your recordings are stored.

Script.png /usr/local/bin/

#Checks the myth recording directory for .mp4 files that don't have a corresponding .mpg. If found, it deletes them.

#Change below to your myth recordings directory
$directory = "/mnt/store";

foreach $f (<$directory/*>) {
        if ( $f =~ /\.mp4$/ )
                $f =~ s/\.mp4$//;
                if (!( -e $f )) { `rm -f $f.mp4`; };

There are a number of ways to have this run automatically, I run this command to have cron automatically execute it once a day

ln -s /usr/local/bin/ /etc/cron.daily/

Streaming from your Webserver

First of all you must make sure your webserver gives the correct mime-types when serving an mp4 file. Look in /etc/mime.types for the entry

video/mp4                mp4

If it's not there, add it (and restart httpd?). The iPhone will try to open the encoded file with quicktime (in fullscreen). If you get an error, please post in the discussion.

Getting to the files

Make sure you can see the files. You'll need to have a link to the recordings directory from your mythweb data directory. (Substitute your own mythweb data, and myth TV recordings directories for these values)

# It might look like this:
# ln -s /srv/www/htdocs/mythweb/data/recordings /mnt/videos/mythtv/
ln -s ${mythweb}/data/recordings/ ${myMythVideosDirectory}

Updating recording.php

If you want a link in recording.php you will need something like the following:

Permission to run the script as the webuser

chmod 755 /usr/local/bin/

An updated recorded.php You should find a row in your recorded.php that has the following line:

<tr id="statusrow_<?php echo $row ?>" class="recorded">
    <td nowrap colspan="<?php echo 6 + ($_SESSION['recorded_descunder'] ? 0 : 1) + $recgroup_cols ?>" align="center">

Add the following afterwards. In this example I include a link to encode in case the file doesn't exist. If you don't want to be able to encode as webuser, then your else statement could be changed to "echo 'Not Available';"

Script.png %mythweb%/modules/tv/tmpl/default/recorded.php

<tr id="statusrow_<?php echo $row ?>" class="recorded">
    <td nowrap colspan="<?php echo 6 + ($_SESSION['recorded_descunder'] ? 0 : 1) + $recgroup_cols ?>" align="center">
			echo '<span style="padding-right: 25px; word-spacing: 0.5em;">ipod stream: <b>';
			$file = basename($show->filename);
			if(file_exists($show->filename . '.mp4')){
				echo '<a href="/mythweb/data/recordings/' . $file . '.mp4" target="_blank">Watch</a> ';
				echo "</b>";
			} else {
				echo '<a href="/mythweb/data/ipodencoder.php?file=recordings/' . basename($show->filename) . '&convert=true" target="_blank">Encode?</a> ';
			echo '</b></span>';

Transcoding remotely

An "ipod encoding" page to perform the encoding (I'm sure there's a better way to do this, I did this because it matches what was done in mythflash and worked well for me)

Script.png %mythweb%/data/ipodencoder.php


MythIPod v.2 
Original from MythFlash v.1 Copyright 2007 Chris Lorenzo                    
This software is licensed under the CC-GNU LGPL <> 
Modified 2007 Doug Johnson - changing to encode for iPod. Changes to the recorded.php also don't
reference the player here, so I've simply left that out

if(!isset($_GET['file']) && (!file_exists($_GET['file'])) ){
        print "<br /><br /><center><strong>No File!</strong></center>";

$file = $_GET['file'];

//Convert the file to mp4 using /usr/local/bin/
if(isset($_GET['convert'])) {
   //recording directory must be writable by owner of apache process eg. www-data or apache
   //runs the command in the background   
   $dir = getcwd() ."/recordings";
   if(!is_writable($dir)) {
   	  $apache_owner = `whoami`;
   	  print "<div style='padding: 15px; font-size: larger;'>Recording directory, $dir, is not writable by ${apache_owner}. <br>
	  Please add ${apache_owner} to mythtv group in /etc/group and make sure that the store directory <br>
	  is writable by ${apache_owner} - ie chmod 775 MYTHTV_STORE .</div>";
   } else {
	   print "<div style='padding: 15px; font-size: larger;'>Converting file, should be done in about the length of the show ...";
	   print "<br> <a href=\"javascript:window.close();\" \">close window</a></div>";
	   system("nohup nice -n 15 /usr/local/bin/ ". $dir ." ". basename($file) ." > /dev/null 2>&1 &");


	<title>iPod Touch Streamer</title>

<script type="text/javascript" language="javascript" src="ufo.js"></script>
<link type="text/css" href="streamer.css" rel="stylesheet">


You shouldn't be here.