はじめに
- 透過ありの動画を作ります。
- MoviePyの出力用の関数では透過の出力ができなかったため、FFmpegを直接利用しています。
- 一時ファイルを作る方法はストレージの負荷になるため、ターミナルのパイプラインを利用しています。
制作物
Youtube上では透過部分は黒色になっています。
実際の利用ではこの動画を別の動画に張り付けるなどの利用を想定しています。
コード
def create_video(clip, audio_file_path, output_file, fps=30):
width, height = clip.size
ffmpeg_cmd = [
'ffmpeg',
'-y',
'-f', 'rawvideo',
'-pixel_format', 'rgba',
'-s', f'{width}x{height}',
'-r', str(fps),
'-i', '-',
'-i', audio_file_path,
'-c:v', 'libvpx-vp9', # VP9ビデオコーデック
'-c:a', 'libopus', # Opusオーディオコーデック
'-pix_fmt', 'yuva420p',
'-lossless', '1',
'-threads', '4',
'-f', 'webm', # 出力形式をWebMに設定
output_file
]
process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
for frame in clip.iter_frames(fps=fps):
process.stdin.write(frame.tobytes())
process.stdin.close()
process.wait()
-
clip:MoviePyで作れるクリップです。clipから生成される画像は4チャネルのRGBA画像を想定しています。
- process.stdin.write(frame.tobytes())の部分のframeではNumpyに変換されています。frameがNumpyであればよいので、MoviePyを利用せずにNumpyでの処理も可能です。
-
audio_file_path:音声ファイルのパス
-
output_file:出力ファイルのパス
-
fps:フレームレート
ffmpegのインストールが必要です。
透過動画でなければMoviePyの関数で問題なく出力できます。
音声ファイルも保存せずにVOICEVOXからちょくせt入力できそう。
テスト用のコード
MoviePyのバージンは1.0.3です。
python test.py
import subprocess
from moviepy.editor import VideoClip
import numpy as np
from PIL import Image, ImageDraw
def create_video(clip, audio_file_path, output_file, fps=30):
width, height = clip.size
ffmpeg_cmd = [
'ffmpeg',
'-y',
'-f', 'rawvideo',
'-pixel_format', 'rgba',
'-s', f'{width}x{height}',
'-r', str(fps),
'-i', '-',
'-i', audio_file_path,
'-c:v', 'libvpx-vp9', # VP9ビデオコーデック
'-c:a', 'libopus', # Opusオーディオコーデック
'-pix_fmt', 'yuva420p',
'-lossless', '1',
'-threads', '4',
'-f', 'webm', # 出力形式をWebMに設定
output_file
]
process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
for frame in clip.iter_frames(fps=fps):
process.stdin.write(frame.tobytes())
process.stdin.close()
process.wait()
# ダミー動画クリップ
def create_dummy_clip(duration=5, size=(640, 480), fps=30):
def make_frame(t):
image = Image.new("RGBA", size, (0, 0, 0, 0))
draw = ImageDraw.Draw(image)
draw.rectangle((100, 100, 200, 200), fill=(255, 0, 0, 255))
image = image.rotate(t*10)
return np.array(image)
return VideoClip(make_frame, duration=duration)
# ダミーwavファイル
def create_dummy_audio(output_file, duration=5, sample_rate=44100):
ffmpeg_cmd = [
'ffmpeg',
'-y',
'-f', 'lavfi',
'-i', f'sine=frequency=440:duration={duration}',
'-ar', str(sample_rate),
output_file
]
subprocess.run(ffmpeg_cmd)
if __name__ == "__main__":
duration = 10
# ダミー動画クリップ作成
dummy_clip = create_dummy_clip(duration)
# ダミー音声ファイル作成
dummy_audio_file = "dummy_audio.wav"
create_dummy_audio(dummy_audio_file, duration)
create_video(dummy_clip, dummy_audio_file, "output.webm")