GoPro HERO10の動画(mp4)からFFMpegでメタデータを抽出する

はじめに

前回の記事で既存のツールでGPSデータの抽出と表示方法を調べました.

素晴らしいツールがたくさん公開されていますので,それはそれで良いのですが,C#の自作アプリに組み込んで使おうと思うと,なかなか良さそうなものがありません.ということで,自作してみようと思います.コンセプトは,

  • 動画(mp4)ファイルからGPS等のセンサデータが格納されたストリームを取り出すのは,FFMpegに任せる.
  • C#のプログラムからffmpeg.exeを実行して,ストリーム(データ)をファイルに保存する.
  • そのファイルからGPSデータを読み出し,GPXとかKMLに変換して保存する.

他のツールで既にできていることですが,C#で自作しておけば,他のアプリに組み込んで使うことができそうなので,やってみます.はい,趣味です.

FFMpegのインストール

FFMpegをインストールして使ってみます.インストールといっても,実行可能なexeをダウンロードして適当なフォルダに置くだけです.まずは公式サイトにアクセスします.

FFmpeg

公式でGitHubからソースコードのみの提供となっていますが,コンパイル済みの実行可能バイナリへのリンクも紹介されていますので,今回はこちらを使います.Windowsのロゴをクリックするとリンクが2つ出てきます.どちらでも良いと思いますが,今回は上のgyan.devにしました.

Windows Build formgyan.devをクリックすると,ビルド済みのウインドウズ用バイナリがたくさんリストアップされます.今回は[release builds]の[latest release version: 4.4.1]にしました.

ダウンロード,解凍すると,解凍したフォルダの中のbinに,ffmpeg.exe,ffplay.exe,ffprobe.exeの3つが格納されているのが確認できます.今回は,この内のffprobe.exeとffmpeg.exeを使います.

ffprobeでデータの内容物を確かめる

ffprobe.exeでmp4ファイル(ファイル名: GX010006.MP4)の中にどんなデータが入っているのかを確かめます.pathを通すのが面倒だったので,上記binフォルダをカレントにして作業しています.

.\ffprobe GX010006.MP4

実行すると,以下のような表示がズラズラと出てきます.

.\ffprobe GX010006.MP4
ffprobe version 4.4.1-essentials_build-www.gyan.dev Copyright (c) 2007-2021 the FFmpeg developers
  built with gcc 11.2.0 (Rev1, Built by MSYS2 project)
  configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
  libavutil      56. 70.100 / 56. 70.100
  libavcodec     58.134.100 / 58.134.100
  libavformat    58. 76.100 / 58. 76.100
  libavdevice    58. 13.100 / 58. 13.100
  libavfilter     7.110.100 /  7.110.100
  libswscale      5.  9.100 /  5.  9.100
  libswresample   3.  9.100 /  3.  9.100
  libpostproc    55.  9.100 / 55.  9.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '..\..\GX010006.MP4':
  Metadata:
    major_brand     : mp41
    minor_version   : 538120216
    compatible_brands: mp41
    creation_time   : 2021-11-14T17:34:25.000000Z
    firmware        : H21.01.01.16.00
  Duration: 00:13:53.20, start: 0.000000, bitrate: 16002 kb/s
  Stream #0:0(eng): Video: hevc (Main) (hvc1 / 0x31637668), yuvj420p(pc, bt709), 1920x1080 [SAR 1:1 DAR 16:9], 15744 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 29.97 tbc (default)
    Metadata:
      creation_time   : 2021-11-14T17:34:25.000000Z
      handler_name    : GoPro H.265
      vendor_id       : [0][0][0][0]
      encoder         : GoPro H.265 encoder
      timecode        : 17:33:22:03
  Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 189 kb/s (default)
    Metadata:
      creation_time   : 2021-11-14T17:34:25.000000Z
      handler_name    : GoPro AAC
      vendor_id       : [0][0][0][0]
      timecode        : 17:33:22:03
  Stream #0:2(eng): Data: none (tmcd / 0x64636D74) (default)
    Metadata:
      creation_time   : 2021-11-14T17:34:25.000000Z
      handler_name    : GoPro TCD
      timecode        : 17:33:22:03
  Stream #0:3(eng): Data: bin_data (gpmd / 0x646D7067), 54 kb/s (default)
    Metadata:
      creation_time   : 2021-11-14T17:34:25.000000Z
      handler_name    : GoPro MET
Unsupported codec with id 0 for input stream 2
Unsupported codec with id 100359 for input stream 3

最後の2行でcodecが無いとか言われてますが,取り敢えず無視しておきます.大事なのはStreamから始まっているところで,下から6行目の

 Stream #0:3(eng): Data: bin_data (gpmd / 0x646D7067), 54 kb/s (default)

がGoProのセンサデータ等が入っているstreamで,その番号は「0:3」ということが分かります.

ffmpegでデータを取り出す

上で,Stream #0:3にセンサデータが入っていることが分かったので,「0:3」を指定してffmpeg.exeでセンサデータのみを抽出します.

.\ffmpeg -y -i GX010006.MP4 -codec copy -map 0:3 -f rawvideo tmp.bin

細かいオプションの意味は調べ切れてませんが,このコマンドで「GX010006.MP4」から「Stream #0:3」のデータを取り出して,「tmp.bin」に書き出してくれます.上のコマンドを実行してtmp.binができていればOKです.

C#プログラムからffmpegを使う

C#コンソールアプリから上記のffprobe,ffmpegを実行し,その標準出力をC#のコンソールウインドウに表示してみます.検索すると参考例はたくさん出てきます.例えば

  • ipconfig.exe コマンドを実行し、その標準出力を例のコンソール ウィンドウにリダイレクトする例
Process.StandardOutput プロパティ (System.Diagnostics)
アプリケーションのテキスト出力の読み取りに使用されるストリームを取得します。
  • ffmpegを用いて動画から画像を取り出す【C#】
ffmpegを用いて動画から画像を取り出す【C#】 - Qiita
ffmpegを用いて動画から画像を取り出す【C#】 「なんか機械学習で遊びてーなー」と思いまして。機械学習フレームワークに食わせる前処理として、動画から画像を取り出す処理が必要になったので調べたの…

などを参考にして,以下を作りました.

using System;
using System.Diagnostics;
using System.IO;

namespace GoTest
{
    class Program
    {
        // ffprobeの実行
        static void Main(string[] args)
        {
            using (Process process = new Process())
            {
                string ffprobePath = Path.GetFullPath(@".\ffprobe.exe");    // フルパス or 相対パスでもOK
                string mp4Path = Path.GetFullPath(@".\GX010002.MP4");
                process.StartInfo.FileName = ffprobePath;            // 実行するファイル指定
                process.StartInfo.Arguments = mp4Path;               // 実行ファイルに渡すオプション
                process.StartInfo.UseShellExecute = false;           // リダイレクトする場合はfalseにする
                process.StartInfo.RedirectStandardOutput = true;     // 標準出力をリダイレクトする
                process.Start();
                StreamReader reader = process.StandardOutput;
                string output = reader.ReadToEnd();
                Console.WriteLine(output);
                process.WaitForExit();
            }

            // ffmpegの実行
            using (Process process = new Process())
            {
                string ffprobePath = Path.GetFullPath(@".\ffmpeg.exe");
                string mp4Path = Path.GetFullPath(@".\GX010002.MP4");
                process.StartInfo.FileName = ffprobePath;
                process.StartInfo.Arguments = "-y -i " + mp4Path + " -codec copy -map 0:3 -f rawvideo tmp.bin";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.Start();
                StreamReader reader = process.StandardOutput;
                string output = reader.ReadToEnd();
                Console.WriteLine(output);
                process.WaitForExit();
            }
        }
    }
}

12-25行目でffprobeを実行,28-41行目でffmpegを実行し,それぞれ出力をコンソールに出力しています.

これを実行すると,C#のコンソール画面にffprobeとffmpegをそれぞれ実行したときの標準出力が表示され,tmp.binにStream #0:3の内容が出力されます.

さて,次回はtmp.binの内容と,GPSデータの抽出について考えていきます.

タイトルとURLをコピーしました