ffmpeg on apple silicon(windows cudaも)
正月まえにPC録画環境>エンコードをちょっと見直しました。
まずはmacから
バイナリ配布してる場所
本来のffmpegと入れ替えた後、
jydie5@MBA2023M2 ~ % ffmpeg -version
zsh: killed ffmpeg -version
野良バイナリは実行させてくれそうになく、このままだと使えないので
xattr -dr com.apple.quarantine /opt/homebrew/Cellar/ffmpeg/6.0_2/bin/ffmpeg
--enable-neon
これで
-c:v libx265
が早くなるはず。
-c:v hevc_videotoolbox -q:v 50
こっちも早いんですが画質にちょっと荒さが目立つ印象。
検証用に書いたフロントエンド
tmpに.tsや.mp4を配置してoutputにエンコードしたものを出力
import gradio as gr
import os
import platform
import subprocess
import stat
def create_ffmpeg_command(input_file, output_file, resolution, codec, quality):
scale_option = "-vf scale=-1:720" if resolution == "720P" else ""
tag_option = "-tag:v hvc1"
if codec == "libx265":
codec_option = "-c:v libx265"
if resolution == "720P":
output_file = output_file.replace("_H265.mp4", "_720P_H265.mp4")
else:
output_file = output_file.replace("_H265.mp4", "_Original_H265.mp4")
else:
codec_option = f"-c:v hevc_videotoolbox -q:v {quality}"
if resolution == "720P":
output_file = output_file.replace("_H265.mp4", f"_720P_HEVC_Q{quality}.mp4")
else:
output_file = output_file.replace("_H265.mp4", f"_Original_HEVC_Q{quality}.mp4")
return f'ffmpeg -i "{input_file}" {scale_option} {codec_option} {tag_option} "{output_file}"\n'
def ensure_folders_exist():
os.makedirs("./tmp", exist_ok=True)
os.makedirs("./output", exist_ok=True)
def generate_batch_file(resolution, codec, quality):
input_folder = "./tmp"
output_folder = "./output"
ensure_folders_exist()
system_type = platform.system()
batch_filename = "convert_to_h265.sh" if system_type == "Darwin" else "convert_to_h265.bat"
batch_filepath = os.path.join("./", batch_filename)
try:
with open(batch_filepath, 'w') as batch_file:
if system_type == "Darwin":
batch_file.write("#!/bin/sh\n")
for filename in os.listdir(input_folder):
if filename.endswith(".ts") or filename.endswith(".mp4"):
input_file = os.path.join(input_folder, filename)
output_file = os.path.join(output_folder, os.path.splitext(filename)[0] + "_H265.mp4")
command = create_ffmpeg_command(input_file, output_file, resolution, codec, quality)
batch_file.write(command)
# Unix系システムの場合、生成されたスクリプトに実行権限を付与
if system_type == "Darwin":
os.chmod(batch_filepath, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
return f"Batch file created: {batch_filepath}"
except Exception as e:
return f"Error: {str(e)}"
def run_batch_file():
system_type = platform.system()
batch_filename = "convert_to_h265.sh" if system_type == "Darwin" else "convert_to_h265.bat"
batch_filepath = os.path.join("./", batch_filename)
try:
result = subprocess.run([batch_filepath], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode != 0:
# デコードエラーが発生する可能性があるため、バイナリデータのまま処理
return f"Error in batch file execution: {result.stderr.decode('utf-8', 'ignore')}"
return result.stdout.decode('utf-8', 'ignore')
except subprocess.CalledProcessError as e:
return f"Subprocess error: {str(e)}"
except Exception as e:
return f"General error: {str(e)}"
with gr.Blocks() as demo:
with gr.Row():
resolution_choice = gr.Dropdown(label="Resolution", choices=["Original", "720P"])
codec_choice = gr.Dropdown(label="Codec", choices=["libx265", "hevc_videotoolbox"])
quality_slider = gr.Slider(minimum=1, maximum=100, step=1, value=50, label="Quality (Only for hevc_videotoolbox)")
generate_button = gr.Button("Generate Batch File")
run_button = gr.Button("Run Batch File")
output_status = gr.Textbox(label="Output Status")
run_status = gr.Textbox(label="Run Status")
generate_button.click(generate_batch_file, inputs=[resolution_choice, codec_choice, quality_slider], outputs=output_status)
run_button.click(run_batch_file, inputs=None, outputs=run_status)
demo.launch(inbrowser=True)
画質とサイズ増大の閾値の折り合いをつける。というエンコード業界の昔からのジレンマに陥ります。時間がある方はソフトエンコードでいいのではと思いました。それでもffmpegのバイナリがneon対応していないと遅いので、コンパイルできる方はコンパイル、ffmpegのバイナリ差し替えなどは有効なのではないかと考えます。
windowsの場合。
バイナリ
あとはcuda環境を整えてHWエンコードできるグラボを入れて
def create_ffmpeg_command(input_file, output_file):
return f'ffmpeg -hwaccel cuda -hwaccel_output_format cuda -c:v mpeg2_cuvid -deint adaptive -drop_second_field 1 -i "{input_file}" -c:v hevc_nvenc -tag:v hvc1 -f mp4 "{output_file}"\n'
-c:v hevc_nvenc
これが使えるようになれば早いと思います。
バッチファイル作成
import gradio as gr
import os
def create_ffmpeg_command(input_file, output_file):
return f'ffmpeg -hwaccel cuda -hwaccel_output_format cuda -c:v mpeg2_cuvid -deint adaptive -drop_second_field 1 -i "{input_file}" -c:v hevc_nvenc -tag:v hvc1 -f mp4 "{output_file}"\n'
def generate_batch_file():
input_folder = "./tmp"
output_folder = "./output"
batch_filename = "convert_to_h265.bat"
batch_filepath = os.path.join("./", batch_filename)
if not os.path.exists(output_folder):
os.makedirs(output_folder)
try:
with open(batch_filepath, 'w', encoding='cp932', errors='ignore') as batch_file:
for filename in os.listdir(input_folder):
if filename.endswith(".ts") or filename.endswith(".mp4"):
input_file = os.path.join(input_folder, filename)
output_file = os.path.join(output_folder, os.path.splitext(filename)[0] + "_H265.mp4")
command = create_ffmpeg_command(input_file, output_file)
batch_file.write(command)
return f"Batch file created: {batch_filepath}"
except Exception as e:
return f"Error: {str(e)}"
with gr.Blocks() as demo:
with gr.Row():
generate_button = gr.Button("Generate Batch File")
output_status = gr.Textbox(label="Output Status")
generate_button.click(generate_batch_file, inputs=None, outputs=output_status)
demo.launch(inbrowser=True)
こんな感じでお正月番組を圧縮していこうか思っています。