FFmpegトリミングテストアプリ try_ffmpeg の画面

try_ffmpeg – NDKビルドした FFmpeg を JNI から叩くトリミングテストアプリ

Android NDK でビルドした FFmpeg(libav* 系 .so)または既製バイナリを、 JNI から実際に叩いて 動画をトリミング するための検証用アプリです。 ebutuoY / Loopit など本番アプリに FFmpeg を組み込む前に、 ビルド・リンク・権限・パフォーマンスを一通り確かめるためのプロジェクトとして作りました。

作品の説明

try_ffmpeg は、ユーザーが選んだ mp4 動画を、 「開始時刻」と「長さ(または終了時刻)」を指定して切り出すだけの シンプルなテストアプリです。 しかし裏側では、NDK でビルドした FFmpeg を 正しくロードし、JNI 経由でコマンドを実行し、 標準出力・標準エラーをログに集めるところまでを一気通貫で確認しています。

最終的には、-ss-t(または -to)を使った 無再エンコード(-c copy)・再エンコードの両方を試し、 どこまでスマホ単体で高速に編集できるかを測ることを目的としています。

  • ① NDK/clang で FFmpeg の libav* をビルド or 既製バイナリを配置
  • ② JNI ラッパー(C/C++)で ffmpeg_main() 相当を呼び出し
  • ③ Kotlin からコマンドライン引数の配列を渡す
  • ④ 標準出力・進捗ログをコールバックで受け取って表示
  • ⑤ 入力動画 → トリミング済み mp4 を指定フォルダに保存

システム概要

try_ffmpeg のアーキテクチャ構成図

UI(Kotlin)のボタンからトリミング要求が飛び、 JNI ラッパーを経由して NDK 側の ffmpeg_main() を呼び出します。 コマンド引数は文字列配列で渡し、FFmpeg 側のログはコールバックで Kotlin に引き上げて TextView や Logcat に表示しています。

技術スタック

  • Android / UI: Kotlin, Activity / Fragment, SAF (Storage Access Framework)
  • NDK / JNI: Android NDK, CMake, C/C++(JNI ラッパ)
  • FFmpeg: libavformat, libavcodec, libavfilter, libswscale, libavutil(ビルド済み .so)
  • Build: CMakeLists による FFmpeg .so のリンク、ABI ごとの出し分け

主要コンポーネント

  • MainActivity: 動画の選択(SAF)、トリミングの開始、ログ表示を行う画面。
  • native-lib.cpp: Kotlin から呼ばれる JNI 関数を定義し、内部で ffmpeg_main() を呼び出す。
  • CMakeLists.txt: FFmpeg のヘッダ・.so を NDK プロジェクトにリンクする設定ファイル。
  • FFmpeg binaries / .so: 自前ビルドした libav* or 既製の FFmpeg バイナリ群。

技術解説

NDK BUILD

FFmpeg を NDK でビルドし、.so としてリンク

try_ffmpeg では、Android 用にクロスビルドした FFmpeg の .so を jniLibs/ 配下に配置し、CMake からリンクする構成を試しました。 ABI 別(arm64-v8a, armeabi-v7a)の .so を分けて配置し、 実機に合わせて正しいバイナリがロードされるかを確認しています。

CMake イメージ:
add_library( # native lib
  native-lib SHARED native-lib.cpp )

add_library( avcodec SHARED IMPORTED )
set_target_properties(avcodec PROPERTIES
  IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec.so")

target_link_libraries(native-lib avcodec avformat avutil log)
JNI BRIDGE

ffmpeg_main() をラップして Kotlin から叩く

C 側では FFmpeg の ffmpeg_main(int argc, char** argv) 風のエントリを 1 つの関数として定義し、その周りを JNI でラップします。 Kotlin 側では String 配列を渡し、C 側で char** に変換して実行します。

JNI 関数イメージ:
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_tryffmpeg_FFmpegRunner_run( JNIEnv* env, jobject thiz, jobjectArray args ) {
  int argc = env->GetArrayLength(args);
  char** argv = convert_to_argv(env, args);
  int ret = ffmpeg_main(argc, argv);
  free_argv(argv, argc);
  return ret;
}
TRIM

-ss / -t / -c copy を使った高速トリミング

動画トリミングは、まずは FFmpeg の王道パターンである -ss <start> -t <duration> -c copy を使用。 再エンコード無しでコンテナを書き換えるだけの高速カットと、 再エンコード有りの場合の画質・速度差も計測しました。

コマンド例:
ffmpeg -ss 00:00:05 -t 00:00:10 -i input.mp4 \
  -c copy output_trimmed.mp4
LOG / PROGRESS

FFmpeg のログを UI に引き上げる

FFmpeg のログコールバックを差し替え、 文字列を JNI 経由で Kotlin 側に送ることで、 アプリ画面上の TextView にリアルタイムにログを表示できるようにしました。 将来的にはログから処理済みフレーム数・時間をパースして プログレスバーに反映する予定です。

実験・結果・課題

60秒の動画を10秒にトリミング

60秒のフルHD mp4 を用意し、5秒〜15秒の区間だけを切り出すテストを行いました。 -c copy の場合は数秒以内に完了し、 再エンコードあり(ビットレート指定)の場合は 端末の性能に応じた処理時間になることを確認しました。

トリミング前の入力動画
(a) トリミング前の入力動画
トリミング後の出力動画
(b) トリミング後の10秒動画

現在の課題

・FFmpeg の .so が大きく、APK サイズが膨らみがち ・ABI ごとにビルドした結果の管理が煩雑 ・一部端末では SAF 経由のパス処理にクセがある など、実運用に向けて解決したいポイントが見えてきました。

APK サイズ最適化は未対応 プログレスバー連携は今後実装

動画リンク

入力動画の選択 → 開始/終了時刻の指定 → トリミング実行 → ログが流れて出力動画がギャラリーに保存されるまでを 収めたデモ動画です。

try_ffmpeg で FFmpeg トリミングを実行するデモ

まだできていない部分と今後

「ただのテスト」から「本番編集エンジン」へ

try_ffmpeg はテストアプリですが、 ここで作った JNI ラッパ・ビルドスクリプト・ログの扱いは そのまま ebutuoY や Loopit の動画編集エンジンとして再利用できます。 将来的には、切り抜きアプリ用に カット / 結合 / 速度変更 / 字幕焼き込みなども実装していく予定です。

  • 短期: プログレスバー連携、エラー時の詳細メッセージ表示。
  • 中期: トリミング以外のフィルタ(クロップ・スケール・音量)対応。
  • 長期: ebutuoY / Loopit と統合し、 UI から直感的に FFmpeg スクリプトを組める編集環境を作る。

これからの改善点と開発計画

コアの NDK 部分を安定させつつ、徐々に UI と機能を広げて 「テストアプリから本番用ライブラリ」へ昇格させていきます。

  • Core: ビルドスクリプトの整理・ABI 管理の自動化。
  • UX: 直感的な区間指定 UI(シークバー / RangeSlider)。
  • Integration: 他アプリ(ebutuoY, Loopit)のモジュールとして切り出し。
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ビルドスクリプト整理・ABI 管理
プログレスバー連携・エラー表示改善
他アプリへの組み込み設計(モジュール化)

このプロジェクトを通して

感じていること

try_ffmpeg を作る中で、「NDK と FFmpeg を自分で回せるようになる」と 動画編集アプリに対して一気に自由度が増すことを実感しました。 既製ライブラリに頼るのではなく、自分でビルド・リンク・JNI を理解しておくことで、 将来の拡張やトラブル対応にも強くなれると感じています。

一方で、ビルド時間やバイナリサイズ、ABI の数など、 実務寄りの制約とも向き合う必要がありました。 このプロジェクトは、単なるテスト以上に「Android × NDK × FFmpeg」の リハビリ兼入門として大きな意味があったと感じています。

今の自分へのメモ

  • ・ビルド手順と CMake をちゃんとドキュメントに残すこと。
  • ・「なぜこのオプションを付けたのか」をコマンドごとにコメントしておく。

リファレンス & リンク

リファレンス

  • FFmpeg Documentation
  • Android Developers – NDK Guides
  • Android Developers – JNI Tips

リンク