将ffmpeg移植到Android

首先要配置ndk,见前面一节:http://blog.csdn.net/huahuahailang/article/details/27373169

下面介绍如何将ffmpeg编译成动态库libffmpegjni.so。

在主目录下新建文件夹ayer/jni,将ffmpeg拷贝到jni目录中。在ayer目录下新建Android.mk文件,程序为:

~/ayer/jni/Android.mk

include $(all-subdir-makefiles)

~/ayer/jni/ffmpeg/Android.mk

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))

~/ayer/jni/ffmpeg/av.mk

# 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))

~/ayer/jni/ffmpeg/libavcodec/Android.mk

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)

~/ayer/jni/ffmpeg/libavformat/Android.mk

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)

~/ayer/jni/ffmpeg/libavfilter/Android.mk

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)

~/ayer/jni/ffmpeg/libavutil/Android.mk

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)

~/ayer/jni/ffmpeg/libpostproc/Android.mk

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)

~/ayer/jni/ffmpeg/libswscale/Android.mk

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)

编写配置脚本:~/ayer/jni/ffmpeg/config.sh

意其中的NDK路径,请修改成你系统中对应的安装路径

 

#!/bin/bash

source config_common.sh

PREBUILT=/home/huaguanglu/bysj/android-ndk-r8b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 

PLATFORM=/home/huaguanglu/bysj/android-ndk-r8b/platforms/android-9/arch-arm 

NDK_ROOT=/home/huaguanglu/bysj/android-ndk-r8b

./configure --target-os=linux \

--arch=arm \

--extra-cflags="-I${NDK_ROOT}/platforms/android-9/arch-arm/usr/include -fPIC -DANDROID -std=c99" \

--disable-everything \

--enable-version3 \

--enable-gpl \

--enable-nonfree \

--disable-stripping \

--disable-ffmpeg \

--disable-ffplay \

--disable-ffserver \

--disable-ffprobe \

--disable-encoders \

--disable-muxers \

--disable-devices \

--disable-protocols \

--disable-network \

--disable-avdevice \

--disable-asm \

--enable-decoder=h264 \

--enable-swscale \

--enable-cross-compile \

--cc=$PREBUILT/bin/arm-linux-androideabi-gcc \

--cross-prefix=$PREBUILT/bin/arm-linux-androideabi- \

--strip=$PREBUILT/bin/arm-linux-androideabi-strip \

--extra-cflags="-fPIC -DANDROID" \

--extra-ldflags="-Wl,-T,$PREBUILT/arm-linux-androideabi/lib/ldscripts/armelf_linux_eabi.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-linux-androideabi/4.4.3/crtbegin.o $PREBUILT/lib/gcc/arm-linux-androideabi/4.4.3/crtend.o -lc -lm -ldl"

终端输入命令:

chmod 755 config.sh

./config.sh

这时会在~/ayer/jni/ffmpeg目录下生成三个文件: config.hconfig.makconfig.log

我们需要修改config.h文件,将其中的

#define av_restrict restrict

修改成

#define av_restrict

修改ffmpeg代码,以便在NDK中编译通过

libavfilter目录中的Makefile的末尾处多了 Clean 这个玩意儿 将其注释掉或者删掉。

修改~/ayer/jni/ffmpeg/libavutil/libm.h文件,将其中的所有static方法都注释掉

修改ffmpeg源码目录中的libavcodevlibavfilterlibavformatlibavutillibpostproclibswscale中的Makefile,将Makefile中的开头:include $(SUBDIR)../config.mak都注释掉。

进入目录:~/ayer

2)、运行命令:ndk-build,找到该命令的前提是你已经把NDK目录加到了PATH系统环境变量中。

假如你没有错误,恭喜你,在目录~/ayer/libs/armeabi下会有libffmpeg.so文件。

使用JNI链接JAVAC

将上面配置好的ffmpeg复制到目录android-ndk-r8b下。将上面生成的libffmpeg.so复制到目录...\android-ndk-r8b\platforms\android-8\arch-arm\usr\lib下,进入目录android-ndk-r8b\samples,新建文件夹Tiquffmpeg,在Tiquffmpeg目录下新建文件夹jni目录,将上面配置好的ffmpeg复制到jni目录下。在jni目录下新建文件Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

PATH_TO_FFMPEG:=$(LOCAL_PATH)/ffmpeg

LOCAL_C_INCLUDES+=$(PATH_TO_FFMPEG)

LOCAL_LDLIBS:=-lffmpeg -llog

LOCAL_SRC_FILES := VideoTiqu.c

LOCAL_MODULE := ffmpegjni

include $(BUILD_SHARED_LIBRARY)

include $(all-subdir-makefiles)

jni目录下新建文件Application.mk

APP_PLATFORM := android-8

#APP_ABI := armeabi

APP_ABI := armeabi-v7a

APP_CFLAGS:=-DDISABLE_NEON

jni目录下新建文件VideoTiqu.c,写上jni程序,终端到jni目录下,输入ndk-build,就会在目录android-ndk-r8b\samples\Tiquffmpeg\libs\armeabi-v7a下生成libffmpegjni.so动态库文件。

VideoTiqu.c的程序为:

#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <ffmpeg/libavcodec/avcodec.h>

struct AVFormatContext *pFormatCtx;
int             i, videoStream;
struct AVCodecContext  *pAVCodecCtx;
struct AVCodec         *pAVCodec;
struct AVPacket        mAVPacket;
struct AVFrame         *pAVFrame; 
struct AVFrame         *pAVFrameYUV;
struct SwsContext      *pImageConvertCtx ;

int iWidth=0;
int iHeight=0;
	
int *colortab=NULL;
int *u_b_tab=NULL;
int *u_g_tab=NULL;
int *v_g_tab=NULL;
int *v_r_tab=NULL;

//short *tmp_pic=NULL;

unsigned int *rgb_2_pix=NULL;
unsigned int *r_2_pix=NULL;
unsigned int *g_2_pix=NULL;
unsigned int *b_2_pix=NULL;

#define LOG_TAG "VideoTiqu.c"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
		
void DeleteYUVTab()
{
//	av_free(tmp_pic);
	
	av_free(colortab);
	av_free(rgb_2_pix);
}

void CreateYUVTab_16()
{
	int i;
	int u, v;
	
//	tmp_pic = (short*)av_malloc(iWidth*iHeight*2); 

	colortab = (int *)av_malloc(4*256*sizeof(int));
	u_b_tab = &colortab[0*256];
	u_g_tab = &colortab[1*256];
	v_g_tab = &colortab[2*256];
	v_r_tab = &colortab[3*256];

	for (i=0; i<256; i++)
	{
		u = v = (i-128);

		u_b_tab[i] = (int) ( 1.772 * u);
		u_g_tab[i] = (int) ( 0.34414 * u);
		v_g_tab[i] = (int) ( 0.71414 * v); 
		v_r_tab[i] = (int) ( 1.402 * v);
	}

	rgb_2_pix = (unsigned int *)av_malloc(3*768*sizeof(unsigned int));

	r_2_pix = &rgb_2_pix[0*768];
	g_2_pix = &rgb_2_pix[1*768];
	b_2_pix = &rgb_2_pix[2*768];

	for(i=0; i<256; i++)
	{
		r_2_pix[i] = 0;
		g_2_pix[i] = 0;
		b_2_pix[i] = 0;
	}

	for(i=0; i<256; i++)
	{
		r_2_pix[i+256] = (i & 0xF8) << 8;
		g_2_pix[i+256] = (i & 0xFC) << 3;
		b_2_pix[i+256] = (i ) >> 3;
	}

	for(i=0; i<256; i++)
	{
		r_2_pix[i+512] = 0xF8 << 8;
		g_2_pix[i+512] = 0xFC << 3;
		b_2_pix[i+512] = 0x1F;
	}

	r_2_pix += 256;
	g_2_pix += 256;
	b_2_pix += 256;
}

void DisplayYUV_16(unsigned int *pdst1, unsigned char *y, unsigned char *u, unsigned char *v, int width, int height, int src_ystride, int src_uvstride, int dst_ystride)
{
	int i, j;
	int r, g, b, rgb;

	int yy, ub, ug, vg, vr;

	unsigned char* yoff;
	unsigned char* uoff;
	unsigned char* voff;
	
	unsigned int* pdst=pdst1;

	int width2 = width/2;
	int height2 = height/2;
	
	if(width2>iWidth/2)
	{
		width2=iWidth/2;

		y+=(width-iWidth)/4*2;
		u+=(width-iWidth)/4;
		v+=(width-iWidth)/4;
	}

	if(height2>iHeight)
		height2=iHeight;

	for(j=0; j<height2; j++) // 涓€娆?x2鍏卞洓涓儚绱?	{
		yoff = y + j * 2 * src_ystride;
		uoff = u + j * src_uvstride;
		voff = v + j * src_uvstride;

		for(i=0; i<width2; i++)
		{
			yy  = *(yoff+(i<<1));
			ub = u_b_tab[*(uoff+i)];
			ug = u_g_tab[*(uoff+i)];
			vg = v_g_tab[*(voff+i)];
			vr = v_r_tab[*(voff+i)];

			b = yy + ub;
			g = yy - ug - vg;
			r = yy + vr;

			rgb = r_2_pix[r] + g_2_pix[g] + b_2_pix[b];

			yy = *(yoff+(i<<1)+1);
			b = yy + ub;
			g = yy - ug - vg;
			r = yy + vr;

			pdst[(j*dst_ystride+i)] = (rgb)+((r_2_pix[r] + g_2_pix[g] + b_2_pix[b])<<16);

			yy = *(yoff+(i<<1)+src_ystride);
			b = yy + ub;
			g = yy - ug - vg;
			r = yy + vr;

			rgb = r_2_pix[r] + g_2_pix[g] + b_2_pix[b];

			yy = *(yoff+(i<<1)+src_ystride+1);
			b = yy + ub;
			g = yy - ug - vg;
			r = yy + vr;

			pdst [((2*j+1)*dst_ystride+i*2)>>1] = (rgb)+((r_2_pix[r] + g_2_pix[g] + b_2_pix[b])<<16);
		}
	}
}

JNIEXPORT jint JNICALL Java_com_videoqt_VideoTiquJni_DecoderBegin
  (JNIEnv* env, jobject thiz, jint width, jint height)
{
/*FILE * fp_ceshi=fopen("/sdcard/x264_ceshiceshi.txt","w");
fprintf(fp_ceshi,"fp_ceshi\r\n");
fflush(fp_ceshi);*/
	iWidth=width;
	iHeight=height;
	if(pAVCodecCtx!=NULL)
	{
		avcodec_close(pAVCodecCtx);
		pAVCodecCtx=NULL;
	}	
	if(pAVFrame!=NULL)
	{
		av_free(pAVFrame);
		pAVFrame=NULL;
	}
	// Register all formats and codecs
    av_register_all();
	LOGD("avcodec register success");
	
	pAVCodec=avcodec_find_decoder(CODEC_ID_H264);
	if(pAVCodec==NULL)
		return -1;

	LOGD("avcodec find decoder success");
	//init AVCodecContext
	pAVCodecCtx=avcodec_alloc_context3(pAVCodec);
	if(pAVCodecCtx==NULL)
	return -1;
	LOGD("avcodec alloc context success");

	
	/* we do not send complete frames */
	if(pAVCodec->capabilities & CODEC_CAP_TRUNCATED)
        pAVCodecCtx->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */

	/* open it */   
    if (avcodec_open(pAVCodecCtx, pAVCodec) < 0)return -1;

	LOGD("avcodec open success");

	av_init_packet(&mAVPacket);
	
	pAVFrame=avcodec_alloc_frame();
	if(pAVFrame==NULL)return -1;

	LOGD("avcodec frame allocat success");

	//pImageConvertCtx = sws_getContext(pAVCodecCtx->width, pAVCodecCtx->height, PIX_FMT_YUV420P,  pAVCodecCtx->width, pAVCodecCtx->height,PIX_FMT_RGB565LE, SWS_BICUBIC,  NULL, NULL, NULL);   
	//LOGD("sws_getContext  return =%d",pImageConvertCtx);

	LOGD("avcodec context  success");

	CreateYUVTab_16();
	LOGD("create yuv table success");

	
		
	
	return 1;
}

JNIEXPORT jint JNICALL Java_com_videoqt_VideoTiquJni_DecoderEnd
  (JNIEnv* env, jobject thiz)
{
	if(pAVCodecCtx!=NULL)
	{

		avcodec_close(pAVCodecCtx);
		pAVCodecCtx=NULL;
	}

	if(pAVFrame!=NULL)
	{

		av_free(pAVFrame);
		pAVFrame=NULL;
	}
	DeleteYUVTab();
	return 1;

}

JNIEXPORT jint JNICALL Java_com_videoqt_VideoTiquJni_DecoderContinue
  (JNIEnv* env, jobject thiz, jbyteArray in, jint inbuf_size, jbyteArray out)
{
	int i;

	jbyte * inbuf = (jbyte*)(*env)->GetByteArrayElements(env, in, 0);
	jbyte * Picture= (jbyte*)(*env)->GetByteArrayElements(env, out, 0);

	avcodec_get_frame_defaults(pAVFrame);
	mAVPacket.data=inbuf;
	mAVPacket.size=inbuf_size;
	int len =-1, got_picture=0;
	len = avcodec_decode_video2(pAVCodecCtx, pAVFrame, &got_picture, &mAVPacket);

	if(len<0)
	{
		LOGD("len=-1,decode error");
		return len;
	}

	if(got_picture>0)
	{
		LOGD("GOT PICTURE");
		/*pImageConvertCtx = sws_getContext (pAVCodecCtx->width,
						pAVCodecCtx->height, pAVCodecCtx->pix_fmt,
						pAVCodecCtx->width, pAVCodecCtx->height,
						PIX_FMT_RGB565LE, SWS_BICUBIC, NULL, NULL, NULL);
		sws_scale (pImageConvertCtx, pAVFrame->data, pAVFrame->linesize,0, pAVCodecCtx->height, pAVFrame->data, pAVFrame->linesize);

		*/
		DisplayYUV_16((int*)Picture, pAVFrame->data[0], pAVFrame->data[1], pAVFrame->data[2], pAVCodecCtx->width, pAVCodecCtx->height, pAVFrame->linesize[0], pAVFrame->linesize[1], iWidth);	
	}
	else
		LOGD("GOT PICTURE fail");
	//if(consumed_bytes > 0)
	//{
		//DisplayYUV_16((int*)Pixel, picture->data[0], picture->data[1], picture->data[2], c->width, c->height, picture->linesize[0], picture->linesize[1], iWidth);
/*
		for(i=0; i<c->height; i++)
			fwrite(picture->data[0] + i * picture->linesize[0], 1, c->width, outf);

		for(i=0; i<c->height/2; i++)
			fwrite(picture->data[1] + i * picture->linesize[1], 1, c->width/2, outf);

		for(i=0; i<c->height/2; i++)
			fwrite(picture->data[2] + i * picture->linesize[2], 1, c->width/2, outf);
// */
	//}
	
    (*env)->ReleaseByteArrayElements(env, in, inbuf, 0);    
    (*env)->ReleaseByteArrayElements(env, out, Picture, 0); 

	return len;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值