Import recorder

From MythTV Official Wiki
Jump to: navigation, search

Introduction

The Import Recorder (aka Import Tuner) was introduced in 0.23 ([65bcb39389]). Originally it was designed as a developer tool for testing Myth. However, it is potentially useful in conjunction with Myth's System Events.

The import recorder is a software "Capture Card" for MythTV that does not interface with an actual hardware device. It accesses a file of MPEG-TS data that could have been created by a recorder or placed there by some process. As such it serves two purposes. It can be used tor testing purposes when no hardware card is available. It can also be used for actual recordings when you have a hardware device that is supported by other software that can create an MPEG file.

Setup

To add an Import Recorder, use mythtv-setup to create a new Capture Card and choose "Import Test Recorder" as the card type. You are prompted to choose a file name. You can either choose an appropriate MPEG TS file, and that will be used as the input for any recording that uses this recorder. Alternatively, in the file selection dialog, leave the file name as "/". Setup will report that the file is not readable. When set up in this way, the import recorder can be used in conjunction with an external program.

Continue with setting up a Video Source for the import recorder, and define a listings provider or select "no grabber". Use Input connections to associate your import recorder with the video source. Use channel editor to add channels, fetch them from the listings source or run mythfilldatabase to add them.

Using an external recording program

Version:
30
This works with MythTV Version 30. Older versions will give problems such as recordings being marked failed and having the wrong length

Set up an import recorder as described above, with a filename of "/".

In mythfrontend, configure RecordingStarted, RecordingFinished events to call appropriate scripts or programs to acquire the recording. Myth will treat it as any other recording with a seek table, commflagging, etc.

Create a script that will do the recording. You can pass into the script any of the fields that are passed into the Recording started system event. See MythTV System Events. Typically you would pass in %FILE% %CHANID% %CARDID% %ENDTIMEISO%. The script will be responsible for the following:

  1. Check the CARDID and make sure it is the import recorder. Otherwise exit.
  2. Locate the recording file in the storage directory where it will have already been created with zero length.
  3. Figure out the channel to be tuned from the CHANID.
  4. Tune the device to the required channel.
  5. Record from that channel into the recording file you have located. This needs to be written as a valid MPEG TS file. If your process writes out another format it can be piped through ffmpeg real time to create MPEG TS format. See the example script below.
  6. Set up a timer to stop recording and writing to the file at the specified end time.

Set the script name and parameters as the Recording Started system event. End the command with "&" so that it continues recording after the event is complete.

MythTV will show the recording as in progress until the recording and writing proces has ended, even if that takes longer than the specified recording time.

You cannot make use of the Recording Finished event to stop the recording. That event only gets fired after your recording process ends. Even if your recording process runs past the specified end time, the Recording Finished event is not triggered until the recording process ends.

Here is an example script that works to record from a dvb device using standard packages available with Ubuntu.

Click Expand to see the Sample script ...

#!/bin/bash
# add this to recording started event
# /home/peter/proj/import_test/bin/import_rec_start.sh %FILE% %CHANID% %CARDID% %ENDTIMEISO% &

# Create the channels.conf using scan. scan needs an initial file of
# frequencies. There are files in /usr/share/dvb but they are the
# wrong format. You need a file where each line is like this without the #
#A 629000000 8VSB
# scan -A 1  my_atsc.txt > channels.conf
# Also see https://github.com/oliv3r/dtv-scan-tables

# Log file redirection
exec 1>>/home/peter/proj/import_test/log/import_rec_start.log
exec 2>&1

date
echo input parameters are $1 $2 $3 $4
set -x
file="$1"
chanid=$2
cardid=$3
endtime=$4
# This needs to be changed to a process that will get the real
# file name, You could have multiple storage groups, so assuming
# one directory is not acceptable. Options are:
# 1 Use find commands on the storage directories
# 2 Use a mythtv API to find the full file name
storage_dir=/home/peter/data/mythtv-build/storage
fullfile="$storage_dir/$file"
# The card id and channel need to be checked against mythtv
# Also the channel id needs to be mapped to channels.conf.
# Options are:
# 1 Hard code all of the ids and channels here (as done below)
# 2 Read the database here
# 3 Use a mythtv API
# In my example there are only 2 channels. The names ION and
# IONLife are the names in channel.conf that were assigned by the
# channel scan.
if [[ "$cardid" == 38 ]] ; then
    if [[ "$chanid" == 13587 ]] ; then
        channame=IONLife
    elif [[ "$chanid" == 13582 ]] ; then
        channame=ION
    fi
    if [[ "$channame" != "" ]] ; then
        azap -c /home/peter/proj/import_test/dvb/channels.conf -r $channame &
        azap_pid=$!
        ffmpeg -i /dev/dvb/adapter0/dvr0 -y -acodec copy -vcodec copy -scodec copy -f mpegts "$fullfile" &
        ffmpeg_pid=$!
        # Calculation of recording duration. The recording is stopped
        # by a sleep timer and a kill command. Another option would
        # be to set a parameter in ffmpeg of the encoding start and end time
        # and let it automatically end after it sees the appropriate
        # quantity of video.
        end=`date -d $endtime +%s`
        now=`date +%s`
        let duration=end-now
        sleep $duration
        kill $azap_pid $ffmpeg_pid
    fi
fi

Click Expand to see the Sample python script ...

#!/usr/bin/env python

# mythtv_import_evtest.py
# sample import script by mythrec (c)2017
# Licensed under GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+>"

# This is for use with the MythTV Import Recorder and System Events
# Typically in conjunction with RecordingStarted and RecordingFinished events
# see https://www.mythtv.org/wiki/Import_recorder
# FIXME Update app path & log filename below as appropriate

# You should call this script in the Recording Started event e.g.
# mythtv_import_evtest.py -s %STARTTIMEUTC% -e %ENDTIMEUTC% -f %FILE% -c %CHANID% -t %CARDID%
# see https://www.mythtv.org/wiki/MythTV_System_Events

from MythTV import MythBE,MythDB,Channel
import sys
import os
import getopt
import datetime
import time

#Import a recording
def imp(card,chanid,st,et,file):
    mythtv = MythBE()
    #xml_id=Channel(chanid).xmltvid

    retry=0
    while True:
        #this is not necessarily the prog we want to start recording
        #i.e. could still be on the previous show if they are back to back
        prog=mythtv.getCurrentRecording(card)
        if prog and prog.filename and file in prog.filename:
            fp=prog.filename
            break
        retry+=1
        if retry>1:
            raise RuntimeError('timeout when waiting for import path')
        time.sleep(30)

    sd=(et - st).total_seconds()
    f.write("Starting import %s\n"%(fp))
    app='(mythtv_import.sh "%s" %d &)' % (fp,sd) #FIXME Update app path
    f.write("%s\n"%(app))
    os.system(app)

#Check command line options
def parseCliOpts(argc,argv):
    opts,args = getopt.getopt(argv[1:],'s:e:f:c:t:')
    for (opt,arg) in opts:
        if opt == '-s':
            start=datetime.datetime.strptime(arg,"%Y%m%d%H%M%S")
        elif opt == '-e':
            end=datetime.datetime.strptime(arg,"%Y%m%d%H%M%S")
        elif opt == '-f':
            file=arg
        elif opt == '-c':
            chan=int(arg)
        elif opt == '-t':
            card=int(arg)
            
    mythdb = MythDB()    
    with mythdb as cursor:     
        cursor.execute("""SELECT cardtype from capturecard WHERE cardid=%s""",(card,))    
        row = cursor.fetchone()     
        if row is None:
            raise ValueError('unknown card number',card)
        ct=row[0]    
                
    if (ct=="IMPORT"):
        imp(card,chan,start,end,file)

#Main
def main(argc,argv):
    parseCliOpts(argc,argv)

if __name__ == "__main__":
    f=open('/home/mythtv/import.log','a') #FIXME Update as appropriate
    try:
        f.write("=======IMP-START:%s=======\n"%(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
        f.write(" ".join(sys.argv[:]))
        f.write("\n")
        main(len(sys.argv),sys.argv)
        f.write("=======IMP-END:%s=======\n"%(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
        f.close()
    except Exception, e:
        print 'Caught main exception:',e
        f.write("IMP-ERR: Exception %s\n"%(e))
        sys.exit(1)

Click Expand to see the Sample 'mythtv_import.sh' for link generation / real-time import (as per dummy test recorder) ...

#!/bin/sh
#Realtime import / copy as per dummy test recorder - this script is referenced in the sample python script
ffmpeg -re -i "./2106_20170505195800.mpg" -c:a copy -c:v copy -t $2 "$1"
#Link generation as per import test recorder
#ln -s "/path/to/target" "$1"

Multiple import recorders

If you have more than one non-supported tuner or if the tuner supports more than one channel, you can add more import recorders. There is still only one script and the script must check cardid to determine which import recorder is in use and consequently which tuner to use.