Example Script using mythtranscode in fifodir mode
From MythTV Official Wiki
The script pipes the output of mythtranscode into ffmpeg, using libx264 to compress the video. The intended use is to set it up as a myth user job.
#!/usr/bin/ruby -w require 'time' require 'fileutils' require 'tmpdir' $stdout.reopen("/var/log/mythtv/mythar#{Time.now.strftime '%F-%T'}.log", "w") $stderr.reopen($stdout) def usage print <<-END Usage: #{$0} --chanid <channel id> --starttime <start time> --title <title> [--subtitle <sub title>] --outdir <directory> [options] #{$0} --chanid <channel id> --starttime <start time> -outfile <path> [options] --chanid <channel id> The channel id for the recording. REQUIRED. --starttime <start time> The start time for the recording. REQUIRED. --title <title> The title of the recording. --subtitle <subtitle> The subtitle of the recording. --outdir <path> The path of the directory for the output file. If one of --title --subtitle and --outdir are specified, then both --title and --outdir must be, and an output file will be chosen on their basis, avoiding conflict with existing files. --outfile <path> The exact path of the output file. ALTERNATIVE to --title --subtitle and --outdir. --vsize <width>x<height> Scale video to specified resolution (the aspect ratio will be maintained using non-square pixels). If not specified, the source resolution will be maintained. --interlaced Assume the source is interlaced and maintain the interlace, provided --vsize not specified. If --vsize is specified then recode at double the frame rate. --passthrough Pass through the audio unaltered if possible. --quality <quality> Quality for compression (default 20.0) END end # Read arguments chanid = nil starttime = nil title = nil subtitle = nil outdir = nil outfile = nil vsize = nil interlaced = false passthrough = false quality = '20.0' processOption = Proc.new do |args| raise "Misspecified option: #{args.join(', ')}" unless args.length == 2 option = args[0].sub(/^--/, '') val = args[1] eval("#{option} = val") end processFlag = Proc.new do |args| raise "Misspecified option: #{args.join(', ')}" unless args.length == 1 option = args[0].sub(/^--/, '') eval("#{option} = true") end handler = {'--chanid' => processOption, '--starttime' => processOption, '--title' => processOption, '--subtitle' => processOption, '--outdir' => processOption, '--outfile' => processOption, '--vsize' => processOption, '--interlaced' => processFlag, '--passthrough' => processFlag, '--quality' => processOption} ARGV.slice_before{|arg| arg =~ /^--/}.each do |par| raise "Unrecognised option: #{par.join(', ')}" unless handler.has_key? par[0] handler[par[0]].call(par) end if chanid.nil? || starttime.nil? raise '--chanid and --starttime required' end if outfile.nil? && outdir.nil? raise 'Use either --outfile, or both --title --outdir, optionally with --subtitle' end if outfile && (title || subtitle || outdir) raise 'Use either --outfile, or both --title --outdir, optionally with --subtitle' end if (title || outdir || subtitle) && ! (title && outdir) raise 'Both --title and --outdir required for this mode of oparation' end mythPassthrough = passthrough ? '--passthrough' : '' #Detect source formats info=`mythtranscode -v general --chanid #{chanid} --starttime #{starttime} --fifoinfo #{mythPassthrough}` svWidth = nil svHeight = nil svAspect = nil svRate = nil saFmt = nil saChans = nil saRate = nil info.each_line do |line| case line.chomp when / FifoVideoWidth (.*)$/ then svWidth = $1 when / FifoVideoHeight (.*)$/ then svHeight = $1 when / FifoVideoAspectRatio (.*)$/ then svAspect = $1 when / FifoVideoFrameRate (.*)$/ then svRate = $1 when / FifoAudioFormat (.*)$/ then saFmt = $1 when / FifoAudioChannels (.*)$/ then saChans = $1 when / FifoAudioHz (.*)$/ then saRate = $1 when / FifoAudioSampleRate (.*)$/ then saRate = $1 end end if !svWidth || !svHeight || !svAspect || !svRate || !saFmt || !saChans || !saRate print info raise 'Failed to derive fifo formats' end svSize="#{svWidth}x#{svHeight}" svAspect = '16:9' if svAspect == '1.77778' svAspect = '4:3' if svAspect == '1.33333' saFmt = 'latm' if saFmt == 'acc_latm' if saFmt == 'latm' ext = '.mkv' fFmt = 'matroska' else ext = '.mp4' fFmt = 'mp4' end # If --outfile not specified, use --outdir --title and --subtitle if !outfile obName = File.join(outdir, title) obName += " - #{subtitle}" if (subtitle && subtitle.length > 0) (0..Float::INFINITY).each do |i| outfile = obName + (i == 0 ? "" : "(#{i})") + ext break if !File.exist? outfile end end # Can do pass through only with formats that ffmpeg will take as input passthrough = false unless %w{ac3 dts mp3 latm}.include? saFmt # Tell ffmpeg what audio to expect and what to do with it if passthrough mythPassthrough = '--passthrough' audInDesc = "-f #{saFmt}" audCodec = 'copy' else mythPassthrough = '' audInDesc = "-f s16le -ac 2 -ar #{saRate}" audCodec = 'ac3 -ab 160k' end filters = [] vFilter = nil vRate = nil vFlags = nil filters.push("crop=#{svWidth}:1080:0:0") if svHeight == '1088' if interlaced if vsize # If scaling interlaced video, we must deinterlace first filters.push('yadif=3') vRate = "-r #{2 * svRate.to_i}" else # Otherwise maintain the interlace vFlags = '-flags +ilme+ildct' end end if vsize filters.push("scale=#{vsize.sub('x', ':')}") end vFilter = '-vf ' + filters.join(',') if filters.length > 0 # Create fifo dir fifoDir = Dir.mktmpdir('mythar') fifoAudio = File.join(fifoDir, 'audout') fifoVideo = File.join(fifoDir, 'vidout') transcodeCmd = "/usr/bin/mythtranscode --chanid #{chanid} --starttime #{starttime} --honorcutlist #{mythPassthrough} --fifodir #{fifoDir} --cleancut" ffmpegCmd = "/usr/bin/ffmpeg #{audInDesc} -i #{fifoAudio} -f rawvideo -top 1 -pix_fmt yuv420p -s #{svSize} -r #{svRate} -i #{fifoVideo} -threads 2 #{vFilter} -vcodec libx264 -preset medium -profile high #{vFlags} -crf #{quality} #{vRate} -aspect #{svAspect} -acodec #{audCodec} -ar #{saRate} -f #{fFmt} \"#{outfile}\"" puts transcodeCmd puts puts ffmpegCmd puts pid = 0 begin pid = Process.spawn(transcodeCmd) while !File.exist?(fifoAudio) || !File.exist?(fifoVideo) raise 'Transcode died before creating pipes' unless Process.wait(pid, Process::WNOHANG).nil? sleep(1) end system(ffmpegCmd) ensure if pid != 0 Process.kill('KILL', pid) Process.wait(pid) end FileUtils.rm_rf fifoDir end