Stream mythtv recordings from mythweb using flash video

From MythTV Official Wiki
Jump to: navigation, search

Time.png Outdated: The information on this page may no longer be relevant to the current release of MythTV, 34.0. Please consider helping to update it. This page was last modified on 2011-11-19.

This article will be OUTDATED because this facility is now being built into MythTV. See MythWeb

I've taken the time to compile the information found on this wiki and created the necessary files. Please start here http://chiefhacker.com/2007/01/22/streaming-mythtv-from-mythweb-using-flash/

Refer back to this wiki for more information on what the files are doing. Also see last post.

Chris Lorenzo


Automatically transcode recordings to flash video and link from Mythweb

I've just setup my myth box to automatically convert everything it records into flash video which can be played in any web browser with flash 7+. The quality of the videos is more than acceptable for playing in a window on your desktop but you wouldnt want to watch a film over it. It streams straight from my PVR with no buffering issues on a 50kbps upstream connection, if you alter the settings it would work fine over a standard ADSL connection.

This guide could also be used to create .mp4 videos for your ipod or whatever just by changing the ffmpeg arguments... have a play :D

This guide is pretty simple to setup, but is not official and has no input from the developers of mythtv or mythweb. Its basically a quick hack I set up when I did a few overnights.


Prerequisites

Package.png Required:

  • mythtv
  • mythweb
  • ffmpeg.

Package.png Optional:

  • flvtool2 - adds metadata to the video file so that the progress bar works. Requires ruby.

There are 3 stages to getting this setup

  1. Automatically transcode the files to FLV's using the user job function in myth
  2. Alter mythweb to link to the recorded files and setup the flash player
  3. Set the files to automatically expire.

Transcode recordings

First create a script as below and put it somewhere in the path of the user that runs mythbackend, eg /usr/local/bin/mythflash.sh and chmod it to 755

Script.png /usr/local/bin/mythflash.sh

directory=$1;
file=$2;

# Create the flash video (flv) file with a frame rate of 20fps, resolution of 300x200
# deinterlace the video and set an apropriate audio sample rate 
ffmpeg -hq -y -i $directory/$file -r 20 -s 300x200 -deinterlace -ar 22050 $directory/$file.flv 1>/dev/null 2>/dev/null

# Add metadata to file file (optional)
cat $directory/$file.flv | flvtool2 -U stdin $directory/$file.flv

This is a simple script which takes 2 arguments, first the folder which stores your recordings and second the file name. It then runs ffmpeg with highquality settings, only outputting 20 frames per second, a resolution on 300x200, deinterlacing the incoming video and setting an apropriate audio sample rate and outputting the flash video file to the same dir as your recordings.

If on a slow connection try reducing the resolution and setting the bitrate to 150 by adding -b 150 to the command.

The last line runs a program callled flvtool2 which adds metadata to the file making the progress bar work and letting you skip between bits of the file. It adds a processing overhead so if your box is struggling then just take that line out and everything will still work.

Next you need to setup the userjob in mythtv. Run mythtv-setup and go to the General Settings section. Under user jobs find a spare job and give it a name of something apropriate (e MythFlash) and the following command:

/usr/local/bin/mythflash.sh "%DIR%" "%FILE%"

Now on the recording settings page set it to run this job, which will call the script with the correct arguments and automatically create the flash video.


Update mythweb

Mythweb should already be up and running on your system. cd to your mythweb directory (eg /var/www/localhost/htdocs/mythweb) and check that data/recordings is a link to the directory which holds your recordings. If not you might have to link it manually (ln -s [your recordings dir] data/recordings

Next get hold of a copy of flvplayer.swf and put the file flvplayer.swf into your data dir, and chmod it appropriately. (You can also use flowplayer, check the documentation on how to make it work).

Final bit is to update the mythweb interface to link to your flash recordings. edit the file [your mythweb dir]/themes/default/tv/recorded.php and change the following (line 254):

echo '<a href="'.video_url().'/'.basename($show->filename)."\" name=\"$row\">"

to

echo '<a href="/mythweb/data/flvplayer.swf?file=/mythweb/data/recordings/'.basename($show->filename).".flv\" name=\"$row\" target=\"_new\">"

This will launch the flash player with the video in a new window when you click on the thumbnail next to a recording.

Expire transcoded recordings

There is now a cool perl script which can autoexpire recordings. When ran it will check your myth recordings for flv files without a corresponding .mpg file and delete any it finds. The code is below, save it as /usr/local/bin/mythexpire.pl and chmod it to 755. Also make sure you change the $directory variable to where your recordings are stored.

Application-x-perl.png /usr/local/bin/mythexpire.pl

#!/usr/bin/perl
#Checks the myth recording directory for .flv 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 =~ /\.flv$/ )
        {
                $f =~ s/\.flv$//;
                if (!( -e $f )) { `rm -f $f.flv`; };
        }
}

Next, add a cron entry as follows to make it run every hour.

0  *  * * *     /usr/local/bin/mythexpire.pl


To do:

Link files from mythweb a bit better Investigate 2 pass encoding in ffmpeg

User Notes

User notes from lynchmv

I had to remove the -hq option from the ffmpeg line in mythflash.sh. I am running MythTV .19, FC4 and followed Jarod's Guide Also, I grabbed flvplayer.swf from here.

After doing the following, I found that the flash wanted to play full screen and thus very pixelated. To get around this I created /path/to/mythweb/data/streamer.php that contains the following code:

<?php
if(!isset($_GET['file'])){
        print"<br /><br /><center><strong>You can not access this page directly!</strong></center>";
        die;
}
if(!isset($_GET['width'])){
        $width = '300';
} else {
        $width = $_GET['width'];
}
if(!isset($_GET['height'])){
        $height = '220'; //accounts for toolbar at bottom
} else {
        $height = $_GET['height'];
}
$toplay = $_GET['file']."&autoStart=true";
?>
<object type="application/x-shockwave-flash" width="<?php echo $width; ?>" height="<?php echo $height; ?>" wmode="transparent" data="flvplayer.swf?file=<?php echo $toplay; ?>">
<param name="movie" value="flvplayer.swf?file=<?php echo $toplay; ?>" />
<param name="wmode" value="transparent" />
</object>

And at line 254 I changed the code to the following:

if(!file_exists('data/recordings/'.basename($show->filename).'.flv')){
    echo '<a href="'.video_url().'/'.basename($show->filename)."\" name=\"$row\">";
} else {
    echo '<a href="/data/streamer.php?file=/data/recordings/'.basename($show->filename).".flv\" name=\"$row\" target=\"_new\">";
}
echo '<img id="'.$show->filename."\" src=\"".root.cache_dir.'/'.basename($show->filename).'.png" '.$attr.' border="0">'

Note that the line last line was previously this and I changed it

.'<img id="'.$show->filename."\" src=\"".root.cache_dir.'/'.basename($show->filename).'.png" '.$attr.' border="0">'

-Lynchmv

User Notes from Grantemsley:

I had a lot of problems getting this to work. My finished version uses the same mythflash.sh and mythexpire.sh scripts.

I'm using ubuntu, so the first problem was compiling my own ffmpeg with --enable-mp3lame so I could get sound. I also had to remove the -hq switch. To make it work well streaming over my DSL (~300kbps upload speed) I added "-b 175" to the ffmpeg switches. I used the player that Lynchmv linked, and used my own streamer.php page:

<html><head><title>Streaming TV</title></head>
<body>

<?php
if(!isset($_GET['file'])){
        print"<br /><br /><center><strong>You can not access this page directly!</strong></center>";
        die;
}
if(!isset($_GET['width'])){
        $width = '300';
} else {
        $width = $_GET['width'];
}
if(!isset($_GET['height'])){
        $height = '220'; //accounts for toolbar at bottom
} else {
        $height = $_GET['height'];
}
$toplay = $_GET['file']."&autoStart=true";
?>

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="<?php echo $width; ?>" height="<?php echo $height; ?>" align="middle">
<param name="movie" value="flvplayer.swf?file=<?php echo $toplay; ?>" />
<param name="quality" value="high" />        <param name="bgcolor" value="#ffffff" />
<embed src="flvplayer.swf?file=<?php echo $toplay; ?>" quality="high" bgcolor="#ffffff" width="<?php echo $width; ?>" height="<?php echo $height; ?>" align="middle" allowscriptaccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
<br>
<a href="<?php echo $_SERVER['PHP_SELF'] . "?file=" . $_GET['file'] . "&width=300&height=220" ?>">Small</a>
<a href="<?php echo $_SERVER['PHP_SELF'] . "?file=" . $_GET['file'] . "&width=600&height=440" ?>">Large</a>
<a href="<?php echo $_SERVER['PHP_SELF'] . "?file=" . $_GET['file'] . "&width=900&height=660" ?>">Huge</a>
</body></html>

I linked the file differently as well. On around line 294 of recorded.php, you have:

</tr><tr id="statusrow_<?php echo $row ?>" class="recorded">
    <td nowrap colspan="<?php echo $_SESSION['recorded_descunder'] ? 7 : 8 ?>" align="center">
        <span style="padding-right: 25px"><?php echo t('has commflag') ?>: 
            <b><?php echo $show->has_commflag ? t('Yes') : t('No') ?></b></span>

I changed this to:

</tr><tr id="statusrow_<?php echo $row ?>" class="recorded">
    <td nowrap colspan="<?php echo $_SESSION['recorded_descunder'] ? 7 : 8 ?>" align="center">
<?php

if(file_exists('/storage/recordings/' . basename($show->filename) . '.flv')){
	echo '<p align="left"><b>Stream This: ';
	echo '<a href="../data/streamer.php?width=300&height=220&file=recordings/' . basename($show->filename) . '.flv" target="_new">Small</a> ';
	echo '<a href="../data/streamer.php?width=600&height=440&file=recordings/' . basename($show->filename) . '.flv" target="_new">Large</a> ';
	echo '<a href="../data/streamer.php?width=900&height=660&file=recordings/' . basename($show->filename) . '.flv" target="_new">Huge</a> ';
	echo "</b></p>";
} else {
	echo '<p align="left"><b>Streaming not available</b></p>';
}
?>
        <span style="padding-right: 25px"><?php echo t('has commflag') ?>: 
            <b><?php echo $show->has_commflag ? t('Yes') : t('No') ?></b></span>

Note that you will have to change the /storage/recordings/ part to point to your actual recordings directory (and there may be problems with it working depending on how PHP is set up, if so, just remove the entire if/else part, and leave in the 5 echo lines. This puts "Stream this: Small Large Huge" in the second row, beside the thumbnail.

The only problem I had with it now, is that it still doesn't auto play. But that's a minor issue. Update: removing the "&autoStart=true" from $toplay = $_GET['file']."&autoStart=true"; in streamer.php seems to make it automatically play.

-Grantemsley

User Notes from Barry:

Update for Mythweb 0.20:

The recordings.php file to be edited is now located in: [your mythweb dir]/modules/tv/tmpl/default/recorded.php

Grantemsley's code above works great.

I was having problems with the MythFlash job failing occasionally and creating a 0-byte file, so I created a cron job with a script to delete any empty FLV files, and create new FLV files for any recordings that have it missing. I'll post the script in the Discussion tab.

-Barry253

User Notes from Mellon:

It's best to put the resolution of your flashvideo in the same aspect of the original recording, and with an easy divider. Example: PAL is 720x576, so I set the resolution to 360x288. (My connection has an upload speed of 1 Mbit/s, so I also set the bitrate to 400), which is just right for that speed).

The PHP-bit in recorded.php as suggested by Grantemsley, I altered a bit, to accomodate the new settings. Small, Large and Huge doesn't mean anything, so I've set 100% and 200%. 100% is 360x288 and 200% is 720x576 (native PAL resolution). (Credits to Ariekanarie).

Also I had some trouble with mythflash.sh, it caused a load of over 14, which is really strange (even on my sluggish system). flvtool2 is the worst. When you first install MythFlash, I recommend you do a test run, and keep an eye on your load. When your loads gets to high, it can cause serious trouble recording other programs at the same time. The 'solution' is to lower the priority of mythflash. My user job is:

/usr/local/bin/mythflash.sh "%DIR%" "%FILE%" nice -n 19

--Mellon 12:23, 20 September 2006 (UTC)

User Notes from Dounoit

Please post a working link for the flasherplayer:

"Also, I grabbed flvplayer.swf from here"

http://pyg.keonox.com/tests/flash_flv_player/ ?

Please update the link or remove it. Also,merge/replace some of the code.

--Dounoit


The link you just reposted workes. You have to download the flvplayer.swf file. That is really all there is to it.

Selbram


Yes Selbram, this player is now working. Must have been some user error :-)

  • Has anyone been able to drag the status bar left or right during playback? All I see is a stop or play or volume.
  • Selbram - This will not work until the file has been processed with flvtool2.
  • Dounoit - I have it all working but the overhead of the flvtool2 takes my box down every sunday when recording all my car shows on spike tv - even with the renicing setup ;-/
  • Selbram - I have two machines running side by side. Once for recording and one for processing files. I use nfs to mount the directories that store my video files and run the jobs from the second machine.

--Dounoit

User Notes from Foosinho:

I managed to hack this to support multiple aspect ratios. In my case, I record both HDTV and SDTV. Fortunately, the SDTV is always 480x480 (PVR500), so it's easy to create a special case. You have to modify a number of files to ensure the transcoding maintains the correct AR, and that the flash player displays the correct AR.

First, mythflash.sh

#!/bin/bash

directory=$1;
file=$2;

# Determine aspect ratio
original_name=$1/$2;

#Grab input file information
videoheight=`mplayer -vo null -ao null -frames 0 -identify "$original_name" 2>/dev/null | grep "ID_VIDEO_HEIGHT" | sed -e 's/ID_VIDEO_HEIGHT=//'`

height=240;
if [ "$videoheight" -gt 480 ]; then
   # "HDTV"
   width=426
else
   # "SDTV"
   width=320
fi

dimensions=$width"x"$height;

# Create the flash video (flv) file with a frame rate of 20fps
# deinterlace the video and set an apropriate audio sample rate
ffmpeg -y -i $directory/$file -r 20 -s $dimensions -deinterlace -ar 22050 $directory/$file.flv 1>/dev/null 2>/dev/null

# Add metadata to file file (optional)
cat $directory/$file.flv | flvtool2 -U stdin $directory/$file.flv

I'm using mplayer to identify the height of the original video. If it's >480, then it's an HDTV stream (720p, 1080i, or other), otherwise it's a standard def stream (encoded 480x480 by the PVR500). Keeping a constant height of 240px, I set the width for the transcoding appropriately and get to work.

If you use the expanded version of the cleanup script (that expires recordings and transcodes any recordings with missing flv files), you need to make a similar change, adjusting the syntax for perl. Here is the mplayer call - the rest is trivial:

$videoheight=system("mplayer -vo null -ao null -frames 0 -identify $f 2>/dev/null | grep ID_VIDEO_HEIGHT | sed -e 's/ID_VIDEO_HEIGHT=//'");

While this creates flash video files of the appropriate aspect ratios, flvplayer will force-fit the file to whatever aspect ratio it is given, so we need to modify both mythweb and streamer.php (NB: I use Grantemsley's versions above). Again, this should look familiar... here is the appropriate part of recorded.php

$filename = '/home/mythtv/data/recordings/' . basename($show->filename);
$videoheight=exec("mplayer -vo null -ao null -frames 0 -identify $filename 2>/dev/null | grep ID_VIDEO_HEIGHT | sed -e 's/ID_VIDEO_HEIGHT=//'");
        if ( $videoheight > 480 )
           $width=426;
        else
           $width=320;
        $height=240;
        echo '<p align="left"><b>Stream This: ';
        echo '<a href="../data/streamer.php?width='.$width.'&height='.($height+20).'&file=/mythweb/data/recordings/' . basename($show->filename) . '.flv" target="_new">Small</a> ';
        echo '<a href="../data/streamer.php?width='.(2*$width).'&height='.(2*$height+20).'&file=/mythweb/data/recordings/' . basename($show->filename) . '.flv" target="_new">Large</a> ';
        echo '<a href="../data/streamer.php?width='.(3*$width).'&height='.(3*$height+20).'&file=/mythweb/data/recordings/' . basename($show->filename) . '.flv" target="_new">Huge</a> ';

And, last but not least, the modification to the links at the bottom of the streamer page:

<?php
$filename=substr($_GET['file'],0,-4);
$videoheight=exec("mplayer -vo null -ao null -frames 0 -identify /var/www/html".$filename." 2>/dev/null | grep ID_VIDEO_HEIGHT | sed -e 's/ID_VIDEO_HEIGHT=//'");
$linkheight=240;
if ( $videoheight > 480 ){
   $linkwidth=426;
}else{
   $linkwidth=320;
}?>
<a href="<?php echo $_SERVER['PHP_SELF'] . "?file=" . $_GET['file'] . "&width=" . $linkwidth . "&height=" . ($linkheight+20) ?>">Small</a>
<a href="<?php echo $_SERVER['PHP_SELF'] . "?file=" . $_GET['file'] . "&width=" . (2*$linkwidth) . "&height=" . (2*$linkheight+20) ?>">Large</a>
<a href="<?php echo $_SERVER['PHP_SELF'] . "?file=" . $_GET['file'] . "&width=" . (3*$linkwidth) . "&height=" . (3*$linkheight+20) ?>">Huge</a>

Adjust for the aspect ratios you record, and the streaming video sizes you want to view! Also note - ffmpeg will complain if you choose sizes that aren't multiples of two. The mplayer call to suss out the size of the video is very quick.

On a personal note, I just wanted to add just how pleased I am with this hack. This works really well, and if you create a User Job and set it to auto-run, the videos "just appear". The quality of the streamed video is pretty decent, and it works well on the LAN and WAN. Good job, gang.

User Notes from Scottix:

For those people who have slow upload connection speeds I have an ffmpeg setting that produces good quality without the big file size. The one main thing you lose is frame rate but it is tolerable.

directory=$1;
file=$2;

# deinterlace 312x208 12fps mono sound
ffmpeg -i $directory/$file -aspect 4:3 -s 312x208 -qmin 12 -qmax 15 -r 12 -deinterlace -acodec mp3 -ab 48 -ar 22050 -ac 1 -y $directory/$file.flv 1>/dev/null 2>/dev/null

# Add metadata to file file (optional)
cat $directory/$file.flv | flvtool2 -U stdin $directory/$file.flv

If you want a little better quality you change -qmin to a smaller value or if the framerate is to slow you can change -r to a higher value.