android 264 decode,ffmpeg H264 decode for Android Native Surface

1、最近做的一个项目涉及到从服务端获取H264编码的帧数据解码并在Android的Surface上显示的处理,从开始学习ffmpeg解码这块发现网上的资料很少,特别是涉及到解码并在Android平台上用Surface刷新显示数据,故将所有代码分享如下。从GitHub上找到了一份版本2.2的ffmpeg的源码,里面包括ffmpeg的所有源码,及H264 解码实现的范例,和ffmpeg编译的sh脚本;现已上传到csdn上面,下载链接:http://download.csdn.net/detail/air798/9074547

2、因需要先编译一个H264解码 .a 静态库,再利用这个静态库与Native的Socket消息发送和接收及Surface刷新显示的cpp文件再一起编译生成最终的.so动态库,所以修改h264decoder.c的实现。此处ffmpeg版本采用的是官网的最新版本。

#include #include

#include

#include

#include

#include

#include

#include

#include "h264decoder.h"

#define  D(x...)  __android_log_print(ANDROID_LOG_ERROR, "h264decoder", x)

typedef struct DecoderContext {

int color_format;

struct AVCodec *codec;

struct AVCodecContext *codec_ctx;

struct AVFrame *src_frame;

struct AVFrame *dst_frame;

struct SwsContext *convert_ctx;

int frame_ready;

} DecoderContext;

DecoderContext *ctx = NULL;

int i, j, k;

void av_log_callback(void *ptr, int level, const char *fmt, __va_list vl) {

static char line[1024] = { 0 };

vsnprintf(line, sizeof(line), fmt, vl);

D(line);

}

void initH264Decoder() {

D("Create native H264 decoder context");

av_register_all();

av_log_set_callback(av_log_callback);

ctx = calloc(1, sizeof(DecoderContext));

//在Android平台上能兼容所有平台的显示格式貌似只有  RGB565

ctx->color_format =

AV_PIX_FMT_RGB565; //AV_PIX_FMT_RGB24 AV_PIX_FMT_RGB565  PIX_FMT_YUV420P

ctx->codec = avcodec_find_decoder(CODEC_ID_H264);

ctx->codec_ctx = avcodec_alloc_context3(ctx->codec);

ctx->codec_ctx->pix_fmt =

AV_PIX_FMT_YUVJ420P;

// 此处的值是你要所解码的帧数据格式

ctx->codec_ctx->flags2 |= CODEC_FLAG2_CHUNKS;

ctx->src_frame = av_frame_alloc();

ctx->dst_frame = av_frame_alloc();

avcodec_open2(ctx->codec_ctx, ctx->codec, NULL);

}

void destroyH264Decoder() {

D("Destroy native H264 decoder context");

avcodec_close(ctx->codec_ctx);

av_free(ctx->codec_ctx);

av_free(ctx->src_frame);

av_free(ctx->dst_frame);

free(ctx);

}

int decodeH264Buffer(char *inBuffer, size_t num_bytes, long pkt_pts, char *outBuffer) {

AVPacket packet = {

.data = (uint8_t*) inBuffer, // 接收到的帧数据

.size = num_bytes,  // 帧数据大小

.pts =(int64_t) pkt_pts   // 帧数据时间戳

};

int frameFinished = 0;

int res = avcodec_decode_video2(ctx->codec_ctx, ctx->src_frame,

&frameFinished, &packet);

if (res < 0) {

return -1;

}

if (frameFinished) {// 判断解码是否成功

int frameSize = avpicture_get_size(ctx->color_format,

ctx->codec_ctx->width, ctx->codec_ctx->height);// 根据颜色空间计算需要转换的帧数据大小

uint8_t *buffer = (uint8_t *) av_malloc(frameSize);//申请内存填充转换的帧数据

avpicture_fill((AVPicture*) ctx->dst_frame, (unsigned char *) buffer,

ctx->color_format, ctx->codec_ctx->width,

ctx->codec_ctx->height);

ctx->convert_ctx = sws_getContext(ctx->codec_ctx->width,

ctx->codec_ctx->height, ctx->codec_ctx->pix_fmt,

ctx->codec_ctx->width, ctx->codec_ctx->height,

ctx->color_format, SWS_FAST_BILINEAR, NULL, NULL, NULL);// 获取转换的context参数

// 关于SWS_FAST_BILINEAR 参数说明链接: http://blog.csdn.net/mashen1989/article/details/7835508

if (ctx->convert_ctx == NULL) {

return -1;

}

// rotate image  从其他地方查找的资料显示要旋转解码的帧数据,这样解码出来的帧图像才不会是倒着的,但是使用过程中没出现这个问题,此处就不做图像旋转;

/* ctx->src_frame->data[0] += ctx->src_frame->linesize[0]

* (ctx->codec_ctx->height - 1);

ctx->src_frame->linesize[0] *= -1;

ctx->src_frame->data[1] += ctx->src_frame->linesize[1]

* (ctx->codec_ctx->height / 2 - 1);

ctx->src_frame->linesize[1] *= -1;

ctx->src_frame->data[2] += ctx->src_frame->linesize[2]

* (ctx->codec_ctx->height / 2 - 1);

ctx->src_frame->linesize[2] *= -1; */

sws_scale(ctx->convert_ctx, (const uint8_t**) ctx->src_frame->data,

ctx->src_frame->linesize, 0, ctx->codec_ctx->height,

ctx->dst_frame->data, ctx->dst_frame->linesize); // image colour format translate

// Android平台的Native Surface上刷新显示的数据需要32位对齐,所以必须判断一下帧数据的宽度是否刚好能整除32,如果不是,就逐行拷贝数据;

//此处直接拷贝buffer中的数据,如果转化的是RGB格式,直接拷贝dst_frame->data[0]中的数据也行;如果是YUV数据则要拷贝dst_frame->data[0]、dst_frame->data[1]、dst_frame->data[2]中的数据

if (ctx->codec_ctx->width % 32 == 0) {

memcpy(outBuffer, buffer, frameSize);

} else {

int delta = 32 - (ctx->codec_ctx->width * 2) % 32;

for (int i = 0; i < ctx->codec_ctx->height; i++) {

memcpy(outBuffer + i * (ctx->codec_ctx->width * 2 + delta),

buffer + i * ctx->codec_ctx->width * 2,

ctx->codec_ctx->width * 2);

}

}

av_free(buffer);

}

return frameFinished;

}

int getWidth() {

return ctx->codec_ctx->width;

}

int getHeight() {

return ctx->codec_ctx->height;

}

int getSize() {

return avpicture_get_size(ctx->color_format, ctx->codec_ctx->width,

ctx->codec_ctx->height);

}

相应的h264decoder.h头文件代码如下:

#include

#include

#include

#include

#include

#include

#include

#include

#ifdef __cplusplus

extern "C" {

#endif

void initH264Decoder();

void destroyH264Decoder();

int getWidth();

int getHeight();

int getSize();

void av_log_callback(void *ptr, int level, const char *fmt, __va_list vl);

int decodeH264Buffer(char *inBuffer, size_t num_bytes, long pkt_pts,char *outBuffer);

#ifdef __cplusplus

}

#endif

sh脚本中需要修改的参数如下:

NDK=~/......./android-ndk-r9d                 // Linux编译环境下你放置的ndk路径

ARM_PLATFORM=$NDK/platforms/android-5/arch-arm/

ARM_PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86

X86_PLATFORM=$NDK/platforms/android-9/arch-x86/

X86_PREBUILT=$NDK/toolchains/x86-4.6/prebuilt/linux-x86

编译.a静态库的Android.mk脚本修改如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := libh264decoder

LOCAL_SRC_FILES := h264decoder.c

#ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)

LOCAL_C_INCLUDES := $(LOCAL_C_INCLUDES) $(LOCAL_PATH)/ffmpeg-android/armv7-a/include

LOCAL_LDFLAGS := $(LOCAL_PATH)/ffmpeg-android/armv7-a/lib/libavformat.a \

$(LOCAL_PATH)/ffmpeg-android/armv7-a/lib/libavcodec.a \

$(LOCAL_PATH)/ffmpeg-android/armv7-a/lib/libavutil.a \

$(LOCAL_PATH)/ffmpeg-android/armv7-a/lib/libswresample.a \

$(LOCAL_PATH)/ffmpeg-android/armv7-a/lib/libswscale.a

#endif

LOCAL_CFLAGS := -std=c99 -Wall -fvisibility=hidden

LOCAL_LDFLAGS := $(LOCAL_LDFLAGS) -Wl,--gc-sections

LOCAL_LDLIBS := -llog -lavformat -lavcodec -lswscale -lavutil

include $(BUILD_STATIC_LIBRARY)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值