使用jni方式调用FFmepg项目中接口,对H264裸码进行解码。

该Demo主要实现从文件中读取H264编码的视频流,然后使用FFmpeg解码,将解码后的码流保存到文件。

工程目录结构如图所示:

Android.mk文件内容如下

LOCAL_PATH := $(call my-dir)  

# FFmpeg library
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec-.so
include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil-.so
include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswresample-.so
include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswscale-.so
include $(PREBUILT_SHARED_LIBRARY) # Program
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -lz
LOCAL_SHARED_LIBRARIES := avcodec swscale avutil swresample
include $(BUILD_SHARED_LIBRARY)

Application.mk内容如下:

APP_ABI := armeabi

HelloJni.java内容如下:

package com.example.hellojni;

import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle; public class HelloJni extends Activity
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); TextView tv = new TextView(this);
if(DecodeH264Video())
{
tv.setText("Decode Video Success");
}
else
{
tv.setText("Decode Video Failed");
}
setContentView(tv);
} public native boolean DecodeH264Video(); static {
System.loadLibrary("avcodec-56");
System.loadLibrary("swscale-3");
System.loadLibrary("hello-jni");
System.loadLibrary("avutil-54");
System.loadLibrary("swresample-1");
}
}

hello-jni.c文件内容如下:

#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <android/log.h> typedef enum
{
FALSE = , TRUE = ,
} C_BOOL; typedef unsigned char uint8_t;
const int IN_BUFFER_SIZE = ; #define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
#define LOGD(format, ...) __android_log_print(ANDROID_LOG_DEBUG, "(-_-)", format, ##__VA_ARGS__) static C_BOOL __DecodeH264Video(FILE* fp_in, FILE* fp_out); JNIEXPORT jboolean JNICALL Java_com_example_hellojni_HelloJni_DecodeH264Video(JNIEnv *env, jobject obj)
{
char filepath_in[] = "/data/video/bxjg_352x288.h264";
FILE *fp_in = fopen(filepath_in, "rb");
if (NULL == fp_in)
{
LOGE("open input h264 video file failed, filename [%s]", filepath_in);
return (jboolean) FALSE;
} char filepath_out[] = "/data/video/bxjg_352x288.yuv";
FILE *fp_out = fopen(filepath_out, "wb");
if (NULL == fp_out)
{
LOGE("open output yuv video file failed, filename [%s]", filepath_out);
return (jboolean) FALSE;
} LOGD("open input and output file success"); if (TRUE == __DecodeH264Video(fp_in, fp_out))
{
LOGD("decode h264 video success");
}
else
{
LOGE("decode h264 video failed");
return (jboolean) FALSE;
} fclose(fp_in);
fclose(fp_out); return (jboolean) TRUE;
} C_BOOL __DecodeH264Video(FILE* fp_in, FILE* fp_out)
{
avcodec_register_all(); AVCodec *pCodec = NULL;
pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (NULL == pCodec)
{
LOGE("avcodec_find_decoder failed");
return FALSE;
} AVCodecContext *pCodecCtx = NULL;
pCodecCtx = avcodec_alloc_context3(pCodec);
if (NULL == pCodecCtx)
{
LOGE("avcodec_alloc_context3 failed");
return FALSE;
} AVCodecParserContext *pCodecParserCtx = NULL;
pCodecParserCtx = av_parser_init(AV_CODEC_ID_H264);
if (NULL == pCodecParserCtx)
{
LOGE("av_parser_init failed");
return FALSE;
} if (avcodec_open2(pCodecCtx, pCodec, NULL) < )
{
LOGE("avcodec_open2 failed");
return FALSE;
} AVFrame *pFrame = NULL;
pFrame = av_frame_alloc();
if (NULL == pFrame)
{
LOGE("av_frame_alloc failed");
return FALSE;
} AVPacket packet;
av_init_packet(&packet); uint8_t in_buffer[IN_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
memset(in_buffer, , sizeof(in_buffer));
uint8_t *cur_ptr = NULL;
int cur_size = ;
int ret = ;
int got_picture = ;
int y_size = ;
int first_time = ; struct SwsContext *img_convert_ctx = NULL;
AVFrame *pFrameYUV = NULL;
uint8_t *out_buffer = NULL; while (TRUE)
{
cur_size = fread(in_buffer, , IN_BUFFER_SIZE, fp_in);
if ( == cur_size)
{
break;
} cur_ptr = in_buffer;
while (cur_size > )
{
int parse_len = av_parser_parse2(pCodecParserCtx, pCodecCtx, &packet.data, &packet.size, cur_ptr, cur_size,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE); cur_ptr += parse_len;
cur_size -= parse_len; if ( == packet.size)
{
continue;
} LOGD("packet size [%d]", packet.size); switch (pCodecParserCtx->pict_type)
{
case AV_PICTURE_TYPE_I:
{
LOGD("AV_PICTURE_TYPE_I");
break;
}
case AV_PICTURE_TYPE_P:
{
LOGD("AV_PICTURE_TYPE_P");
break;
}
case AV_PICTURE_TYPE_B:
{
LOGD("AV_PICTURE_TYPE_B");
break;
}
default:
{
LOGD("OTHER_PICTURE_TYPE");
break;
}
} LOGD("CodecParserCtx->output_picture_number [%d]", pCodecParserCtx->output_picture_number); ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); if (ret < )
{
LOGE("avcodec_decode_video2 failed");
return FALSE;
} if (got_picture)
{
if (first_time)
{
LOGD("CodecCtx->codec->long_name [%s]", pCodecCtx->codec->long_name);
LOGD("CodecCtx->width [%d], CodecCtx->height [%d]", pCodecCtx->width, pCodecCtx->height); //SwsContext
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); pFrameYUV = av_frame_alloc(); out_buffer = (uint8_t *) av_malloc(
avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *) pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width,
pCodecCtx->height); y_size = pCodecCtx->width * pCodecCtx->height; first_time = ;
} sws_scale(img_convert_ctx, (const uint8_t* const *) pFrame->data, pFrame->linesize, ,
pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); fwrite(pFrameYUV->data[], , y_size, fp_out); //Y
fwrite(pFrameYUV->data[], , y_size / , fp_out); //U
fwrite(pFrameYUV->data[], , y_size / , fp_out); //V LOGD("succeed to decode one frame"); }
} } //Flush Decoder
packet.data = NULL;
packet.size = ; while (TRUE)
{
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);
if (ret < )
{
LOGE("avcodec_decode_video2 failed");
return FALSE;
} if (!got_picture)
{
break;
} if (got_picture)
{ sws_scale(img_convert_ctx, (const uint8_t* const *) pFrame->data, pFrame->linesize, , pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize); fwrite(pFrameYUV->data[], , y_size, fp_out); //Y
fwrite(pFrameYUV->data[], , y_size / , fp_out); //U
fwrite(pFrameYUV->data[], , y_size / , fp_out); //V LOGD("Flush Decoder: Succeed to decode 1 frame");
}
} sws_freeContext(img_convert_ctx);
av_frame_free(&pFrameYUV);
av_parser_close(pCodecParserCtx);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
av_free(pCodecCtx); return TRUE;
}

如果仅使用FFmpeg中的解码功能,可用如下的配置选项对编译的库进行瘦身,使得编译出来的解码动态库libavcodec.so大小为2M左右!

#!/bin/bash
NDK="/home/alchen/android-ndk-r9d"
TARGET="android-19"
SYSROOT="$NDK/platforms/$TARGET/arch-arm"
TOOLCHAIN="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64" function build_one
{
./configure \
--prefix=$PREFIX \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--enable-decoder=h264 \
--enable-shared \
--enable-version3 \
--enable-gpl \
--enable-nonfree \
--enable-protocol=file \
--enable-avfilter \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-armv5te \
--disable-static \
--disable-decoders \
--disable-doc \
--disable-muxers \
--disable-demuxers \
--disable-bsfs \
--disable-indevs \
--disable-outdevs \
--disable-filters \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffserver \
--disable-ffprobe \
--disable-encoders \
--disable-devices \
--disable-protocols \
--disable-network \
--disable-avdevice \
--arch=arm \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one

使用FFmpeg解码H264-2016.01.14的更多相关文章

  1. 在iOS平台使用ffmpeg解码h264视频流(转)

    在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,ffmpeg提供avformat_open_input接口,直接将文件路径或UR ...

  2. 在iOS平台使用ffmpeg解码h264视频流

    来源:http://www.aichengxu.com/view/37145 在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,f ...

  3. FFmpeg解码H264及swscale缩放详解

    本文概要: 本文介绍著名开源音视频编解码库ffmpeg如何解码h264码流,比较详细阐述了其h264码流输入过程,解码原理,解码过程.同时,大部分应用环境下,以原始码流视频大小展示并不是最佳方式,因此 ...

  4. 【图像处理】FFmpeg解码H264及swscale缩放详解

      http://blog.csdn.net/gubenpeiyuan/article/details/19548019 主题 FFmpeg 本文概要: 本文介绍著名开源音视频编解码库ffmpeg如何 ...

  5. 使用X264编码yuv格式的视频帧使用ffmpeg解码h264视频帧

    前面一篇博客介绍在centos上搭建点击打开链接ffmpeg及x264开发环境.以下就来问个样例: 1.利用x264库将YUV格式视频文件编码为h264格式视频文件 2.利用ffmpeh库将h264格 ...

  6. (转)FFMPEG解码H264拼帧简解

    http://blog.csdn.net/ikevin/article/details/7649095 H264的I帧通常 0x00 0x00 0x00 0x01 0x67 开始,到下一个帧头开始之前 ...

  7. 控件activeX开发之项目ffmpeg解码h264——总结

    1. 编译好ffmpeg的lib库和dll库 2. 播放器作为一个dilog类player,然后在ctrol中的oncreate重写方法中用全局属性cplayer *player里new cplaye ...

  8. 利用ffmpeg解码h264流的代码

    这里也直接给出代码: h264dec.h: #pragma once #include "tdll.h" #include "avcodec.h" #inclu ...

  9. FFMPEG实现H264的解码(从源代码角度)

    农历2014年底了,将前段时间工作中研究的FFMPEG解码H264流程在此做一下整理,也算作年终技术总结了! H264解码原理: H264的原理参考另一篇博文 http://blog.csdn.net ...

随机推荐

  1. (C#) 引用工程中发现有黄色叹号

    一个Project 引用 另外 一个Project 显示黄色叹号,后来发现 后一本Project的build设定为.Net4.5, 前一个为4.0, 将版本改为一致后,问题解决.

  2. XML封装与验证消息

    Document document = DocumentHelper.createDocument(); document.setXMLEncoding(ChARSET_UTF_8); Element ...

  3. PLSQL_闪回删除FlashBack Drop表误删除如何进行恢复(案例)

    2014-06-25 Created By BaoXinjian

  4. wcf Svcutil用法

    [转] WCF中可以使用SVCUtil.exe生成客户端代理类和配置文件 1.找到如下地址“C:\Windows\System32\cmd.exe”  命令行工具,右键以管理员身份运行(视系统是否为w ...

  5. mysql server has gone away 与max_allowed_packed

    今天在本地测试测试给所有店铺批量发信息的功能,大约2万条记录,在本地和备用linux服务器上测试都没有问题,但上windows时一直报mysql server has gone away的错误,搜索一 ...

  6. transform.localPosition操作时的一些注意事项

    移动GameObject是非常平常的一件事情,一下代码看起来很简单: transform.localPosition += new Vector3 ( 10.0f * Time.deltaTime, ...

  7. YCbCr;YUV;RGB

    1.  来源的差异 yuv色彩模 型来源于rgb模型,该模型的特点是将亮度和色度分离开,从而适合于图像处理领域. 应用:basic color model used in analogue color ...

  8. mac eclipse 下安装subclipse

    参考 http://www.cnblogs.com/yinxiangpei/articles/3859057.html 推荐安装homebrew 在安装javahl时注意版本对应 http://sub ...

  9. HTTP Get请求URL最大长度

    各浏览器HTTP Get请求URL最大长度并不相同,几类常用浏览器最大长度及超过最大长度后提交情况如下: IE6.0                :url最大长度2083个字符,超过最大长度后无法提 ...

  10. ubuntu 格式化U盘,并制作系统镜像

    1. 先要卸载U盘,使用如下命令: #umount /dev/sdb1 注意:/dev/后面的设备要根据你的实际情况而定,否则后面格式化,丢失数据!! 格式化U盘,并建立vfat文件系统 #mkfs. ...