FFmpeg集成

Posted 一杯清泉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg集成相关的知识,希望对你有一定的参考价值。

FFmpeg生成Android so库

        上篇介绍了如何生成so库,这次介绍如何集成。

一、新建一个android C++项目

这时候主工程目录下会有cpp文件夹,包含如下两个文件:

  • CMakeLists.txt:cmake脚本配置文件,cmake会根据该脚本文件中的指令去编译相关的C/C++源文件,并将编译后产物生成共享库或静态块,然后Gradle将其打包到APK中。
  • native-lib.cpp:用来写C相关的代码。

二、引入so库

  • app/src/main/cpp:将生成的include整体复制进去
  • src/main/jniLibs/arm64-v8a:创建文件路径,将生成的lib中的so库复制进去

三、配置app/build.gradle 

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"
    defaultConfig {
        applicationId "com.simple.ffmpeg"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        //自动生成
        externalNativeBuild {
            cmake {
                cppFlags ''
            }
        }
        //1、手动添加
        ndk {
            abiFilters "arm64-v8a"
            //abiFilters "armeabi-v7a"
        }
        //2、手动添加
        packagingOptions {
            pickFirst 'lib/arm64-v8a/libswscale.so'
            pickFirst 'lib/arm64-v8a/libavcodec.so'
            pickFirst 'lib/lib/arm64-v8a/libavutil.so'
            pickFirst 'lib/arm64-v8a/libavutil.so'
            pickFirst 'lib/arm64-v8a/libavformat.so'
            pickFirst 'lib/arm64-v8a/libavfilter.so'
            pickFirst 'lib/arm64-v8a/libswresample.so'
            pickFirst 'lib/arm64-v8a/libavdevice.so'
        }
    }
    
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    //自动生成
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.10.2'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

第一个手动添加如果没有会报:

Execution failed for task ':app:externalNativeBuildDebug'.
> Build command failed.
  Error while executing process ../Android/sdk/cmake/3.10.2.4988404/bin/ninja with arguments {-C /Users/kewang/code/code_test/Ffmpeg/app/.cxx/cmake/debug/armeabi-v7a native-lib}
  ninja: Entering directory `/Users/kewang/code/code_test/Ffmpeg/app/.cxx/cmake/debug/armeabi-v7a'

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

第二个手动添加如果没有会报:

Execution failed for task ':app:mergeDebugNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > More than one file was found with OS independent path 'lib/arm64-v8a/libswscale.so'. If you are using jniLibs and CMake IMPORTED targets, see https://developer.android.com/studio/preview/features#automatic_packaging_of_prebuilt_dependencies_used_by_cmake

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

四、CMakeLists.txt链接so库

        需要在CMakeLists.txt中把相关的 so 添加和链接起来,要不然没法使用。

cmake_minimum_required(VERSION 3.10.2)

# 支持gnu++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

# 1. 定义so库和头文件所在目录,方面后面使用
set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR})

# 2. 添加头文件目录
include_directories(${ffmpeg_head_dir}/include)

# 3. 添加ffmpeg相关的so库
add_library( avutil
        SHARED
        IMPORTED )
set_target_properties( avutil
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavutil.so )

add_library( swresample
        SHARED
        IMPORTED )
set_target_properties( swresample
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libswresample.so )

add_library( avcodec
        SHARED
        IMPORTED )
set_target_properties( avcodec
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavcodec.so )

add_library( avfilter
        SHARED
        IMPORTED)
set_target_properties( avfilter
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavfilter.so )

add_library( swscale
        SHARED
        IMPORTED)
set_target_properties( swscale
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libswscale.so )

add_library( avformat
        SHARED
        IMPORTED)
set_target_properties( avformat
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavformat.so )

add_library( avdevice
        SHARED
        IMPORTED)
set_target_properties( avdevice
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavdevice.so )

# 查找代码中使用到的系统库
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log )

# 配置目标so库编译信息
add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp
        )

# 指定编译目标库时,cmake要链接的库
target_link_libraries(

        # 指定目标库,native-lib 是在上面 add_library 中配置的目标库
        native-lib

        # 4. 连接 FFmpeg 相关的库
        avutil
        swresample
        avcodec
        avfilter
        swscale
        avformat
        avdevice

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} )

五、native-lib.cpp添加测试代码

        完成好CMakeLists之后需要测试下到底成功没,这块打印输出FFmpeg的信息。

#include <jni.h>
#include <string>
#include <unistd.h>

extern "C" JNIEXPORT jstring JNICALL
Java_com_simple_ffmpeg_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavcodec/jni.h>

JNIEXPORT jstring JNICALL
Java_com_simple_ffmpeg_MainActivity_ffmpegInfo(JNIEnv *env, jobject  /* this */) {

    char info[40000] = {0};
    AVCodec *c_temp = av_codec_next(NULL);
    while (c_temp != NULL) {
        if (c_temp->decode != NULL) {
            sprintf(info, "%sdecode:", info);
        } else {
            sprintf(info, "%sencode:", info);
        }
        switch (c_temp->type) {
            case AVMEDIA_TYPE_VIDEO:
                sprintf(info, "%s(video):", info);
                break;
            case AVMEDIA_TYPE_AUDIO:
                sprintf(info, "%s(audio):", info);
                break;
            default:
                sprintf(info, "%s(other):", info);
                break;
        }
        sprintf(info, "%s[%s]\\n", info, c_temp->name);
        c_temp = c_temp->next;
    }

    return env->NewStringUTF(info);
}
}

注意这个函数名称:Java_com_simple_ffmpeg_MainActivity_stringFromJNI,Java_包名的下划线格式_Java需要调用的方法名称。

六、加载native-lib.cpp测试代码

public class MainActivity extends AppCompatActivity {

    // 加载so库
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.sample_text);
        //调用JNI方法
        tv.setText(ffmpegInfo());
    }

    /**
     * A native method that is implemented by the 'ffmpeg' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native String ffmpegInfo();
}

结果如下:

以上是关于FFmpeg集成的主要内容,如果未能解决你的问题,请参考以下文章

使用 FFmpeg 通过管道输出视频片段

PHP ffmpeg_php测试片段

使用 ffmpeg 将 Mp4 转换为 HLS

使用 ffmpeg 生成单个 MPEG-Dash 片段

如何用ffmpeg截取视频片段&截取时间不准确的坑

如何利用ffmpeg将一小段视频截取成图片