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 <stdio.h>

#include <stdlib.h>
#include <inttypes.h>
#include <stdarg.h>


#include <android/log.h>

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#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 <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdarg.h>


#include <android/log.h>


#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>


#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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值