編輯:Android開發實例
最近在做 Android 上的項目,我被惡心的一塌糊塗。本以為 Java 是 Android 上的一等公民,結果深入學習之後才發現,Java 在 Android 上 就是個做 UI 的,除此之外無論想干什都得用 C 語言去實現。Android 一個非常糟糕差勁的操作系統,甚至連 Windows Mobile 都不如。Android 能取得今天的市場占有率只是因為當年微軟的 Window Phone 7 還在開發中,而 iOS 又只給 iPhone用,所以手機生產商沒得選,只能被迫采用 Android 這個連 Linux 內核開發團隊都不承認的 Linux 操作系統。而基於 Linux 內核就是 Android 唯一的優點了,正是因為如此我們才想辦法能把那些 Linux 上的偉大開源項目移植到 Android 上以彌補 Android 的不足。
Android 的多媒體功能是如此之弱,限制是如此之多,逼著我只能想辦法去把 FFmpeg 移植到 Android 上。 感謝 havlenapetr 給出的示例代碼,感謝 ABitNo 整理的說明文檔,沒有他們的貢獻,我不可能把 FFmpeg 成功移植到 Android 上。下面我將說明將 FFmpeg 移植到 Android 上的詳細步驟,希望能對正在進行同樣工作的朋友有所幫助。
一、下載必要軟件
Oracle VM VirtualBox 3.2.12
Ubuntu Desktop Edition 10.10 32-bit
Android NDK r4b(需要翻牆訪問)
Android NDK r5(需要翻牆訪問)
FFmpeg 0.6.1
二、配置編譯環境
三、准備編譯 FFmpeg
include $(all-subdir-makefiles)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_WHOLE_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale
LOCAL_MODULE := ffmpeg
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
# LOCAL_PATH is one of libavutil, libavcodec, libavformat, or libswscale
#include $(LOCAL_PATH)/../config-$(TARGET_ARCH).mak
include $(LOCAL_PATH)/../config.mak
OBJS :=
OBJS-yes :=
MMX-OBJS-yes :=
include $(LOCAL_PATH)/Makefile
# collect objects
OBJS-$(HAVE_MMX) += $(MMX-OBJS-yes)
OBJS += $(OBJS-yes)
FFNAME := lib$(NAME)
FFLIBS := $(foreach,NAME,$(FFLIBS),lib$(NAME))
FFCFLAGS = -DHAVE_AV_CONFIG_H -Wno-sign-compare -Wno-switch -Wno-pointer-sign
FFCFLAGS += -DTARGET_CONFIG=\"config-$(TARGET_ARCH).h\"
ALL_S_FILES := $(wildcard $(LOCAL_PATH)/$(TARGET_ARCH)/*.S)
ALL_S_FILES := $(addprefix $(TARGET_ARCH)/, $(notdir $(ALL_S_FILES)))
ifneq ($(ALL_S_FILES),)
ALL_S_OBJS := $(patsubst %.S,%.o,$(ALL_S_FILES))
C_OBJS := $(filter-out $(ALL_S_OBJS),$(OBJS))
S_OBJS := $(filter $(ALL_S_OBJS),$(OBJS))
else
C_OBJS := $(OBJS)
S_OBJS :=
endif
C_FILES := $(patsubst %.o,%.c,$(C_OBJS))
S_FILES := $(patsubst %.o,%.S,$(S_OBJS))
FFFILES := $(sort $(S_FILES)) $(sort $(C_FILES))
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_CFLAGS += -include "string.h" -Dipv6mr_interface=ipv6mr_ifindex
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
#if !HAVE_LRINT
static av_always_inline av_const long int lrint(double x)
{
return rint(x);
}
#endif /* HAVE_LRINT */
#if !HAVE_LRINTF
static av_always_inline av_const long int lrintf(float x)
{
return (int)(rint(x));
}
#endif /* HAVE_LRINTF */
#if !HAVE_ROUND
static av_always_inline av_const double round(double x)
{
return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);
}
#endif /* HAVE_ROUND */
#if !HAVE_ROUNDF
static av_always_inline av_const float roundf(float x)
{
return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);
}
#endif /* HAVE_ROUNDF */
#if !HAVE_TRUNCF
static av_always_inline av_const float truncf(float x)
{
return (x > 0) ? floor(x) : ceil(x);
}
#endif /* HAVE_TRUNCF */
include $(SUBDIR)../subdir.mak和
include $(SUBDIR)../config.mak
PREBUILT=/root/android-ndk-r4b/build/prebuilt/linux-x86/arm-eabi-4.4.0使用 Android NDK r5 編譯時內容如下
PLATFORM=/root/android-ndk-r4b/build/platforms/android-8/arch-arm
./configure --target-os=linux \
--arch=arm \
--enable-version3 \
--enable-gpl \
--enable-nonfree \
--disable-stripping \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffserver \
--disable-ffprobe \
--disable-encoders \
--disable-muxers \
--disable-devices \
--disable-protocols \
--enable-protocol=file \
--enable-avfilter \
--disable-network \
--disable-mpegaudio-hp \
--disable-avdevice \
--enable-cross-compile \
--cc=$PREBUILT/bin/arm-eabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-eabi- \
--nm=$PREBUILT/bin/arm-eabi-nm \
--extra-cflags="-fPIC -DANDROID" \
--disable-asm \
--enable-neon \
--enable-armv5te \
--extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"
#!/bin/bash
PREBUILT=/root/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86
PLATFORM=/root/android-ndk-r5/platforms/android-8/arch-arm
./configure --target-os=linux \
--arch=arm \
--enable-version3 \
--enable-gpl \
--enable-nonfree \
--disable-stripping \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffserver \
--disable-ffprobe \
--disable-encoders \
--disable-muxers \
--disable-devices \
--disable-protocols \
--enable-protocol=file \
--enable-avfilter \
--disable-network \
--disable-mpegaudio-hp \
--disable-avdevice \
--enable-cross-compile \
--cc=$PREBUILT/bin/arm-eabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-eabi- \
--nm=$PREBUILT/bin/arm-eabi-nm \
--extra-cflags="-fPIC -DANDROID" \
--disable-asm \
--enable-neon \
--enable-armv5te \
--extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"
chmod +x config.sh
./config.sh
#define restrict restrict修改為
#define restrict
四、開始編譯 FFmpeg
很多人在用 havlenapetr 的方法編譯 FFmpeg 時只得到一個 1599 字節 1.6KB 大小的 libffmpeg.so 文件,無論是用 Android NDK r4b 編譯還是用 Android NDK r5 編譯結果都是如此,很讓人抓狂。我也很郁悶,最後花時間研究了一下 NDK,終於發現了解決方法,而且 Android NDK r4b 和 Android NDK r5 的情況還是完全不同的,請繼續往下讀。
打開 android-ndk-r4b/build/toolchains/arm-eabi-4.4.0 目錄中的 setup.mk 文件,你會發現 Google 在裡面定義了一個用於編譯動態庫的 cmd-build-shared-library 函數。在cmd-build-shared-library 函數中 Google 使用了 PRIVATE_WHOLE_STATIC_LIBRARIES 函數。但是你在 android-ndk-r4b/build/core 目錄中的 build-binary.mk 文件裡卻找不到 PRIVATE_WHOLE_STATIC_LIBRARIES 函數…… 外?WHY?終於搞清楚了,原來得不到正確的 libffmpeg.so 文件不是我的錯,而是 Android NDK r4b 的 BUG!你妹啊!你大爺啊!坑爹呢這是!發布前不做測試嗎!居然漏掉一個函數!!!(我敢說這是個 BUG 是因為 Google 在 Android NDK r5 中修復了這個 BUG)
木辦法,只好手動替 Google 修補這個 BUG。好在修改方法很簡單,只需要照 build-binary.mk 文件裡的 PRIVATE_STATIC_LIBRARIES 增加一個 PRIVATE_WHOLE_STATIC_LIBRARIES 就行了。具體方法見下圖
修改前的 build-binary.mk 文件
修改後的 build-binary.mk 文件
保存 build-binary.mk 文件之後,運行下面的命令編譯
/root/android-ndk-r4b/ndk-build NDK_PROJECT_PATH=/root/ffmpeg
接著你會看到 warning 不停的出現在屏幕上,熬過這段心驚肉跳的時間之後,你會看到 libffmpeg.so 文件已經被編譯生成了。
看看 /root/ffmpeg/obj/local/armeabi 目錄中的 libffmpeg.so 文件,文件大小是 12.2MB
再看看 /root/ffmpeg/libs/local/armeabi 目錄中的 libffmpeg.so 文件,文件大小是 3.2MB
打開 android-ndk-r5/build/core 目錄中的 build-binary.mk 文件,發現 Google 這次沒有忘記 PRIVATE_WHOLE_STATIC_LIBRARIES,但還最後編譯得到的 libffmpeg.so 文件大小還是不正確。 這次的問題是,android-ndk-r5 默認是使用 arm-linux-androideabi-4.4.3 編譯,而不是 arm-eabi-4.4.0。但 android-ndk-r5/toolchains/arm-linux-androideabi-4.4.3 目錄中的 setup.mk 文件裡定義的 cmd-build-shared-library 函數並沒有將靜態庫文件鏈接在一起生成動態庫文件。所以解決的辦法就是在執行 ndk-build 時加上 NDK_TOOLCHAIN 參數,指定使用 arm-eabi-4.4.0 來編譯。完整命令如下
/root/android-ndk-r5/ndk-build NDK_PROJECT_PATH=/root/ffmpeg NDK_TOOLCHAIN=arm-eabi-4.4.0 NDK_PLATFORM=android-8
五、結語
我的最終目的是實現 Android 平台下對 Apple Http Live Streaming 的支持,但現在我已經被 Android 折磨的死去活來了。不知道在實現最終目標之前,我還要被 Android 惡心多少次……
Android 讓這個世界變得更混亂了,Windows Phone 7 才是救世主。
目前智能電視終端(智能電視和智能電視盒子)已經越來越火,過去主打視頻功能,如今的智能電視終端不僅會繼續完善視頻功能,還會加入電視游戲功能,同時這也趕上了“電視游戲
本文實例講述了Android中WebView用法。分享給大家供大家參考,具體如下: WebView相當於一個迷你浏覽器,采用WebKit內核,因此完美支持html
知識點: 這次將繼續上一篇文章沒有講完的Menu的學習,上下文菜單(Context menu)和彈出菜單(Popup menu)。 上下文菜單 上下文菜單提供對U
本文實例講述了Android編程學習之異步加載圖片的方法。分享給大家供大家參考,具體如下: 最近在android開發中碰到比較棘手的問題,就是加載圖片內存溢出。我