From 38ce226de8da14880a354239054efe4a69aa32a4 Mon Sep 17 00:00:00 2001 From: Focus Luo Date: Tue, 14 May 2024 03:37:02 -0700 Subject: [PATCH] [ffmpeg-qsv] add string api support Signed-off-by: Focus Luo --- lib/ffmpeg/qsv/encoder.py | 130 +++++++++++++++++++++++++++-- lib/ffmpeg/qsv/util.py | 32 ++++++++ lib/string_api.py | 167 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 322 insertions(+), 7 deletions(-) create mode 100644 lib/string_api.py diff --git a/lib/ffmpeg/qsv/encoder.py b/lib/ffmpeg/qsv/encoder.py index 7af0bea0..7b68fb11 100644 --- a/lib/ffmpeg/qsv/encoder.py +++ b/lib/ffmpeg/qsv/encoder.py @@ -10,11 +10,13 @@ from ....lib import platform from ....lib.ffmpeg.encoderbase import BaseEncoderTest, Encoder as FFEncoder from ....lib.ffmpeg.util import have_ffmpeg_hwaccel, have_ffmpeg_encoder, have_ffmpeg_decoder -from ....lib.ffmpeg.qsv.util import mapprofile, using_compatible_driver, have_encode_main10sp +from ....lib.ffmpeg.qsv.util import mapprofile, mapprofile_stringapi, using_compatible_driver, have_encode_main10sp from ....lib.ffmpeg.qsv.decoder import Decoder -from ....lib.common import mapRangeInt, get_media, call, exe2os +from ....lib.common import mapRangeInt, get_media, call, exe2os, timefn, filepath2os from ....lib.codecs import Codec from ....lib.formats import PixelFormat +from ....lib.string_api import * + class Encoder(FFEncoder): hwaccel = property(lambda s: "qsv") @@ -57,12 +59,125 @@ def inner(quality): return f" -preset {quality}" return self.ifprop("quality", inner) + def map_profile_stringapi(self, codec, profile): + return mapprofile_stringapi(codec, profile) + @property def encparams(self): - return ( - f"{super().encparams}{self.ldb}" - f"{self.iqfactor}{self.bqfactor}" - f"{self.iqoffset}{self.bqoffset}" +# return ( +# f"{super().encparams}{self.ldb}" +# f"{self.iqfactor}{self.bqfactor}" +# f"{self.iqoffset}{self.bqoffset}" +# ) + _encparams = "" + if self.profile != None and len(self.profile.strip())>0: + r,_,_profile=self.profile.partition('-profile:v') + _profile=_profile.strip() + _codec_profile = self.map_profile_stringapi(self.codec, _profile) + if _codec_profile is None: + slash.skip_test("profile is not supported".format(**vars(self))) + _encparams = f"CodecProfile={_codec_profile}" + if self.level != None and len(self.level.strip())>0: + _level=re.findall("\d+", self.level)[0] + _encparams = f"{_encparams}:CodecLevel={_level}" + if self.rcmode != None and len(self.rcmode.strip())>0: + _rc_mode=RateControlMethod.MFX_RATECONTROL_CQP.value + if self.rcmode.upper() in ["CQP"]: + _rc_mode=RateControlMethod.MFX_RATECONTROL_CQP.value + elif self.rcmode.upper() in ["CBR"]: + _rc_mode=RateControlMethod.MFX_RATECONTROL_CBR.value + elif self.rcmode.upper() in ["VBR"]: + _rc_mode=RateControlMethod.MFX_RATECONTROL_VBR.value + elif self.rcmode.upper() in ["ICQ"]: + _rc_mode=RateControlMethod.MFX_RATECONTROL_ICQ.value + _encparams = f"{_encparams}:RateControlMethod={_rc_mode}" + if self.qp != None and len(self.qp.strip())>0: + _qp=re.findall("\d+", self.qp)[0] + _rc_mode=RateControlMethod.MFX_RATECONTROL_CQP.value + _encparams = f"{_encparams}:QPI={_qp}:QPP={_qp}:QPB={_qp}" + if self.gop != None and len(self.gop.strip())>0: + _gop=re.findall("\d+", self.gop)[0] + _encparams = f"{_encparams}:GopPicSize={_gop}" + if self.bframes != None and len(self.bframes.strip())>0: + _bframes=re.findall("\d+", self.bframes)[0] + _encparams = f"{_encparams}:GopRefDist={_bframes}" + if self.slices != None and len(self.slices.strip())>0: + _slices=re.findall("\d+", self.slices)[0] + _encparams = f"{_encparams}:NumSlice={_slices}" + if self.maxframesize != None and len(self.maxframesize.strip())>0: + _maxframesize=re.findall("\d+", self.maxframesize)[0] + _encparams = f"{_encparams}:MaxSliceSize={_maxframesize}" + if self.minrate != None and len(self.minrate.strip())>0: + _minrate=re.findall("\d+", self.minrate)[0] + _encparams = f"{_encparams}:TargetKbps={_minrate}" + if self.maxrate != None and len(self.maxrate.strip())>0: + _maxrate=re.findall("\d+", self.maxrate)[0] + _encparams = f"{_encparams}:MaxKbps={_maxrate}" + if self.refs != None and len(self.refs.strip())>0: + _refs=re.findall("\d+", self.refs)[0] + _encparams = f"{_encparams}:NumRefFrame={_refs}" + if self.ladepth != None and len(self.ladepth.strip())>0: + _ladepth=re.findall("\d+", self.ladepth)[0] + _encparams = f"{_encparams}:LookAheadDepth={ladepth}" + if self.intref != None and len(self.intref.strip())>0: + # for exampe: self.intref=" -int_ref_type 1 -int_ref_cycle_size 4 -int_ref_cycle_dist 20" + # to get intref_type + r,_,_intref_type=self.intref.partition('-int_ref_type') + _intref_type,_,r=_intref_type.partition('-int_ref_cycle_size') + # to get intref_size + _intref_type=_intref_type.strip() + r,_,_intref_size=self.intref.partition('-int_ref_cycle_size') + _intref_size,_,r=_intref_size.partition('-int_ref_cycle_dist') + _intref_size=_intref_size.strip() + # to get intref_dist + r,_,_intref_refdist=self.intref.partition('-int_ref_cycle_dist') + _intref_refdist=_intref_refdist.strip() + _encparams = f"{_encparams}:mfxExtCodingOption2.IntRefType={_intref_type}:mfxExtCodingOption2.IntRefCycleSize={_intref_size}:mfxExtCodingOption3.IntRefCycleDist={_intref_refdist}" + if self.extbrc != None and len(self.extbrc.strip())>0: + _extbrc=re.findall("\d+", self.extbrc)[0] + _encparams = f"{_encparams}:ExtBRC={_extbrc}" + if self.ldb != None and len(self.ldb.strip())>0: + _ldb=re.findall("\d+", self.ldb)[0] + _encparams = f"{_encparams}:mfxExtCodingOption3.LowDelayBRC={_ldb}" + if self.tilecols != None and len(self.tilecols.strip())>0: + _tilecols=re.findall("\d+", self.tilecols)[0] + _encparams = f"{_encparams}:mfxExtAV1TileParam.NumTileColumns={_tilecols}" + if self.tilerows != None and len(self.tilerows.strip())>0: + _tilerows=re.findall("\d+", self.tilerows)[0] + _encparams = f"{_encparams}:mfxExtAV1TileParam.NumTileRows={_tilerows}" + if self.strict != None and len(self.strict.strip())>0: + _strict=re.findall("\d+", self.strict)[0] + _encparams = f"{_encparams}:GopOptFlag={_strict}" + if self.pict != None and len(self.pict.strip())>0: + _pict=re.findall("\d+", self.pict)[0] + _encparams = f"{_encparams}:mfxExtCodingOption.PicTimingSEI={_pict}" + if self.quality != None and len(self.quality.strip())>0: + _quality=re.findall("\d+", self.quality)[0] + _encparams = f"{_encparams}:TargetUsage={_quality}" + if self.lowpower != None and len(self.lowpower.strip())>0: + _lowpower=re.findall("\d+", self.lowpower)[0] + _encparams = f"{_encparams}:LowPower={_lowpower}" + #{self.forced_idr} ffmpeg + #{self.pict}{self.rqp} + + if _encparams != None and len(_encparams.strip()) > 1: + if ':' == _encparams[0]: + _encparams = _encparams[1:] + return f"{_encparams}" + + @timefn("ffmpeg:encode") + def encode(self): + if vars(self).get("_encoded", None) is not None: + get_media().artifacts.purge(self._encoded) + self._encoded = get_media().artifacts.reserve(self.encoded_ext) + + return call( + f"{exe2os('ffmpeg')} -v verbose {self.hwinit}" + f" -f rawvideo -pix_fmt {self.format} -s:v {self.width}x{self.height}" + f" {self.fps} -i {self.ossource}" + f" -vf 'format={self.hwformat}{self.hwupload}{self.roi}'" + f" -an -c:v {self.ffencoder} -qsv_params '{self.encparams}'" + f" -vframes {self.frames} -y {self.ffoutput}" ) @slash.requires(*have_ffmpeg_hwaccel("qsv")) @@ -160,7 +275,8 @@ def check_output(self): # intref if vars(self).get("intref", None) is not None: patterns = [ - f"IntRefType: {self.intref['type']};", + f"IntRefType: {self.intref['type']};" + f"|IntRefType, value: {self.intref['type']}", f"IntRefCycleSize: {self.intref['size']};", f"IntRefCycleDist: {self.intref['dist']}", ] diff --git a/lib/ffmpeg/qsv/util.py b/lib/ffmpeg/qsv/util.py index a6f75900..bc43faab 100644 --- a/lib/ffmpeg/qsv/util.py +++ b/lib/ffmpeg/qsv/util.py @@ -7,6 +7,7 @@ from ....lib.common import memoize, get_media from ....lib.ffmpeg.util import * from ....lib.codecs import Codec +from ....lib.string_api import * def using_compatible_driver(): return get_media()._get_driver_name() in ["iHD", "d3d11", "dxva2"] @@ -78,6 +79,37 @@ def mapprofile(codec, profile): }, }.get(codec, {}).get(profile, None) +@memoize +def mapprofile_stringapi(codec, profile): + return { + Codec.AVC : { + "high" : CodecProfile.MFX_PROFILE_AVC_HIGH.value, + "main" : CodecProfile.MFX_PROFILE_AVC_MAIN.value, + "baseline" : CodecProfile.MFX_PROFILE_AVC_BASELINE.value, + "unknown" : CodecProfile.MFX_PROFILE_UNKNOWN.value + }, + Codec.HEVC : { + "main" : CodecProfile.MFX_PROFILE_HEVC_MAIN.value, + "main444" : CodecProfile.MFX_PROFILE_HEVC_REXT.value, + "scc" : CodecProfile.MFX_PROFILE_HEVC_SCC.value, + "scc-444" : CodecProfile.MFX_PROFILE_HEVC_SCC.value, + "mainsp" : CodecProfile.MFX_PROFILE_HEVC_MAINSP.value, + "main10" : CodecProfile.MFX_PROFILE_HEVC_MAIN10.value, + "main10sp" : CodecProfile.MFX_PROFILE_HEVC_MAINSP.value, + "main444-10" : CodecProfile.MFX_PROFILE_HEVC_REXT.value, + "unknown" : CodecProfile.MFX_PROFILE_UNKNOWN.value + }, + Codec.AV1 : { + "main" : CodecProfile.MFX_PROFILE_AV1_MAIN.value, + }, + Codec.VP9 : { + "profile0" : CodecProfile.MFX_PROFILE_VP9_0, + "profile1" : CodecProfile.MFX_PROFILE_VP9_1, + "profile2" : CodecProfile.MFX_PROFILE_VP9_2, + "profile3" : CodecProfile.MFX_PROFILE_VP9_3 + }, + }.get(codec, {}).get(profile, None) + def load_test_spec(*ctx): from ....lib import util as libutil return libutil.load_test_spec("ffmpeg-qsv", *ctx) diff --git a/lib/string_api.py b/lib/string_api.py new file mode 100644 index 00000000..0ab55de5 --- /dev/null +++ b/lib/string_api.py @@ -0,0 +1,167 @@ +from enum import Enum, unique + +#define come from _install/include/vpl/mfxstructures.h + +class CodecProfile(Enum): + MFX_PROFILE_UNKNOWN =0 #/*!< Unspecified profile. */ + + #/* Combined with H.264 profile these flags impose additional constrains. See H.264 specification for the list of constrains. */ + MFX_PROFILE_AVC_CONSTRAINT_SET0 = (0x100 << 0) + MFX_PROFILE_AVC_CONSTRAINT_SET1 = (0x100 << 1) + MFX_PROFILE_AVC_CONSTRAINT_SET2 = (0x100 << 2) + MFX_PROFILE_AVC_CONSTRAINT_SET3 = (0x100 << 3) + MFX_PROFILE_AVC_CONSTRAINT_SET4 = (0x100 << 4) + MFX_PROFILE_AVC_CONSTRAINT_SET5 = (0x100 << 5) + + #/* H.264 Profiles. */ + MFX_PROFILE_AVC_BASELINE =66 + MFX_PROFILE_AVC_MAIN =77 + MFX_PROFILE_AVC_EXTENDED =88 + MFX_PROFILE_AVC_HIGH =100 + MFX_PROFILE_AVC_HIGH10 =110 + MFX_PROFILE_AVC_HIGH_422 =122 + MFX_PROFILE_AVC_CONSTRAINED_BASELINE =MFX_PROFILE_AVC_BASELINE + MFX_PROFILE_AVC_CONSTRAINT_SET1 + MFX_PROFILE_AVC_CONSTRAINED_HIGH =MFX_PROFILE_AVC_HIGH + MFX_PROFILE_AVC_CONSTRAINT_SET4 + MFX_PROFILE_AVC_CONSTRAINT_SET5 + MFX_PROFILE_AVC_PROGRESSIVE_HIGH =MFX_PROFILE_AVC_HIGH + MFX_PROFILE_AVC_CONSTRAINT_SET4 + + #/* HEVC profiles */ + MFX_PROFILE_HEVC_MAIN =1 + MFX_PROFILE_HEVC_MAIN10 =2 + MFX_PROFILE_HEVC_MAINSP =3 + MFX_PROFILE_HEVC_REXT =4 + MFX_PROFILE_HEVC_SCC =9 + + #/* AV1 Profiles */ + MFX_PROFILE_AV1_MAIN = 1 + MFX_PROFILE_AV1_HIGH = 2 + MFX_PROFILE_AV1_PRO = 3 + + #/* VP9 Profiles */ + MFX_PROFILE_VP9_0 = 1 + MFX_PROFILE_VP9_1 = 2 + MFX_PROFILE_VP9_2 = 3 + MFX_PROFILE_VP9_3 = 4 + +class CodecLevel(Enum): + MFX_LEVEL_UNKNOWN =0 #/*!< Unspecified level. */ + #/* H.264 level 1-1.3 */ + MFX_LEVEL_AVC_1 =10 + MFX_LEVEL_AVC_1b =9 + MFX_LEVEL_AVC_11 =11 + MFX_LEVEL_AVC_12 =12 + MFX_LEVEL_AVC_13 =13 + #/* H.264 level 2-2.2 */ + MFX_LEVEL_AVC_2 =20 + MFX_LEVEL_AVC_21 =21 + MFX_LEVEL_AVC_22 =22 + #/* H.264 level 3-3.2 */ + MFX_LEVEL_AVC_3 =30 + MFX_LEVEL_AVC_31 =31 + MFX_LEVEL_AVC_32 =32 + #/* H.264 level 4-4.2 */ + MFX_LEVEL_AVC_4 =40 + MFX_LEVEL_AVC_41 =41 + MFX_LEVEL_AVC_42 =42 + #/* H.264 level 5-5.2 */ + MFX_LEVEL_AVC_5 =50 + MFX_LEVEL_AVC_51 =51 + MFX_LEVEL_AVC_52 =52 + #/* H.264 level 6-6.2 */ + MFX_LEVEL_AVC_6 =60 + MFX_LEVEL_AVC_61 =61 + MFX_LEVEL_AVC_62 =62 + + #/* HEVC levels */ + MFX_LEVEL_HEVC_1 = 10 + MFX_LEVEL_HEVC_2 = 20 + MFX_LEVEL_HEVC_21 = 21 + MFX_LEVEL_HEVC_3 = 30 + MFX_LEVEL_HEVC_31 = 31 + MFX_LEVEL_HEVC_4 = 40 + MFX_LEVEL_HEVC_41 = 41 + MFX_LEVEL_HEVC_5 = 50 + MFX_LEVEL_HEVC_51 = 51 + MFX_LEVEL_HEVC_52 = 52 + MFX_LEVEL_HEVC_6 = 60 + MFX_LEVEL_HEVC_61 = 61 + MFX_LEVEL_HEVC_62 = 62 + + #/* AV1 Levels */ + MFX_LEVEL_AV1_2 = 20 + MFX_LEVEL_AV1_21 = 21 + MFX_LEVEL_AV1_22 = 22 + MFX_LEVEL_AV1_23 = 23 + MFX_LEVEL_AV1_3 = 30 + MFX_LEVEL_AV1_31 = 31 + MFX_LEVEL_AV1_32 = 32 + MFX_LEVEL_AV1_33 = 33 + MFX_LEVEL_AV1_4 = 40 + MFX_LEVEL_AV1_41 = 41 + MFX_LEVEL_AV1_42 = 42 + MFX_LEVEL_AV1_43 = 43 + MFX_LEVEL_AV1_5 = 50 + MFX_LEVEL_AV1_51 = 51 + MFX_LEVEL_AV1_52 = 52 + MFX_LEVEL_AV1_53 = 53 + MFX_LEVEL_AV1_6 = 60 + MFX_LEVEL_AV1_61 = 61 + MFX_LEVEL_AV1_62 = 62 + MFX_LEVEL_AV1_63 = 63 + MFX_LEVEL_AV1_7 = 70 + MFX_LEVEL_AV1_71 = 71 + MFX_LEVEL_AV1_72 = 72 + MFX_LEVEL_AV1_73 = 73 + + +class RateControlMethod(Enum): +#/*! The RateControlMethod enumerator itemizes bitrate control methods. */ + MFX_RATECONTROL_CBR =1 #/*!< Use the constant bitrate control algorithm. */ + MFX_RATECONTROL_VBR =2 #/*!< Use the variable bitrate control algorithm. */ + MFX_RATECONTROL_CQP =3 #/*!< Use the constant quantization parameter algorithm. */ + MFX_RATECONTROL_AVBR =4 #/*!< Use the average variable bitrate control algorithm. */ + MFX_RATECONTROL_RESERVED1 =5 + MFX_RATECONTROL_RESERVED2 =6 + MFX_RATECONTROL_RESERVED3 =100 + MFX_RATECONTROL_RESERVED4 =7 + #/*! + # Use the VBR algorithm with look ahead. It is a special bitrate control mode in the AVC encoder that has been designed + # to improve encoding quality. It works by performing extensive analysis of several dozen frames before the actual encoding and as a side + # effect significantly increases encoding delay and memory consumption. + + # The only available rate control parameter in this mode is mfxInfoMFX::TargetKbps. Two other parameters, MaxKbps and InitialDelayInKB, + # are ignored. To control LA depth the application can use mfxExtCodingOption2::LookAheadDepth parameter. + + # This method is not HRD compliant. + #*/ + MFX_RATECONTROL_LA =8 + #/*! + # Use the Intelligent Constant Quality algorithm. This algorithm improves subjective video quality of encoded stream. Depending on content, + # it may or may not decrease objective video quality. Only one control parameter is used - quality factor, specified by mfxInfoMFX::ICQQuality. + #*/ + MFX_RATECONTROL_ICQ =9 + #/*! + # Use the Video Conferencing Mode algorithm. This algorithm is similar to the VBR and uses the same set of parameters mfxInfoMFX::InitialDelayInKB, + # TargetKbpsandMaxKbps. It is tuned for IPPP GOP pattern and streams with strong temporal correlation between frames. + # It produces better objective and subjective video quality in these conditions than other bitrate control algorithms. + # It does not support interlaced content, B-frames and produced stream is not HRD compliant. + #*/ + MFX_RATECONTROL_VCM =10 + #/*! + # Use Intelligent Constant Quality algorithm with look ahead. Quality factor is specified by mfxInfoMFX::ICQQuality. + # To control LA depth the application can use mfxExtCodingOption2::LookAheadDepth parameter. + # + # This method is not HRD compliant. + #*/ + MFX_RATECONTROL_LA_ICQ =11 + #/*! + # MFX_RATECONTROL_LA_EXT has been removed + #*/ + + #/*! Use HRD compliant look ahead rate control algorithm. */ + MFX_RATECONTROL_LA_HRD =13 + #/*! + # Use the variable bitrate control algorithm with constant quality. This algorithm trying to achieve the target subjective quality with + # the minimum number of bits, while the bitrate constraint and HRD compliance are satisfied. It uses the same set of parameters + # as VBR and quality factor specified by mfxExtCodingOption3::QVBRQuality. + #*/ + MFX_RATECONTROL_QVBR =14