Low Quality Transcode

From MythTV Official Wiki
Jump to: navigation, search

Author Doug Haber (though Raymond Wagner did the hard work)
Description This script does a fairly low quality transcode, useful for converting an 1080i 5.1CH recording to something usable over a wireless network. I use this for my son's cartoons which he watches on a laptop. This uses as slightly altered version of Raymond Wagner's Transcode wrapper stub so it automatically handles all database operations for transcoding a file in place.
Supports Version24.png  Version25.png  


This script follows the transcode stub model so it can be run as:

transcode_low.py %JOBID%
  --- or ---
transcode_low.py --chanid=%CHANID% --starttime=%STARTTIME%


The script uses mythffmpeg to do the transcoding. On my machine it takes HD-PVR files from ~2Gb to ~400MB which work fine on my wireless network and are suitable for viewing on a laptop.

NOTE: This script requires that mythffmpeg is compiled with --enable-libxvid --enable-libmp3lame

It uses the following mythffmpeg commandline:

mythffmpeg -y -i INFILE -ac 2 -vf "scale=640:360" -f avi -vcodec libxvid -vtag XVID -qmin 3 -qmax 3 -acodec libmp3lame -ab 64k OUTFILE 2> /dev/null

Here's an explanation of each parameter

Parameter Reason
-y Overwrite the outputfile if it already exists
-i INFILE specify the input file
-ac 2 Converts audio to 2 channels
-vf "scale=640:360 Converts the video from 1080i to 640 by 360 but keeps the same Aspect Ratio
-f avi Convert to avi format
-vcodec libxvid Use the XVID codec. Note that myth must have been compiled with --enable-libxvid
-vtag XVID Mark the file as using the XVID codec (not sure if this is necessary)
-qmin 3 -qmax 3 Forces the quality to be 3. The range is 1 (best) to 32 (worst). 3 seemed to be decent quality with lowish bitrates.
-acodec libmp3lame Convert to the audio to mp3. Note that myth must have been compiled with --enable-libmp3lame
-ab 64k Set the audio bit rate to 64k.
OUTFILE The file to write out to
2> /dev/null mythffmpeg writes out a lot of data to STDERR. This causes the Python System call in 0.24 to deadlock when the 64KB pipe buffer fills. Since we don't need the output this will dump it all to /dev/null. This is not required in 0.25. Note that this syntax requires your shell be `sh` or `bash`.


PythonIcon.png transcode_low.py

#!/usr/bin/env python

from MythTV import Job, Recorded, System, MythDB, mythproto, MythError

from optparse import OptionParser
import sys
import os

################################
#### adjust these as needed ####
transcoder = '/usr/local/bin/mythffmpeg'
flush_commskip = False
build_seektable = False
################################

def runjob(jobid=None, chanid=None, starttime=None):
    db = MythDB()
    if jobid:
        job = Job(jobid, db=db)
        chanid = job.chanid
        starttime = job.starttime
    rec = Recorded((chanid, starttime), db=db)

    sg = mythproto.findfile(rec.basename, rec.storagegroup, db=db)
    if sg is None:
        print 'Local access to recording not found.'
        sys.exit(1)

    infile = os.path.join(sg.dirname, rec.basename)
    outfile = '%s.t.avi' % infile.rsplit('.',1)[0]
    #### list of segments to be cut
    # rec.markup.gencutlist()
    #### list of segments to keep
    # rec.markup.genuncutlist()

    task = System(path=transcoder, db=db)
    try:
##############################################
#### probably need to adjust this one too ####
        output = task('-y',
                      '-i "%s"' % infile,
                      '-ac 2',
                      '-vf "scale=640:360"',
                      '-f avi',
                      '-vcodec libxvid',
                      '-vtag XVID',
                      '-qmin 3',
                      '-qmax 3',
                      '-acodec libmp3lame',
                      '-ab 64k',
                      '"%s"' % outfile,
                      '2> /dev/null')
##############################################
    except MythError, e:
        print 'Command failed with output:\n%s' % e.stderr
        sys.exit(e.returncode)

    rec.basename = os.path.basename(outfile)
    os.remove(infile)
    rec.filesize = os.path.getsize(outfile)
    rec.transcoded = 1
    rec.seek.clean()

    if flush_commskip:
        for index,mark in reversed(list(enumerate(rec.markup))):
            if mark.type in (rec.markup.MARK_COMM_START, rec.markup.MARK_COMM_END):
                del rec.markup[index]
        rec.bookmark = 0
        rec.cutlist = 0
        rec.markup.commit()

    if build_seektable:
        task = System(path='mythcommflag')
        task.command('--chanid %s' % chanid,
                     '--starttime %s' % starttime,
                     '--rebuild')

    rec.update()

    if jobid:
        job.update({'status':272, 'comment':'Transcode Completed'})

def main():
    parser = OptionParser(usage="usage: %prog [options] [jobid]")

    parser.add_option('--chanid', action='store', type='int', dest='chanid',
            help='Use chanid for manual operation')
    parser.add_option('--starttime', action='store', type='int', dest='starttime',
            help='Use starttime for manual operation')
    parser.add_option('-v', '--verbose', action='store', type='string', dest='verbose',
            help='Verbosity level')

    opts, args = parser.parse_args()

    if opts.verbose:
        if opts.verbose == 'help':
            print MythLog.helptext
            sys.exit(0)
        MythLog._setlevel(opts.verbose)

    if len(args) == 1:
        runjob(jobid=args[0])
    elif opts.chanid and opts.starttime:
        runjob(chanid=opts.chanid, starttime=opts.starttime)
    else:
        print 'Script must be provided jobid, or chanid and starttime.'
        sys.exit(1)

if __name__ == '__main__':
    main()