第一次尝试使用Android 移植FFmpeg算法,一路坎坷,最终做如下总结,适用于Android手机、Android开发板。亲测可用。

一、下载组件

    在Android Studio中下载所需组件:CMake、LLDB、NDK(NDK如果版本太高>r16,则选择后期单独下载)。其中,CMake为构建工具,LLDB为调试工具。

二、创建支持C++的AS工程

新建工程,勾选"Include C++ support":

当新建工程后,就默认新建了最简单的C++算法实现工程。点击运行即可显示如下图:

三、配置环境

接下来进行环境配置,工程需修改文件如下图:

在libs文件夹中,放置FFmpeg库和头文件,另外将opencv库和头文件也一并放入。

在AndroidManifest.xml文件中,添加相关权限:

<?xml version="1.0" encoding="utf-8"?>
    package="com.jcet.susan.ffmpegdemo">
   
 
    
    
    
    
    
    
    
    

             android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
            
                

                
            

        
    

在cpp文件中放置第三方算法文件,其中native-lib.cpp为新建C++支持工程后自动生成的C++算法文件,直接修改此文件如下:

   

获取视频帧数算法涉及到相关算法,都放置于cpp文件夹中:

在MainActivity中进行具体的java代码操作,注意:针对Android6.0(23版本及以上时,需要动态获取权限);需将所有动态库加载其中;具体代码如下:

package com.XXXX.XXXXX.ffmpegdemo;import android.Manifest;import android.annotation.TargetApi;import android.content.pm.PackageManager;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;import com.XXXX.XXXXX.NativeCaller;import java.io.File;public class MainActivity extends AppCompatActivity {    private final int GET_PERMISSION_REQUEST = 100; //权限申请自定义码    private TextView intValue, time, objectValue;    // Used to load the 'native-lib' library on application startup.    static {        System.loadLibrary("opencv_java3");        System.loadLibrary("avutil-55");        System.loadLibrary("swresample-2");        System.loadLibrary("avcodec-57");        System.loadLibrary("avformat-57");        System.loadLibrary("swscale-4");        System.loadLibrary("postproc-54");        System.loadLibrary("avfilter-6");        System.loadLibrary("avdevice-57");        System.loadLibrary("native-lib");    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        checkPermissions();    }    /**     * 初始化View     */    private void initView() {        time = (TextView) this.findViewById(R.id.time);        intValue = (TextView) this.findViewById(R.id.intValue);        objectValue = (TextView) this.findViewById(R.id.objectValue);    }    /**     * 获取权限     */    private void checkPermissions() {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager                    .PERMISSION_GRANTED &&                    ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager                            .PERMISSION_GRANTED &&                    ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager                            .PERMISSION_GRANTED) {                doWork();            } else {                //不具有获取权限,需要进行权限申请                ActivityCompat.requestPermissions(MainActivity.this, new String[]{                        Manifest.permission.WRITE_EXTERNAL_STORAGE,                        Manifest.permission.RECORD_AUDIO,                        Manifest.permission.CAMERA}, GET_PERMISSION_REQUEST);            }        } else {            doWork();        }    }    @TargetApi(23)    @Override    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        switch (requestCode) {            // requestCode即所声明的权限获取码,在checkSelfPermission时传入            case GET_PERMISSION_REQUEST: {                int size = 0;                if (grantResults.length >= 1) {                    int writeResult = grantResults[0];                    //读写内存权限                    boolean writeGranted = writeResult == PackageManager.PERMISSION_GRANTED;//读写内存权限                    if (!writeGranted) {                        size++;                    }                    //录音权限                    int recordPermissionResult = grantResults[1];                    boolean recordPermissionGranted = recordPermissionResult == PackageManager.PERMISSION_GRANTED;                    if (!recordPermissionGranted) {                        size++;                    }                    //相机权限                    int cameraPermissionResult = grantResults[2];                    boolean cameraPermissionGranted = cameraPermissionResult == PackageManager.PERMISSION_GRANTED;                    if (!cameraPermissionGranted) {                        size++;                    }                    if (size == 0) {                        doWork();//必须要写,不然会有问题                    } else {                        checkPermissions();                    }                }            }        }    }    /**     * 在此处做获得权限的操作,否则强制一直在获取权限     */    private void doWork() {        new Thread() {            @Override            public void run() {                super.run();//               String path = Environment.getExternalStorageDirectory() + File.separator + "DCIM/Camera/CELLDETECT/clip_20190314093232.mp4";                String path = Environment.getExternalStorageDirectory() + File.separator + "DCIM/clip_20190314092927.mp4";                Message msg = new Message();                Bundle bundle = new Bundle();                long time1 = System.currentTimeMillis();                int retValue = NativeCaller.getIntValueFromJNI(path);                bundle.putInt("intValue", retValue);                long time2 = System.currentTimeMillis();                bundle.putInt("time2-1", (int) (time2 - time1));                msg.obj = bundle;                handler.sendMessage(msg);            }        }.start();    }    Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            Bundle bundle = (Bundle) msg.obj;            intValue.setText("帧数-->" + bundle.getInt("intValue"));            time.setText("耗时-->time2-1:" + bundle.getInt("time2-1"));        }    };}

最重要的就是CMakeList.txt文件配置了!!!

具体代码如下,附注释:

# For more information about using CMake with Android Studio, read the# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.#CMake最低版本:3.4.1cmake_minimum_required(VERSION 3.4.1)set(CMAKE_VERBOSE_MAKEFILE on)# Creates and names a library, sets it as either STATIC# or SHARED, and provides the relative paths to its source code.# You can define multiple libraries, and CMake builds them for you.# Gradle automatically packages shared libraries with your APK.#为生成的CPP文件生成的动态库命名:native-libadd_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).             src/main/cpp/native-lib.cpp             src/main/cpp/FrameCvMat.cpp             src/main/cpp/Dynamic.cpp)# Searches for a specified prebuilt library and stores the path as a# variable. Because CMake includes system libraries in the search path by# default, you only need to specify the name of the public NDK library# you want to add. CMake verifies that the library exists before# completing its build.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 )# Specifies libraries CMake should link to your target library. You# can link multiple libraries, such as libraries you define in this# build script, prebuilt third-party libraries, or system libraries.#动态库路径set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)#添加动态库#添加libopencv_java3.so库,其名称为:opencv_java3 SHARED->动态库 IMPORTED->导入方式add_library(libopencv_java3 SHARED IMPORTED )#opencv_java3库的路径,以CMake路径为基础进行查找set_target_properties(libopencv_java3 PROPERTIES                      IMPORTED_LOCATION                       ../../../../libs/armeabi/libopencv_java3.so)add_library( avutil-55             SHARED             IMPORTED )set_target_properties( avutil-55                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libavutil-55.so )add_library( swresample-2             SHARED             IMPORTED )set_target_properties( swresample-2                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libswresample-2.so )add_library( avcodec-57             SHARED             IMPORTED )set_target_properties( avcodec-57                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libavcodec-57.so )add_library( avfilter-6             SHARED             IMPORTED)set_target_properties( avfilter-6                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libavfilter-6.so )add_library( swscale-4             SHARED             IMPORTED)set_target_properties( swscale-4                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libswscale-4.so )add_library( avdevice-57             SHARED             IMPORTED)set_target_properties( avdevice-57                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libavdevice-57.so )add_library( avformat-57             SHARED             IMPORTED)set_target_properties( avformat-57                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libavformat-57.so )add_library( postproc-54             SHARED            IMPORTED)set_target_properties( postproc-54                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi/libpostproc-54.so)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")#导入FFmpeg头文件,在include文件夹下include_directories(libs/include)#target_include_directories(native-lib PRIVATE libs/include)#连接FFmpeg、opencv动态库target_link_libraries( native-lib android log libopencv_java3 avutil-55 swresample-2 avcodec-57 avfilter-6 swscale-4 avdevice-57 avformat-57 postproc-54                       ${log-lib} )

修改app\build.gradle文件

在build.gradle文件中需修改编译的CPU类型,并对Android架构作一定限制。通过多款机型测试,armeabi版本符合所有机型和开发板。具体代码如下:

apply plugin: 'com.android.application'android {    compileSdkVersion 25    buildToolsVersion "25.0.2"    defaultConfig {        applicationId "com.XXXX.XXXXX.ffmpegdemo"        minSdkVersion 15        targetSdkVersion 25        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"        externalNativeBuild {            cmake {                cppFlags ""                arguments '-DANDROID_PLATFORM=android-15',                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'            }        }        ndk {            abiFilters "armeabi"        }    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }    lintOptions {        checkReleaseBuilds false        // Or, if you prefer, you can continue to check for errors in release builds,        // but continue the build even when errors are found:        abortOnError false    }//给系统指定jniLibs的目录    sourceSets.main {        jniLibs.srcDirs = ['libs']        jni.srcDirs = []    }    externalNativeBuild {        cmake {            path "CMakeLists.txt"        }    }}dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {        exclude group: 'com.android.support', module: 'support-annotations'    })    compile 'com.android.support:appcompat-v7:25.3.1'    compile 'com.android.support.constraint:constraint-layout:1.0.2'    testCompile 'junit:junit:4.12'}

activity——main.xml文件如下:

<?xml version="1.0" encoding="utf-8"?>                        

四、结果显示

最终显示结果如下:视频的帧数为31帧,耗时8931ms。

 

更多相关文章

  1. Android(安卓)数据操作(一) 自定义AttributeSet属性
  2. Android(安卓)APP 漏洞整理
  3. 安卓系统的启动流程
  4. android 在线预览pdf文件(目前最全)
  5. android NDK学习篇4之two-libs——使用(单个动态库)来生成动态库
  6. Android7.0适配教程与心得
  7. 高通平台环境搭建,编译,系统引导流程分析
  8. Android(安卓)持续集成实践(二)——配置 Docker + gitlab-runner
  9. Android(安卓)项目中文件夹作用(res文件夹详细介绍)

随机推荐

  1. JBox2D 引擎 for Android(安卓)详解
  2. Android高人必修 Ant 编译Android工程
  3. 2014-11-8Android学习------Android(安卓
  4. JNI之------NDK开发环境的配置
  5. [原创] android学习和广告平台赚钱
  6. Android中的控件
  7. 谷歌发布 Android(安卓)2.2:运行速度提高2
  8. Android的Socket通信编程实例
  9. Android事件分发机制完全解析,带你从源码
  10. Android的事件简介