您的位置:首页 > 移动开发 > Android开发

Android - FFmpeg & Mac & AndroidStudio & CMake 环境搭建

2018-01-03 15:44 399 查看
AndroidStudio 2.2 开始支持 
CMake
 编译,网上的文章清一色都是 
ndk-build
,记录一下我用CMake搭建环境的过程。


基础环境

MacOS 10.12.3
AndroidStudio 2.3
NDK 13.1.*
FFmpeg 3.2.*


下载
FFmpeg 源码

1
git clone https://git.ffmpeg.org/ffmpeg.git 
切换到 3.2 分支
1
git checkout -b 3.2 remotes/origin/release/3.2
当然你也可以去去官网下载压缩包。


编译
FFmpeg for Android


修改
FFmpeg 的 configure

打开 configure 文件,找到:
1234
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'  LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'  SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'
替换成:
1234
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'  LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'  SLIB_INSTALL_LINKS='$(SLIBNAME)'


编写
Android 编译脚本

123456789101112131415161718192021222324252627282930313233343536373839
#!/bin/bashNDK=/Users/gavin/Develop/android-sdk/ndk-bundleSYSROOT=$NDK/platforms/android-15/arch-arm/TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64CPU=armPREFIX=$(pwd)/android/$CPUADDI_CFLAGS="-marm"function build_one{./configure \ --prefix=$PREFIX \ --enable-shared \ --disable-static \ --disable-doc \ --disable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --disable-ffserver \ --disable-avdevice \ --disable-doc \ --disable-symver \ --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \ --target-os=linux \ --arch=arm \ --enable-cross-compile \ --sysroot=$SYSROOT \ --extra-cflags="-Os -fpic $ADDI_CFLAGS" \ --extra-ldflags="$ADDI_LDFLAGS" \ $ADDITIONAL_CONFIGURE_FLAGmake cleanmakemake install}build_one
前三行自行更换成自己的 NDK 路径即可,
PREFIX
 就是最终编译输出地址,根据情况也可以修改。

保存文件到 
FFmpeg
 根目录为 
build_android.sh
chmod
+x build_android.sh
 赋予执行权限。


编译

执行脚本进行编译。
1
./build_android.sh
小憩一会儿,编译完成。

进入 PREFIX 目录:
123456789101112131415
── include│   ├── libavcodec│   ├── libavfilter│   ├── libavformat│   ├── libavutil│   ├── libswresample│   └── libswscale└── lib    ├── libavcodec-57.so    ├── libavfilter-6.so    ├── libavformat-57.so    ├── libavutil-55.so    ├── libswresample-2.so    ├── libswscale-4.so    └── pkgconfig
我们需要的 so 和头文件都有啦。


导入
Android 工程

对于如何开始一个 C++ 项目,官方已经有中文文档了:

https://developer.android.google.cn/studio/projects/add-native-code.html

官网有的东西不赘述,再次声明学什么东西优先看 官方文档,官方文档,官方文档,并且Google dev现在已经有很多中文资源了。


新建一个
C/C++ 工程





勾选 Include C++ Support 即可得到一个基于CMake的模板工程。


拷贝
so 和 incude 文件到 libs

因为我们只编译了 arm 版本的 so,所以需要把 so 拷到 armeabi-v7a 目录下,完整路径如下:
1234567891011121314151617181920212223
├── build.gradle├── libs│   ├── armeabi-v7a│   │   ├── libavcodec-57.so│   │   ├── libavfilter-6.so│   │   ├── libavformat-57.so│   │   ├── libavutil-55.so│   │   ├── libswresample-2.so│   │   └── libswscale-4.so│   └── include│       ├── libavcodec│       ├── libavfilter│       ├── libavformat│       ├── libavutil│       ├── libswresample│       └── libswscale├── proguard-rules.pro└── src    └── main        ├── AndroidManifest.xml        ├── cpp        ├── java        └── res


指定
jniLibs 路径

12345678
android {    ...    sourceSets {        main {            jniLibs.srcDirs = ['libs']        }    }}


指定
abiFilters

因为 so 只有 armeabi-v7a 的,所以我们还需要指定一下 abi 为 armeabi-v7a
123456789
android {    ...    defaultConfig {      ...      ndk {          abiFilters 'armeabi-v7a'      }    }}


编写
CMakeLists

脚本怎么写,有什么讲究其实官方文档已经很清楚了:
创建
CMake 构建脚本
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
# 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_minimum_required(VERSION 3.4.1)# 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.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).             src/main/cpp/native-lib.cpp )add_library( avcodec-57             SHARED             IMPORTED )set_target_properties( avcodec-57                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi-v7a/libavcodec-57.so )add_library( avfilter-6             SHARED             IMPORTED )set_target_properties( avfilter-6                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi-v7a/libavfilter-6.so )add_library( avformat-57             SHARED             IMPORTED )set_target_properties( avformat-57                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi-v7a/libavformat-57.so )add_library( avutil-55             SHARED             IMPORTED )set_target_properties( avutil-55                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi-v7a/libavutil-55.so )add_library( swresample-2             SHARED             IMPORTED )set_target_properties( swresample-2                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi-v7a/libswresample-2.so )add_library( swscale-4             SHARED             IMPORTED )set_target_properties( swscale-4                       PROPERTIES IMPORTED_LOCATION                       ../../../../libs/armeabi-v7a/libswscale-4.so )include_directories( libs/include )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.target_link_libraries( # Specifies the target library.                       native-lib                       avcodec-57                       avfilter-6                       avformat-57                       avutil-55                       swresample-2                       swscale-4                       # Links the target library to the log library                       # included in the NDK.                       ${log-lib} )


Java
引入 so 库

12345678910
static {    System.loadLibrary("native-lib");    System.loadLibrary("avcodec-57");    System.loadLibrary("avfilter-6");    System.loadLibrary("avformat-57");    System.loadLibrary("avutil-55");    System.loadLibrary("swresample-2");    System.loadLibrary("swscale-4");}


运行测试一下

如果成功运行不报错的话,就证明一切没有问题。


编写
FFmpeg 测试代码


UI

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="cn.gavinliu.android.ffmpeg.MainActivity">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <Button            android:id="@+id/button"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:onClick="protocol"            android:text="Protocol"            android:textAllCaps="false" />        <Button            android:id="@+id/button2"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:onClick="filter"            android:text="Filter"            android:textAllCaps="false" />        <Button            android:id="@+id/button3"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:onClick="format"            android:text="Format"            android:textAllCaps="false" />        <Button            android:id="@+id/button4"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:onClick="codec"            android:text="Codec"            android:textAllCaps="false" />    </LinearLayout>    <ScrollView        android:layout_width="match_parent"        android:layout_height="wrap_content">        <TextView            android:id="@+id/sample_text"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:scrollbars="vertical"            android:text="Hello World!" />    </ScrollView></LinearLayout>
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
package cn.gavinliu.android.ffmpeg;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    // Used to load the 'native-lib' library on application startup.    static {        System.loadLibrary("native-lib");        System.loadLibrary("avcodec-57");        System.loadLibrary("avfilter-6");        System.loadLibrary("avformat-57");        System.loadLibrary("avutil-55");        System.loadLibrary("swresample-2");        System.loadLibrary("swscale-4");    }    private TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // Example of a call to a native method        tv = (TextView) findViewById(R.id.sample_text);        tv.setText(stringFromJNI());    }    public void protocol(View view) {        tv.setText(urlprotocolinfo());    }    public void filter(View view) {        tv.setText(avformatinfo());    }    public void format(View view) {        tv.setText(avfilterinfo());    }    public void codec(View view) {        tv.setText(avcodecinfo());    }    /**     * A native method that is implemented by the 'native-lib' native library,     * which is packaged with this application.     */    public native String stringFromJNI();    public native String avformatinfo();    public native String urlprotocolinfo();    public native String avcodecinfo();    public native String avfilterinfo();}


C++

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
#include <jni.h>#include <string>extern "C" {#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libavfilter/avfilter.h>JNIEXPORT jstring JNICALLJava_cn_gavinliu_android_ffmpeg_MainActivity_stringFromJNI(        JNIEnv *env,        jobject /* this */) {    std::string hello = "Hello from C++";    return env->NewStringUTF(hello.c_str());}JNIEXPORT jstring JNICALLJava_cn_gavinliu_android_ffmpeg_MainActivity_avformatinfo(        JNIEnv *env,        jobject /* this */) {    char info[40000] = {0};    av_register_all();    AVInputFormat *if_temp = av_iformat_next(NULL);    AVOutputFormat *of_temp = av_oformat_next(NULL);    while (if_temp != NULL) {        sprintf(info, "%sInput: %s\n", info, if_temp->name);        if_temp = if_temp->next;    }    while (of_temp != NULL) {        sprintf(info, "%sOutput: %s\n", info, of_temp->name);        of_temp = of_temp->next;    }    return env->NewStringUTF(info);}JNIEXPORT jstring JNICALLJava_cn_gavinliu_android_ffmpeg_MainActivity_urlprotocolinfo(        JNIEnv *env,        jobject /* this */) {    char info[40000] = {0};    av_register_all();    struct URLProtocol *pup = NULL;    struct URLProtocol **p_temp = &pup;    avio_enum_protocols((void **) p_temp, 0);    while ((*p_temp) != NULL) {        sprintf(info, "%sInput: %s\n", info, avio_enum_protocols((void **) p_temp, 0));    }    pup = NULL;    avio_enum_protocols((void **) p_temp, 1);    while ((*p_temp) != NULL) {        sprintf(info, "%sInput: %s\n", info, avio_enum_protocols((void **) p_temp, 1));    }    return env->NewStringUTF(info);}JNIEXPORT jstring JNICALLJava_cn_gavinliu_android_ffmpeg_MainActivity_avcodecinfo(        JNIEnv *env,        jobject /* this */) {    char info[40000] = {0};    av_register_all();    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[%10s]\n", info, c_temp->name);        c_temp = c_temp->next;    }    return env->NewStringUTF(info);}JNIEXPORT jstring JNICALLJava_cn_gavinliu_android_ffmpeg_MainActivity_avfilterinfo(JNIEnv *env, jobject /* this */) {    char info[40000] = {0};    avfilter_register_all();    AVFilter *f_temp = (AVFilter *) avfilter_next(NULL);    while (f_temp != NULL) {        sprintf(info, "%s%s\n", info, f_temp->name);        f_temp = f_temp->next;    }    return env->NewStringUTF(info);}}


运行



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: