Notes on using HDHomeRun recorder under Ubuntu for lowest CPU usage when recording from HDHomeRun device

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Sometimes I write articles for others, but sometimes I use this blog just to post a collection of notes on something I had a need to do.  This is one of the latter type.  You are welcome to look, and if it helps anyone else, great, but it’s not a full, step-by-step, “here’s how to do it” article, and I definitely do not advise you to just blindly emulate what I did, since there are probably more elegant ways to do this. This was one of those “I just want to get the thing working” type projects, and I must say that it works very well, at least for me.

HDHomeRun recorder is a simple Python script that will record specific channels based on a pre-established schedule.  It is less CPU intensive (and in my opinion, much easier to set up) than some of the other ways to do this, such as Myth TV.  But it is more limited — in particular the current script will only record from a single tuner, and there’s no GUI at all.  Everything is done in script and configuration files.

Here’s what I did:

Using  apt-get or Synaptic, install the following:  hdhomerun-config-gui, hdhomerun-config, libhdhomerun1, python-pip:
sudo apt-get install hdhomerun-config-gui hdhomerun-config libhdhomerun1 python-pip

Install apscheduler:
sudo pip install apscheduler

EDIT (for newer version mentioned by Fred C. in the comments): Then go to this page and click the ZIP button to download a ZIP file containing HDHomeRun recorder.  Unzip it into a subdirectory off of your user directory (I named the subdirectory HDHomeRun_Recorder on my system) — you should get several files and a scripts directory, which contains a couple more files. At this point you might want to check the ownership and permissions of the files — in particular make sure the .py files are executable. Do NOT follow the instructions in the README file; they are for an older version of the software. You may want to read the file, particularly if you have a WDTV Live media player, but the INSTALL section needs updating.

You then need to modify a couple of the files.  This is a bit tricky because what might seem the obvious way to do it won’t work (unless you apply my fix mentioned below).  So here is what you need to do:

(Read through this before actually doing anything, it may save you some effort!)

First, modify config-file.  There are three things you need to change here.  First you need to change this line as shown:

hdhomerun_config = /usr/bin/hdhomerun_config

This assumes the hdhomerun_config program is in /usr/bin, which it will be if you installed it using apt-get or Synaptic.  Next, you need to modify the tuners line to show the correct tuner IDs for your HDHomeRun — you can discover these using the HDHomeRun Configuration utility, if you don’t already know them.  And, you need to replace the entries in the channelmap section with your local channels.  You can run the hdhomerun_setup.py program (run it with no arguments and it will tell you what arguments it needs) and it will show you a list of channels (note it takes a few minutes to run, so be patient), and it appears as if it produces an entire config-file for you, but it doesn’t (keep reading before you do anything, there’s a fix for this below that you might want to try).  So look at the list of channels it outputs and then change each line to match the format you see in the included config-file.  For example, let’s say you see:

35.1   = 11   3   WGVU-11
35.2   = 11   4   WGVU-11
35.3   = 11   5   WGVU-11
35.4   = 11   6   WGVU-11
0 = 11 9 (control)
0 = 11 10 (control)

What that needs to be changed to in config-file is:

35.1 = 8vsb:11, 3       ; WGVU-11
35.2 = 8vsb:11, 4       ; WGVU-11
35.3 = 8vsb:11, 5       ; WGVU-11
35.4 = 8vsb:11, 6       ; WGVU-11

I have no clue why the hdhomerun_setup.py program doesn’t produce the correct output, it just doesn’t. I tried fixing it to produce the correct output (despite the fact that Python is a very incoherent language to me), and think I have it, so if you want to try my fix to hdhomerun_setup.py, the code is at the end of this article.

Second, you need to modify schedule-file. This is an example schedule file, and you just modify the entries with programs you like to record.

When you run the python script, I found that you need to actually be in its directory, and also that when you run it, it will seem like nothing is happening. And the command prompt won’t come back either, so you may want to initially run it using screen:

screen ./hdhomerun_recorder.py

You can use Control-A followed by D to detach from the process. To check that it started and everything is working okay, check logfile (in the same directory as the hdhomerun_recorder.py program). I found that the following could be used to kill the process:

pkill -f “python ./hdhomerun_recorder.py”

I made a bash shell script that looks like this:

#!/bin/bash
export USER=yourusername
pkill -f “python ./hdhomerun_recorder.py”
cd HDHomeRun_Recorder
./hdhomerun_recorder.py &

I can run this script to restart the Python script (in case I make a change to the schedule) and I can also add it to my startup items to start the script after a reboot (the pkill is ignored in that case).

Note: If you need a way to determine the channels in your area and don’t want to run hdhomerun_setup.py, see hdhomeruntoolbox, but note that the .strm files it produces are not in the correct format for XBMC (in case you are using XBMC, and if you are and you know anything about Java, maybe you want to grab a Java Decompiler and see if you can fix the output). However, after it runs, the information you will need for each channel in HDHomeRun recorder will be in a file named scan_tuner1.txt in the (hidden) .hdhomerun directory. When you look in that file, you will see sections for each working channel that contain information such as this:

SCANNING: 201000000 (us-bcast:11)
LOCK: 8vsb (ss=100 snq=91 seq=100)
TSID: 0x05E7
PROGRAM 3: 35.1 WGVU-11
PROGRAM 4: 35.2 WGVU-11
PROGRAM 5: 35.3 WGVU-11
PROGRAM 6: 35.4 WGVU-11
PROGRAM 9: 0 (control)
PROGRAM 10: 0 (control)

In config-file, the above would translate to something like this:

35.1 = 8vsb:11, 3 ; WGVU-11
35.2 = 8vsb:11, 4 ; WGVU-11
35.3 = 8vsb:11, 5 ; WGVU-11
35.4 = 8vsb:11, 6 ; WGVU-11

Note that stations will not always have the real channel numbers after the callsign, so the real channel number actually comes from the (us-bcast:11) in this case.

Setting up config-file is probably the hardest (not actually hard, but maybe tedious) part, if you can’t get my modifications to the hdhomerun_setup.py script (below) to work, but remember that you only need to add the channels from which you might want to record programming, not every channel available. Remember to restart the Python script if you change the schedule, but of course you DON’T want to do that while it’s recording programming!

I should add that since HDHomeRun recorder is a Python script, it’s probably possible to make it run under operating systems other than Ubuntu, but I just haven’t attempted to do that.

Here are my changes for hdhomerun_setup.py, that should cause it to print out usable lines that replace the existing lines in config-file (you will have to copy and paste them; it does NOT overwrite config-file). Note that there will only be one tuner shown on the tuners line, so you should add your additional tuners if you want to use them For example, if you have a HDHomeRun Dual, and this script prints out tuners = 10ABCDEF:0 you should add the second tuner so that the line is tuners = 10ABCDEF:0, 10ABCDEF:1 (with comma and space separating the two).

#!/usr/bin/env python

def channel_iter(file):
    for line in file:
        if line.startswith("SCANNING: "):
            channel = line.split()[2].strip('()')
            channel = channel.split(':')[1]
        elif line.startswith("LOCK: "):
            modulation = line.split()[1]
        elif line.startswith("PROGRAM "):
            (PROGRAM, subchannel, vchannel, name) = line.split(None, 3)
            subchannel = subchannel.rstrip(':')
            name = name.strip()     # remove new line
            name = name.replace(' ', '-')
            yield (vchannel, modulation, channel, subchannel, name)

def channel_info(hdhomerun_config, device_id, tuner):
    import subprocess
    import tempfile

    f = tempfile.TemporaryFile()
    cmd = [hdhomerun_config, device_id, "scan", "/tuner%d" % tuner]
    p = subprocess.Popen(cmd, stdout=f)
    p.wait()
    f.seek(0)
    return list(channel_iter(f))

def main():
    import sys, os, os.path

    usage = ("usagde: %s path-to-hdhomerun-config device-id tuner-number"
             % sys.argv[0])
    if len(sys.argv) != 4:
        sys.exit(usage)
    hdhomerun_config = sys.argv[1]
    device_id = sys.argv[2]
    tuner = int(sys.argv[3])
    if not os.path.exists(hdhomerun_config):
        sys.exit("%s doesn't exist, aborting!")
    if not os.path.isfile(hdhomerun_config):
        sys.exit("%s is not a regular file, aborting!")
    if not os.access(hdhomerun_config, os.X_OK):
        sys.exit("%s doesn't have execute permission set, aborting!")

    chan_info = channel_info(hdhomerun_config, device_id, tuner)
    if not len(chan_info):
        sys.exit("couldn't find any channels, quitting!")

    print("[global]")
    print("logfile = logfile")
    print("media_dir = media")
    print("schedule_file = schedule-file")
    print("hdhomerun_config = %s" % hdhomerun_config)
    print("tuners = %s:%s" % (device_id, tuner))
    print("[channelmap]")
    print("# virtual-channel = physical-channel program-number name-of-program")
    for (vchannel, modulation, channel, subchannel, name) in chan_info:
        line = "%s = %s:%s, %s t; %s" % (vchannel, modulation, channel, subchannel, name)
        if vchannel != '0':
            print(line)

if __name__ == '__main__':
    main()

11 thoughts on “Notes on using HDHomeRun recorder under Ubuntu for lowest CPU usage when recording from HDHomeRun device

  1. I wrote the recorder script for my own consumption and used it to record kids programs in SD quality. Now almost everything is aired in HD, and my tuner seems to drop some network frames while recording HD channels. This happens with all my computers, so it has to be something wrong with my tuner box. I don’t record anything anymore, but I am willing to help who ever wants to use it.

    Originally I created this project at github. I wanted to make it easy to install by putting it in pypi, but I couldn’t finish it. Since people were trying to install from pypi, I just removed it from there couple days ago. I don’t know enough about python packaging to put it at pypi.

    About the hdhomerun_setup.py, it was originally written to work with hdhomerunfs (my other project!). Maybe, I should make both programs take similar config file and fix hdhomrun_setup.py to work with both.

    Also, if you change schedule file, just send HUP signal to the recorder instead of killing and restarting it (you will lose currently recorded program if you kill it). The details are at the beginning of the log file!

    I wish I have time to make all the changes that I have in my mind for this project, but I can’t!

    1. Malahal, thank you for the comments. I don’t know what you mean by “send HUP signal to the recorder” – the first line in my log file says:

      2012-12-13 21:50:18,028: Main process PID: 1784, use this for sending SIGHUP for re-reading the schedule-file

      But I have no clue how you’d do that, and as long as I don’t restart it while it’s actually recording a program, I don’t seem to have a problem.

      Also, if that PID changes on every run then I would think it would make it kind of hard to use that in a batch file, even if I knew how. Just as a thought, would it be possible to add a line to the hdhomerun_recorder.py script that creates (or overwrites) a “reread.sh” bash script that would contain the command to send the HUP signal, so that any time you change the schedule you could then run reread.sh and it would send the proper command? That would help those of us who have no idea how to send a HUP signal. Granted you would have to make it executable before the first run but after that, if the file is overwritten the permissions should not change.

      As for the skipping frames on HD, I have found that the tuner sends a proper stream but the computer itself is very susceptible to too many processes running at once. For example, if I forget and leave a VNC window open to my media center PC, the video will be choppy. If I kill VNC then it clears right up. You might want to try doing

      killall vino-server

      from the Linux command prompt to make sure there are no active VNC sessions. Of course if you don’t use VNC, it may be some other process. I’ve also heard some people say that if you have too many switches between the HDHomeRun and the computer doing the recording it can affect quality, although my guess would be that it’s more an issue of the quality of the switches than the quantity. If your switches are only 10/100 I’d try replacing them with gigabit switches. We are running a gigabit network here and don’t seem to have have any problem with HD video quality as long as something else isn’t sucking up CPU cycles on the receiving computer.

      I understand being busy, but if you can find the time I’d sure like to see any improvements you have in mind for your program. The thing I like about it is that it is so “lightweight” and easy to install and configure, once you understand it. While I’d like to see a nice GUI (nothing fancy, just usable) for setting up or changing the recording schedules, the main thing for me is that it works and is not nearly as complicated (and probably not nearly as CPU intensive) as MythTV. It looks like you need a degree in computer science to set that thing up! So thanks for writing this, at least it makes recording possible on my Acer Aspire Revo, which is not exactly a high-powered computer.

  2. You can actually start it like this (“man screen” for help):
    screen -S srecorder -dm ./hdhomerun_recorder.py

    1. From the man page for screen:

      -S sessionname
      When creating a new session, this option can be used to specify a meaningful name for the session. This name identifies the session for “screen -list” and “screen -r” actions. It substitutes the default [tty.host] suffix.

      -d -m Start screen in “detached” mode. This creates a new session but doesn’t attach to it. This is useful for system startup scripts.

      Once you know that the program works, I’m not sure what the advantage would be in starting it that way, since the program doesn’t produce any output while it runs (except to the log file). What am I missing?

  3. Just as an experiment, and because no one else had done it, I created an unofficial HDHomeRun community in Google+ communities. You have to be logged into Google+, then in the left-hand menu click “Communities”, then enter HDHomeRun in the search box. I don’t know if anyone will use it but as I say, it was just an experiment to see if it could be done.

  4. Sending HUP signal: “kill -SIGHUP “. Technically, “kill” command does not kill a process but sends a given signal. So it is just a matter of reading the first line from “log” file to get PID and then run “kill” command. It is quite possible that we can use pkill or killall, but that may require small changes to signal handling in the python recorder script though.

    screen -S name -dr: Since the recorder is not a daemon, you will have to use “screen” command as you are doing now. “-dr” options detaches the terminal for you automatically (will avoid Control-A followed by D to detach screen session in your instruction!). “-S” option is not needed, it just gives an identifiable name if you are running too many “screen” sessions!

    1. Malahal, I get it now. The “screen” command is one way to run the script in the background. I am doing something similar in my batch file, by invoking the script with the line

      ./hdhomerun_recorder.py &

      The & character at the end of the line runs the script as a background task. I don’t know which method is lighter on system resources, though.

      Thank you for the information on SIGHUP. It’s little things like that, that Linux users pick up along the way, that aren’t really known by those of us who primarily use a Linux desktop version such as Ubuntu, and only rarely interact with the command prompt.

      I’m wondering how you ever got the HDHomeRun configured to use a static IP address (or does it just pick one and force you to use a compatible one on the PC?). I’ve looked for the option to use a static IP on my HDHomeRun Dual and have yet to find it. In any case, I have seen the exact opposite of the problem you describe. In my case, the video gets choppy but the audio is fine. Usually when that happens. I am pretty sure some background process is running, I just don’t know what. Last night i recorded something that lasted two hours and the first hour was completely fine, while the second had video glitches. Since it was actually two separate programs back to back, it’s entire possible that the problem was with the source — since I didn’t see it while it was live, I just don’t know for sure.

      In any case it sounds like you have done everything you can to eliminate the possibility of a network issue. Thanks again for your comments!

    2. Malahal, one more question. In your main program there is a line (well, two lines actually) that looks like this:

      logging.info(“Main process PID: %d, use this for sending SIGHUP ”
      “for re-reading the schedule-file”, os.getpid())

      This is obviously what is placing the PID in the log. What I’d like to have it do at the same time is open a file called reread.sh and in it write these two lines:

      #!/bin/bash
      kill -SIGHUP %d

      Where %d will be replaced by the PID, as in the lines mentioned above. Then the file should be closed. Do you know offhand how to code that in Python? Seems like it shouldn’t take more than four or five lines tops, but it would probably take me hours to figure it out given my lack of familiarity with Python (in contrast, back in the day I could have done something like this in BASIC in no more than a minute. I miss BASIC!).

      EDIT: As a stopgap I figured out that this would work in reread.sh as long as the logfile format doesn’t change:

      #!/bin/bash
      pid=$(grep PID logfile | cut -d " " -f 6 | cut -d "," -f 1); kill -HUP "$pid"

      The idea is that in Midnight Commander, after making a change to schedule-file, all I’d need to do is double-click on reread.sh and that would take care of rereading the schedule, hopefully without interrupting any recordings in progress.

  5. My hdhomerun dropping frames for HD programming: I tried with 2 different routers, 3 different PCs (one laptop with fedora linux, one with windows7/MC, one WDTV). Swapped cables! I added a Gigabit switch as well. I also tested without any router or switch — I directly connected the hdhomerun to my laptop. Due to absence of DHCP, the tuner gets a static ip and you need to configure your PC to use static ip as well.

    It does record or live OK for most past, but drops frames at times. I also noticed that when ever it drops frames, I can’t detect anything wrong with video, but audio gets chopped!

  6. NOTICE: All comments above this one were imported from the original Michigan Telephone Blog and may or may not be relevant to the edited article above.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Recent Posts

Recent Comments

Archives

Categories

Meta

GiottoPress by Enrique Chavez