Mac OS下為Android Studio編譯FFmpeg解碼庫(kù)的詳細(xì)教程
NDK部分
1、下載ndk
這里就一筆帶過了。
2、解壓ndk
不要解壓,文件權(quán)限會(huì)出錯(cuò)。執(zhí)行之,會(huì)自動(dòng)解壓,然后mv到想放的地方。我放到了”/usr/local/bin/android-ndk-r10d”(此目錄之后用$NDK_DIR指代)。
3、下載Ffmpeg
我下的是2.5.3版本。
4、解壓Ffmpeg
解壓Ffmpeg到$NDK_DIR/sources/ffmpeg-2.5.3。
5、修改Ffmpeg編譯配置
在ffmpeg-2.5.3目錄下把configure文件中的這幾行,目的是去掉默認(rèn)生成的庫(kù)名字libavcodec.so.55最后那個(gè)”55″的版本號(hào)。
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)' LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)' SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' SLIB_INSTALL_LINKS='$(SLIBNAME)'
6、編譯Ffmpeg
在ffmpeg-2.5.3目錄下創(chuàng)建文件build_android.sh。
注意前三行要按照自己的路徑正確配置。
#!/bin/bash
NDK=/usr/local/android-ndk-r10d
SYSROOT=$NDK/platforms/android-15/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build_one
{
./configure \
--prefix=$PREFIX \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-avdevice \
--disable-doc \
--disable-symver \
--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
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one
保存后執(zhí)行
sudo chmod +x build_android.sh ./build_android.sh
編譯會(huì)花上一段時(shí)間。
7、查看編譯結(jié)果
編譯完成后$NDK_DIR/sources/ffmpeg-2.5.3下面會(huì)多出一個(gè)android目錄,里面就是我們想要的編譯好的庫(kù)。
[cg@local]# ls -R android/
arm android//arm: Android.mk include lib android//arm/include: libavcodec libavfilter libavformat libavutil libswresample libswscale android//arm/include/libavcodec: avcodec.h avfft.h dv_profile.h dxva2.h old_codec_ids.h vaapi.h vda.h vdpau.h version.h vorbis_parser.h xvmc.h android//arm/include/libavfilter: asrc_abuffer.h avcodec.h avfilter.h avfiltergraph.h buffersink.h buffersrc.h version.h android//arm/include/libavformat: avformat.h avio.h version.h android//arm/include/libavutil: adler32.h avstring.h cast5.h downmix_info.h hash.h macros.h opt.h replaygain.h time.h aes.h avutil.h channel_layout.h error.h hmac.h mathematics.h parseutils.h ripemd.h timecode.h attributes.h base64.h common.h eval.h imgutils.h md5.h pixdesc.h samplefmt.h timestamp.h audio_fifo.h blowfish.h cpu.h ffversion.h intfloat.h mem.h pixelutils.h sha.h version.h audioconvert.h bprint.h crc.h fifo.h intreadwrite.h motion_vector.h pixfmt.h sha512.h xtea.h avassert.h bswap.h dict.h file.h lfg.h murmur3.h random_seed.h stereo3d.h avconfig.h buffer.h display.h frame.h log.h old_pix_fmts.h rational.h threadmessage.h android//arm/include/libswresample: swresample.h version.h android//arm/include/libswscale: swscale.h version.h android//arm/lib: libavcodec-56.so libavfilter-5.so libavformat-56.so libavutil-54.so libswresample-1.so libswscale-3.so pkgconfig libavcodec.so libavfilter.so libavformat.so libavutil.so libswresample.so libswscale.so android//arm/lib/pkgconfig: libavcodec.pc libavfilter.pc libavformat.pc libavutil.pc libswresample.pc libswscale.pc
8、給Ffmpeg庫(kù)寫Android.mk使其可用
創(chuàng)建$NDK_DIR/sources/ffmpeg-2.5.3/android/arm/Android.mk文件,內(nèi)容如下:
要注意其中.so前面的數(shù)字應(yīng)該改成你的ffmpeg版本編譯出來(lái)的數(shù)字。
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= libavcodec LOCAL_SRC_FILES:= lib/libavcodec-56.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE:= libavformat LOCAL_SRC_FILES:= lib/libavformat-56.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE:= libswscale LOCAL_SRC_FILES:= lib/libswscale-3.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE:= libavutil LOCAL_SRC_FILES:= lib/libavutil-54.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE:= libavfilter LOCAL_SRC_FILES:= lib/libavfilter-5.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE:= libswresample LOCAL_SRC_FILES:= lib/libswresample-1.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY)
至此ndk配置完畢,后面是配置Android Studio的部分。
Android Studio部分
Android Studio和Eclipse不太一樣,它有一定的自動(dòng)生成Android.mk并自動(dòng)搞定JNI的能力。
但目前還并不足以讓我們使用起來(lái)Ffmpeg庫(kù)。
因此我們的思路是禁用掉Android Studio自動(dòng)ndk-build的功能,手動(dòng)編譯我們的C代碼達(dá)到目的。
首先當(dāng)然要新建一個(gè)Android Studio項(xiàng)目。
我們使用$ROOT_DIR指代項(xiàng)目根目錄。
1、Android Studio配置ndk路徑
$ROOT_DIR/local.properties原先只配置了sdk。
sdk.dir=/usr/local/bin/android-sdk-macosx
給它增加一行ndk的配置
sdk.dir=/usr/local/bin/android-sdk-macosx ndk.dir=/usr/local/bin/android-ndk-r10d
2、配置build.gradle
項(xiàng)目里面有兩個(gè)build.gradle,一個(gè)在根目錄下,一個(gè)在$ROOT_DIR/app/src下,我們要修改的是后者。
A>添加這一段以禁用自動(dòng)ndk-build。
sourceSets.main.jni.srcDirs = []
B>添加這一段讓它知道用庫(kù)
ndk {
abiFilter "armeabi"
moduleName "ovsplayer"
ldLibs "log", "z", "m", "jnigraphics", "android"
}
修改后的build.gradle是這樣的。
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
sourceSets.main.jni.srcDirs = [] // 禁用自動(dòng)執(zhí)行ndk-build
defaultConfig {
applicationId "com.example.chengang.myapplication"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
ndk {
abiFilter "armeabi"
moduleName "ovsplayer" // 這個(gè)是C文件的名字
ldLibs "log", "z", "m", "jnigraphics", "android"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
3、生成頭文件
執(zhí)行命令,注意路徑要根據(jù)自己的情況更改。
javah -d jni -classpath ..\..\build\intermediates\classes\debug com.example.nativeapp.app.MainActivity
會(huì)生成這個(gè)文件$ROOT_DIR/app/src/main/jni/com_example_chengang_myapplication_MainActivity.h
4、編寫C文件
$ROOT_DIR/app/src/main/jni/ovsplayer.c內(nèi)容如下:
#include <stdio.h>
#include <stdlib.h>
#include "com_example_chengang_myapplication_MainActivity.h"
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
JNIEXPORT jstring JNICALL Java_com_example_chengang_myapplication_MainActivity_getStringFromNative
(JNIEnv * env , jobject obj)
{
const char *url = "/mnt/sdcard/1.mp4";
av_register_all();
AVFormatContext *pFormatCtx = NULL;
int ret = avformat_open_input(&pFormatCtx, url, NULL, NULL);
ret = avformat_find_stream_info(input_context, NULL);
int streamNum = input_context->nb_streams;
char wd[512];
sprintf(wd, "AVCODEC VERSION %u\n, streamNum[%d]"
, avcodec_version()
, streamNum
);
return (*env)->NewStringUTF(env, wd);
}
5、編寫Java文件
$ROOT_DIR/app/src/main/java/com/example/chengang/myapplication/MainActivity.java內(nèi)容如下。
其中g(shù)etStringFromNative()方法是我們實(shí)現(xiàn)的,打印了Ffmpeg庫(kù)的版本號(hào)(我編譯的這個(gè)是3673444)和視頻文件的信息出來(lái)。
package com.example.chengang.myapplication;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView view = (TextView) findViewById(R.id.mytext);
view.setText(this.getStringFromNative());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public native String getStringFromNative();
static {
System.loadLibrary("swresample-1");
System.loadLibrary("avutil-54");
System.loadLibrary("avformat-56");
System.loadLibrary("avcodec-56");
System.loadLibrary("swscale-3");
System.loadLibrary("ovsplayer");
}
}
附上布局文件是這樣的。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/mytext" android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
6、編寫項(xiàng)目Android.mk
Android.mk放到$ROOT_DIR/app/build/intermediates/ndk/debug/下。
注意其中的絕對(duì)路徑”/Users/chengang/Code/Android/MyApplication4″是咱們的$ROOT_DIR。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ovsplayer LOCAL_LDLIBS := \ -lm \ -ljnigraphics \ -landroid \ -llog \ -lz \ LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libavutil LOCAL_SRC_FILES := \ /Users/chengang/Code/Android/MyApplication4/app/src/main/jni/ovsplayer.c \ LOCAL_C_INCLUDES += /Users/chengang/Code/Android/MyApplication4/app/src/main/jni LOCAL_C_INCLUDES += /Users/chengang/Code/Android/MyApplication4/app/src/debug/jni include $(BUILD_SHARED_LIBRARY) $(call import-module,ffmpeg-2.5.3/android/arm)
簡(jiǎn)單說下LOCAL_LDLIBS和LOCAL_SHARED_LIBRARIES的區(qū)別,前者鏈接系統(tǒng)庫(kù),后者鏈接第三方庫(kù)。
并不能換著用。
7、編譯C代碼
在終端下執(zhí)行這個(gè)命令編譯C代碼。
/usr/local/bin/android-ndk-r10d/ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=/Users/chengang/Code/Android/MyApplication4/app/build/intermediates/ndk/debug/Android.mk APP_PLATFORM=android-21 NDK_OUT=/Users/chengang/Code/Android/MyApplication4/app/build/intermediates/ndk/debug/obj NDK_LIBS_OUT=/Users/chengang/Code/Android/MyApplication4/app/build/intermediates/ndk/debug/lib APP_ABI=armeabi
8、運(yùn)行項(xiàng)目
回到Android Studio中Ctrl+R運(yùn)行項(xiàng)目。
會(huì)看到手機(jī)上打印出Ffmpeg庫(kù)的版本號(hào)(我編譯的這個(gè)是3673444)和視頻文件的信息出來(lái)。
Ffmpeg庫(kù)已經(jīng)可以調(diào)用了,然后繼續(xù)寫自己的邏輯就好了。
9、另外
另外說兩個(gè)C代碼中可能遇見的錯(cuò)誤:
A>如果你通過標(biāo)準(zhǔn)庫(kù)想得到文件大小,又寫錯(cuò)了文件名。安卓上,在不存在的文件的句柄上執(zhí)行fseek會(huì)報(bào)如下錯(cuò)誤:”could not disable core file generation.”;
B>如果avformat_open_input()返回-1330794744了。那有兩種可能,一是你忘記調(diào)用av_register_all()了,二是編譯Ffmpeg的時(shí)候沒有編譯進(jìn)對(duì)應(yīng)的DEMUXER或者指定了disable-everything。
相關(guān)文章
5個(gè)Android開發(fā)中比較常見的內(nèi)存泄漏問題及解決辦法
本文主要介紹了5個(gè)Android開發(fā)中比較常見的內(nèi)存泄漏問題及解決辦法,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02
Android開發(fā)實(shí)現(xiàn)的ViewPager引導(dǎo)頁(yè)功能(動(dòng)態(tài)加載指示器)詳解
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)的ViewPager引導(dǎo)頁(yè)功能(動(dòng)態(tài)加載指示器),結(jié)合實(shí)例形式詳細(xì)分析了Android使用ViewPager引導(dǎo)頁(yè)的具體步驟,相關(guān)布局、功能使用技巧,需要的朋友可以參考下2017-11-11
Android實(shí)現(xiàn)將一個(gè)Activity設(shè)置成窗口樣式的方法
這篇文章主要介紹了Android實(shí)現(xiàn)將一個(gè)Activity設(shè)置成窗口樣式的方法,涉及Android的窗口樣式設(shè)置與布局技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-02-02
Android實(shí)現(xiàn)EditText中添加和刪除bitmap的方法
這篇文章主要介紹了Android實(shí)現(xiàn)EditText中添加和刪除bitmap的方法,實(shí)例分析了Android中EditText控件的bitmap操作技巧,需要的朋友可以參考下2016-01-01

