Code: Select all
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# 2015 Michael Stucky
# This script is based on Raymond Wagner's transcode wrapper stub.
# Designed to be a USERJOB of the form </path to script/transcode-h264.py %JOBID%>
from MythTV import Job, Recorded, System, MythDB, findfile, MythError, MythLog, datetime
from optparse import OptionParser
from glob import glob
from shutil import copyfile
import sys
import os
import errno
import time
import subprocess
transcoder = '/usr/bin/nice'
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 = findfile('/'+rec.basename, rec.storagegroup, db=db)
if sg is None:
print 'Local access to recording not found.'
sys.exit(1)
if rec.transcoded == 1:
if jobid:
job.update({'status':272, 'comment':'Recording already transcoded'})
sys.exit(0)
infile = os.path.join(sg.dirname, rec.basename)
tmpfile = '%s.tmp' % infile.rsplit('.',1)[0]
outfile = '%s.mpg' % infile.rsplit('.',1)[0]
# reformat 'starttime' for use with mythtranscode/ffmpeg/mythcommflag
starttime = str(starttime.utcisoformat().replace(u':', '').replace(u' ', '').replace(u'T', '').replace('-', ''))
# Lossless transcode to strip cutlist
if rec.cutlist == 1:
if jobid:
job.update({'status':4, 'comment':'Removing Cutlist'})
task = System(path='mythtranscode', db=db)
try:
output = task('--verbose --chanid "%s"' % chanid,
'--starttime "%s"' % starttime,
'--mpeg2 -v',
'--allaudiotracks',
'--honorcutlist',
'--profile autodetect',
'--allkeys',
'-o "%s"' % tmpfile,
'> /tmp/mythtranscode_status')
except MythError, e:
print 'Command failed with output:\n%s' % e.stderr
if jobid:
job.update({'status':304, 'comment':'Error, transcoding without cutlist'})
#sys.exit(e.retcode)
#Don't exit, just transcode without the cutlist
os.remove(tmpfile)
task = System(path='/usr/bin/mkfifo', db=db)
try:
output = task('%s.tmp' % infile.rsplit('.',1)[0])
except MythError, e:
print 'Command failed with output:\n%s' % e.stderr
if jobid:
job.update({'status':304, 'comment' : 'Mkfifo failed'})
sys.exit(e.retcode)
try:
subprocess.Popen(['nohup','cp',infile,tmpfile])
except subprocess.CalledProcessError as e:
print 'Command failed with returncode:\n%s' % e.returncode
if jobid:
job.update({'status':304, 'comment':'Copying to mkfifo failed'})
sys.exit(e.returncode)
else:
task = System(path='/usr/bin/mkfifo', db=db)
try:
output = task('%s.tmp' % infile.rsplit('.',1)[0])
except MythError, e:
print 'Command failed with output:\n%s' % e.stderr
if jobid:
job.update({'status':304, 'comment' : 'Mkfifo failed'})
sys.exit(e.retcode)
try:
subprocess.Popen(['nohup','cp',infile,tmpfile])
except subprocess.CalledProcessError as e:
print 'Command failed with returncode:\n%s' % e.returncode
if jobid:
job.update({'status':304, 'comment':'Copying to mkfifo failed'})
sys.exit(e.returncode)
# Transcode to x264
if jobid:
job.update({'status':4, 'comment':'Transcoding to x264'})
task = System(path=transcoder, db=db)
try:
output = task('-n 19 ffmpeg -y -i "%s"' % tmpfile,
'-map 0',
'-c:a libfdk_aac -profile:a aac_he -b:a 64k',
'-vcodec libx264',
'-scodec dvb_teletext',
'-preset medium',
'-crf 23',
'-f mpegts',
'"%s"' % outfile,
'> /tmp/ffmpeg_status 2>&1 < /dev/null')
except MythError, e:
print 'Command failed with output:\n%s' % e.stderr
if jobid:
job.update({'status':304, 'comment':'Transcoding to x264 failed'})
sys.exit(e.retcode)
rec.basename = os.path.basename(outfile)
os.remove(infile)
# Cleanup the old *.png files
for filename in glob('%s*.png' % infile):
os.remove(filename)
os.remove(tmpfile)
try:
os.remove('%s.map' % tmpfile)
except OSError:
pass
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()
task = System(path='mythutil')
task.command('--clearcutlist',
'--chanid %s' % chanid,
'--starttime %s' % starttime)
rec.cutlist = 0
rec.update()
if jobid:
job.update({'status':4, 'comment':'Rebuilding seektable'})
if build_seektable:
task = System(path='mythtranscode')
task.command('--mpeg2',
'--buildindex',
'--allkeys',
'--showprogress',
'--infile "%s"' % outfile)
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()
I modified it a bit from the original by making it work with a FIFO instead of copying over the entire .ts into a new .tmp file, instead it copies to the FIFO, forks into the background and runs ffmpeg on the FIFO as 'cp' feeds it from the .ts file.
Well it was working great until recently when I added the '-scodec dvb-teletext' option and now I'm not sure why, but it fails on the first attempt to run it. I tried to 'su' into the mythtv user and run the script manually to troubleshoot what was happening, but that happens to run successfully. It's just the initial launching of the script from the MythTV frontend that doesn't seem to work, and then manually launching works fine, so I can't troubleshoot it much. All I can tell is it's failing at:
Code: Select all
task = System(path=transcoder, db=db)
try:
output = task('-n 19 ffmpeg -y -i "%s"' % tmpfile,
'-map 0',
'-c:a libfdk_aac -profile:a aac_he -b:a 64k',
'-vcodec libx264',
'-scodec dvb_teletext',
'-preset medium',
'-crf 23',
'-f mpegts',
'"%s"' % outfile,
'> /tmp/ffmpeg_status 2>&1 < /dev/null')
except MythError, e:
print 'Command failed with output:\n%s' % e.stderr
if jobid:
job.update({'status':304, 'comment':'Transcoding to x264 failed'})
sys.exit(e.retcode)
It may be evident, but I don't know much about Python. I'm capable of writing basic applications in C so I've just been using that knowledge to fumble through the script.
Anyone want to help me get this working? I could just disable to the teletext, but I'm hearing impaired so I love subtitles and was exciting that I found a way to include them. You'll have to compile ffmpeg with --enable-libzvbi in order to use the dvb_teletext codec. Might also need to compile with --enable-libfdk-aac and --enable-nonfree, or select a different audio codec if your ffmpeg isn't built with libfdk-aac.