diff --git a/videoarchiver/ffmpeg/encoder_params.py b/videoarchiver/ffmpeg/encoder_params.py index a04267d..d58e699 100644 --- a/videoarchiver/ffmpeg/encoder_params.py +++ b/videoarchiver/ffmpeg/encoder_params.py @@ -14,36 +14,36 @@ class EncoderParams: QUALITY_PRESETS = { "gaming": { "crf": "20", - "preset": "fast", + "preset": "p4", # NVENC preset "tune": "zerolatency", "x264opts": "rc-lookahead=20:me=hex:subme=6:ref=3:b-adapt=1:direct=spatial" }, "animation": { "crf": "18", - "preset": "slow", + "preset": "p7", # NVENC preset "tune": "animation", "x264opts": "rc-lookahead=60:me=umh:subme=9:ref=6:b-adapt=2:direct=auto:deblock=-1,-1" }, "film": { "crf": "22", - "preset": "medium", + "preset": "p6", # NVENC preset "tune": "film", "x264opts": "rc-lookahead=50:me=umh:subme=8:ref=4:b-adapt=2:direct=auto" } } + # NVENC specific presets + NVENC_PRESETS = ["p1", "p2", "p3", "p4", "p5", "p6", "p7"] + # CPU specific presets + CPU_PRESETS = ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow"] + # Minimum bitrates to ensure quality MIN_VIDEO_BITRATE = 500_000 # 500 Kbps MIN_AUDIO_BITRATE = 64_000 # 64 Kbps per channel MAX_AUDIO_BITRATE = 192_000 # 192 Kbps per channel def __init__(self, cpu_cores: int, gpu_info: Dict[str, bool]): - """Initialize encoder parameters manager - - Args: - cpu_cores: Number of available CPU cores - gpu_info: Dict containing GPU availability information - """ + """Initialize encoder parameters manager""" self.cpu_cores = cpu_cores self.gpu_info = gpu_info logger.info(f"Initialized encoder with {cpu_cores} CPU cores and GPU info: {gpu_info}") @@ -64,6 +64,9 @@ class EncoderParams: gpu_params = self._get_gpu_specific_params() if gpu_params: params.update(gpu_params) + # Convert CPU preset to GPU preset if using NVENC + if params.get("c:v") == "h264_nvenc" and params.get("preset") in self.CPU_PRESETS: + params["preset"] = "p6" # Default to p6 for NVENC logger.debug(f"GPU-specific parameters: {gpu_params}") # Calculate and update bitrate parameters @@ -105,7 +108,11 @@ class EncoderParams: # Detect content type content_type = self._detect_content_type(video_info) if content_type in self.QUALITY_PRESETS: - params.update(self.QUALITY_PRESETS[content_type]) + preset_params = self.QUALITY_PRESETS[content_type].copy() + # Don't copy preset if we're using NVENC + if self.gpu_info.get("nvidia", False): + preset_params.pop("preset", None) + params.update(preset_params) # Additional optimizations based on content analysis if video_info.get("has_high_motion", False): @@ -128,7 +135,7 @@ class EncoderParams: if self.gpu_info.get("nvidia", False): return { "c:v": "h264_nvenc", - "preset": "p7", + "preset": "p6", # Use NVENC preset "rc:v": "vbr", "cq:v": "19", "b_ref_mode": "middle", @@ -195,19 +202,19 @@ class EncoderParams: "ac": str(audio_channels) }) - # Adjust CRF based on target size + # Adjust quality based on target size input_bitrate = int(video_info.get("bitrate", 0)) if input_bitrate > 0: compression_ratio = input_bitrate / video_bitrate if compression_ratio > 4: params["crf"] = "26" - params["preset"] = "faster" + params["preset"] = "p4" if self.gpu_info.get("nvidia", False) else "faster" elif compression_ratio > 2: params["crf"] = "23" - params["preset"] = "medium" + params["preset"] = "p6" if self.gpu_info.get("nvidia", False) else "medium" else: params["crf"] = "20" - params["preset"] = "slow" + params["preset"] = "p7" if self.gpu_info.get("nvidia", False) else "slow" logger.info(f"Calculated bitrates - Video: {video_bitrate}bps, Audio: {audio_bitrate}bps") return params @@ -254,10 +261,13 @@ class EncoderParams: if params["c:v"] not in ["libx264", "h264_nvenc", "h264_amf", "h264_qsv"]: raise ValueError(f"Invalid video codec: {params['c:v']}") - # Validate preset - valid_presets = ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "p1", "p2", "p3", "p4", "p5", "p6", "p7"] - if params["preset"] not in valid_presets: - raise ValueError(f"Invalid preset: {params['preset']}") + # Validate preset based on codec + if params["c:v"] == "h264_nvenc": + if params["preset"] not in self.NVENC_PRESETS: + raise ValueError(f"Invalid NVENC preset: {params['preset']}") + elif params["c:v"] == "libx264": + if params["preset"] not in self.CPU_PRESETS: + raise ValueError(f"Invalid CPU preset: {params['preset']}") # Validate pixel format if params["pix_fmt"] not in ["yuv420p", "nv12", "yuv444p"]: