HEVC单帧码流硬解渲染

项目里面需要使用读取每一帧单帧码流进行渲染.特此记录一下.

硬解码

  • 硬解码使用的MediaCodec . 一般它与 MediaExtractor 配合使用.
  • MediaExtractor 从MP4等格式中抽取出 码流数据 送给MediaCodec解码器.

  • H.264码流主要分Annex-BAVCC两种格式,H.265码流主要分为Annex-BHVCC格式。AnnexBAVCC/HVCC的区别在于参数集与帧格式,AnnexB的参数集spsppsNAL的形式存在码流中(带内传输),以startcode分割NAL
  • AVCC/HVCC 的参数集存储在extradata中(带外传输),使用NALU长度(固定字节,通常为4字节,从extradata中解析)分隔NAL,通常MP4MKV使用AVCC格式来存储。
  • Android的硬解只接受Annex-B格式的码流,所以在解码MP4 Demux出的视频流时,需要解析extradata,取出sps、pps,通过CSD(Codec-Specific Data)来初始化解码器;并且将AVCC码流转换为Annex-B,在ffmpeg中使用h264_mp4toannexb_filterhevc_mp4toannexb做转换。

方法:

  • 一般编码出来的是Annex-B 格式码流,手动去解析出 CSD 来初始化解码器.
  • 源码链接
package jp.yohhoy.hevcdec;import android.app.Activity;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;public class MainActivity extends Activity {private static final String TAG = "MainActivity";private static byte[] loadRawResource(Context ctx, int resId) {int size = (int) ctx.getResources().openRawResourceFd(resId).getLength();try {byte data[] = new byte[size];InputStream is = ctx.getResources().openRawResource(resId);is.read(data);return data;} catch (IOException ex) {return null;}}private static MediaCodec findHevcDecoder() {// "video/hevc" may select hardware decoder on the device.// "OMX.google.hevc.decoder" is software decoder.final String[] codecNames = {"video/hevc", "OMX.google.hevc.decoder"};for (String name : codecNames) {try {MediaCodec codec = MediaCodec.createByCodecName(name);Log.i(TAG, "codec \"" + name + "\" is available");return codec;} catch (IOException | IllegalArgumentException ex) {Log.d(TAG, "codec \"" + name + "\" not found");}}Log.w(TAG, "HEVC decoder is not available");return null;}private static ByteBuffer extractHevcParamSets(byte[] bitstream) {final byte[] startCode = {0x00, 0x00, 0x00, 0x01};int nalBeginPos = 0, nalEndPos = 0;int nalUnitType = -1;int nlz = 0;ByteArrayOutputStream baos = new ByteArrayOutputStream();for (int pos = 0; pos < bitstream.length; pos++) {if (2 <= nlz && bitstream[pos] == 0x01) {nalEndPos = pos - nlz;if (nalUnitType == 32 || nalUnitType == 33 || nalUnitType == 34) {// extract VPS(32), SPS(33), PPS(34)Log.d(TAG, "NUT=" + nalUnitType + " range={" + nalBeginPos + "," + nalEndPos + "}");try {baos.write(startCode);baos.write(bitstream, nalBeginPos, nalEndPos - nalBeginPos);} catch (IOException ex) {Log.e(TAG, "extractHevcParamSets", ex);return null;}}nalBeginPos = ++pos;nalUnitType = (bitstream[pos] >> 1) & 0x2f;if (0 <= nalUnitType && nalUnitType <= 31) {break;  // VCL NAL; no more VPS/SPS/PPS}}nlz = (bitstream[pos] != 0x00) ? 0 : nlz + 1;}return ByteBuffer.wrap(baos.toByteArray());}private static Size calcImageSize(MediaFormat format) {int cropLeft = format.getInteger("crop-left");int cropRight = format.getInteger("crop-right");int cropTop = format.getInteger("crop-top");int cropBottom = format.getInteger("crop-bottom");int width = cropRight + 1 - cropLeft;int height = cropBottom + 1 - cropTop;return new Size(width, height);}private static Size renderHevcImage(byte[] bitstream, Surface surface) {MediaCodec decoder = findHevcDecoder();if (decoder == null) {return null;}// configure HEVC decoderMediaFormat inputFormat = MediaFormat.createVideoFormat("video/hevc", 640, 480);inputFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bitstream.length);inputFormat.setByteBuffer("csd-0", extractHevcParamSets(bitstream));Log.d(TAG, "input-format=" + inputFormat);decoder.configure(inputFormat, surface, null, 0);MediaFormat outputFormat = decoder.getOutputFormat();Log.d(TAG, "output-format=" + outputFormat);Size imageSize = calcImageSize(outputFormat);decoder.start();// set bitstream to decoderint inputBufferId = decoder.dequeueInputBuffer(-1);if (inputBufferId < 0) {Log.e(TAG, "dequeueInputBuffer return " + inputBufferId);return null;}ByteBuffer inBuffer = decoder.getInputBuffer(inputBufferId);inBuffer.put(bitstream);decoder.queueInputBuffer(inputBufferId, 0, bitstream.length, 0, 0);// notify end of streaminputBufferId = decoder.dequeueInputBuffer(-1);if (inputBufferId < 0) {Log.e(TAG, "dequeueInputBuffer return " + inputBufferId);return null;}decoder.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);// get decoded imageMediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();while (true) {int outputBufferId = decoder.dequeueOutputBuffer(bufferInfo, -1);if (outputBufferId >= 0) {decoder.releaseOutputBuffer(outputBufferId, true);break;} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {outputFormat = decoder.getOutputFormat();Log.d(TAG, "output-format=" + outputFormat);imageSize = calcImageSize(outputFormat);} else {Log.d(TAG, "dequeueOutputBuffer return " + outputBufferId);}}decoder.flush();decoder.stop();decoder.release();return imageSize;}private SurfaceView mSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mSurfaceView = new SurfaceView(this);setContentView(mSurfaceView);// load HEVC bitstream (Annex.B format)final byte[] bitstream = loadRawResource(this, R.raw.lena_std);Log.d(TAG, "length=" + bitstream.length);mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.d(TAG, "surfaceChanged format=" + format + " size=" + width + "x" + height);Size sz = renderHevcImage(bitstream, holder.getSurface());if (sz != null) {// fit SurfaceView to decoded imageViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();lp.width = sz.getWidth();lp.height = sz.getHeight();mSurfaceView.setLayoutParams(lp);}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}});}
}

参考文献

如何渲染编码的裸数据
码流格式
Android转码SDK实战

项目记录: HEVC单帧码流硬解渲染相关推荐

  1. HM-16.0编码过程:将YUV文件编码成HEVC格式的码流

    HM-16.0编码:将YUV文件编码成HEVC格式的码流 注: 1   为了快速优化运行(不调程序的时候),可以将程序的版本设为"release",否则还是设为"debu ...

  2. H265/HEVC视频分层码流分析语义元素解释

    H265/HEVC视频分层码流分析语义元素解释 H265分层结构 H265码流结构 Ffmpge 抽取H264,H265码流的命令 视频参数集VPS 序列参数集SPS(Sequence Paramet ...

  3. ffplay使用dxva2实现硬解渲染

    ffplay自定义系列 第一章 自定义播放器接口 第二章 倍速播放 第三章 dxva2硬解渲染(本章) 第四章 提供C#接口 第五章 制作wpf播放器 文章目录 ffplay自定义系列 前言 一.ff ...

  4. 深入讲解音视频编码原理,H264码流详解——手写H264编码器

    音视频高手课08-H264 I帧 P帧 B帧及手写H264编码器 1 三种帧的说明 1.I 帧:帧内编码帧,帧表示关键帧,你可以理解为这一帧画面的完整保留:解码时只需要本帧数据就可以完成(因为包含完整 ...

  5. 介绍DVB-S码流_PAT_PMT_NIT_CAT

    介绍DVB-S码流,PAT,PMT,NIT,CAT 摘要 本文介绍了电子节目指南信息的构成以及MPEG-2 PSI和DVB-SI信息规范,提出了一种EPG信息的存储结构和节目数据库的生成方案,并据此讲 ...

  6. H264码流分析工具

    概述 本文作为一个笔记,记录笔者学习H264码流的过程.重点记录使用工具分析H264码流.使用该工具,可方便查看码流中NALU的结构,为我们学习和理解有很大帮助. H264码流介绍 笔者直接参考:ht ...

  7. matlab双极性眼图,实验报告三编程做单极性码眼图仿真

    <实验报告三编程做单极性码眼图仿真>由会员分享,可在线阅读,更多相关<实验报告三编程做单极性码眼图仿真(4页珍藏版)>请在人人文库网上搜索. 1.1.实验目的(1)掌握单极性码 ...

  8. 【开源工程】之裸码流提取工具--H264/H265

    序  工程中常常需要分析一些码流,但码流文件过大的话,不利于分析具体的帧数据,由此编写了码流提取软件.  此工具是在业余时间完成,很多功能尚待完善,如果茫茫中,你发现了此工具,也感兴趣研究,希望你将工 ...

  9. Qt结合FFmpeg转码码流数据(h264)生成不同视频格式(mp4、mov、flv、avi等)

    目录 1.转码流程分析 2.创建一个类专门用来转码 .h文件 构造函数 打开对应的码流数据 转码得到最终的封装格式 主函数测试 转码运行结果 1.转码流程分析 /*转码流程分析: * 1.注册组件 * ...

最新文章

  1. 刮刮乐html5效果擦除,利用HTML5的画布Canvas实现刮刮卡效果
  2. ValueError: Bin labels must be one fewer than the number of bin edges
  3. SQL 交集 差集 并集 笛卡尔积 应用实例
  4. 参数修饰符 params、in
  5. 纪实:西藏少数民族儿童的“悲苦童年”(组图)
  6. officeopenxml excelpackage 需要安装excel嘛_使用ABAP操作Excel的几种方法
  7. hashmap中的key是有序的么_HashMap?面试?我是谁?我在哪
  8. SpringBoot2.1.9 多数据源JDBC配置
  9. 【mysql的设计与优化专题(6)】mysql索引攻略
  10. ubuntu下使用ppa安装codeblocks集成开发环境
  11. 拿下微软、Google、Adobe,印度为何盛产科技圈 CEO?
  12. tp5分页不加载搜索参数
  13. java 获取运行时参数,Java8增强反射可以在运行时获取参数名
  14. JavaScript从父页面获取子页面的值(子页面又如何访问父页面)
  15. 遥感数据集_最新高光谱遥感数据集
  16. 班级管理servlet项目开发详细讲解,其中涉及js、jq、ajax、等多项技术合计,本项目来源自网络,如有雷同,请私聊博主
  17. 标记集合 java编译_深入理解Java虚拟机读书笔记-java编译期和运行期优化
  18. 华为交换机主备命令_华为交换机基本命令
  19. Python之多张图片拼接
  20. The Innovation | 用系统生物学的观点鸟瞰肿瘤易感基因

热门文章

  1. tipsy - Facebook-style tooltip plugin for jQuery
  2. 基于51单片机的智能煤气天然气CO检测阈值报警器排气风扇方案原理图设计
  3. GBase 8a MPP使用时 数据库基础问题之管理工具三
  4. 华云大咖说 | 混合IT架构的统一管理——安超云套件产品介绍
  5. 【论文摘要】基于多数投票模式和超混沌加密的彩色图像鲁棒安全零水印算法
  6. mysql聚类函数排序_聚类算法大盘点 - 如鱼饮水,冷暖自知 - OSCHINA - 中文开源技术交流社区...
  7. [SpringBoot] SpringBoot入门
  8. 世界坐标系和图像坐标系的对应关系
  9. 拉格朗日插值公式---读题题
  10. 为什么游戏玩家抗拒NFT?