使用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. FindBugs插件

    在日常开发过程中难免会因为一时疏忽而留下一些Bug,这些Bug就是埋在程序里的定时炸弹,如果不能及时铲除就会导致程序的不稳定,异常或闪退的现象,从而导致用户的体验的下降.那么怎么才能找出这些埋在程序里 ...

  2. ADF_ADF Faces系列1_使用JSF开发基于Ajax的用户界面:ADF Faces 富客户端组件简介(Part1)

    2013-05-01 Created By BaoXinjian

  3. 网页地图map

    <map name="map"> <area shape="rect" coords="75,75,99,99" nohr ...

  4. javascript当文本框获得焦点设置边框

    javascript当文本框获得焦点设置边框:本章节介绍一下当文本框获得焦点以后如何设置文本框的边框样式,本来是一个非常简单的问题,但是有可能前台美工人员对javascript并不是太了解,所以还是通 ...

  5. Linux中变量$#,$@,$0,$1,$2,$*,$$,$?的含义(转载)

    From:http://dadekey.blog.51cto.com/107327/119938 我们先写一个简单的脚本,执行以后再解释各个变量的意义   # touch variable # vi ...

  6. SPR EAD NET 6

    SPR EAD_NET6 下载地址 http://www.gcpowertools.com.cn/downloads/trial/Spread.NET/EN_SPREAD_NET6_SETUP_RA_ ...

  7. sikuli+java实例

      新建java工程,导入sikuli-script.jar包 public class TestSikuli { public static void openPage() throws FindF ...

  8. Maven项目中,编译proto文件成Java类

    新建Maven项目 新建一个 Maven 项目: pom定义了最小的maven2元素,即:groupId,artifactId,version. groupId:项目或者组织的唯一标志,并且配置时生成 ...

  9. Django下载中文名文件:

    Django下载中文名文件: from django.utils.http import urlquote from django.http import HttpResponse content = ...

  10. lower_bound 和 upper_bound

    Return iterator to lower bound Returns an iterator pointing to the first element in the range [first ...