今日は動画のファイルサイズを小さくするPythonプログラムを紹介します。といっても動画の圧縮はPythonで行うのではなく、ffmpegというフリーソフトを使用します。ffmpegは動画の切り出しや圧縮など多様な機能を持つプログラムですが、コマンドベースで使うため、PCのコマンド操作に慣れていない人には使いにくいと感じてしまうのではないかと思います。そこでGUIベースでffmpegを扱うためのPythonプログラムを作ったので紹介します。
- もっと簡単にffmpegを使いたい
- 動画の圧縮をしたいけどお金をかけたくない
- ただ動画のサイズを小さくしたい(細かい設定は気にしない)
といった人には役に立つのではないかと思います。
準備
まずはffmpegを以下のサイトからダウンロードしてください。
https://ffmpeg.org/download.html
ここではWindowsを使っていることを前提に説明していきます。

「Windows builds by BtbN」のリンクをクリックすると、以下のようなサイトに移動します。ここでファイル名に「win64」と入っているものを選択してください。この説明では「ffmpeg-N-104402-g2aa343bb6f-win64-gpi.zip」を選択しました。

ダウンロードできました。

ダウンロードした圧縮ファイルを解凍し、解凍したフォルダを適当な場所に置きます。ここでは解凍したフォルダの名前を「ffmpeg」にして、Cドライブの直下に置きました(フォルダ名を変更しなくても構いませんが、長い名前だと煩わしいのでここではシンプルな名前に変更しています)。

「ffmpeg」フォルダの中身は以下のようになっています。

さらにbinフォルダの中にffmpegの本体が入っています。

次にffmpegの本体が入っている「C:\ffmpeg\bin」フォルダをWindowsのパスに登録します。
デスクトップのPCアイコンを右クリックして、「プロパティ」を選択すると出てくるダイアログの右側に以下のような項目がありますので、「システムの詳細設定」選びます。

システムのプロパティで「環境変数」を選択します。

環境変数の「Path」を選択し、「編集」ボタンを押します。

環境変数名の編集で、「新規」ボタンを押し、追加したいパス「C:\ffmpeg\bin」を入力して、「OK」を押します。

以上でWindowsにffmpegのパスを通すことができました。
「パスを通すとかよく分からないし面倒くさい」という人は、binフォルダの中身(ffmpeg.exe、ffplay.exe、ffprobe.exeの3ファイル)をこのあと紹介するPythonスクリプトと同じフォルダに入れても動作します。
ソースコード
先に今回のPythonプログラムのソースコードをお示しします。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
import subprocess import csv import os import tkinter as tk import tkinter.filedialog as fd import tkinter.ttk as ttk from tkinter import messagebox ############################################################################### # # クラス定義 # ############################################################################### class ShrinkMovie(): def run(self): self.root.mainloop() #-------------------------------------------------------------------------# def __init__(self): self.root = tk.Tk() self.root.title(u"動画圧縮") self.root.geometry("320x100") # 入力フォーマット self.label1 = ttk.Label(self.root, text="入力フォーマット") self.label1.grid(row=1, column=0, padx=5, pady=5) format_in = ("mp4", "wmv", "avi", "すべて") self.comb_format_in = ttk.Combobox(self.root, values=format_in, height=4, width=7, state="readonly", justify="center") self.comb_format_in.current(0) self.comb_format_in.grid(row=1, column=1, padx=5, pady=5) # 圧縮率 self.label3 = ttk.Label(self.root, text="圧縮率") self.label3.grid(row=2, column=0, padx=5, pady=5) comp_rate = ("高", "低") self.comb_comp_rate = ttk.Combobox(self.root, values=comp_rate, height=2, width=5, state="readonly", justify="center") self.comb_comp_rate.current(0) self.comb_comp_rate.grid(row=2, column=1, padx=5, pady=5) self.sep = ttk.Separator(self.root, orient="horizontal", style="blue.TSeparator") self.sep.grid(row=4, column=0, columnspan=2, sticky="ew") self.button_mov = tk.Button(self.root, text="圧縮処理", width=20, command=self.shrink_mov_files) self.button_mov.grid(row=6, column=0, padx=5, pady=5) self.button_exit = tk.Button(self.root, text="終了", width=20, command=self.exit_program) self.button_exit.grid(row=6, column=1, padx=5, pady=5) #-------------------------------------------------------------------------# def exit_program(self): self.root.quit() exit() #-------------------------------------------------------------------------# def ask_input_filenames(self, msg = None, types = [('', '*.*')]): """ 入力用ファイル名の設定 """ rt = tk.Tk() rt.withdraw() filenames = fd.askopenfilenames(title = msg, filetypes = types) rt.destroy() return filenames #-------------------------------------------------------------------------# def shrink_mov_files(self): # 入力ファイルの選択 frm_in = self.comb_format_in.current() if frm_in == 0: frm_str = '*.mp4' elif frm_in == 1: frm_str = '*.wmv' elif frm_in == 2: frm_str = '*.avi' else: frm_str = '*.*' filepaths = self.ask_input_filenames("動画ファイルを選んでください", types=[('', frm_str)]) # キャンセルされた場合 if filepaths == '': return # 圧縮率の取得 # Constant Rate Factor(CRF):0~51の値を取り、値が大きくなるほど圧縮率が高くなる(画質が低下する) cmp_rate = self.comb_comp_rate.current() if cmp_rate == 0: crf = 28 else:#if cmp_rate == 1: crf = 23 for input_file in filepaths: try: dirname = os.path.dirname(input_file) base_filename = os.path.splitext(os.path.basename(input_file))[0] # 拡張子なしの入力ファイル名 output_file = os.path.join(dirname, base_filename + "_圧縮.mp4") subprocess.call(f'ffmpeg -i "{input_file}" -c:a copy -c:v libx265 -crf {crf} "{output_file}"') except Exception as e: print(e.args) messagebox.showinfo("動画ファイル圧縮エラー", "動画ファイル圧縮中にエラーが発生しました。") return messagebox.showinfo("動画ファイル圧縮終了", "動画ファイルの圧縮が終わりました。") #-------------------------------------------------------------------------# ############################################################################### # # 以下、メイン処理 # ############################################################################### if __name__ == "__main__": app = ShrinkMovie() app.run() |
ファイルはこちらからダウンロードできます。
プログラムの動作説明
それではPythonプログラムの動作について説明していきます。
この説明では以下にあるような5つのMP4ファイルを圧縮していきたいと思います。

Pythonプログラムを実行すると、以下のようなダイアログが表示されます。

入力フォーマットとしてデフォルトではmp4が指定されていますが、他にwmvとavi、さらにその他のフォーマット(すべて)も選ぶことができます。

圧縮率として「高」「低」の2段階を用意しています。

入力フォーマットと圧縮率を選んだあと、「圧縮処理」ボタンを押すと、ファイルを選択するダイアログが開きます。このダイアログで1つもしくは複数のファイルを選択し、「開く(O)」ボタンを押すと、動画ファイルの圧縮が始まります。

圧縮処理が始まると、以下のようにコンソール画面に圧縮処理のメッセージが表示されます。圧縮処理自体はffmpegが行っています。今回紹介しているPythonプログラムは、ファイルの選択と、選択したファイルをffmpegコマンドに渡す役割をするものだと思ってください。

しばらく待つと、処理終了のメッセージが出て完了です。

※注意
圧縮処理中はプログラムが「(応答なし)」状態になって、マウスクリックに反応しなくなります。何らかの理由でプログラムを途中で止めたい場合は、コンソール画面上でCtrl+Cを押してください。

それでは圧縮されたファイルを見てみましょう。圧縮されたファイルは元のファイル名の最後に「_圧縮」という文字が追加されて、元のファイルと同じフォルダに入っています。
まず圧縮率「高」で圧縮した結果です。98,964KBだったファイル「MVI_0351.MP4」が15,500KBの「MVI_0351_圧縮.mp4」にと、大幅にサイズが圧縮されています。ファイルによって圧縮される割合が違いますが、おおむねファイルサイズが元のサイズの20~30%にまで圧縮されています。ちょっと圧縮しすぎな感じもあります(汗)。

一方、圧縮率「低」で圧縮した結果です。元のサイズの40~70%まで圧縮されているのが分かります。

では画質をチェックするため実際の映像を見てみましょう。
元の動画はこちら
高圧縮したもの
低圧縮したもの
圧縮率「高」では映像が「もやっ」とした感じがして画質の悪さが気になりますが、圧縮率「低」であればほとんど違いに気づかないレベルです。
※注意
今回はかなりファイルサイズが圧縮されましたが、元のファイルがすでに圧縮されている場合は、かえってファイルサイズが大きくなることがあるかもしれませんのでご了承ください。
プログラムの説明
簡単にプログラムの説明をします。
ShrinkMovieというクラスを定義し、すべての処理をこのクラスで行っています。今回はtkinterを使用したGUIアプリになっていて、初期化関数(__init__関数)でGUIの設定をしています。
|
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
############################################################################### # # クラス定義 # ############################################################################### class ShrinkMovie(): def run(self): self.root.mainloop() #-------------------------------------------------------------------------# def __init__(self): self.root = tk.Tk() self.root.title(u"動画圧縮") self.root.geometry("320x100") # 入力フォーマット self.label1 = ttk.Label(self.root, text="入力フォーマット") self.label1.grid(row=1, column=0, padx=5, pady=5) format_in = ("mp4", "wmv", "avi", "すべて") self.comb_format_in = ttk.Combobox(self.root, values=format_in, height=4, width=7, state="readonly", justify="center") self.comb_format_in.current(0) self.comb_format_in.grid(row=1, column=1, padx=5, pady=5) # 圧縮率 self.label3 = ttk.Label(self.root, text="圧縮率") self.label3.grid(row=2, column=0, padx=5, pady=5) comp_rate = ("高", "低") self.comb_comp_rate = ttk.Combobox(self.root, values=comp_rate, height=2, width=5, state="readonly", justify="center") self.comb_comp_rate.current(0) self.comb_comp_rate.grid(row=2, column=1, padx=5, pady=5) self.sep = ttk.Separator(self.root, orient="horizontal", style="blue.TSeparator") self.sep.grid(row=4, column=0, columnspan=2, sticky="ew") self.button_mov = tk.Button(self.root, text="圧縮処理", width=20, command=self.shrink_mov_files) self.button_mov.grid(row=6, column=0, padx=5, pady=5) self.button_exit = tk.Button(self.root, text="終了", width=20, command=self.exit_program) self.button_exit.grid(row=6, column=1, padx=5, pady=5) |
「圧縮処理」ボタンが押されるとshrink_mov_files関数が呼び出されるようになっています。shrink_mov_files関数が呼び出されると、まず入力ファイルの選択ダイアログを開きます。ドロップダウンメニューで選択された拡張子をもつファイルだけがファイル選択ダイアログに表示するようになっています。
|
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
def shrink_mov_files(self): # 入力ファイルの選択 frm_in = self.comb_format_in.current() if frm_in == 0: frm_str = '*.mp4' elif frm_in == 1: frm_str = '*.wmv' elif frm_in == 2: frm_str = '*.avi' else: frm_str = '*.*' filepaths = self.ask_input_filenames("動画ファイルを選んでください", types=[('', frm_str)]) # キャンセルされた場合 if filepaths == '': return |
つぎに圧縮率を設定しています。私はビデオのフォーマットについてはあまり詳しくないのですが、今回のプログラムではH.265というフォーマットを使うことでファイルサイズの圧縮をしています。
このH.265フォーマットはConstant Rate Factor(CRF)という値を調整することで圧縮率を変えることができます。CRFは0~51の値を取り、値が大きくなるほど圧縮率が高くなります(画質が低下します)。圧縮率を変えたい場合はここの値を変えてみてください。今回、圧縮率「高」をCRF=28、圧縮率「低」をCRF=23としましたが、もう少し圧縮率を小さくしてもいいかもと思いました。このあたりは好みに合わせて調整してください。
|
92 93 94 95 96 97 98 |
# 圧縮率の取得 # Constant Rate Factor(CRF):0~51の値を取り、値が大きくなるほど圧縮率が高くなる(画質が低下する) cmp_rate = self.comb_comp_rate.current() if cmp_rate == 0: crf = 28 else:#if cmp_rate == 1: crf = 23 |
出力ファイル名を作成した後、subprocessのcall関数を使って、ffmpegコマンドを呼び出しています。
|
102 103 104 105 106 |
try: dirname = os.path.dirname(input_file) base_filename = os.path.splitext(os.path.basename(input_file))[0] # 拡張子なしの入力ファイル名 output_file = os.path.join(dirname, base_filename + "_圧縮.mp4") subprocess.call(f'ffmpeg -i "{input_file}" -c:a copy -c:v libx265 -crf {crf} "{output_file}"') |
以上が簡単なプログラムの説明です。
さいごに
今日紹介したプログラムは、簡単に動画ファイルのサイズを小さくしたいという動機から作ったものです。誰かのお役に立てば幸いです。
- 投稿タグ
- プログラミング