※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
に置いてください。
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
}
}
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
に配置
//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が返ります。
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