使用Android NKD編譯havlenapetr-FFMpeg-7c27aa2

轉自 http://blog.csdn.net/conowen/article/details/7526398 java

一、 linux

        使用NDK去編譯官方的FFmpeg原版的話,還得本身實現JNI層與java層工程量比較大。因此移植FFmpeg到Android平臺時,能夠移植一些已經實現JNI與JAVA層的開源項目,畢竟軟件行業歷來都是站在巨人肩膀上發展的。 android


二、移植havlenapetr/FFMpeg   git


havlenapetr的開源項目是比較出名的一個FFmpeg工程,不少Android多媒體項目都是在此基礎上面修改的。 github


下載地址:https://github.com/havlenapetr/FFMpeg shell

能夠直接ZIP包:https://github.com/havlenapetr/FFMpeg/zipball/debug express

或者經過git方式下載,新建一個目錄,而後在linux的終端下執行,固然了,你要事情安裝git的相關工具 apache

git clone https://github.com/havlenapetr/FFMpeg.git

三、利用NDK編譯生成so庫 canvas


下載後直接在havlenapetr-FFMpeg-7c27aa2的頂級目錄下執行 app

$ndk/ndk-build

是能夠編譯經過的,不會提示任何error。

關於如何利用NDK編譯,能夠參考我以前的博文:http://blog.csdn.net/conowen/article/details/7518870


四、導入java工程,實現播放

       而後把在eclipse裏面,把havlenapetr-FFMpeg-7c27aa2這個項目import進來,就能夠播放視頻了。


4.一、須要注意的是:這個版本的havlenapetr FFmpeg工程只能在Android 2.2上面運行,由於havlenapetr採用的是音視頻直接在JNI層輸入。能夠注意到havlenapetr-FFMpeg-7c27aa2目錄下有prebuilt這樣一個目錄,此目錄下有Android 2.2版本的libjniaudio.so和libjnivideo.so兩個庫文件。


4.二、Android版本不一樣致使不能播放:

havlenapetr的FFmpeg項目音視頻輸出以下

音頻:採用Android底層的audiotrack輸出。

視頻:在FFmpeg解碼以後,獲得YUV信號,而後轉換成RGB信號,最終經過Android底層的surface輸出。


提示:能夠移植SDL開源庫實現音視頻輸出,由於SDL的視頻輸出機制是經過OPenGL呈現畫面,這樣就能夠兼容全部的Android平臺。


       可是問題就來了,Android每一個版本的framework都是不大同樣的,因此要在底層使用Android的audiotrack和surface來輸入音視頻信號,就要在相應版本的Android源代碼中,從新編譯生成libjniaudio.so和libjnivideo.so兩個庫文件了。


五、編譯havlenapetr FFmpeg工程Android 2.3版本的libjniaudio.so和libjnivideo.so

          首先要明白一點,Android的官方源代碼編譯以後,是不會生成libjniaudio.so和libjnivideo.so的。因此要本身添加audiotrack.cpp、surface.cpp和Android.mk文件到Android源代碼裏面編譯生成。(每次編譯libjniaudio.so和libjnivideo.so都要從新編譯這個Android源代碼,時間比較長。)


5.1下載audio與video文件夾

能夠在https://github.com/havlenapetr/android_frameworks_base下載audiotrack.cpp、surface.cpp和Android.mk,注意要選擇正確的branch(分支)

froyo---->Android 2.2

gingerbread---->Android 2.3

ICS---->Android 4.0


關於havlenapetr-FFMpeg在Android 4.0(ICS)的補充說明



5.二、編譯Android系統源代碼

     下載以後,而後找到裏面的native文件夾,把裏面的audio和video文件夾拖進Android源代碼的frameworks/base/native目錄下。

綠色的是新加入的文件

須要注意的一點是:

gingerbread下載以後,裏面是沒有audio和video文件夾的,可是能夠用froyo版本的audio和video文件夾。(也就是下載gingerbread感受也沒啥用Orz~~~)

可是咱們可使用froyo的audio和video文件夾,編譯Android源代碼是能夠成功經過的,ndk-build也能夠經過,可是在Android的java工程裏面使用就會有如下錯誤信息。

java.lang.NoSuchFieldError: no field with name='mSurface' signature='I' in class Landroid/view/Surface;

加載庫時,找不到mSruface類
修改方法是:
將surface.cpp中mSurface改成 mNativeSurface ,而後從新編譯便可。固然了,你也能夠用ICS的surface.cpp文件,這個版本是沒有問題的。


另外編譯havlenapetr FFmpeg工程Android 4.0版本的libjniaudio.so和libjnivideo.so與上面步驟差很少。


/************************************************************************/

附上我所使用的audio與video(來源havlenapetr的項目)

video/jni/surface.cpp(注意目錄結構)

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <android/surface.h>
#include <surfaceflinger/Surface.h>
#include <utils/Log.h>
#include <SkBitmap.h>
#include <SkCanvas.h>

#define TAG "SurfaceWrapper"

using namespace android;

static Surface*		sSurface;
static SkBitmap		sBitmapClient;
static SkBitmap		sBitmapSurface;

static Surface* getNativeSurface(JNIEnv* env, jobject jsurface) {
	jclass clazz = env->FindClass("android/view/Surface");
	jfieldID field_surface = env->GetFieldID(clazz, "mNativeSurface", "I");
	if(field_surface == NULL) {
		return NULL;
	}
	return (Surface *) env->GetIntField(jsurface, field_surface);
}

static int initBitmap(SkBitmap *bitmap, int format, int width, int height, bool allocPixels) {
	switch (format) {
        case PIXEL_FORMAT_RGBA_8888:
            bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
            break;
			
        case PIXEL_FORMAT_RGBA_4444:
            bitmap->setConfig(SkBitmap::kARGB_4444_Config, width, height);
            break;
			
        case PIXEL_FORMAT_RGB_565:
            bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height);
            break;
			
        case PIXEL_FORMAT_A_8:
            bitmap->setConfig(SkBitmap::kA8_Config, width, height);
            break;
			
        default:
            bitmap->setConfig(SkBitmap::kNo_Config, width, height);
            break;
    }
	
	if(allocPixels) {
		bitmap->setIsOpaque(true);
		//-- alloc array of pixels
		if(!bitmap->allocPixels()) {
			return -1;
		}
	}
	return 0;
}

int AndroidSurface_register(JNIEnv* env, jobject jsurface) {
	__android_log_print(ANDROID_LOG_INFO, TAG, "registering video surface");
	
	sSurface = getNativeSurface(env, jsurface);
	if(sSurface == NULL) {
	     return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
	}
	
	__android_log_print(ANDROID_LOG_INFO, TAG, "registered");
	
	return ANDROID_SURFACE_RESULT_SUCCESS;
}

int AndroidSurface_getPixels(int width, int height, void** pixels) {
	__android_log_print(ANDROID_LOG_INFO, TAG, "getting surface's pixels %ix%i", width, height);
	if(sSurface == NULL) {
		return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
	}
	if(initBitmap(&sBitmapClient, PIXEL_FORMAT_RGB_565, width, height, true) < 0) {
		return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_CLIENT;
	}
	*pixels = sBitmapClient.getPixels();
	__android_log_print(ANDROID_LOG_INFO, TAG, "getted");
	return ANDROID_SURFACE_RESULT_SUCCESS;
}

static void doUpdateSurface() {
	SkCanvas	canvas(sBitmapSurface);
	SkRect		surface_sBitmapClient;
	SkRect		surface_sBitmapSurface;
	SkMatrix	matrix;
	
	surface_sBitmapSurface.set(0, 0, sBitmapSurface.width(), sBitmapSurface.height());
	surface_sBitmapClient.set(0, 0, sBitmapClient.width(), sBitmapClient.height());
	matrix.setRectToRect(surface_sBitmapClient, surface_sBitmapSurface, SkMatrix::kFill_ScaleToFit);
	
	canvas.drawBitmapMatrix(sBitmapClient, matrix);
}

static int prepareSurfaceBitmap(Surface::SurfaceInfo* info) {
	if(initBitmap(&sBitmapSurface, info->format, info->w, info->h, false) < 0) {
		return -1;
	}
	sBitmapSurface.setPixels(info->bits);
	return 0;
}

int AndroidSurface_updateSurface() {
	static Surface::SurfaceInfo surfaceInfo;
	if(sSurface == NULL) {
		return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
	}
	if (!Surface::isValid (sSurface)){
		return ANDROID_SURFACE_RESULT_NOT_VALID;
	}
	if (sSurface->lock(&surfaceInfo) < 0) {
		return ANDROID_SURFACE_RESULT_COULDNT_LOCK;
	}
	
	if(prepareSurfaceBitmap(&surfaceInfo) < 0) {
		return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_SURFACE;
	}
	
	doUpdateSurface();
	
	if (sSurface->unlockAndPost() < 0) {
		return ANDROID_SURFACE_RESULT_COULDNT_UNLOCK_AND_POST;
	}
	return ANDROID_SURFACE_RESULT_SUCCESS;
}

int AndroidSurface_unregister() {
	__android_log_print(ANDROID_LOG_INFO, TAG, "unregistering video surface");
	__android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");
    return ANDROID_SURFACE_RESULT_SUCCESS;
}

video/jni/Android.mk(注意目錄結構)

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

# our source files
#
LOCAL_SRC_FILES:= \
    surface.cpp

LOCAL_SHARED_LIBRARIES := \
    libskia \
        libsurfaceflinger_client \
        libutils \
    liblog

LOCAL_C_INCLUDES += \
    $(JNI_H_INCLUDE) \
    external/skia/src/core \
    external/skia/include/core \
    frameworks/base/include \
    frameworks/base/native/include

# Optional tag would mean it doesn't get installed by default
LOCAL_MODULE_TAGS := optional

LOCAL_PRELINK_MODULE := false

LOCAL_MODULE:= libjnivideo

include $(BUILD_SHARED_LIBRARY)

/audio/jni/Android.mk(注意目錄結構)

LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) # our source files # LOCAL_SRC_FILES:= \     audiotrack.cpp LOCAL_SHARED_LIBRARIES := \     libbinder \         libmedia \         libutils \     liblog LOCAL_C_INCLUDES += \     $(JNI_H_INCLUDE) \     frameworks/base/include \     frameworks/base/native/include # Optional tag would mean it doesn't get installed by default LOCAL_MODULE_TAGS := optional LOCAL_PRELINK_MODULE := false LOCAL_MODULE:= libjniaudio include $(BUILD_SHARED_LIBRARY) 
相關文章
相關標籤/搜索