使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

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

1、

        使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与java层工程量比较大。所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件行业从来都是站在巨人肩膀上发展的。


2、移植havlenapetr/FFMpeg  


havlenapetr的开源项目是比较出名的一个FFmpeg工程,很多Android多媒体项目都是在此基础上面修改的。


下载地址:https://github.com/havlenapetr/FFMpeg

可以直接ZIP包:https://github.com/havlenapetr/FFMpeg/zipball/debug

或者通过git方式下载,新建一个目录,然后在linux的终端下执行,当然了,你要事情安装git的相关工具

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

3、利用NDK编译生成so库


下载后直接在havlenapetr-FFMpeg-7c27aa2的顶级目录下执行

$ndk/ndk-build

是可以编译通过的,不会提示任何error。

关于如何利用NDK编译,可以参考我之前的博文:http://blog.csdn.net/conowen/article/details/7518870


4、导入java工程,实现播放

       然后把在eclipse里面,把havlenapetr-FFMpeg-7c27aa2这个项目import进来,就可以播放视频了。


4.1、需要注意的是:这个版本的havlenapetr FFmpeg工程只能在Android 2.2上面运行,因为havlenapetr采用的是音视频直接在JNI层输入。可以注意到havlenapetr-FFMpeg-7c27aa2目录下有prebuilt这样一个目录,此目录下有Android 2.2版本的libjniaudio.so和libjnivideo.so两个库文件。


4.2、Android版本不同导致不能播放:

havlenapetr的FFmpeg项目音视频输出如下

音频:采用Android底层的audiotrack输出。

视频:在FFmpeg解码之后,得到YUV信号,然后转换成RGB信号,最终通过Android底层的surface输出。


提示:可以移植SDL开源库实现音视频输出,因为SDL的视频输出机制是通过OPenGL呈现画面,这样就可以兼容所有的Android平台。


       但是问题就来了,Android每个版本的framework都是不大一样的,所以要在底层使用Android的audiotrack和surface来输入音视频信号,就要在相应版本的Android源代码中,重新编译生成libjniaudio.so和libjnivideo.so两个库文件了。


5、编译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.2、编译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)


转载于:https://my.oschina.net/u/555002/blog/94010

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值