14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UnityでMobile-FFmpegを使う

Last updated at Posted at 2020-09-13

※2021/4/19
Mobile-FFmpegの更新は FFmpegKit に移行されました。

はじめに

Unityで開発中のアプリ内で動画ファイル・音声ファイルを加工する処理が必要になり、Mobile-FFmpegを利用することになった。導入して使えるようになるまでにやったことのメモ。

Mobile-FFmpeg とは

FFmpegのAndroid/iOS対応版オープンソースライブラリ。内容はFFmpegおよびFFprobeの実装と外部ライブラリからなる。アップデートも活発に行われている。

コマンドライン版FFmpegと全く同じ感覚でオプションコマンド文字列を指定して呼び出すシンプルなAPIになっているのが特徴。
https://github.com/tanersener/mobile-ffmpeg
https://github.com/tanersener/ffmpeg-kit
https://github.com/arthenica/ffmpeg-kit

注意

MobileFFmpegは40以上の外部依存ライブラリがあるため、まずMobileFFmpegをアプリのどこに使うのか予め使用目的を定め、それに必要な機能と必須のライブラリを明確にしておくと良いでしょう。

この記事はMobile-FFmpeg Version 4.4、Unity2018以降を対象に書かれています。

ビルド済みバイナリ

自前でビルドできますがビルド済みバイナリも用意されているので今回はこれを使いました。
バイナリは内包する外部ライブラリの違いで8種類のバリエーションが用意されています。

Packages

  • min : 最小限のパッケージ
  • min-gpl : minにGPLライセンスライブラリを追加したもの
  • https : TLS関係のライブラリを追加したもの
  • https-gpl : httpsにGPLライセンスライブラリを追加したもの
  • audio : オーディオ関係のライブラリを追加したもの
  • video : ビデオ関係のライブラリを追加したもの
  • full : フルセット(GPLライセンスを含まない)
  • full-gpl : フルセット(GPLライセンスを含む)

用途に応じて上記の中からpackageを選択し、ファイルをダウンロード
Android用のライブラリはaarファイルをダウンロード
・(ファイル名) : mobile-ffmpeg-[Package]-[Version].aar
iOS用のライブラリはxcframework.zipファイルをダウンロードして解凍
・(ファイル名) : mobile-ffmpeg-[Package]-[Version]-ios-xcframework.zip

パッケージごとの詳細は以下を確認。
2.1 Packages

Unityへのインポート

ダウンロードした
.aarファイルを
/Assets/Plugins/Android
zipを解凍してframeworkファイル群を
/Assets/Plugins/iOS
に配置。

Unity側のC#から呼び出す

AndroidとiOSそれぞれ、Pluginモジュール呼び出し用ラッパーを実装します。基本的にはExecuteとCancelのAPI2つ呼び出せればOKです。

MobileFFmpegのGitHub、isshue#258のスレッドを参考にさせていただきました。
https://github.com/tanersener/mobile-ffmpeg/issues/258#issuecomment-663913978

コード

ソースファイルは3つあります。.mmファイルはAssets/Plugins/iOSに置いてください。

FFmpegWrapper.cs
using UnityEngine;

public class FFmpegWrapper
{
    private static int Execute(string command)
    {
#if UNITY_ANDROID
        using (AndroidJavaClass configClass = new AndroidJavaClass("com.arthenica.mobileffmpeg.Config"))
        {
            AndroidJavaObject paramVal = new AndroidJavaClass("com.arthenica.mobileffmpeg.Signal").GetStatic<AndroidJavaObject>("SIGXCPU");
            configClass.CallStatic("ignoreSignal", new object[] { paramVal });

            using (AndroidJavaClass ffmpeg = new AndroidJavaClass("com.arthenica.mobileffmpeg.FFmpeg"))
            {
                int code = ffmpeg.CallStatic<int>("execute", new object[] { command });
                return code;
            }
        }
#elif UNITY_IOS
        return MobileFFmpegIOS.Execute(command);
#else
        return 0;
#endif
    }

    private static int Cancel()
    {
#if UNITY_ANDROID
        using (AndroidJavaClass configClass = new AndroidJavaClass("com.arthenica.mobileffmpeg.Config"))
        {
            using (AndroidJavaClass ffmpeg = new AndroidJavaClass("com.arthenica.mobileffmpeg.FFmpeg"))
            {
                int code = ffmpeg.CallStatic<int>("cancel");
                return code;
            }
        }
#elif UNITY_IOS
        return MobileFFmpegIOS.Cancel();
#else
        return 0;
#endif
    }
}
MobileFFmpegIOS.cs
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;


public class MobileFFmpegIOS : MonoBehaviour
{
#if UNITY_IOS
    [DllImport("__Internal")]
    private static extern int _execute(string command);

    [DllImport("__Internal")]
    private static extern void _cancel();
#endif

    /**
     * Synchronously executes FFmpeg command provided. Space character is used to split command
     * into arguments.
     *
     * @param command FFmpeg command
     * @return zero on successful execution, 255 on user cancel and non-zero on error
     */
    public static int Execute(string command)
    {
        int result = -1;
#if UNITY_IOS
        if (Application.platform == RuntimePlatform.IPhonePlayer)
        {
            result = _execute(command);
        }
#endif

        return result;
    }

    /**
     * Cancels an ongoing operation.
     *
     * This function does not wait for termination to complete and returns immediately.
     */
    public static void Cancel()
    {
#if UNITY_IOS
        if (Application.platform == RuntimePlatform.IPhonePlayer)
        {
            _cancel();
        }
#endif
    }
}

以下はAssets/Plugins/iOSに配置

MobileFFmpeg.mm
//In unity, You'd place this file in your "Assets>plugins>ios" folder
//Objective-C Code
#import <mobileffmpeg/MobileFFmpeg.h>

extern "C"
{
    /**
    * Synchronously executes FFmpeg command provided. Space character is used to split command
    * into arguments.
    *
    * @param command FFmpeg command
    * @return zero on successful execution, 255 on user cancel and non-zero on error
    */
    int _execute(const char* command)
    {
        return [MobileFFmpeg execute: @(command)];
    }
    
    /**
    * Cancels an ongoing operation.
    *
    * This function does not wait for termination to complete and returns immediately.
    */
    void _cancel()
    {
        [MobileFFmpeg cancel];
    }
}

Unityから呼び出す

Executeのパラメータにオプションコマンドを文字列で渡し呼び出します("ffmpeg "は不要なので注意)。成功すれば0が返ります。

Example
private void Test()
{
    var input = Application.persistentDataPath + "/input.mov";
    var output = Application.persistentDataPath + "/output.mp4";
    int rc = FFmpegWrapper.Execute(string.Format("-i {0} {1}", input, output));
    Debug.Log("Return Code is " + rc);
}

非同期実行のFFmpeg.executeAsync関数も用意されていますが試してません。申し訳ございません。。。

その他

○Xcodeでリンカーエラーが出たら

**[Link Binary with Libraries]**に以下のライブラリがなければ追加する。
bzip2, iconv, libuuid, zlib, AudioToolbox, VideoToolbox, AVFoundation
(libz.tbd, libbz2.tbd, libiconv.tbd)

Armv7に対応してないっぽいのでArchitecturesはArm64専用でビルドしました

○実行時エラー

・ファイル書き込み権限がない、Outputファイルと同名のファイルがすでに存在する場合失敗します
・コマンドに応じて必要なライブラリが決まるので、モジュールがない場合は失敗します

参考記事

AndroidでFFmpegを使って音声ファイルを解析・変換する
https://qiita.com/tarumzu/items/a4d15957a144f520f842
それFFmpegで出来るよ!
https://qiita.com/cha84rakanal/items/e84fe4eb6fbe2ae13fd8
FFmpegで動画をGIFに変換
https://qiita.com/wMETAw/items/fdb754022aec1da88e6e

14
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?