腐宅
精华
|
战斗力 鹅
|
回帖 0
注册时间 2023-11-30
|
本帖最后由 普通论坛用户 于 2024-7-23 13:59 编辑
用的svt-av1,它在视频质量、低比特率和编码速度达成较佳的平衡,感谢楼下的推荐
真人视频,比如电视剧、电影等 - ffmpeg -i input.mp4 -c:a copy -c:v libsvtav1 -preset 4 -crf 35 -pix_fmt yuv420p10le -svtav1-params tune=2:lp=12:film-grain=8:enable-variance-boost=1:variance-boost-strength=2 output.mp4
复制代码 如果是恐怖片、低对比度或高对比度的电影,-svtav1-params参数可以设variance-boost-strength=3
2D视频(手绘2Dshi - ffmpeg -i input.mp4 -c:a copy -c:v libsvtav1 -preset 4 -crf 50 -g 100 -pix_fmt yuv420p10le -svtav1-params tune=2:lp=12:film-grain=4:enable-variance-boost=1:variance-boost-strength=2 output.mp4
复制代码
3D视频大概不需要方差提升细节,可删enable-variance-boost=1取消提升细节,3D没有胶片颗粒感,可删film-grain=4 - ffmpeg -i input.mp4 -c:a copy -c:v libsvtav1 -preset 4 -crf 45 -g 100 -pix_fmt yuv420p10le -svtav1-params tune=2:lp=12 output.mp4
复制代码
有啥好的压片设置交流下,https://gitlab.com/AOMediaCodec/ ... /Docs/Parameters.md 参数是挺多的,不知道这些参数在压缩视频的表现性哪个更好些
python写了简单脚本,可看不看
脚本主要功能:
- 当前目录包括子目录,批量转码视频至AV1
- 编码成功的原片移至回收站,保留编码失败的原片
- 跳过AV1、VP9、HEVC已编码文件
- 中断后记编码过的视频,续编时不重码
- 任务完成或ctrl+c手动中断,清理日志、ab-av1采样等杂项文件
- 遇到未知视频流就去掉
- -1073741819之类的错误重试编码,其它错误报告看着办
若要保留原文件,106行的send2trash.send2trash(old_file)注释掉,但VMAF分数94,压制过的视频和原片画质没啥差别了
依赖ffprobe、ab-av1、ffmpeg工具实现,用闲置的PC跑脚本,用的单线程(因为一个ffmpeg进程就占用90%以上,做啥别的都卡,如果不想电脑卡顿,可加--svt lp=xx参数,xx为CPU核心的三分之一),av1预设设为8
环境:
pip install send2trash
工具:
https://github.com/alexheretic/ab-av1/releases
https://github.com/GyanD/codexffmpeg/releases (full版本,share版本皆可)
资源管理器的视频目录空白处shift+右键选择powershell或cmd,就省了cd写路径的功夫,执行下面创建的py文件:
- C:\Users\Administrator>pip install send2trash
- C:\Users\Administrator>cd /d "D:\视频目录"
- D:\视频目录>python "D:\av1.py"
复制代码
av1.py:
代码:- # -*- coding=utf-8
- import datetime
- import logging
- import subprocess
- import sys
- from io import TextIOWrapper
- from re import search, findall
- import os
- from typing import TextIO
- import send2trash
- # 配置日志
- logging.basicConfig(filename='error_log.txt', filemode='a', format='%(asctime)s - %(levelname)s - %(message)s',
- datefmt='%Y-%m-%d %H:%M:%S', level=logging.ERROR)
- def is_video(filename: str, skip=False) -> bool:
- """
- 判断文件是否为视频文件,True为视频文件,False为非视频文件
- :param skip: bool 跳过av1、hevc、vp9编码器的视频文件
- :param filename: str 文件路径
- :return: bool
- """
- try:
- cmd = subprocess.run(
- rf'ffprobe -show_streams "{filename}"',
- shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8')
- if skip:
- # 跳过特定的动图格式
- unalloyed_bool = any(filename.lower().endswith(suffix) for suffix in ['.swf', '.apng', '.gif'])
- # 跳过已压缩的某些编码器
- if search('\ncodec_name=(av1|hevc|vp9)\n', cmd.stdout) or unalloyed_bool:
- return False
- # 跳过字幕文件格式
- unalloyed_bool = any(filename.lower().endswith(suffix) for suffix in
- ['.srt', '.ass', '.ssa', '.vtt', '.lrc', '.cc', '.utf', '.txt', '.qly', '.ksc', '.a16'])
- if unalloyed_bool:
- return False
- # 判断依据是视频流信息是否包含视频文件的时长
- video_search = search('\nduration=(\\d+\\.\\d+)', cmd.stdout)
- # 判断依据是视频文件的后缀名,一些动图或swf不压缩
- if video_search:
- # 视频长度至少0.1秒,说明是视频文件
- if float(video_search.group(1)) > 0.1:
- return True
- if search('\ncodec_name=(vp8|vp9)\n', cmd.stdout):
- # vp8没有duration信息,但可以判断是否为视频文件
- return True
- logging.info(f'{filename}是未知的编码器')
- return False
- except:
- # 报错,说明文件不是视频文件
- return False
- def check_files(file: TextIO, file_path: str):
- """
- 检查文件是否已经存在于skip.txt中,用于跳过编码失败的视频
- :param file:
- :param file_path:
- :return:
- """
- file.seek(0)
- for line in file.readlines():
- if file_path == line[:-1]:
- return True
- return False
- def del_logging():
- # 检查日志文件是否为空,如果为空则删除
- log_file_path = 'error_log.txt'
- logging.shutdown()
- if os.path.exists(log_file_path):
- if os.path.getsize(log_file_path) == 0:
- os.remove(log_file_path)
- # 删除ab-av1残留的视频文件夹
- for dir_path in os.listdir(os.getcwd()):
- if dir_path.startswith('.ab-av1-'):
- send2trash.send2trash(dir_path)
- def video_compress(directory, settings: int = 0):
- """
- 对目录下所有视频文件进行压缩,使用SVT-AV1压缩
- :param directory:
- :param settings: 额外参数, 0: 普通视频(3D/2D/真人等普通视频), 1: 2D动画, 2: 2D老动画, 3: 真人视频, 4: 快速编码
- :return:
- """
- # 一些自定义的压缩参数
- some_cmd = {
- 0: '--preset 5 --svt tune=0 --min-vmaf 94',
- 1: '--preset 5 --svt tune=0 --svt film-grain=4 --min-vmaf 94',
- 2: '--preset 5 --svt tune=0 --svt film-grain=6 --max-crf 60 --min-crf 5 --min-vmaf 94',
- 3: '--preset 5 --svt tune=0 --svt film-grain=8 --max-crf 60 --min-crf 5 --min-vmaf 94',
- 4: '--preset 10 --svt tune=0'
- }
- etc = some_cmd[settings]
- def execute_cmd(cmd):
- subprocess.run(
- cmd,
- shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
- encoding='utf-8')
- print(f'{str(datetime.datetime.now())[:-7]} 压缩完成')
- send2trash.send2trash(old_file)
- # 创建txt文件写入编码失败的视频,确保下次编码跳过该文件
- skip_txt = open('skip.txt', 'a+',encoding='utf-8')
- for foldername, subfolders, filenames in os.walk(directory):
- for filename in filenames:
- old_file = os.path.join(foldername, filename)
- if not check_files(skip_txt, old_file):
- # 通过cmd执行命令
- cmd = f'ab-av1 auto-encode -i "{old_file}" {etc}'
- try:
- if is_video(old_file, skip=True):
- print(f'{str(datetime.datetime.now())[:-7]} 命令{settings} 正在压缩 {old_file}')
- execute_cmd(cmd)
- except KeyboardInterrupt:
- # 手动中断
- print(f'{str(datetime.datetime.now())[:-7]} 压缩任务被中断,正在清理残留文件中……')
- del_logging()
- sys.exit()
- except subprocess.CalledProcessError as e:
- # 如果命令执行出错,记录错误信息
- error_msg = e.stderr
- if 'Error: Failed to find a suitable crf' in error_msg:
- # 找不到合适的crf值
- print(f'{str(datetime.datetime.now())[:-7]} 压缩失败,原因:未能找到合适的crf值')
- elif t := search('\'(.*?)\' is not recognized as an internal or external command', error_msg):
- # ab-av1.exe执行路径未添加到环境变量
- print(
- f'{str(datetime.datetime.now())[:-7]} 找不到{t.group(1)},可能是你没有下载该程序或{t.group(1)}的所在路径未添加到环境变量')
- del_logging()
- sys.exit()
- elif 'Error: ffmpeg vmaf exit code -1073741819' in error_msg:
- # 内存泄漏,尝试重新编码
- try:
- execute_cmd(cmd)
- except:
- logging.error(
- f"{'-' * 100}\n命令:{cmd}\n重新编码失败,可能是其它错误: {old_file}\n{error_msg}\n{'-' * 100}\n\n原先错误报告:{error_msg}")
- print(
- f"{str(datetime.datetime.now())[:-7]} 也许发生了内存泄漏,重新编码失败,可能是其它错误,具体错误参见error_log.txt")
- elif 'Error: ffmpeg encode exit code -22' in error_msg:
- # 编码失败,尝试跳过未知视频流
- stream_index = findall('Could not find tag for codec none in stream #(\\d+)', error_msg)
- try:
- if stream_index.__len__() > 0:
- print('跳过未知视频流,正在重新编码中……')
- for index in stream_index:
- cmd += f' --enc map=-0:{index}'
- execute_cmd(cmd)
- else:
- raise Exception()
- except:
- logging.error(
- f"{'-' * 100}\n命令:{cmd}\n找不到视频流,可能是其它错误: {old_file}\n{error_msg}\n{'-' * 100}\n\n原先错误报告:{error_msg}")
- print(
- f"{str(datetime.datetime.now())[:-7]} 找不到视频流,可能是其它错误,具体错误参见error_log.txt")
- elif r'''error: a value is required for '--svt <SVT_ARGS>' but none was supplied''' in error_msg:
- # SVT参数值异常
- print("发生了SVT参数值异常,请检查编码指令是否正确")
- sys.exit()
- else:
- logging.error(f"{'-' * 100}\n命令:{cmd}\n压缩视频出错: {old_file}\n{error_msg}\n{'-' * 100}")
- print(f"{str(datetime.datetime.now())[:-7]} 压缩视频发生了错误,具体错误参见error_log.txt")
- skip_txt.write(f'{old_file}\n')
- # 关闭文件对象
- skip_txt.close()
- if __name__ == '__main__':
- # 0: 普通视频(3D/2D/真人等普通视频), 1: 2D动画, 2: 2D老动画, 3: 真人视频, 4: 快速编码
- video_compress(os.getcwd(), 1)
- # 清理残留文件
- os.remove('skip.txt')
- del_logging()
复制代码
建议上好一点的水冷或超大号风冷,我发现编码过程中电脑频繁死机,鼠标、键盘操作无反应,CPU停止工作(包括CPU风扇也停止工作),显示器只能显示CPU风扇异常关闭前的最后一帧,我猜可能是CPU长时间保持99%,设备过热触发温度警戒线强制关闭CPU
水冷和风冷不要吝啬金钱,最好上千块,一分钱一分货
如果没钱,就加入额外参数lp=1~4,保持CPU 30~50%占用率使其不过热,代价是降低编码速度
ab-av1的参数是--svt lp=xxx
|
|