上一节记录了Android使用FFmpeg环境搭建过程。这一节记录视频解码过程。

问题描述

在开发中使用某摄像头的SDK,只能获取到一帧帧的 H264 视频数据,不知道视频流地址,需要自己解码出图像并播放。

问题解决

编译FFmpeg

点击查看

开发环境配置

点击查看

解码H264

原始数据格式

首先看我们能获取到数据格式

public class VideoStream{//video bufferbyte[] streamBuffer;//ppsbyte[] ppsBuffer;//spsbyte[] spsBuffer;//当前是I帧还是P帧int frameType;
}

Java层代码

我们需要将从Java层取到的原始数据通过JNI传递到C层,交给FFmpeg解析。 Java 类大致如下:

public class H264FrameRender {static {//加载自己的 so 库System.loadLibrary("decoder");}//保存C中的对象内存地址private long nativeObject;public H264FrameRender() {long address = this._init();if (address <= 0) {throw new IllegalStateException("init failed");}this.nativeObject = address;}/*** 将当前的buffer写入缓冲队列,等待解析*/public void write(VideoStream videoStream) {if (nativeObject <= 0) {throw new IllegalStateException("H264FrameRender init failed,cannot decode ");}byte[] streamBuffer = videoStream.getmStreamBuffer();byte[] spsBuffer = videoStream.getmSPSBuffer();byte[] ppsBuffer = videoStream.getmPPSBuffer();int spsLen = spsBuffer == null ? 0 : spsBuffer.length;int ppsLen = ppsBuffer == null ? 0 : ppsBuffer.length;boolean isIFrame = videoStream.getmType() == VideoStream.IFrameType;_write(nativeObject, streamBuffer, streamBuffer.length, spsBuffer, spsLen, ppsBuffer,ppsLen, isIFrame);}/*** 释放内存,调用该方法后,将会释放C分配的内存空间,并将该Java对象标记为不可用*/  public void release() {if (this.nativeObject > 0) {_release(this.nativeObject);this.nativeObject = 0;}}/*** 如果没有主动release,C 申请的空间将在Java类销毁时自动释放**@throws Throwable*/@Overrideprotected void finalize() throws Throwable {release();super.finalize();}/*** 初始化内存空间,会调用C申请一段内存,并返回内存地址*/private native long _init();/*** 将数据通过 JNI 交给 C 去解析*/private native void _write(long nativeObject, byte[] streamBuffer, int length, byte[] spsBuffer,int spsLen, byte[] ppsBuffer, int ppsLen, boolean isIFrame);}

JNI 代码

新建 h264_render.ch264_render.h ,添加两个JNI方法:

//h264_render.h
#ifndef LITTLELF_JNI_H264_RENDER_H
#define LITTLELF_JNI_H264_RENDER_H#include <jni.h>//Java: _init()
JNIEXPORT jlong JNICALL
Java_com_hencenx_littlelf_jni_H264FrameRender__1init(JNIEnv *env, jobject instance);//Java: _write(long nativeObject, long nativeObject, byte[] streamBuffer, int length, byte[] spsBuffer,int spsLen, byte[] ppsBuffer, int ppsLen, boolean isIFrame)
JNIEXPORT void JNICALL
Java_com_hencenx_littlelf_jni_H264FrameRender__1write(JNIEnv *env, jobject instance,jlong nativeObject, jbyteArray streamBuffer_,jint length, jbyteArray spsBuffer_,jint spsLen, jbyteArray ppsBuffer_,jint ppsLen, jboolean isIFrame);
//Java: _release(long nativeObject)
JNIEXPORT void JNICALL
Java_com_hencenx_littlelf_jni_H264FrameRender__1release(JNIEnv *env, jobject instance,jlong nativeObject);#endif //LITTLELF_JNI_H264_RENDER_H

至此,我们可以在 C 层访问到流的 buffer 了。

FFmpeg 解析视频流

解码基本流程

FFmpeg 提供了一个解码和编码的 demo,代码很简单,精简后的代码如下:

//1.注册所有编解码器,注册后才能使用
avcodec_register_all();
//2.从注册的解码器里找到H264解码器
AVCodec * codec = avcodec_find_decoder(AV_CODEC_ID_H264);
//3. 初始化解码的上下文,上下文很关键,包含了解码所需要的信息
AVCodecContext * ctx = avcodec_alloc_context3(codec);
//准备一个容器用来装需要解码的原始H264数据
AVPacket avpkt;
//4. 准备一个容器用来装解码后的数据,AVFrame既可以表示视频数据,也可以表示音频数据
AVFrame frame = av_frame_alloc();
//5. 初始化avpkt,并将H264数据放进去(此处代码省略)
//6. 初始化解码上下文,设置视频宽高等(因为可能是从I帧中获取的,所以写在这一步,此处代码省略)
//7. 根据解码上下文打开解码器,这样解码器才算初始完毕,可以解码了
avcodec_open(ctx, codec, NULL);
//8. 解码 - 发送需要解码的数据给上下文
avcodec_send_packet(ctx, avpkt);
//9. 解码 - 从上下文中获取解码后的frame,解码完成
avcodec_receive_frame(ctx, frame);
利用 sps 和 pps 初始化 上下文

我们的处理流程和 demo 类似,但稍有不同。因为解码H264必须提供视频的宽高,否则解析过程就会报错。 但是原始数据中并没有提供视频宽高。经过查阅得知,sps 和 pps 中就包含了视频宽高和其他一些解码必须的数据。我们需要将 sps 和 pps 放到AVCodecContext 的 extradata 中。
代码如下:

int extra_len = sps_len + pps_len;
ctx->extradata = av_malloc(sizeof(uint8_t) * (size_t) (extra_len + AV_INPUT_BUFFER_PADDING_SIZE));
ctx->extradata_size = extra_len;
memcpy(ctx->extradata, (uint8_t *) input_sps, (size_t) sps_len);
memcpy(ctx->extradata + (size_t) sps_len, (uint8_t *) input_pps, (size_t) pps_len);            

经过以上处理,H264 解码就完成了。

Android使用FFmpeg 解码H264并播放(二)相关推荐

  1. ffmpeg 切片花屏_利用ffmpeg解码H264,花屏,该如何解决

    利用ffmpeg解码H264,花屏 UINT CMP4File::VideoCap_Thread_Fun(void* pParam) { CMP4File  *pMP4File=(CMP4File*) ...

  2. ffmpeg解码H264缺少帧的解决办法

    最近用ffmpeg解码H264裸码流文件,发现解码总是少几帧.上网查了些资料,解决了. 当使用avcodec_decode_video2时,如果第三个参数的值为1,则表示完成一帧的解码,如果为0,表示 ...

  3. 使用FFMPEG解码和OpenAL播放音乐

    使用FFMPEG解码和OpenAL播放音乐 OpenAL是一个开源的音效库,然而这里只用它来播放音乐.  FFMPEG负责把音乐解码并转换成指定的编码和采样率,然后送到OpenAL中播放.  (已在w ...

  4. android下实时传输h264并播放

    这周给安排了个任务,在android端读取H264原始视频数据,传一帧播一帧,播就播吧,还要Socket实时传输实时播,虽然并没有接触过,都无从下手,不过想到直播都是这么个形式,说明肯定是行得通的,而 ...

  5. Android使用MediaCodec解码H264视频解码器

    前些日子有写了一篇博客[Android使用MediaCodec硬解码播放H264格式视频文件](http://blog.csdn.net/true100/article/details/5399293 ...

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

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

  7. FFmpeg解码H264裸流并转换成opencv Mat

    感谢雷霄骅博士的在中文视频编解码的付出,http://blog.csdn.net/leixiaohua1020 最近要搞一些视频推流的事情,要解析H264裸流并且获取opencv格式的Mat数据给算法 ...

  8. android ffmpeg 解码,如何在Android用FFmpeg解码图像

    白猪掌柜的 创建一个VideoPicture结构体用来保存解码出来的图像./** SDL_Lesson.c** Created on: Aug 12, 2014* Author: clarck*/#i ...

  9. FFmpeg解码的全能播放器

    http://www.eoeandroid.com/thread-53373-1-1.html

  10. 使用ffmpeg解码音频sdl(push)播放

    自定义播放器系列 第一章 视频渲染 第二章 音频(push)播放(本章) 第三章 音频(pull)播放 第四章 实现时钟同步 第五章 实现通用时钟同步 第六章 实现播放器 文章目录 自定义播放器系列 ...

最新文章

  1. shell查看并修复网络连接
  2. linux搭建--centos使用qemu-kvm,libvirt搭建虚拟机,并搭建libvmi来虚拟机自省(四)
  3. 滚动条禁止_Axure 教程:不可见滚动条的页面滚动效果
  4. 其实你女朋友也不是很爱你...
  5. Python稳基修炼的经典案例7(计算机二级、初学者必须掌握的例题)
  6. 几行Python代码生成饭店营业额模拟数据并保存为CSV文件
  7. xp下固态硬盘测试软件,XP真的老矣?SSD实战XP/Win7/Win8系统
  8. 购买域名和个人网站备案流程
  9. 命令行查看ubuntu版本号
  10. 这些年,这些ACM大佬-吴作凡访谈
  11. 原神紫晶块采集点位置在哪 紫晶块采集点路线图详情
  12. Mocha.js官方文档翻译 —— 简单、灵活、有趣
  13. 【Pygame小游戏】这款“打地鼠”小游戏要火了(来来来)
  14. 跟我学RocketMQ之批量消息发送源码解析
  15. Visual Studio2022 运行代码时“发生生成错误,是否继续并运行上次的成功生成”
  16. OKHTTP和retrofit 网络框架集成的有https验证的APP破解抓包
  17. 被缠上了,小王问我怎么在 Spring Boot 中使用 JDBC 连接 MySQL
  18. matlab中的measure,Matlab中 awgn 函数输入参数带有‘measured’ 时snr的含义
  19. 人类发明技术本来是为了便利,但最终自己的生活却被技术无情地改变甚至主宰了
  20. Mybatis 查询 返回值中只有id有值,其他都是null;

热门文章

  1. NX的尺寸控制与半径补偿(重要)
  2. 揭开A股38位「 90后」董事长的神秘面纱
  3. tcgames使用有延迟_Tcgames下载使用方法
  4. 美国出台最严技术出口管制!14项前沿科技面临封锁
  5. 2022年京东新百货七夕礼遇季活动有什么亮点?
  6. Idea插件官网下载安装(plugins)
  7. linux syn 队列,TCP SYN队列与Accept队列详解
  8. 最新kali之bulk_extractor
  9. 电脑重装系统后被格式化了怎么恢复之前的数据?
  10. 教你使用反格式化工具恢复格式化数据!