Android Studio+NDK+Cmake 移植FFmpeg命令行工具

一、编译

参考大神的帖子,亲测一次编译成功:https://blog.csdn.net/bobcat_kay/article/details/80889398

鉴于以前查文档的经验,这里附上编写例子的时间:2018年7月22日

我用的是ubantu,注意事项:

1、路径这里,home/ndk是不对的,真实路径是home/电脑名/ndk,具体以cd ls命令的为基准

2、文件必须要在ubantu下载,我第一次是在win下下载,然后通过VMware传过去的,结果出问题了,后来百度了一下发现了问题,就果断直接ubantu直接下载了一个,然后就可以了

3、build.sh和confinger这两个文件编辑的时候,一定要确保文本的格式不是doc而是unit(单词应该是错了的)格式

二、移植

大神的帖子用的是mk的,而我这里比较喜欢用cmake。

流程基本一致,但有些小细节,可能大神功力比较深,没遇到啥问题,我是在这个步骤卡了大半个晚上。

附上项目地址:https://gitee.com/imxiaoyu_admin/CmdForFFmpeg4.0.2.git

1、创建新项目

(1)环境

        Android Studio 3.2 

        NDK 17.1.4828580

        SDK 28

(2)新建项目

            看截图吧,主要就是勾上这个东西,其他都默认就好。

2、复制文件

(1)复制so文件

都是按照默认目录了,我在cpp目录下新建了个文件夹ffmpeg/armeabi-v7a,将so包都复制进来了

(2)复制fftools的代码

ffmpeg文件夹下新建文件夹include,然后将文件夹fftools里面的部分文件复制到include文件夹中,并且在源代码最外层将config.h复制进来

(3)复制源码

本着宁杀错不放过的原则,我将源码根目录下的所有文件夹(除了fftools)都复制到了include里面。(参考大神的做法,发现好多文件都少了头文件,然后一直报错编译不过)

3、创建jni的调用类

(1)Java类

        创建FFmpegCmd类

(2)创建c类

        看上图,直接alt+enter就可以创建相关的方法了,Android Studio非常智能

        然后将c类重命名为ffmpeg_cmd_utils.c

(3)特别说明

        之所以是c而不是cpp,我也很无奈,如果是cpp的话,编译的时候爆了很多警告,并且是不给编译,目前也没有找到到底是什么问题,毕竟我水平也是有限。

4、配置

(1)build.gradle

        主要的几个地方都标出来了

(2)CMakeLists.txt文件

        主要就是将各个so包加载进来,还有就是ffmpeg_cmd_utils.c这个类,以及fftools文件夹里面的ffmpeg.c等类(并不需要用到全部,如果全部加载进来反而会报一些莫名其妙的错误,具体要哪些就看cmake文件吧)

        如果我没记错步骤的话,到这一步基本上可以编译通过了,下一步开始一直cmd工具

cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.
             ffmpegcmd

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
              src/main/cpp/ffmpeg/include/cmdutils.c
              src/main/cpp/ffmpeg/include/ffmpeg.c
              src/main/cpp/ffmpeg/include/ffmpeg_filter.c
              src/main/cpp/ffmpeg/include/ffmpeg_hw.c
              src/main/cpp/ffmpeg/include/ffmpeg_opt.c
             src/main/cpp/ffmpeg_cmd_utils.c
              )

#添加libavcodec-57.so
add_library( avcodec
             SHARED
             IMPORTED)
set_target_properties( avcodec
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavcodec.so)

#添加libavdevice-57.so
add_library( avdevice
             SHARED
             IMPORTED)
set_target_properties( avdevice
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavdevice.so)

add_library( avfilter
             SHARED
             IMPORTED)
set_target_properties( avfilter
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavfilter.so)

add_library( avformat
             SHARED
             IMPORTED)
set_target_properties( avformat
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavformat.so)

add_library( avutil
             SHARED
             IMPORTED)
set_target_properties( avutil
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavutil.so)

add_library( swresample
             SHARED
             IMPORTED)
set_target_properties( swresample
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libswresample.so)

add_library( swscale
             SHARED
             IMPORTED)
set_target_properties( swscale
                       PROPERTIES IMPORTED_LOCATION
                       ${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libswscale.so)



include_directories(src/main/cpp/ffmpeg/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 )

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

target_link_libraries( # Specifies the target library.

                       ffmpegcmd
                       avcodec
                       avdevice
                       avfilter
                       avformat
                       avutil
                       swresample
                       swscale

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

5、修改代码

(1)cmdutils.c

        注释掉exit_program方法的内容:

        

(2)cmdutils.h

        void show_help_children(const AVClass *class, int flags);

        改成:

        void show_help_children(const AVClass *clazz, int flags);

(3)ffmpeg.c

        入口就是main函数,改个名字,这里跟着大神用ffmpeg_exec

        

        注释掉ffmpeg_exec函数末尾的一句代码:

        

        找到ffmpeg_cleanup函数,末尾加上:

    nb_filtergraphs = 0;
    nb_output_files = 0;
    nb_output_streams = 0;
    nb_input_files = 0;
    nb_input_streams = 0;

        加上之后如图:

        

        并且将nb_filtergraphs声明那里赋值为0,保险起见。。。

        

        在ffmpeg_exce函数的末尾加上,不然的话,第二次运行命令行的时候,你的手机就会爆炸。

    nb_filtergraphs = 0;
    progress_avio = NULL;

    input_streams = NULL;
    nb_input_streams = 0;
    input_files = NULL;
    nb_input_files = 0;

    output_streams = NULL;
    nb_output_streams = 0;
    output_files = NULL;
    nb_output_files = 0;

        下面这一步可以按需减免,主要是用于打印命令行运行之后的输出信息:

        添加声明:


#include "android/log.h"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , "ffmpeg.c", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , "ffmpeg.c", __VA_ARGS__)

        给log_callback_null函数加上内容:

static void log_callback_null(void *ptr, int level, const char *fmt, va_list vl)
{
    static int print_prefix = 1;
    static int count;
    static char prev[1024];
    char line[1024];
    static int is_atty;

    av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);

    strcpy(prev, line);
    //sanitize((uint8_t *)line);

    if (level <= AV_LOG_WARNING) {
        LOGE("输出:%s", line);
    } else {
        LOGE("输出:%s", line);
    }
}

(4)ffmpeg.h

        在末尾加上ffmpeg_exec函数的函数声明

        

(5)ffmpeg_cmd_utils.c

        直接上调用代码

#include <android/log.h>
#include <ffmpeg.h>
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"8858",FORMAT,##__VA_ARGS__);

JNIEXPORT jint JNICALL
Java_com_imxiaoyu_test_FFmpegPlayer_playMyMedia(JNIEnv *env, jobject instance,jint cmdLen,
                                                jobjectArray cmd) {


    int argc = (*env)->GetArrayLength(env, cmd);
    char *argv[argc];
    int i;
    for (i = 0; i < argc; i++) {
        jstring js = (jstring) (*env)->GetObjectArrayElement(env, cmd, i);
        argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
        LOGE("命令行argCmd=%s",argv[i]);
    }
    ffmpeg_exec(argc, argv);
    return 1;
}

        没记错的话,到这一步就已经ok了

6、调用

(1)权限

        读写手机内存的权限,这里必须吐槽一下,我在手机是小米6,本着偷懒的原则,在设置的应用的详情页面点击权限管理给app权限了之后,居然是不生效的,命令行一运行就崩溃,必须是代码申请权限才有用,我还以为是编译或者是代码的问题,起码在这一步浪费了三四个小时。不确定是MIUI的特有还是Android 8.0的锅,尽量把工作做足,不要偷懒吧。

        后来跑起来了的时候,大家应该都了解我当时的心情,有喜有泪,哭笑不得。

        申请权限的代码我就不贴上来了。

        

(2)调用

 final String cmd[]=str.split(" ");//
        new Thread(){
            @Override
            public void run() {
                PathUtils pathUtils=new PathUtils();
                String str="ffmpeg -i "+pathUtils.getMusicCacheEditorPath()+"/12.mp3 -y "+pathUtils.getMusicCacheEditorPath()+"/haha1.wav";//具体路径,自由发挥吧
                long startTime = System.currentTimeMillis();
                try {
                    fFmpegPlayer.playMyMedia(cmd.length,cmd);
                }catch (Exception e){
                    Log.e("处错误了:",e.toString());
                }
                Log.e("FFmpegTest", "run: 耗时:"+(System.currentTimeMillis()-startTime));
            }
        }.start();

(3)结果

(4)注意事项

        命令行的所有文件的名字,不要带特殊字符,如空格、换行符、emoji等。

        总觉得我还漏了什么没说的,不管了,遇到问题了,记住,猥琐发育,别浪,问题总会解决的。

废话

        说真的,我从ffmpeg 3.0的时候就对这件事情很感兴趣了,但是一直卡在编译so包这一步了,对linux系统不熟悉,一直不得要领,然后这一次是照着大神的脚步,一两次就直接编译出来so包了,我就知道这次是有戏了。

        ffmpeg的命令行工具其实已经可以做不少比较好玩的事情了,预祝大家玩的开心。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值