ffmpegでGoProの撮影日時を修正

タイムゾーンが無視されて記録されたGoProの動画の日時情報をffmpegで修正する


日時情報の取得

ffmpegでは-iオプションでファイルを指定して実行すればメタ情報を含む動画情報を表示してくれます。今回は下記例のcreation_timeを修正していきます

% ffmpeg -i GH010179.MP4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'GH010179.MP4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    creation_time   : 2020-08-21T15:59:12.000000Z
    encoder         : Lavf58.47.100
  Duration: 00:00:12.71, start: 0.000000, bitrate: 60623 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc, bt709), 3840x2160 [SAR 1:1 DAR 16:9], 60428 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc (default)
    Metadata:
      creation_time   : 2020-08-21T15:59:12.000000Z
      handler_name    : GoPro AVC
      timecode        : 08:58:40:06
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 189 kb/s (default)
    Metadata:
      creation_time   : 2020-08-21T15:59:12.000000Z
      handler_name    : GoPro AAC
    Stream #0:2(eng): Data: none (tmcd / 0x64636D74), 0 kb/s
    Metadata:
      creation_time   : 2020-08-21T15:59:12.000000Z
      handler_name    : GoPro AVC
      timecode        : 08:58:40:06

日時取得 (Python)

参考までに、下記はPythonからcreation_timeを取得するサンプルコードです。datetime.fromisoformat()関数で日時文字列を読み込みます。その際末尾の”Z”はフォーマットエラーを発生させるため取り除きます

def get_creation_time(mp4_file, ffmpeg=ffmpeg_path):
    cmd = [ffmpeg, '-i', mp4_file]
    ret = subprocess.run(cmd, capture_output=True, encoding='utf8')
    for line in ret.stderr.splitlines():
        if m:= re.match(r'\s*creation_time\s*:\s*(\S+)Z', line):
            return datetime.fromisoformat(m.group(1))
    return None

日時更新

続いてffmpeg-metadataオプションでメタ情報を変更していきます。この際動画情報自体の再エンコードなどは行わないため-c copyオプションを指定します。注意点として、ffmpegは既存ファイルのメタ情報の上書き保存はできないため、メタ情報を変更した新規ファイルの作成が必要になります。

% ffmpeg -i GH010179.MP4 -metadata creation_time="2020-08-21T23:59:12.000000Z" GH010179_new.MP4

日時更新 (python)

参考までに、こちらもpythonからffmpegを実行するサンプルコードです。こちらでは最初に元ファイルをold_*にリネームしてから、元の名前で新規ファイルを作成しています

def set_creation_time(mp4_file, new_c_time, ffmpeg=ffmpeg_path):
    old_file = os.path.join(os.path.dirname(mp4_file),
                            'old_' + os.path.basename(mp4_file))
    success = False
    try:
        os.rename(mp4_file, old_file)
        cmd = [ffmpeg, '-i', old_file, '-c', 'copy']
        cmd.extend(['-metadata', f"creation_time={new_c_time.isoformat()}Z"])
        cmd.append(mp4_file)
        ret = subprocess.run(cmd)
        if ret.returncode == 0:
            success = True
    finally:
        if not success:
            print(f'Rename back {old_file} to {mp4_file}')
            os.rename(old_file, mp4_file)


Pythonでまとめて処理

タイムゾーンを修正する際にffmpegを一つ一つのファイルに対して実行していくのは面倒なため、pythonなどのスクリプトで処理します。上記の2つの関数を使えばJSTへの修正も容易に行えます

for mp4_file in files:
    c_time = get_creation_time(mp4_file)
    new_c_time = c_time - 9 # JSTはUTC+9
    set_creation_time(mp4_file, new_c_time)

おすすめ