def Measure(self, bitrate, videofile, workdir): result = {} tempyuvfile = "%s/%stempyuvfile.yuv" % (workdir, videofile.basename) if os.path.isfile(tempyuvfile): print "Removing tempfile before decode:", tempyuvfile os.unlink(tempyuvfile) commandline = "../bin/ffmpeg -codec:v %s -i %s/%s.%s %s" % ( self.codecname, workdir, videofile.basename, self.extension, tempyuvfile) print commandline returncode = subprocess.call(commandline, shell=True) if returncode: raise encoder.Error('Decode failed') bitrate = videofile.MeasuredBitrate( os.path.getsize('%s/%s.%s' % (workdir, videofile.basename, self.extension))) commandline = "../bin/psnr %s %s %d %d 9999" % ( videofile.filename, tempyuvfile, videofile.width, videofile.height) print commandline psnr = subprocess.check_output(commandline, shell=True) print "Bitrate", bitrate, "PSNR", psnr result['bitrate'] = int(bitrate) result['psnr'] = float(psnr) os.unlink(tempyuvfile) return result
def Measure(self, bitrate, videofile, workdir): result = {} tempyuvfile = "%s/%stempyuvfile.yuv" % (workdir, videofile.basename) if os.path.isfile(tempyuvfile): print "Removing tempfile before decode:", tempyuvfile os.unlink(tempyuvfile) # The special thing here is that it rescales back to the original size. # TODO(hta): Factor out the difference by itself. commandline = "../bin/ffmpeg -i %s/%s.h261 -s %sx%s %s" % ( workdir, videofile.basename, videofile.width, videofile.height, tempyuvfile) print commandline returncode = subprocess.call(commandline, shell=True) if returncode: raise encoder.Error('Decode failed') bitrate = videofile.MeasuredBitrate( os.path.getsize('%s/%s.h261' % (workdir, videofile.basename))) commandline = "../bin/psnr %s %s %d %d 9999" % ( videofile.filename, tempyuvfile, videofile.width, videofile.height) print commandline psnr = subprocess.check_output(commandline, shell=True) print "Bitrate", bitrate, "PSNR", psnr result['bitrate'] = int(bitrate) result['psnr'] = float(psnr) os.unlink(tempyuvfile) return result
def EncoderVersion(self): version_output = subprocess.check_output( [encoder.Tool('ffmpeg'), '-version']) match = re.match('(ffmpeg .*) Copyright', version_output) if match: return match.group(0) raise encoder.Error('ffmpeg did not find its version string')
def Score(self, encoding): result = encoding.result if not result: raise encoder.Error('Trying to score an encoding without result') score = self.score_function(encoding.bitrate, result) # Weakly penalize long command lines. score -= len(encoding.encoder.parameters.values) * 0.00001 return score
def EncoderVersion(self): try: subprocess.check_output([encoder.Tool('TAppEncoderStatic')]) except subprocess.CalledProcessError, err: helptext = str(err.output) for line in helptext.split('\n'): if re.match('HM software:', line): return line raise encoder.Error('HM version string not found')
def Score(self, encoding, scoredir=None): if scoredir is None: result = encoding.result else: result = self.context.cache.ReadEncodingResult(encoding, scoredir=scoredir) if not result: raise encoder.Error('Trying to score an encoding without result') score = self.score_function(encoding.bitrate, result) # Weakly penalize long command lines. score -= len(encoding.encoder.parameters.values) * 0.00001 return score
def EncoderVersion(self): # The vpxenc command line tool outputs the version number of the # encoder as part of its error message on illegal arguments. try: subprocess.check_output([encoder.Tool('vpxenc')], stderr=subprocess.STDOUT) except Exception, err: version_output = str(err.output) for line in version_output.split('\n'): match = re.match(r'\s+vp9\s+- (.+)$', line) if match: return match.group(1) raise encoder.Error('Did not find vp9 version string')
class Vp9Codec(file_codec.FileCodec): def __init__(self, name='vp9'): super(Vp9Codec, self).__init__(name) self.extension = 'webm' self.option_set = encoder.OptionSet( encoder.IntegerOption('cpu-used', 0, 16), # The "best" option gives encodes that are too slow to be useful. encoder.ChoiceOption(['good', 'rt']).Mandatory(), encoder.IntegerOption('passes', 1, 2), ) def StartEncoder(self, context): return encoder.Encoder( context, encoder.OptionValueSet( self.option_set, '--passes=1 --good --noise-sensitivity=0 --cpu-used=5')) def EncodeCommandLine(self, parameters, bitrate, videofile, encodedfile): commandline = (encoder.Tool('vpxenc') + ' ' + parameters.ToString() + ' --target-bitrate=' + str(bitrate) + ' --fps=' + str(videofile.framerate) + '/1' + ' -w ' + str(videofile.width) + ' -h ' + str(videofile.height) + ' ' + videofile.filename + ' --codec=vp9 ' + ' -o ' + encodedfile) return commandline def DecodeCommandLine(self, videofile, encodedfile, yuvfile): commandline = '%s %s --i420 -o %s' % (encoder.Tool("vpxdec"), encodedfile, yuvfile) return commandline def ResultData(self, encodedfile): more_results = {} more_results['frame'] = file_codec.MatroskaFrameInfo(encodedfile) return more_results def EncoderVersion(self): # The vpxenc command line tool outputs the version number of the # encoder as part of its error message on illegal arguments. try: subprocess.check_output([encoder.Tool('vpxenc')], stderr=subprocess.STDOUT) except Exception, err: version_output = str(err.output) for line in version_output.split('\n'): match = re.match(r'\s+vp9\s+- (.+)$', line) if match: return match.group(1) raise encoder.Error('Did not find vp9 version string') raise encoder.Error('Getting vp9 version from help message failed')
class HevcCodec(file_codec.FileCodec): def __init__(self, name='hevc', formatter=None): self.name = name self.codecname = 'hevc' self.extension = 'hevc' super(HevcCodec, self).__init__(name, formatter=formatter) def StartEncoder(self, context): return encoder.Encoder(context, encoder.OptionValueSet(self.option_set, '')) def EncodeCommandLine(self, parameters, bitrate, videofile, encodedfile): commandline = ( '%s --SourceWidth=%d ---SourceHeight=%d ' '-c %s ' '--FrameRate=%d --InputFile=%s ' '--FramesToBeEncoded=%d ' '--IntraPeriod=-1 ' '%s --TargetBitrate=%d --BitstreamFile=%s' % ( encoder.Tool('TAppEncoderStatic'), videofile.width, videofile.height, encoder.Tool('hevc_ra_main.cfg'), # Configuration file videofile.framerate, videofile.filename, videofile.FrameCount(), parameters.ToString(), bitrate, encodedfile)) return commandline def DecodeCommandLine(self, videofile, encodedfile, yuvfile): commandline = "%s --BitstreamFile=%s --ReconFile=%s" % ( encoder.Tool('TAppDecoderStatic'), encodedfile, yuvfile) return commandline def EncoderVersion(self): try: subprocess.check_output([encoder.Tool('TAppEncoderStatic')]) except subprocess.CalledProcessError, err: helptext = str(err.output) for line in helptext.split('\n'): if re.match('HM software:', line): return line raise encoder.Error('HM version string not found') raise encoder.Error('HM did not return help text as expected')
def VerifyEncode(self, parameters, bitrate, videofile, workdir): """Returns true if a new encode of the file gives exactly the same file.""" old_encoded_file = '%s/%s.%s' % (workdir, videofile.basename, self.extension) if not os.path.isfile(old_encoded_file): raise encoder.Error('Old encoded file missing: %s' % old_encoded_file) new_encoded_file = '%s/%s_verify.%s' % (workdir, videofile.basename, self.extension) self._EncodeFile(parameters, bitrate, videofile, new_encoded_file) if not VideoFilesEqual(old_encoded_file, new_encoded_file, self.extension): # If there is a difference, we leave the new encoded file so that # they can be compared by hand if desired. return False os.unlink(new_encoded_file) return True
def PickCodec(name): if name is None: name = 'vp8' if name in codec_map: return codec_map[name]() raise encoder.Error('Unrecognized codec name %s' % name)
def FinishWorkDir(dirname): # Verification of validity if encoder_configuration.conf.sysdir() != dirname: raise encoder.Error('FinishWorkDir got dirname %s but expected %s' % (dirname, encoder_configuration.conf.sysdir())) shutil.rmtree(dirname)
def EmptyWorkDirectory(): dirname = encoder_configuration.conf.workdir() if not dirname.startswith(tempfile.gettempdir()): raise encoder.Error('Workdir is %s, not a tempfile' % dirname) shutil.rmtree(dirname) os.mkdir(dirname)
def PickCodec(name): if name is None: name = 'vp8' if name in CODEC_MAP: return CODEC_MAP[name].constructor() raise encoder.Error('Unrecognized codec name %s' % name)
def EncoderVersion(self): raise encoder.Error('File codecs must define their own version')
def ShortName(name): """Return a pretty but short name for the codec.""" if name in codec_map: return codec_map[name].shortname raise encoder.Error('Unrecognized codec name %s' % name)
def LongName(name): """Return a pretty but long name for the codec.""" if name in codec_map: return codec_map[name].longname raise encoder.Error('Unrecognized codec name %s' % name)
def FinishWorkDir(dirname): # Verification of validity if os.environ['CODEC_WORKDIR'] != dirname: raise encoder.Error('Dirname was wrong in FinishWorkDir') shutil.rmtree(dirname)
def EncodeCommandLine(self, parameters, bitrate, videofile, encodedfile): """This function returns the command line that should be executed in order to turn an YUV file into an encoded file.""" # pylint: disable=W0613,R0201 raise encoder.Error('EncodeCommandLine not defined')
class Vp8Codec(file_codec.FileCodec): def __init__(self, name='vp8'): super(Vp8Codec, self).__init__(name) self.extension = 'webm' self.option_set = encoder.OptionSet( encoder.Option('overshoot-pct', ['0', '15', '30', '45']), encoder.Option('undershoot-pct', ['0', '25', '50', '75', '100']), # CQ mode is not considered for end-usage at the moment. encoder.Option('end-usage', ['cbr', 'vbr']), # End-usage cq doesn't really make sense unless we also set q to something # between min and max. This is being checked. # encoder.Option('end-usage', ['cbr', 'vbr', 'cq']), encoder.Option('end-usage', ['cbr', 'vbr']), encoder.Option('min-q', ['0', '2', '4', '8', '16', '24']), encoder.Option('max-q', ['32', '56', '63']), encoder.Option( 'buf-sz', ['200', '500', '1000', '2000', '4000', '8000', '16000']), encoder.Option( 'buf-initial-sz', ['200', '400', '800', '1000', '2000', '4000', '8000', '16000' ]), encoder.Option('max-intra-rate', ['100', '200', '400', '600', '800', '1200']), encoder.ChoiceOption(['good', 'best', 'rt']), encoder.IntegerOption('cpu-used', -16, 16), ) def StartEncoder(self, context): return encoder.Encoder( context, encoder.OptionValueSet( self.option_set, '--lag-in-frames=0 ' '--kf-min-dist=3000 ' '--kf-max-dist=3000 --cpu-used=0 --static-thresh=0 ' '--token-parts=1 --end-usage=cbr --min-q=2 --max-q=56 ' '--undershoot-pct=100 --overshoot-pct=15 --buf-sz=1000 ' '--buf-initial-sz=800 --buf-optimal-sz=1000 --max-intra-rate=1200 ' '--resize-allowed=0 --drop-frame=0 ' '--passes=1 --good --noise-sensitivity=0')) def ConfigurationFixups(self, config): # In RT mode, vp8 will change encoding based on elapsed time if # cpu-used is positive, thus making encodings unstable. # Negative values give stable encodings, with -1 # being the slowest variant. if config.HasValue('good/best/rt'): if config.GetValue('good/best/rt') == 'rt': if (not config.HasValue('cpu-used') or int(config.GetValue('cpu-used')) >= 0): return config.ChangeValue('cpu-used', '-1') return config def EncodeCommandLine(self, parameters, bitrate, videofile, encodedfile): commandline = (encoder.Tool('vpxenc') + ' ' + parameters.ToString() + ' --target-bitrate=' + str(bitrate) + ' --fps=' + str(videofile.framerate) + '/1' + ' -w ' + str(videofile.width) + ' -h ' + str(videofile.height) + ' ' + videofile.filename + ' --codec=vp8 ' + ' -o ' + encodedfile) return commandline def DecodeCommandLine(self, videofile, encodedfile, yuvfile): commandline = '%s -i %s %s' % (encoder.Tool("ffmpeg"), encodedfile, yuvfile) return commandline def ResultData(self, encodedfile): more_results = {} more_results['frame'] = file_codec.MatroskaFrameInfo(encodedfile) return more_results def EncoderVersion(self): # The vpxenc command line tool outputs the version number of the # encoder as part of its error message on illegal arguments. try: subprocess.check_output([encoder.Tool('vpxenc')], stderr=subprocess.STDOUT) except Exception, err: version_output = str(err.output) for line in version_output.split('\n'): match = re.match(r'\s+vp8\s+- (.+)$', line) if match: return match.group(1) raise encoder.Error('Did not find vp8 version string') raise encoder.Error('Getting version from vp8 help message failed')