1. 程式人生 > >Android開發學習之路--Android Studio cmake編譯ffmpeg

Android開發學習之路--Android Studio cmake編譯ffmpeg

  最新的android studio2.2引入了cmake可以很好地實現ndk的編寫。這裡使用最新的方式,對於以前的android下的ndk編譯什麼的可以參考之前的文章:Android開發學習之路–NDK、JNI之初體驗

1.ffmpeg編譯


  新建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\n", 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\n", 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\n", info, if_temp->name);
        if_temp = if_temp->next;
    }
    while (of_temp != NULL) {
        sprintf(info, "%sOutput: %s\n", 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]\n", 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\n", 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來做一些音視訊相關的東西了。

相關推薦

Android開發學習--Android Studio cmake編譯ffmpeg

  最新的android studio2.2引入了cmake可以很好地實現ndk的編寫。這裡使用最新的方式,對於以前的android下的ndk編譯什麼的可以參考之前的文章:Android開發學習之路–NDK、JNI之初體驗。 1.ffmpeg編譯  

Android開發學習--Android Studio外掛開發

前言 因為使用了mvp和dagger,所以每次新的一個功能頁面都需要重新寫一堆東西,比如Activity, Fragment, Presenter,dagger等,而這些程式碼基本上都是大同小異,完全可以寫一個模板,然後生成,略微修改便可以完成我們需要的功能。

Android開發學習--異步消息Handler,Message,Looper和AsyncTask初體驗

被調用 project 輸入 gettext npos article app sso 音樂播放 在簡易音樂播放器中。用了Handler。也沒有過多地去研究學習,這裏再學習下android下的異步消息處理機制。這裏用了Handler主要是在線程中不能更新UI

Android開發學習--圖表實現(achartengine/MPAndroidChart)初體驗

bundle 喜歡 嵌入式linux Y軸 tid ren sca ref java代碼 ??已經有一段時間沒有更新博客了,在上周離開工作了4年的公司,從此不再安安穩穩地工作了。很多其它的是接受挑戰和實現自身價值的提高。離開了嵌入式linux,從此擁抱移

Android開發學習-EventBus使用

轉載自: https://www.cnblogs.com/Fndroid/p/5910992.html EventBus是一個通過釋出、訂閱事件實現元件間訊息傳遞的工具。 它存在的目的,就是為了優化元件之間傳遞訊息的過程。傳統元件之間傳遞訊息的方法有使用廣播,回撥等,而這些方法使用都比較

Android開發學習--Drawable mutations

  時間過得很快,明天終於可以拿到房子了,交完這次房租,也可以成為房東了,看看部落格也好久沒有更新了,最近一直在整機器人,也沒有太多時間整理部落格。   今天下午和同事一起遇到了一個問題,就是明明沒有

Android開發學習--RxAndroid簡單原理

  學習了RxAndroid,其實也就是RxJava了,但是還是不是非常清楚到底RxAndroid有什麼用呢?為什麼要使用RxAndroid呢?這篇文章講得不錯,RxJava的原理。但是這裡還是把整個過

Android開發學習--Activity初體驗

    環境也搭建好了,android系統也基本瞭解了,那麼接下來就可以開始學習android開發了,相信這麼學下去肯定可以把android開發學習好的,再加上時而再溫故下linux下的知識,看看an

Android開發學習--在Android應用中愉快地寫C/C++程式碼

1 前言 一直想在android層面寫c程序,然後java可以與c程序互動,以前在android原始碼中想玩就可以直接在init.rc中加上交叉編譯好的c程序就可以了,而在ide中,也就是ndk編譯後各種許可權問題就有點不得而知了。花了幾天時間研究實踐,也終於

Android開發學習--資料持久化初體驗

    上班第一天,雖然工作上處於醬油模式,但是學習上依舊不能拉下,接著學習android開發吧,這裡學習資料持久化的 知識。     其實資料持久化就是資料可以儲存起來,一般我們儲存資料都是以檔案,或者資料庫的形式儲存的,android程式也有 檔案和資料庫的儲存,此外還

Android開發學習--非同步訊息Handler,Message,Looper和AsyncTask初體驗

    在簡易音樂播放器中,用了Handler,也沒有過多地去研究學習,這裡再學習下android下的非同步訊息處理機制。這裡用了Handler主要是線上程中不能更新UI,而需要通過Handler才可以。關於非同步訊息處理有幾個概念。     1、Message:訊息,執行

Android開發學習--NDK、JNI初體驗

    好久沒有更新部落格了,最近一直在看一個仿微信專案,然後看原始碼並自己實現下,相信經過這個專案可以讓自己瞭解一個專案中的程式碼以及種種需要注意的事項。不知不覺中部落格已經快要40w訪問量,而且排名也即將突破3000了,在此感謝朋友們的支援和認可。今天趁著有點時間就來完

Android開發學習--UI基本佈局

    上一篇文章中主要介紹了ui的控制元件,這裡就學習下佈局吧。android的基本佈局在layout下主要如圖:     從上圖可以看出有FrameLayout(單幀佈局),LinearLayo

Android 音視訊開發學習

  一直欠大家一篇音視訊入門之路的文章,這篇文章是我見過寫的最詳細的一篇了,今天算還了哈。作者從入門、進階、探究分別編寫了一系列文章。   Android 音視訊開發這塊目前的確沒有比較系統的教程或者書籍,網上的部落格文章也都是比較零散的。只能通過一點點的學習和積累把這塊的知識串聯積累起

Android破解學習(十二)—— GP錄影漢化過程及添加布局

## 前言 最近閒著發慌,想起了很久之前就想漢化的一款錄影APP,APP大小不到1MB,但是好用,本期就給大家帶來漢化的基本步驟以及如何在APP中新增我們漢化的資訊 ## 漢化思路 1. **查詢關鍵字** 關鍵字挺好找的,由於APP本身就是英文,我們找到某個英文單詞進行搜尋即可 2. **找到string.

Android破解學習(十三)—— 另類的破解VIP思路

前言 一般按照以往,我們想要獲得某個軟體的VIP,一般是通過修改支付寶的支付流程,原本購買失敗的,我們修改程式碼,從而使得失敗變成了成功,不花費金錢 另類思路 有些軟體將判斷使用者是否為VIP的程式碼寫在了本地,這樣我們就可以修改這個程式碼獲得VIP,注意是有些軟體,並不是所有 判斷VIP也是一個簡單

android 通訊學習 socket udp tcp

原文 https://www.jianshu.com/p/61de9478c9aa   整體步驟流程 先來說一下整體的步驟思路吧: 傳送 UDP 廣播,大家都知道 UDP 廣播的特性是整個網段的裝置都可以收到這個訊息。 接收方收到了 UDP 的廣播,將

Android破解學習(十)—— 我們戀愛吧 三色繪戀 二次破解

前言 好久沒有寫破解教程了(我不會告訴你我太懶了),找到一款戀愛遊戲,像我這樣的宅男只能玩玩戀愛遊戲感覺一下戀愛的心動了。。 這款遊戲免費試玩,但是後續章節得花6元錢購買,我怎麼會有錢呢,而且身在吾愛的大家庭裡,不破解一波怎麼對得起我破解渣渣的身份呢! 喲,還是支付寶購買的,直接9000大法,但是破解的時候沒

Android破解學習(十四)——【Unity3D】王牌大作戰破解

一、前言 今天帶來的是王牌大作戰的破解教程,遊戲下載的話,我是直接去TapTap官網下載的 支付寶內購破解用老套了,今天學點破解的新花樣吧!! 二、支付寶內購破解 支付寶的內購破解已經很熟悉了, 直接搜尋“9000”,之後找到程式碼,修改判斷條件即可,若不明白,請看我之前寫的部落格,Android破解

Android開發學習TypedArray類

     在學習Android的開發中,學習Gallery檢視顯示圖片的過程中,在設定圖片介面卡的時候,用到了此TypedArray型別,這次根據android SDK,一塊把此型別弄清楚!      android.content.res.TypedArray