30.FFmpeg+OpenGLES+OpenSLES播放器实现(四.Android Studio ndk开发环境和CMakeLists脚本编写)

项目源码
FFmpeg开发文档

1.环境配置

ffmpeg库已经编译好了,接下来准备将so引入Android studio进行开发
我们创建一个新的项目,注意在创建过程中这几个选项的勾选


6C~L[SE8UA3Z]JEAFB~CU24.png

勾选添加C++支持,Android studio会自动帮我们做一些配置,后边进行简单的解释


Y%~`T`0D1PM`(`}}$ENC9G2.png

C++ Standard:使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。有C11和C14两种,我们选择C11

Exceptions Support:如果希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。

Runtime Type Information Support:(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。如果希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。

项目创建出来之后,可以看到,项目默认创建了一个调用C++代码的小demo输出一行字符串。在app根目录可以看到一个CMakeLists.txt的文件,这是添加c++支持后默认创建的cmake脚本,我们将使用这个脚本对ffmpeg进行编译

打开app目录下的build.gradle,可以看到下边两项配置

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                //C++标准选择C11之后做的配置
                cppFlags "-std=c++11"
            }
        }
    }
 
    externalNativeBuild {
        cmake {
            //指定的CMakeLists脚本文件的路径
            path "CMakeLists.txt"
        }
    }
}

这是工具自动做好的配置,接下来还需要我们手动做一些处理,来完善ffmpeg编译的环境。
第一. ffmpeg播放视频会涉及到操作内存卡,所以需要配置存储权限,6.0及以上Android版本还要记得动态权限获取的配置,这里不多说

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

第二. 在app main目录下创建jniLibs目录,将我们创建好的so放在这个目录下,或者直接把so放在libs目录下,但是这种方式需要我们在build.gradle中配置路径,这里选用第二种方式


dir.png.png

然后打开app下build.gradle文件,在android/defaultConfig节点下添加如下配置,指定so文件的存放目录,默认是jniLibs

      sourceSets{
            //将so放在libs文件夹下,需要指定这个路径,因为默认路径是jniLibs
            main{
                jniLibs.srcDirs=['libs']
            }
        }

还有一点,因为我们只编译了armeabi-v7a版本的ffmpeg,所以需要指定过滤版本,在android/defaultConfig/externalNativeBuild节点中添加

           ndk{
                abiFilters "armeabi-v7a"
            }

此时,整个build.gradle文件应该是这样的(只留下了ndk相关的配置)

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
            //默认情况下,Gradle 会针对 [NDK 支持的 ABI](https://developer.android.google.cn/ndk/guides/abis.html?hl=zh-cn#sa) 
            //将原生库构建到单独的 .so文件中,并将其全部打包到 APK 中。如果希望 Gradle 仅构建和打包原生
            //库的特定 ABI 配置,可以在模块级build.gradle文件中使用 ndk.abiFilters标志指定这些配置
            ndk{
                //abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
                abiFilters "armeabi-v7a"
            }
        }
        sourceSets{
            //将so放在libs文件夹下,需要指定这个路径,因为默认路径是jniLibs
            main{
                jniLibs.srcDirs=['libs']
            }
        }
    }
    ...
    //将 Gradle 关联到原生库,需要提供一个指向 CMake 或 ndk-build 脚本文件的路径。在构建应用时,Gradle
    //会以依赖项的形式运行 CMake 或 ndk-build,并将共享的库打包到 APK 中
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    ...
}

2.CMakeLists.txt脚本文件的编写

CMake 构建脚本是一个纯文本文件,必须将其命名为 CMakeLists.txt,一般放在项目根目录(app的根目录),后边会附上我测试成功的CMakeLists.txt,
CMakeLists Android官方教程

add_library():

该命令用于向CMake构建脚本添加源文件和库,它有三个参数,每个参数的解释如下

add_library(
             //这个参数指定你的源文件被编译或者库被引入后的名字,可以指
             //定任意你觉得合适的名字
             native-lib
             //第二个参数有STATIC 和SHARED两种选择,SHARED表示会编
             //译成动态库,STATIC 表示静态库
             SHARED
             //这个位置用于指定源文件的相对路径(相对于CMakeLists.txt的
             //路径),或者如果你是在引入其他库,那么这里指定IMPORTED
             //属性
             src/main/cpp/native-lib.cpp )
set_target_properties:

如果你add_library引入的是已经编译好的库文件,那么你需要通过set_target_properties指定被引入的库文件的路径

//这两个一一对应,这两个命令结合可以引入一个so库,一个so库对应这两个命令
add_library(
              avcodec 
              SHARED 
              IMPORTED)
set_target_properties(
              //指定是给谁设置属性,这里是上边add的avcodec 
              avcodec 
              //指定是设置什么样的属性,这里是引入的路径,是一个相对路径
              PROPERTIES IMPORTED_LOCATION          
              //这里具体指定相对于脚本文件的路径
              ${FFMPEG_DIR}/libavcodec.so)
include_directories():

通过上边两个命令,库文件会被添加进来,这些库一般会依赖一些头文件,这时我们可以通过include_directories来指定头文件的位置,确保 CMake 在编译时可以定位到头文件

include_directories(
              ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/include)

以ffmpeg的编译为例,我们会add很多的so库,add_library了好多次,将所有需要的so添加,我们还add了自己的源文件,最终我们指定了这些源文件被编译成ffmpeg的so

add_library(
            ffmpeg 
            SHARED 
            ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/native-lib.cpp )

这些源文件依赖于添加的ffmpeg中的库,所以最终我们要把我们自己编译的库和其他这些so链接到一块,这时就需要命令

target_link_libraries( 
              ffmpeg 
              avcodec 
              avfilter 
              avformat 
              avutil 
              swresample 
              swscale 
              ${log-lib} )

看到这里有个这样的引入${log-lib},这个库是ndk中提供的,通过find_library命令引入

find_library

Android NDK 提供了一套实用的原生 API 和库。通过将NDK 库包含到项目的 CMakeLists.txt脚本文件中,预构建的 NDK 库已经存在于 Android 平台上,因此,无需再构建或将其打包到 APK 中。由于 NDK 库已经是 CMake 搜索路径的一部分,甚至不需要在本地 NDK 安装中指定库的位置 - 只需要向 CMake 提供希望使用的库的名称,并将其关联到自己的原生库上即可。

将 find_library()命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。可以使用此变量在构建脚本的其他部分引用 NDK 库。以下示例可以定位Android 特定的日志支持库并将其路径存储在 log-lib 中

find_library( log-lib log )
3.编译ffmpeg的CMakeLists.txt完整脚本
#指定Cmake构建工具的最低版本
cmake_minimum_required(VERSION 3.4.1)

#设置头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/include)

#设置FFmpeg库路径变量
#CMAKE_CURRENT_SOURCE_DIR,指的是当前处理的 CMakeLists.txt 所在的路径,CMAKE_SOURCE_DIR,不论采用何种编译方式,都是工程顶层目录

set(FFMPEG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI})

#添加avcodec
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavcodec.so)

#添加avfilter
add_library(avfilter SHARED IMPORTED)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavfilter.so)

#添加avformat
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavformat.so)

#添加avutil
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavutil.so)

#添加swresample
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libswresample.so)

#添加swscale
add_library(swscale SHARED IMPORTED)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libswscale.so)

add_library( ffmpeg SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/native-lib.cpp )

find_library( log-lib log )

target_link_libraries( ffmpeg avcodec avfilter avformat avutil swresample swscale ${log-lib} )
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350