• Android开发学习之路--Android Studio cmake编译ffmpeg


      最新的android studio2.2引入了cmake可以很好地实现ndk的编写。这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路–NDK、JNI之初体验

    1.ffmpeg编译

      进入正题,既然是ffmpeg的移植编译,那么就先下载ffmpeg,https://ffmpeg.org/download.html#releases。这里下载的是3.0.3版本。
      新建ffmpeg文件夹,然后新建脚本用来编译ffmpeg,命名为build.sh,脚本如下:

    #!/bin/bash
    cd ffmpeg
    export TMPDIR=/Users/jared/Desktop/jared/study/external/ffmpeg/tempdir
    NDK=/Users/jared/Desktop/jared/software/sdk/ndk-bundle
    SYSROOT=$NDK/platforms/android-16/arch-arm/
    TOOLCHAIN=/Users/jared/Desktop/jared/software/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
    CPU=arm
    PREFIX=/Users/jared/Desktop/jared/study/external/ffmpeg/output
    ADDI_CFLAGS="-marm"
    function build_one
    {
        ./configure 
            --prefix=$PREFIX 
            --enable-shared 
            --disable-static 
            --disable-doc 
            --disable-ffmpeg 
            --disable-ffplay 
            --disable-ffprobe 
            --disable-ffserver 
            --disable-doc 
            --disable-symver 
            --enable-small 
            --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_FLAG
        make clean
        make
        make install
    }
    build_one
    cd ../
    

      注意的是这里的路径是对应的ndk的相关路径。

      然后chmod 777 build.sh,然后执行source build.sh。开始编译ffmpeg。小憩片刻之后,编译完成,在output目录下就会生成对应的so文件:

    ➜  ffmpeg cd output
    ➜  output ls
    include lib
    ➜  output cd lib
    ➜  lib ls
    libavcodec-57.so   libavfilter-6.so   libavutil-55.so    libswscale-4.so
    libavcodec.so      libavfilter.so     libavutil.so       libswscale.so
    libavdevice-57.so  libavformat-57.so  libswresample-2.so pkgconfig
    libavdevice.so     libavformat.so     libswresample.so
    ➜  lib

      编译完成,这里只是用了arm的平台,如果需要mips,或者x86需要修改build.sh脚本的arch和路径这里不赘述了。
      既然库文件都已经编译出来了那就可以android开搞了。

    2.android环境搭建

      首先需要最新的android studio2.2,并且安装好cmake和ndk。然后新建工程,可以开始了。
      新建工程取名为helloffmpeg,然后选中include c++ support,然后下一步直到新建完成为止。如下图:
    这里写图片描述
      然后比一般的工程多了些东西,首先是build.gradle了,

      CMake的cpp的flags

    externalNativeBuild {
                cmake {
                    cppFlags ""
                }
            }

      CMake的路径

    externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }

      然后看CMakeLists.txt文件,这里主要讲解下配置。先看下已经配置好的库文件:

    # Sets the minimum version of CMake required to build the native
    # library. You should either keep the default value or only pass a
    # value of 3.4.0 or lower.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    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(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
    
    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( 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( swscale-4
                 SHARED
                 IMPORTED)
    set_target_properties( swscale-4
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libswscale-4.so )
    add_library( avdevice-57
                 SHARED
                 IMPORTED)
    set_target_properties( avdevice-57
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavdevice-57.so )
    add_library( avformat-57
                 SHARED
                 IMPORTED)
    set_target_properties( avformat-57
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavformat-57.so )
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
    
    add_library( native-lib
                 SHARED
                 src/main/cpp/native-lib.cpp )
    
    include_directories(libs/include)
    
    #target_include_directories(native-lib PRIVATE libs/include)
    
    target_link_libraries( native-lib swresample-2 avcodec-57 avfilter-6 swscale-4 avdevice-57 avformat-57
                           ${log-lib} )
    

      cmake_minimum_required(VERSION 3.4.1):表示cmake的最低版本是3.4.1。
      add_library():添加库,分为两种,一种是需要编译为库的代码,一种是已经编译好的库文件。
      比如上面编译好的ffmpeg的库,

    add_library( avutil-55
                 SHARED
                 IMPORTED )

      这里avutil-55表示库的名称,SHARED表示是共享库,一般.so文件,还有STATIC,一般.a文件。IMPORTED表示引用的不是生成的。
      接着是这里用到的需要生产的.so文件

    add_library( native-lib
                 SHARED
                 src/main/cpp/native-lib.cpp )

      最后的参数是源码的路径,如果有更多的源码就接下去写上。

      set_target_properties:对于已经编译好的so文件需要引入,所以需要设置。

    set_target_properties( avutil-55
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavutil-55.so )

      这里avutil-55是名字,然后是PROPERTIES IMPORTED_LOCATION加上库的路径。
      include_directories:一般外面引入的库文件需要头文件,所以可以通过这个来引入:

    include_directories(libs/include)

      target_link_libraries:链接,把需要的so文件链接起来,这里native-lib需要链接ffmpeg的库文件。

      最后看下和java,res同级的cpp目录下有native-lib.cpp文件,系统自动生成的。
      首先是native-lib.cpp内容:

    #include <jni.h>
    #include <string>
    
    extern "C"
    jstring
    Java_com_jared_helloffmpeg_MainActivity_stringFromJNI(
            JNIEnv *env, jobject) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    

      这里有一个函数Java_com_jared_helloffmpeg_MainActivity_stringFromJNI,命名方式也可以知道,Java开头,com_jared_helloffmpeg为包名,MainActivity是调用的java类,最后stringFromJNI是方法名。然后返回了hello from c++字符。

      看下java的代码:

     // Example of a call to a native method
     TextView tv = (TextView) findViewById(R.id.sample_text);
     tv.setText(avformatinfo());
    /**
      * A native method that is implemented by the 'native-lib' native library,
      * which is packaged with this application.
     */
      public native String stringFromJNI();
      // Used to load the 'native-lib' library on application startup.
        static {
            loadLibrary("native-lib");
        }

      也很好理解这里的stringFromJNI的native方法,具体需要看jni的基本概念了,这里就不多赘述了。好了基本上最简单的通了,那么接下去就是ffmpeg的使用了。

    3.修改使用ffmpeg

      接着来修改界面显示ffmpeg的一些基本信息。如下:

    <?xml version="1.0" encoding="utf-8"?>
    <layout>
    
        <data class="MainBinding"></data>
    
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            tools:context="com.jared.helloffmpeg.MainActivity">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
    
                <Button
                    android:id="@+id/btn_protocol"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:text="Protocol"
                    android:textAllCaps="false" />
    
                <Button
                    android:id="@+id/btn_format"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:text="Format"
                    android:textAllCaps="false" />
    
                <Button
                    android:id="@+id/btn_codec"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:text="Codec"
                    android:textAllCaps="false" />
    
                <Button
                    android:id="@+id/btn_filter"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:text="Filter"
                    android:textAllCaps="false" />
            </LinearLayout>
    
            <ScrollView
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
                <TextView
                    android:id="@+id/tv_info"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Hello World!" />
            </ScrollView>
    
        </LinearLayout>
    </layout>

      主要是实现了四个button,分别是protocol,format,codec和filter。如下图所示:

      
      修改jni部分的代码,也就是native-lib.cpp的代码如下:

    #include <jni.h>
    #include <string>
    
    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    
    jstring
    Java_com_jared_helloffmpeg_MainActivity_stringFromJNI(
            JNIEnv *env, jobject) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
    jstring
    Java_com_jared_helloffmpeg_MainActivity_urlprotocolinfo(
            JNIEnv *env, jobject) {
        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
    ", 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
    ", info, avio_enum_protocols((void **) p_temp, 1));
        }
        return env->NewStringUTF(info);
    }
    
    jstring
    Java_com_jared_helloffmpeg_MainActivity_avformatinfo(
            JNIEnv *env, jobject) {
        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
    ", info, if_temp->name);
            if_temp = if_temp->next;
        }
        while (of_temp != NULL) {
            sprintf(info, "%sOutput: %s
    ", info, of_temp->name);
            of_temp = of_temp->next;
        }
        return env->NewStringUTF(info);
    }
    
    jstring
    Java_com_jared_helloffmpeg_MainActivity_avcodecinfo(
            JNIEnv *env, jobject) {
        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]
    ", info, c_temp->name);
            c_temp = c_temp->next;
        }
    
        return env->NewStringUTF(info);
    }
    
    jstring
    Java_com_jared_helloffmpeg_MainActivity_avfilterinfo(
            JNIEnv *env, jobject) {
        char info[40000] = {0};
        avfilter_register_all();
    
        AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);
        while(f_temp != NULL) {
            sprintf(info, "%s%s
    ", info, f_temp->name);
            f_temp = f_temp->next;
        }
        return env->NewStringUTF(info);
    }
    }

      这里要特别注意ffmpeg的头文件已经调用的代码要extern “C”里面,要不然会报错误的。

      最后就是java调用代码了。如下所示:

    package com.jared.helloffmpeg;
    
    import android.databinding.DataBindingUtil;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    
    import com.jared.helloffmpeg.databinding.MainBinding;
    
    import static java.lang.System.loadLibrary;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private MainBinding binding;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //setContentView(R.layout.activity_main);
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.btnProtocol.setOnClickListener(this);
            binding.btnCodec.setOnClickListener(this);
            binding.btnFilter.setOnClickListener(this);
            binding.btnFormat.setOnClickListener(this);
            // Example of a call to a native method
            //TextView tv = (TextView) findViewById(R.id.sample_text);
            //tv.setText(avformatinfo());
        }
    
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_protocol:
                    binding.tvInfo.setText(urlprotocolinfo());
                    break;
                case R.id.btn_format:
                    binding.tvInfo.setText(avformatinfo());
                    break;
                case R.id.btn_codec:
                    binding.tvInfo.setText(avcodecinfo());
                    break;
                case R.id.btn_filter:
                    binding.tvInfo.setText(avfilterinfo());
                    break;
                default:
                    break;
            }
        }
    
        /**
         * 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 urlprotocolinfo();
        public native String avformatinfo();
        public native String avcodecinfo();
        public native String avfilterinfo();
    
        // Used to load the 'native-lib' library on application startup.
        static {
            loadLibrary("native-lib");
        }
    }
    

      都准备好了,那么就继续运行看看结果了:

      基本上Android Studio下的cmake编译ffmpeg就到此结束了,那么接下去就可以在android手机上使用ffmpeg来做一些音视频相关的东西了。

    github源码下载地址

  • 相关阅读:
    krakend 扩展开发概述
    varnish/api-gateway-benchmarks api gateway 性能压测工具
    krakend 加速配置工具
    krakend 支持发布订阅后端
    krakend 消费amqp mq 消息
    krakend cache 后端请求
    krakend 请求流量镜像的处理
    krakend 请求&&相应的检查
    krakend 的静态proxy
    krakend 的串行proxy
  • 原文地址:https://www.cnblogs.com/wuyida/p/6299935.html
Copyright © 2020-2023  润新知