mirror of
https://github.com/linyqh/NarratoAI.git
synced 2025-12-12 11:22:51 +00:00
优化视频处理中的硬件加速检测与兼容性检查
- 在 Windows 系统上增加显卡信息检测,避免使用不兼容的硬件加速 - 添加强制使用软件编码的选项,提升兼容性 - 增强错误处理机制,确保在硬件加速失败时能够回退到软件编码 - 更新日志信息,提供更清晰的处理反馈
This commit is contained in:
parent
8cdb450009
commit
fa2541f95c
@ -63,6 +63,9 @@ def get_hardware_acceleration_option() -> Optional[str]:
|
|||||||
Optional[str]: 硬件加速参数,如果不支持则返回None
|
Optional[str]: 硬件加速参数,如果不支持则返回None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
# 检测操作系统
|
||||||
|
is_windows = os.name == 'nt'
|
||||||
|
|
||||||
# 检查NVIDIA GPU支持
|
# 检查NVIDIA GPU支持
|
||||||
nvidia_check = subprocess.run(
|
nvidia_check = subprocess.run(
|
||||||
['ffmpeg', '-hide_banner', '-hwaccels'],
|
['ffmpeg', '-hide_banner', '-hwaccels'],
|
||||||
@ -70,18 +73,45 @@ def get_hardware_acceleration_option() -> Optional[str]:
|
|||||||
)
|
)
|
||||||
output = nvidia_check.stdout.lower()
|
output = nvidia_check.stdout.lower()
|
||||||
|
|
||||||
if 'cuda' in output:
|
# 首先尝试获取系统信息,Windows系统使用更安全的检测方法
|
||||||
|
if is_windows:
|
||||||
|
try:
|
||||||
|
# 尝试检测显卡信息
|
||||||
|
gpu_info = subprocess.run(
|
||||||
|
['wmic', 'path', 'win32_VideoController', 'get', 'name'],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False
|
||||||
|
)
|
||||||
|
gpu_info_output = gpu_info.stdout.lower()
|
||||||
|
|
||||||
|
# 检测是否为AMD显卡
|
||||||
|
if 'amd' in gpu_info_output or 'radeon' in gpu_info_output:
|
||||||
|
logger.info("检测到AMD显卡,为避免兼容性问题,将使用软件编码")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 检测是否为集成显卡
|
||||||
|
if 'intel' in gpu_info_output and ('hd graphics' in gpu_info_output or 'uhd graphics' in gpu_info_output):
|
||||||
|
# 在Windows上,Intel集成显卡可能不稳定,建议使用软件编码
|
||||||
|
logger.info("检测到Intel集成显卡,为避免兼容性问题,将使用软件编码")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"获取显卡信息失败: {str(e)},将谨慎处理硬件加速")
|
||||||
|
|
||||||
|
# 根据ffmpeg支持的硬件加速器决定使用哪种
|
||||||
|
if 'cuda' in output and not is_windows:
|
||||||
|
# 在非Windows系统上使用CUDA
|
||||||
return 'cuda'
|
return 'cuda'
|
||||||
elif 'nvenc' in output:
|
elif 'nvenc' in output and not is_windows:
|
||||||
|
# 在非Windows系统上使用NVENC
|
||||||
return 'nvenc'
|
return 'nvenc'
|
||||||
elif 'qsv' in output: # Intel Quick Sync
|
elif 'qsv' in output and not (is_windows and ('amd' in gpu_info_output if 'gpu_info_output' in locals() else False)):
|
||||||
|
# 只有在非AMD系统上使用QSV
|
||||||
return 'qsv'
|
return 'qsv'
|
||||||
elif 'videotoolbox' in output: # macOS
|
elif 'videotoolbox' in output: # macOS
|
||||||
return 'videotoolbox'
|
return 'videotoolbox'
|
||||||
elif 'vaapi' in output: # Linux VA-API
|
elif 'vaapi' in output and not is_windows: # Linux VA-API
|
||||||
return 'vaapi'
|
return 'vaapi'
|
||||||
else:
|
else:
|
||||||
logger.info("没有找到支持的硬件加速器,将使用软件编码")
|
logger.info("没有找到支持的硬件加速器或系统不兼容,将使用软件编码")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"检测硬件加速器时出错: {str(e)},将使用软件编码")
|
logger.warning(f"检测硬件加速器时出错: {str(e)},将使用软件编码")
|
||||||
@ -175,8 +205,32 @@ def process_single_video(
|
|||||||
# 构建基本命令
|
# 构建基本命令
|
||||||
command = ['ffmpeg', '-y']
|
command = ['ffmpeg', '-y']
|
||||||
|
|
||||||
# 添加硬件加速参数
|
# 安全检查:如果在Windows上,则慎用硬件加速
|
||||||
|
is_windows = os.name == 'nt'
|
||||||
|
if is_windows and hwaccel:
|
||||||
|
logger.info("在Windows系统上检测到硬件加速请求,将进行额外的兼容性检查")
|
||||||
|
try:
|
||||||
|
# 对视频进行快速探测,检测其基本信息
|
||||||
|
probe_cmd = [
|
||||||
|
'ffprobe', '-v', 'error',
|
||||||
|
'-select_streams', 'v:0',
|
||||||
|
'-show_entries', 'stream=codec_name,width,height',
|
||||||
|
'-of', 'csv=p=0',
|
||||||
|
input_path
|
||||||
|
]
|
||||||
|
result = subprocess.run(probe_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
|
||||||
|
|
||||||
|
# 如果探测成功,使用硬件加速;否则降级到软件编码
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.warning(f"视频探测失败,为安全起见,禁用硬件加速: {result.stderr}")
|
||||||
|
hwaccel = None
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"视频探测出错,禁用硬件加速: {str(e)}")
|
||||||
|
hwaccel = None
|
||||||
|
|
||||||
|
# 添加硬件加速参数(根据前面的安全检查可能已经被禁用)
|
||||||
if hwaccel:
|
if hwaccel:
|
||||||
|
try:
|
||||||
if hwaccel == 'cuda' or hwaccel == 'nvenc':
|
if hwaccel == 'cuda' or hwaccel == 'nvenc':
|
||||||
command.extend(['-hwaccel', 'cuda'])
|
command.extend(['-hwaccel', 'cuda'])
|
||||||
elif hwaccel == 'qsv':
|
elif hwaccel == 'qsv':
|
||||||
@ -185,6 +239,11 @@ def process_single_video(
|
|||||||
command.extend(['-hwaccel', 'videotoolbox'])
|
command.extend(['-hwaccel', 'videotoolbox'])
|
||||||
elif hwaccel == 'vaapi':
|
elif hwaccel == 'vaapi':
|
||||||
command.extend(['-hwaccel', 'vaapi', '-vaapi_device', '/dev/dri/renderD128'])
|
command.extend(['-hwaccel', 'vaapi', '-vaapi_device', '/dev/dri/renderD128'])
|
||||||
|
logger.info(f"应用硬件加速: {hwaccel}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"应用硬件加速参数时出错: {str(e)},将使用软件编码")
|
||||||
|
# 重置命令,移除可能添加了一半的硬件加速参数
|
||||||
|
command = ['ffmpeg', '-y']
|
||||||
|
|
||||||
# 输入文件
|
# 输入文件
|
||||||
command.extend(['-i', input_path])
|
command.extend(['-i', input_path])
|
||||||
@ -209,16 +268,31 @@ def process_single_video(
|
|||||||
'-r', '30', # 设置帧率为30fps
|
'-r', '30', # 设置帧率为30fps
|
||||||
])
|
])
|
||||||
|
|
||||||
# 选择编码器
|
# 选择编码器 - 考虑到Windows和特定硬件的兼容性
|
||||||
|
use_software_encoder = True
|
||||||
|
|
||||||
|
if hwaccel:
|
||||||
if hwaccel == 'cuda' or hwaccel == 'nvenc':
|
if hwaccel == 'cuda' or hwaccel == 'nvenc':
|
||||||
|
try:
|
||||||
|
# 在使用NVIDIA编码器前先检查其可用性
|
||||||
|
subprocess.run(['ffmpeg', '-hide_banner', '-encoders'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
command.extend(['-c:v', 'h264_nvenc', '-preset', 'p4', '-profile:v', 'high'])
|
command.extend(['-c:v', 'h264_nvenc', '-preset', 'p4', '-profile:v', 'high'])
|
||||||
elif hwaccel == 'qsv':
|
use_software_encoder = False
|
||||||
|
except Exception:
|
||||||
|
logger.warning("NVENC编码器不可用,将使用软件编码")
|
||||||
|
elif hwaccel == 'qsv' and not is_windows: # 在Windows上避免使用QSV
|
||||||
command.extend(['-c:v', 'h264_qsv', '-preset', 'medium'])
|
command.extend(['-c:v', 'h264_qsv', '-preset', 'medium'])
|
||||||
elif hwaccel == 'videotoolbox':
|
use_software_encoder = False
|
||||||
|
elif hwaccel == 'videotoolbox': # macOS
|
||||||
command.extend(['-c:v', 'h264_videotoolbox', '-profile:v', 'high'])
|
command.extend(['-c:v', 'h264_videotoolbox', '-profile:v', 'high'])
|
||||||
elif hwaccel == 'vaapi':
|
use_software_encoder = False
|
||||||
|
elif hwaccel == 'vaapi' and not is_windows: # Linux VA-API
|
||||||
command.extend(['-c:v', 'h264_vaapi', '-profile', '100'])
|
command.extend(['-c:v', 'h264_vaapi', '-profile', '100'])
|
||||||
else:
|
use_software_encoder = False
|
||||||
|
|
||||||
|
# 如果前面的条件未能应用硬件编码器,使用软件编码
|
||||||
|
if use_software_encoder:
|
||||||
|
logger.info("使用软件编码器(libx264)")
|
||||||
command.extend(['-c:v', 'libx264', '-preset', 'medium', '-profile:v', 'high'])
|
command.extend(['-c:v', 'libx264', '-preset', 'medium', '-profile:v', 'high'])
|
||||||
|
|
||||||
# 设置视频比特率和其他参数
|
# 设置视频比特率和其他参数
|
||||||
@ -234,11 +308,56 @@ def process_single_video(
|
|||||||
|
|
||||||
# 执行命令
|
# 执行命令
|
||||||
try:
|
try:
|
||||||
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
# logger.info(f"执行FFmpeg命令: {' '.join(command)}")
|
||||||
|
process = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
logger.info(f"视频处理成功: {output_path}")
|
||||||
return output_path
|
return output_path
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logger.error(f"处理视频失败: {e.stderr.decode() if e.stderr else str(e)}")
|
error_msg = e.stderr.decode() if e.stderr else str(e)
|
||||||
raise RuntimeError(f"处理视频失败: {str(e)}")
|
logger.error(f"处理视频失败: {error_msg}")
|
||||||
|
|
||||||
|
# 如果使用硬件加速失败,尝试使用软件编码
|
||||||
|
if hwaccel:
|
||||||
|
logger.info("尝试使用软件编码作为备选方案")
|
||||||
|
try:
|
||||||
|
# 构建新的命令,使用软件编码
|
||||||
|
fallback_cmd = ['ffmpeg', '-y', '-i', input_path]
|
||||||
|
|
||||||
|
# 保持原有的音频设置
|
||||||
|
if not keep_audio:
|
||||||
|
fallback_cmd.extend(['-an'])
|
||||||
|
else:
|
||||||
|
has_audio = check_video_has_audio(input_path)
|
||||||
|
if has_audio:
|
||||||
|
fallback_cmd.extend(['-c:a', 'aac', '-b:a', '128k'])
|
||||||
|
else:
|
||||||
|
fallback_cmd.extend(['-an'])
|
||||||
|
|
||||||
|
# 保持原有的视频过滤器
|
||||||
|
fallback_cmd.extend([
|
||||||
|
'-vf', f"{scale_filter},{pad_filter}",
|
||||||
|
'-r', '30',
|
||||||
|
'-c:v', 'libx264',
|
||||||
|
'-preset', 'medium',
|
||||||
|
'-profile:v', 'high',
|
||||||
|
'-b:v', '5M',
|
||||||
|
'-maxrate', '8M',
|
||||||
|
'-bufsize', '10M',
|
||||||
|
'-pix_fmt', 'yuv420p',
|
||||||
|
output_path
|
||||||
|
])
|
||||||
|
|
||||||
|
logger.info(f"执行备选FFmpeg命令: {' '.join(fallback_cmd)}")
|
||||||
|
subprocess.run(fallback_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
logger.info(f"使用软件编码成功处理视频: {output_path}")
|
||||||
|
return output_path
|
||||||
|
except subprocess.CalledProcessError as fallback_error:
|
||||||
|
fallback_error_msg = fallback_error.stderr.decode() if fallback_error.stderr else str(fallback_error)
|
||||||
|
logger.error(f"备选软件编码也失败: {fallback_error_msg}")
|
||||||
|
raise RuntimeError(f"无法处理视频 {input_path}: 硬件加速和软件编码都失败")
|
||||||
|
|
||||||
|
# 如果不是硬件加速导致的问题,或者备选方案也失败了,抛出原始错误
|
||||||
|
raise RuntimeError(f"处理视频失败: {error_msg}")
|
||||||
|
|
||||||
|
|
||||||
def combine_clip_videos(
|
def combine_clip_videos(
|
||||||
@ -247,6 +366,7 @@ def combine_clip_videos(
|
|||||||
video_ost_list: List[int],
|
video_ost_list: List[int],
|
||||||
video_aspect: VideoAspect = VideoAspect.portrait,
|
video_aspect: VideoAspect = VideoAspect.portrait,
|
||||||
threads: int = 4,
|
threads: int = 4,
|
||||||
|
force_software_encoding: bool = False, # 新参数,强制使用软件编码
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
合并子视频
|
合并子视频
|
||||||
@ -256,6 +376,7 @@ def combine_clip_videos(
|
|||||||
video_ost_list: 原声播放列表 (0: 不保留原声, 1: 只保留原声, 2: 保留原声并保留解说)
|
video_ost_list: 原声播放列表 (0: 不保留原声, 1: 只保留原声, 2: 保留原声并保留解说)
|
||||||
video_aspect: 屏幕比例
|
video_aspect: 屏幕比例
|
||||||
threads: 线程数
|
threads: 线程数
|
||||||
|
force_software_encoding: 是否强制使用软件编码(忽略硬件加速检测)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: 合并后的视频路径
|
str: 合并后的视频路径
|
||||||
@ -273,9 +394,18 @@ def combine_clip_videos(
|
|||||||
video_width, video_height = aspect.to_resolution()
|
video_width, video_height = aspect.to_resolution()
|
||||||
|
|
||||||
# 检测可用的硬件加速选项
|
# 检测可用的硬件加速选项
|
||||||
hwaccel = get_hardware_acceleration_option()
|
hwaccel = None if force_software_encoding else get_hardware_acceleration_option()
|
||||||
if hwaccel:
|
if hwaccel:
|
||||||
logger.info(f"将使用 {hwaccel} 硬件加速")
|
logger.info(f"将使用 {hwaccel} 硬件加速")
|
||||||
|
elif force_software_encoding:
|
||||||
|
logger.info("已强制使用软件编码,跳过硬件加速检测")
|
||||||
|
else:
|
||||||
|
logger.info("未检测到兼容的硬件加速,将使用软件编码")
|
||||||
|
|
||||||
|
# Windows系统上,默认使用软件编码以提高兼容性
|
||||||
|
if os.name == 'nt' and hwaccel:
|
||||||
|
logger.warning("在Windows系统上检测到硬件加速,但为了提高兼容性,建议使用软件编码")
|
||||||
|
# 不强制禁用hwaccel,而是在process_single_video中进行额外安全检查
|
||||||
|
|
||||||
# 重组视频路径和原声设置为一个字典列表结构
|
# 重组视频路径和原声设置为一个字典列表结构
|
||||||
video_segments = []
|
video_segments = []
|
||||||
@ -339,6 +469,28 @@ def combine_clip_videos(
|
|||||||
logger.info(f"视频 {segment['index'] + 1}/{len(video_segments)} 处理完成")
|
logger.info(f"视频 {segment['index'] + 1}/{len(video_segments)} 处理完成")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"处理视频 {segment['path']} 时出错: {str(e)}")
|
logger.error(f"处理视频 {segment['path']} 时出错: {str(e)}")
|
||||||
|
# 如果使用硬件加速失败,尝试使用软件编码
|
||||||
|
if hwaccel and not force_software_encoding:
|
||||||
|
logger.info(f"尝试使用软件编码处理视频 {segment['path']}")
|
||||||
|
try:
|
||||||
|
process_single_video(
|
||||||
|
input_path=segment['path'],
|
||||||
|
output_path=temp_output,
|
||||||
|
target_width=video_width,
|
||||||
|
target_height=video_height,
|
||||||
|
keep_audio=segment['keep_audio'],
|
||||||
|
hwaccel=None # 使用软件编码
|
||||||
|
)
|
||||||
|
processed_videos.append({
|
||||||
|
"index": segment["index"],
|
||||||
|
"path": temp_output,
|
||||||
|
"keep_audio": segment["keep_audio"]
|
||||||
|
})
|
||||||
|
logger.info(f"使用软件编码成功处理视频 {segment['index'] + 1}/{len(video_segments)}")
|
||||||
|
except Exception as fallback_error:
|
||||||
|
logger.error(f"使用软件编码处理视频 {segment['path']} 也失败: {str(fallback_error)}")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not processed_videos:
|
if not processed_videos:
|
||||||
@ -539,17 +691,17 @@ def combine_clip_videos(
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
video_paths = [
|
video_paths = [
|
||||||
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-00-00-00-00-26.mp4',
|
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/S01E02_00_14_09_440.mp4',
|
||||||
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-01-15-00-01-29.mp4',
|
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/S01E08_00_27_11_110.mp4',
|
||||||
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/6e7e343c7592c7d6f9a9636b55000f23/vid-00-04-41-00-04-58.mp4',
|
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/S01E08_00_34_44_480.mp4',
|
||||||
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-04-58-00-05-20.mp4',
|
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/S01E08_00_42_47_630.mp4',
|
||||||
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-05-45-00-05-53.mp4',
|
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/S01E09_00_29_48_160.mp4'
|
||||||
'/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/6e7e343c7592c7d6f9a9636b55000f23/vid-00-06-00-00-06-03.mp4'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
combine_clip_videos(
|
combine_clip_videos(
|
||||||
output_video_path="/Users/apple/Desktop/home/NarratoAI/storage/temp/merge/merged_123.mp4",
|
output_video_path="/Users/apple/Desktop/home/NarratoAI/storage/temp/merge/merged_123.mp4",
|
||||||
video_paths=video_paths,
|
video_paths=video_paths,
|
||||||
video_ost_list=[1, 0, 1, 0, 0, 1],
|
video_ost_list=[1, 1, 1,1,1],
|
||||||
video_aspect=VideoAspect.portrait
|
video_aspect=VideoAspect.portrait,
|
||||||
|
force_software_encoding=False # 默认不强制使用软件编码,让系统自动决定
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user