Android FFMPEGFFmpeg音视频开发音视频

《Android音视频系列-3》Android项目中使用FFmp

2019-06-20  本文已影响224人  蓝师傅_Android

上一篇已经编译出FFmpeg动态库,这一篇就来介绍如何在Android项目中使用动态库。

新建一个项目,在Android Studio 3.4.1中发现新建项目发生了一些改变

如果是要support c++,那么应该选Native c++,然后一路next,发现AS帮我们把cmake配置好了,cmake是c++代码构建工具,以前的老项目可能还是用mk的构建方式,我们要与时俱进,用cmake准没错。


我电脑已经装好cmake工具了,如果没有装的打开setting->sdk 进行打钩下载

LLDB是c++调试需要的
CMake 不要用最新版,会报错

然后这样就可以编译跑到手机上了,这一步就不演示了。

直接进入正题

一、拷贝动态库

怎么把FFmpeg的几个so搞进去

1、新建jniLibs目录,把上一篇编译成功的FFmpeg 的8个so放到armeabi目录,把头文件include目录拷贝到jniLibs目录下

然后需要修改CMakeLists.txt,为了看起来清晰点,先把CMakeLists.txt移动到app目录下,(默认是在app/src/main/cpp),然后修改一下app下的build.gradle

android {
    compileSdkVersion 28
    buildToolsVersion = '28.0.3'
    defaultConfig {
        applicationId "com.lanshifu.ffmpegdemo"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        externalNativeBuild {
            cmake {
                cppFlags "-fexceptions" //使用的c++标准
                abiFilters "armeabi"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt" //CMakeLists 配置文件路径
        }
    }

}

就是修改 externalNativeBuild ,有注释的两个地方

进入下一步

二、修改MakeLists.txt

直接加注释贴上来吧

# cmake参考 https://www.jianshu.com/p/528eeb266f83

cmake_minimum_required(VERSION 3.4.1)


# 需要引入我们头文件,以这个配置的目录为基准
include_directories(src/main/jniLibs/include)

# 添加共享库(so)搜索路径
LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi)

# 指定源文件目录,把当前工程目录下的 src/main/cpp 目录的下的所有 .cpp 和 .c 文件赋值给 SRC_LIST
AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/main/cpp SRC_LIST)


#这个一般不用怎么改,最终把cpp编译成共享库 native-lib
add_library(
        # 编译生成的库的名称叫native-lib,对应System.loadLibrary("native-lib");
        native-lib
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        ${SRC_LIST}
)


#为native-lib 链接额外的库,例如ffmpeg、ndk库
target_link_libraries(
        native-lib
        # 编解码(最重要的库)
        avcodec-57
        # 设备信息
        avdevice-57
        # 滤镜特效处理库
        avfilter-6
        # 封装格式处理库
        avformat-57
        # 工具库(大部分库都需要这个库的支持)
        avutil-55
        # 后期处理
        postproc-54
        # 音频采样数据格式转换库
        swresample-2
        # 视频像素数据格式转换
        swscale-4
        # 链接 android ndk 自带的一些库
        android
        # Links the target library to the log library
        # included in the NDK.
        log)

当然,这些是最简单的,实际项目应该会很复杂。

然后直接run,如果没有报错,说明FFmpeg动态库已经集成到Android项目里了

验证一下:
使用 APK Analyzer查看。
选择 Build > Analyze APK > 选择apk打开

发现里so已经打包进去了

然后就可以通过JNI来调用FFmpeg了

三、来个简单的例子

一个mp3文件,使用FFmpeg来获取音频信息

1、在sd卡中放一个 input1.mp3
2、Activity中处理,读取sd卡动态权限就不贴了

 File mMusicFile = new File(Environment.getExternalStorageDirectory(), "input1.mp3");

//onCreate调用
printAudioInfo(mMusicFile.getAbsolutePath());


public native void printAudioInfo(String url);

// Used to load the 'native-lib' library on application startup.
static {
     System.loadLibrary("native-lib");
}

printAudioInfo 是 native方法,按option+enter,自动生成对应c++方法


extern "C"
JNIEXPORT void JNICALL
Java_com_lanshifu_ffmpegdemo_MainActivity_printAudioInfo(JNIEnv *env,jobject instance,jstring url_) {

}

用cmake 的好处之一就是c++方法可以自动生成,不用什么javah获取头文件,然后复制啥的...

上代码吧

#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

extern "C"
JNIEXPORT void JNICALL
Java_com_lanshifu_ffmpegdemo_MainActivity_printAudioInfo(JNIEnv *env, jobject instance,
                                                         jstring url_) {
    const char *url = env->GetStringUTFChars(url_, 0);

    //1、初始化所有组件,只有调用了该函数,才能使用复用器和编解码器(源码)
    av_register_all();
    AVFormatContext *avFormatContext = NULL;
    int audio_stream_idx;
    AVStream *audio_stream;
    //读文件头,对 mp4 文件而言,它会解析所有的 box。但它知识把读到的结果保存在对应的数据结构下。
    // 这个时候,AVStream 中的很多字段都是空白的。
    int open_res = avformat_open_input(&avFormatContext, url, NULL, NULL);
    if (open_res != 0) {
        LOGE("lxb->Can't open file: %s", av_err2str(open_res));
        return ;
    }
    //获取文件信息
    //读取一部分视音频数据并且获得一些相关的信息,会检测一些重要字段,如果是空白的,就设法填充它们。
    // 因为我们解析文件头的时候,已经掌握了大量的信息,avformat_find_stream_info 就是通过这些信息来填充自己的成员,
    // 当重要的成员都填充完毕后,该函数就返回了。这中情况下,该函数效率很高。但对于某些文件,单纯的从文件头中获取信息是不够的,
    // 比如 video 的 pix_fmt 是需要调用 h264_decode_frame 才可以获取其pix_fmt的。
    int find_stream_info_res = avformat_find_stream_info(avFormatContext, NULL);
    if (find_stream_info_res < 0) {
        LOGE("lxb->Find stream info error: %s", av_err2str(find_stream_info_res));
        goto __avformat_close;
    }

    // 获取采样率和通道
    //av_find_best_stream:获取音视频及字幕的 stream_index , 以前没有这个函数时,我们一般都是写的 for 循环。
    audio_stream_idx = av_find_best_stream(avFormatContext, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audio_stream_idx < 0) {
        LOGE("lxb->Find audio stream info error: %s", av_err2str(find_stream_info_res));
        goto __avformat_close;
    }
    audio_stream = avFormatContext->streams[audio_stream_idx];
    LOGE("采样率:%d", audio_stream->codecpar->sample_rate);
    LOGE("通道数: %d", audio_stream->codecpar->channels);
    LOGE("format: %d", audio_stream->codecpar->format);
    LOGE("extradata_size: %d", audio_stream->codecpar->extradata_size);

    __avformat_close:
    avformat_close_input(&avFormatContext);
}

解码流程参考


解码流程

FFmpeg API说明:
1:av_register_all() 初始化所有组件,只有调用了该函数,才能使用复用器和编解码器
2:avformat_open_input:读文件头,对 mp4 文件而言,它会解析所有的 box。但它只是把读到的结果保存在对应的数据结构下
3:avformat_find_stream_info:读取一部分视音频数据并且获得一些相关的信息。因为我们解析文件头的时候,已经掌握了大量的信息,avformat_find_stream_info 就是通过这些信息来填充自己的成员
4:av_find_best_stream:获取音视频及字幕的 stream_index , 以前没有这个函数时,我们一般都是写的 for 循环
5:AVStream中保存了音视频文件的一堆信息,在内部AVCodecParameters 结构体中

然后run一下,可以看到打印

06-19 21:36:57.732 29875-29875/? E/JNI_TAG: 采样率:44100
06-19 21:36:57.732 29875-29875/? E/JNI_TAG: 通道数: 2
06-19 21:36:57.732 29875-29875/? E/JNI_TAG: format: 6
06-19 21:36:57.732 29875-29875/? E/JNI_TAG: extradata_size: 0

也就说明已经成功在项目中使用FFmpeg了。

参考:https://www.jianshu.com/p/d8300535bbf0

上一篇下一篇

猜你喜欢

热点阅读