この記述で、動画を縮小すると同時に、動画のメタデータと更新日時を撮影日時にセットすることが出来ます。撮影日時が入ることで、ローカルでもクラウドでも管理が楽になります。
環境
iPhone 11 iOS ver 15.1
Windows10 (iTtunes入り)
課題
・FHDで撮影した動画がgoogleフォトにアップロードされて15G制限の容量を圧迫している。
・iphoneとUSB接続したwindowsで読み込んだ動画(MOVファイル)は撮影日時データを持っている様だが、ファイルの更新日時には反映されていない。
・ffmpegを使って(Windows上で)縮小画像を簡単に得ることが出来るが、撮影日時データが失われるので、縮小動画をgoogleフォトにアップロードしたときに、作業日の日付になってしまう。
解決策
pythonで簡単なプログラムを作成し、撮影日時が反映された縮小動画をwindows上で作成する。
これをgoogleフォトにアップロードする。大きな元画像はブラウザなどでgoogleフォトにアクセスし別途削除する。削除対象はgoogleフォトで「mov」を検索することで見つけられる。また、縮小動画の作成の際はmp4ファイルとすることで識別しやすくした。
よく分からないこと
googleフォトで元画像がmovで検索出来るが、変換後画像をmp4で検索しても一部しか出てこない。反映されるのに時間がかかるのか?
しかし、jpgで検索すると先ほどアップロードした写真がちゃんと表示される。
その他
googoleフォトはクラウド側が正なので、ローカルフォルダ内で縮小や加工をしても反映されない。このため、変換前の大きなサイズの動画や写真は手動で削除する必要がある。(良い方法があれば教えてください)
縮小動画作成の手順
ffmpegの準備
公式サイトから、ソースコードをダウンロードしてビルドする。のは面倒なので、公式のリンクからビルド済みのEXEファイルをダウンロードして解凍する。pathなど設定はpythonプログラムから直接指定するので特に不要。今回、リンク先からダウンロードしたのはffmpeg-2022-10-17-git-3bd0bf76fb-full_build.7z 7Z圧縮ファイルの解凍は「7-Zip File Manager」を使った。
https://ffmpeg.org/download.html#build-windows
https://www.gyan.dev/ffmpeg/builds/
pythonの実行環境
今回はminicondaでpython3.9.12(作業時最新)をダウンロードしインストールした。
anacondaでも良い。Sony NNCのpython環境を流用することもできる。
https://docs.conda.io/en/latest/miniconda.html
https://qiita.com/katakaku/items/39a2694d45a2c8fcebd0
エディタ
試行錯誤するなら必要。ここでは、VScodeを用いた。pythonのファイルを開いて、python拡張機能を追加する。pythonインタプリンタを指定するよう求められるので、anaconda miniconda等のpython.exeのありかを指定する。
https://azure.microsoft.com/ja-jp/products/visual-studio-code/
jupyternotebookでもよい。命令の実行だけなら、ターミナルで python test.py でも。
全体のプログラム
ライブラリ
subprocess os datetime glob の4つ。pipやcondaでインストールしなくても標準で入っている。
パスの指定
ffmpegと日付を含むメタデータを出力するtempファイルを指定している。
windowsで動かすので、区切り文字がバックスラッシュ2個であることに注意。
ffmpeg_path="D:\temp_ffmpeg\bin\ffmpeg.exe"
temp_file="D:\temp_ffmpeg\bin\temp.txt"
関数 get_mytime(mp4_file):
動画ファイルを引数として、撮影日時を返す。mp4でもmovでもいい。
コマンドラインで、
ffmpeg -i 動画ファイル
と命令すると得られる出力(標準エラー出力)をtempファイルに保存する。
なお、標準出力はリダイレクト >でファイルに落とし込めるが、標準エラー出力は2>でリダイレクトする。
そのファイルから、creation_time をキーワードに、撮影日時を含む行を抽出し、「:」の位置を手がかりに撮影日時を抽出し戻り値とする。撮影日時は日本時間ではないので+0000と明言している。
subprocess.call(命令文 ,shell=True)とすると、DOS窓(コンソール)で打ち込んだ時と同じことが出来る。
メタデータを得る手段としては、
ffmpeg.exe -i 動画ファイル -f ffmetadata temp.txt
もあるが、日時が入っていないiphoneで撮影した動画ファイルもあったため、上記の法が堅牢である。
関数 kousin_time_set(my_time,out_mov_file):
縮小後の動画の更新日時(ファイルエクスプローラやファイルのプロパティで確認できる)を撮影時刻にセットする関数。引数として、get_mytime関数の戻り値と動画ファイルのパスを与える。
os.utime というメソッドを利用する。
本文
original_mov_file_folder="D:\temp_ffmpeg\original_mov\*.MOV"
out_mp4_file_folder="D:\temp_ffmpeg\out_mp4\"
入力動画用のフォルダと出力動画用のフォルダを指定する。
original_mov_files = glob.glob(original_mov_file_folder)
入力用フォルダの複数の動画のフルパスをリスト形式で得る。
以下、forループで動画ファイル1つずつループを回す。
out_mp4_file = out_mp4_file_folder + os.path.splitext(os.path.basename(original_mov_file))[0]+"_HD.mp4"
出力動画のファイル名を、オリジナルのファイル名に_HDを加え、拡張子をmp4にする。
final_cmd として、ffmpegで動画を縮小しつつ、日時メタデータを加えるコマンドを作成する。
-vf scale=-1:ih*2/3 とすることで、元画像の2/3のサイズ(縦横の長さ)になる。ihが高さで、幅は-1としているのは自動設定の意味。FHDの2/3のサイズとするとHDサイズになる。ファイルサイズは約半分になる。
https://irilyuu.com/ffmpeg/ffmpeg-resize/
-c:a copy を加えることで、音声は再エンコードしない。処理時間も早くなる。
なお、GPUボードがあれば、-c:v h264_nvencを加えると処理が早い。(GTX1050で8-10倍速)
https://neos21.net/blog/2022/01/28-01.html
https://www.usagi1975.com/202107182149/
今回ダウンロードしたffmpegはオプションを加え、Nvidiaのドライバを更新するだけで利用できた。
動画作成後、ファイルの更新日時を撮影日時に合わせるため、kousin_time_set関数を実行して終わり。(これを利用して、オリジナルの動画の更新日時を修正することもできる)20221116追記:標準時を標準時と明記せずdatetimeメソッドに渡していたので、9時間ずれていたのを修正。tzinfo=datetime.timezone.utcを入れた。
以下、今回の全コード。パスを適当に改変して利用する。
import subprocess
import os
import datetime
import glob
ffmpeg_path="D:\\temp_ffmpeg\\bin\\ffmpeg.exe"
temp_file="D:\\temp_ffmpeg\\bin\\temp.txt"
# ファイルパスの区切りがLinuxでは/スラッシュだが、windowsではバックスラッシュ¥なのでエスケープが必要。
def get_mytime(mp4_file):
if(os.path.isfile(temp_file) == True):
os.remove(temp_file)#あらかじめTempファイルを削除する
# 作成時刻などをtempファイルに出力する。標準エラー出力をtempファイルに入力する
subprocess.call(ffmpeg_path + ' -i ' + mp4_file + ' 2> ' + temp_file ,shell=True)
#tempファイルに出力した内容から、作成日時をmy_time文字列変数として抽出する
with open(temp_file) as f:
for i, line in enumerate(f):
if 'creation_time' in line:# creation_timeを含む行を調べる
break
print(i)
print(line)
index = line.find(":")
print(index)
print(line[index+2:])
my_time = line[index+2:index+21]#最後の改行は落とす
print("+++++++++++++++++++++++++++")
print(my_time)
return my_time +'+0000'# 作成日時をmy_time文字列変数として戻す creation_timeは標準時なので、明記する
def kousin_time_set(my_time,out_mov_file):
y=int(my_time[0:4])
m=int(my_time[5:7])
d=int(my_time[8:10])
h=int(my_time[11:13])
min=int(my_time[14:16])
s=int(my_time[17:19])
atime = datetime.datetime(year=y, month=m, day=d, hour=h, minute=min, second=s, microsecond=0,tzinfo=datetime.timezone.utc)
print(y,m,d,h,m,s)
os.utime(path=out_mov_file,times=(atime.timestamp(),atime.timestamp()))
original_mov_file_folder="D:\\temp_ffmpeg\\original_mov\\*.MOV"
out_mp4_file_folder="D:\\temp_ffmpeg\\out_mp4\\"
original_mov_files = glob.glob(original_mov_file_folder)
for original_mov_file in original_mov_files:
#original_mov_file="D:\\temp_ffmpeg\\bin\\IMG_3515cp.MOV"
#original_mov_file=original_mov_files[0] 1つめのファイルを個別に指定するとき
out_mp4_file = out_mp4_file_folder + os.path.splitext(os.path.basename(original_mov_file))[0]+"_HD.mp4"
#元の名前にHDを加えて、拡張子をmp4に変える。パスも指定している。
my_time=get_mytime(original_mov_file)
meta_option = "-metadata" + " creation_time"+"=" + "\"" + my_time + "\""
print(meta_option)
#CPUのとき final_cmd = ffmpeg_path + " -i " + original_mov_file + " " + meta_option + " -vf scale=-1:ih*2/3 " + out_mp4_file
final_cmd = ffmpeg_path + " -i " + original_mov_file + " " + meta_option + " -vf scale=-1:ih*2/3 " +"-c:v h264_nvenc "+ "-c:a copy "+out_mp4_file#GPU対応
# -vf scale=-1:ih*2/3 とすると、元画像FHDの2/3の大きさでHD相当の大きさになる。
subprocess.run(final_cmd, shell=True)#ffmpegのコマンドを実行して、縮小されたmp4ファイルを作成する。
kousin_time_set(my_time,out_mp4_file)#作成した動画の更新日時を撮影日時にする。
その他のトライアル
このサイトから大きなヒントを得ましたが、そのままでは自分の環境では動かなかった。
簡潔に利用法が記述されているサイト。メタデータのコピーについて記載があるが、自分の環境では動作しなかった。
メタデータがグローバルやチャプターごとなど適用範囲があることを記述している。
公式サイト(翻訳して読むが難しい。wwwがあるのと、ないのとどっちを見るんだ?)
subprocessrun( )がうまく使えなかった。バージョンアップにより機能が次々増えている模様。 shell=Trueオプションを付けることでうまくいった
ファイル更新日時の操作については、以下のサイトを参考にしました。