NDK开发前奏 - 视频转码压缩

作者: 你也不知道 | 来源:发表于2020-07-03 19:28 被阅读0次

    这几次的分享都是基于 NDK 部分的实战,并没有涉及到 C 和 C++ 的基础知识,春节后会从 C 基础的 HelloWorld 开始,还望我们能耐心等待。视频转码压缩开发中比较常见,记得上次有个哥们问我,自己基于 FFmpeg 去压缩,致命的是压缩时间太长了。这里给大家分享几种解决方案:
    1. 基于服务器压缩
    2. 基于开源库 FFmpeg 转码压缩 (NDK)
    3. 基于开源库 iosparser 压缩

    2. 基于开源库 FFmpeg 转码压缩


    下载 FFmpeg 版本自己选择,写个脚本编译好 so 库。这个步骤是比较麻烦的,文章这里先略过。如果感兴趣可以自己查查资料试试,实在不行可以先用我编译好的即可。(后面我会单独写编译步骤)

    编译好会有一大堆的 .so 和 .h 文件,选择性的 copy 到我们自己的工程目录下进行配置,这里用的是 AS2.3 + CMake 的方式。之前在 Android图片压缩加密上传 - JPEG压缩算法解析 中我们用的是 Android.mk 和 Application.mk 的方式。两种方式随便选择哪种都行,贴一下 CMakeLists.txt 中的配置。

    cmake_minimum_required(VERSION 3.4.1)
    
    #FFMpeg配置
    #FFmpeg配置目录
    set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../src/main/jniLibs)
    
    # 编解码(最重要的库)
    add_library(
                avcodec
                SHARED
                IMPORTED)
    set_target_properties(
                avcodec
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libavcodec-56.so)
    
    # 设备信息
    add_library(
                avdevice
                SHARED
                IMPORTED)
    set_target_properties(
                avdevice
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libavdevice-56.so)
    
    # 滤镜特效处理库
    add_library(
                avfilter
                SHARED
                IMPORTED)
    set_target_properties(
                avfilter
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libavfilter-5.so)
    
    # 封装格式处理库
    add_library(
                avformat
                SHARED
                IMPORTED)
    set_target_properties(
                avformat
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libavformat-56.so)
    
    # 工具库(大部分库都需要这个库的支持)
    add_library(
                avutil
                SHARED
                IMPORTED)
    set_target_properties(
                avutil
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libavutil-54.so)
    
    # 后期处理
    add_library(
                postproc
                SHARED
                IMPORTED)
    set_target_properties(
                postproc
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libpostproc-53.so)
    
    # 音频采样数据格式转换库
    add_library(
                swresample
                SHARED
                IMPORTED)
    set_target_properties(
                swresample
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libswresample-1.so)
    
    # 视频像素数据格式转换
    add_library(
                swscale
                SHARED
                IMPORTED)
    set_target_properties(
                swscale
                PROPERTIES IMPORTED_LOCATION
                ../../../../src/main/jniLibs/armeabi/libswscale-3.so)
    
    #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
    #判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
    if(CMAKE_COMPILER_IS_GNUCXX)
                 
        set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
        message(STATUS "optional:-std=c++11")
    endif(CMAKE_COMPILER_IS_GNUCXX)
    
    
    #需要引入我们头文件
    include_directories(src/main/jniLibs/include)
    include_directories(src/main/jniLibs/other)
    
    
    # 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 it for you.
    # Gradle automatically packages shared libraries with your APK.
    
    add_library( # Sets the name of the library.
                 ffmpeg-lib
    
                 # Sets the library as a shared library.
                 SHARED
                 
                 # Provides a relative path to your source file(s).
                 src/main/cpp/native-lib.cpp )
    
    # Searches for a specified prebuilt library and stores the path as a
    # variable. Because system libraries are included 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 the
    # build script, prebuilt third-party libraries, or system libraries.
    
    target_link_libraries( # Specifies the target library.
                           # 配置链接动态库
                           ffmpeg-lib avcodec avdevice avfilter avformat avutil postproc swresample swscale
    
                           # Links the target library to the log library
                           # included in the NDK.
                           ${log-lib} )
    

    整合好之后,最好还是先运行下,如果能正常运行再开始写压缩代码。FFmpeg下载后我们可以直接用命令行去压缩视频,我们自己写代码也是基于这个,如果不是特别了解可以先看看文档:

    #include <jni.h>
    #include <android/log.h>
    #include <malloc.h>
    #include <string.h>
    
    // 定义 log 日志打印
    #define NDK_TAG "NDK_TAG"
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, NDK_TAG, __VA_ARGS__)
    
    // 回调Java层刷新进度
    static jobject callback_jobj = NULL; //java object
    static JNIEnv *mEnv;
    void compress_callback(int total, int current) {
        if (callback_jobj != NULL && mEnv != NULL) {
            // 回调 java 层代码
            jclass callback_class = mEnv->GetObjectClass(callback_jobj);
            jmethodID callbak_mid = mEnv->GetMethodID(callback_class, "nativeCompressCallback",
                                                      "(II)V");
            mEnv->CallVoidMethod(callback_jobj, callbak_mid, total, current);
        }
    }
    
    extern "C" {
    // 函数声明
    JNIEXPORT void JNICALL
    Java_com_darren_ndk_day02_VideoCompress_ffmpegCompress(JNIEnv *env, jobject instance,
                                                           jobjectArray compressParams);
    // 声明压缩函数
    // argc 是参数的个数
    // argv 二维数组指针
    int ffmpegmain(int argc, char **argv,void(*callback)(int, int));
    }
    
    // 压缩方法实现
    JNIEXPORT void JNICALL
    Java_com_darren_ndk_day02_VideoCompress_ffmpegCompress(JNIEnv *env, jobject instance,
                                                           jobjectArray compressParams) {
    
        // 1. 获取数组的长度
        int argc = env->GetArrayLength(compressParams);
        LOGE("数组长度:%d", argc);
        // 2. Java String[] -> char**
        // 2.1 开辟内存空间
        char **argv = (char **) malloc(sizeof(char *) * argc);
        // 2.2 填充内容
        for (int i = 0; i < argc; ++i) {
            jstring j_param = (jstring) env->GetObjectArrayElement(compressParams, i);
            const char *c_param = env->GetStringUTFChars(j_param, NULL);
            argv[i] = (char *) c_param;
            LOGE("数组元素:%s", c_param);
        }
        // 3. 调用方法压缩
        // 开始转码(实现就是命令行一样) 并且处理回调,一种死办法
        mEnv = env;
        callback_jobj = env->NewGlobalRef(instance);
        ffmpegmain(argc, argv,compress_callback);
    
        // 4. 释放资源
        for (int i = 0; i < argc; ++i) {
            free(argv[i]);
        }
        free(argv);
        // free(mEnv);
        env->DeleteGlobalRef(callback_jobj);
    }
    

    基于 FFmpeg 的压缩效果是非常好的,但由于采用的是软编码很致命的是压缩时间比较长,肯定有人问怎么才能优化,目前自己能力有限无能为力了,唯一能做的就是把压缩进度回调出去。

    更多的 FFmpeg 音视频处理学习可以参考雷霄骅的博客http://blog.csdn.net/leixiaohua1020

    3. 基于开源库 iosparser 压缩

    iosparser 主要是基于 android 下的 android.media ,使用起来方便且简单,这里也有个开源库大家可以参考一下:https://github.com/RudreshJR/VideoCompression。速度很不错,就是质量和兼容性有所欠缺。

    视频讲解地址:https://pan.baidu.com/s/1eTYofQE

    相关文章

      网友评论

        本文标题:NDK开发前奏 - 视频转码压缩

        本文链接:https://www.haomeiwen.com/subject/gjreqktx.html