• 在Android中使用FFmpeg(android studio环境)


    1.首先我们需要一个已经编译好的libffmpeg.so文件。(怎么编译是个大坑,可以参考windows环境下编译android中使用的FFmpeg,也可以用网上下载的现成的,本文相关的github项目里也有。),当然也要下载好ffmpeg的源码,一会要用到。

    2.打开你的Android工程,在 src/main/ 目录下新建 jni 目录。并将libffmpeg.so文件丢进去。

    3.创建FFmpegKit.java。写入如下代码

    package codepig.ffmpegcldemo;
    
    import android.os.AsyncTask;
    
    public class FFmpegKit {
        public interface KitInterface{
            void onStart();
            void onProgress(int progress);
            void onEnd(int result);
        }
    
        static{
            System.loadLibrary("ffmpeg");
            System.loadLibrary("ffmpeginvoke");
        }
    
        public static int execute(String[] commands){
            return run(commands);
        }
    
        public static void execute(String[] commands, final KitInterface kitIntenrface){
            new AsyncTask<String[],Integer,Integer>(){
                @Override
                protected void onPreExecute() {
                    if(kitIntenrface != null){
                        kitIntenrface.onStart();
                    }
                }
                @Override
                protected Integer doInBackground(String[]... params) {
                    return run(params[0]);
                }
                @Override
                protected void onProgressUpdate(Integer... values) {
                    if(kitIntenrface != null){
                        kitIntenrface.onProgress(values[0]);
                    }
                }
                @Override
                protected void onPostExecute(Integer integer) {
                    if(kitIntenrface != null){
                        kitIntenrface.onEnd(integer);
                    }
                }
            }.execute(commands);
        }
    
        public native static int run(String[] commands);
    }

    这个是用来调用ffmpeg可执行文件的。

    4.在终端中切到src/main/java文件夹下,输入:

    javah codepig.ffmpegcldemo.FFmpegKit

    (这里注意你自己的文件的实际位置)
    然后就会在该目录生成 codepig_ffmpegecldemo_FFmpegKit.h 文件,将这个文件移动到 jni 目录。

    5.复制FFmpeg源码文件 ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到jni目录下。
    在 jni 目录新建文件 Android.mk Application.mk codepig_ffmpegcldemo_FFmpegKit.c。

    6.编辑ffmpeg.c,把

    int main(int argc, char **argv)

    改名为

    int run(int argc, char **argv)

    编辑ffmpeg.h, 在文件末尾添加函数申明:

    int run(int argc, char **argv)

    7.编辑cmdutils.c中的exit_program函数,删掉函数中原来的内容, 添加 return ret;并修改函数的返回类型为int。
    长这样:

    int exit_program(int ret)
    {
        return ret;
    }

    编辑cmdutils.h中exit_program的申明,把返回类型修改为int。
    长这样:

    int exit_program(int ret);

    8.在 codepig_ffmpegcldemo_FFmpegKit.c 中实现 codepig_ffmpegcldemo_FFmpegKit.h 中的方法。

    #include <stdio.h>
    #include "codepig_ffmpegcldemo_FFmpegKit.h"
    #include "ffmpeg.h"
    #include "logjam.h"
    
    JNIEXPORT jint JNICALL Java_codepig_ffmpegcldemo_FFmpegKit_run
    (JNIEnv *env, jclass obj, jobjectArray commands){
        //FFmpeg av_log() callback
        int argc = (*env)->GetArrayLength(env, commands);
        char *argv[argc];
    
        LOGD("Kit argc %d
    ", argc);
        int i;
        for (i = 0; i < argc; i++) {
            jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
            argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
            LOGD("Kit argv %s
    ", argv[i]);
        }
        return run(argc, argv);
    }

    9.编辑Android.mk

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := ffmpeg
    LOCAL_SRC_FILES := libffmpeg.so
    include $(PREBUILT_SHARED_LIBRARY)
    include $(CLEAR_VARS)
    LOCAL_MODULE := ffmpeginvoke
    LOCAL_SRC_FILES := codepig_ffmpegcldemo_FFmpegKit.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
    LOCAL_C_INCLUDES := F:/demo/ffmpeg-3.0
    LOCAL_LDLIBS := -llog -lz -ldl
    LOCAL_SHARED_LIBRARIES := ffmpeg
    include $(BUILD_SHARED_LIBRARY)

    其中LOCAL_C_INCLUDES的值为ffmpeg源码文件夹地址

    10.编辑Application.mk 文件

    APP_ABI := armeabi-v7a
    APP_PLATFORM := android-17

    其中APP_ABI的值是支持的cpu类型。要支持多种cpu的话,可以把类型写一起用空格隔开,比如

    APP_ABI := armeabi-v7a x86

    11.在终端中定位到jni目录,执行ndk -build
    成功后就会在libs文件夹生成相应的libffmpeg.so和libffmpeginvoke.so文件。这些so文件就是最终我们用来调用的FFmpeg可执行文件。
    如果出现如下错误提示

    Android NDK: Could not find application project directory !
    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.

    则在Application.mk文件中添加

    APP_BUILD_SCRIPT := Android.mk

    如果出现如下提示

    Android NDK: Your Android application project path contains spaces: 'F:/qzd android/Android/workspace/'
    Android NDK: The Android NDK build cannot work here. Please move your project to a different location.

    那大约是因为项目所在文件夹名称有空格。改名就好了(感觉好弱鸡)

    12.在build.gradle文件中修改下库文件地址的指向

    android {
        sourceSets {
            main {
                jniLibs.srcDirs = ['src/main/libs']
                jni.srcDirs=[]
            }
        }
    }

    现在终于可以在android中使用ffmpeg库了。

    13.举个栗子
    (以下例子里的videoUrl是原始视频文件地址,imageUrl是水印图片地址,musicUrl是音频mp3地址,outputUrl是最终输出视频地址。)

    (1) 给视频添加图片水印:

    Runnable compoundRun=new Runnable() {
                @Override
                public void run() {
                    String[] commands = new String[10];
                    commands[0] = "ffmpeg";
                    commands[1] = "-i";
                    commands[2] = videoUrl;
                    commands[3] = "-i";
                    commands[4] = imageUrl;
                    commands[5] = "-filter_complex";
                    commands[6] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
                    commands[7] = "-codec:a";
                    commands[8] = "copy";
                    commands[9] = outputUrl;
    
                    FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {
                        @Override
                        public void onStart() {
                            Log.d("FFmpegLog LOGCAT","FFmpeg 命令行开始执行了...");
                        }
    
                        @Override
                        public void onProgress(int progress) {
                            Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行执行进度..."+progress);
                        }
    
                        @Override
                        public void onEnd(int result) {
                            Log.d("FFmpegLog LOGCAT","FFmpeg 命令行执行完成...");
                        }
                    });
                }
            };
            ThreadPoolUtils.execute(compoundRun);

    (2) 合成音频视频

    Runnable compoundRun=new Runnable() {
                @Override
                public void run() {
                    String[] commands = new String[6];
                    commands[0] = "ffmpeg";
                    commands[1] = "-i";
                    commands[2] = videoUrl;
                    commands[3] = "-i";
                    commands[4] = musicUrl;
                    commands[5] = outputUrl;
    
                    FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {
                        @Override
                        public void onStart() {
                            Log.d("FFmpegLog LOGCAT","FFmpeg 命令行开始执行了...");
                        }
    
                        @Override
                        public void onProgress(int progress) {
                            Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行执行进度..."+progress);
                        }
    
                        @Override
                        public void onEnd(int result) {
                            Log.d("FFmpegLog LOGCAT","FFmpeg 命令行执行完成...");
    //                        getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    //                        Message msg = new Message();
    //                        msg.what = 1;
    //                        mHandler.sendMessage(msg);
                        }
                    });
                }
            };
            ThreadPoolUtils.execute(compoundRun);

    (3)把这两个命令写一起

    Runnable compoundRun=new Runnable() {
                @Override
                public void run() {
                    String[] commands = new String[11];
                    commands[0] = "ffmpeg";
                    commands[1] = "-i";
                    commands[2] = videoUrl;
                    commands[3] = "-i";
                    commands[4] = imageUrl;
                    commands[5] = "-filter_complex";
                    commands[6] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
                    commands[7] = "-i";
                    commands[8] = musicUrl;
                    commands[9] = "-y";
                    commands[10] = outputUrl;
    
                    FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {
                        @Override
                        public void onStart() {
                            Log.d("FFmpegLog LOGCAT","FFmpeg 命令行开始执行了...");
                        }
    
                        @Override
                        public void onProgress(int progress) {
                            Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行执行进度..."+progress);
                        }
    
                        @Override
                        public void onEnd(int result) {
                            Log.d("FFmpegLog LOGCAT","FFmpeg 命令行执行完成...");
    //                        getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                        }
                    });
                }
            };
            ThreadPoolUtils.execute(compoundRun);

    附:ffmpeg命令格式简要说明
    基本形式:

    ffmpeg -i inputFile -参数名 参数值 …… outputFile;
    

    头尾不变,中间的参数顺序无所谓。但是一个操作的参数必须写一起。
    参数是以 -参数名+参数值 这样的成对形式赋值。


    参考:
    ffmpeg参数解释
    FFmpeg常用命令大全,并简单封装
    Android最简单的基于FFmpeg的例子(四)---以命令行的形式来使用ffmpeg
    本文github项目:
    ffmpegCLDemo

    作者链接:https://www.jianshu.com/p/e0c32c8b0ebc
  • 相关阅读:
    Redis主从同步分析(转)
    Jedis使用总结【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】(转)
    PHP之PDO_MYSQL扩展安装步骤(转)
    MongoDB 那些坑(转)
    CF 222 (DIV 1)
    TC SRM601
    TC SRM600 DIV2
    Github入门教程
    2013长春区域赛总结
    退役了~~~~~~~~~~~~
  • 原文地址:https://www.cnblogs.com/Sharley/p/10438660.html
Copyright © 2020-2023  润新知