Android获取 H264视频流中的SPS和PPS

代码其实很简单,主要是要知道H264帧数据结构。分析H264码流结构的文章一大把,自己网上搜索一下就知道了。

背景:

h264+aac封装mp4格式的时候,需要获取视频流的sps,pps。

封装mp4文件格式的大体方案

使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件(硬解码的方式) (纯java)

使用MP4v2将H264/H265码流以及AAC音频封装成MP4格式(软解码的方式)(JNI+C)

使用FFmpeg将H264/H265码流以及AAC音频封装成MP4格式(软解码的方式)(JNI+C)
"csd-0" 和 "csd-1" 是什么,对于 H264 视频的话,它对应的是 sps 和 pps,对于 AAC 音频的话,对应的是 ADTS,做音视频开发的人应该都知道,它一般存在于编码器生成的 IDR 帧之中。

创建并配置 codec。配置 codec 时,若手动创建 MediaFormat 对象的话,一定要记得设置 "csd-0" 和 "csd-1" 这两个参数。 "csd-0" 和 "csd-1" 这两个参数一定要和接收到的帧对应上。

c 复制代码
   /**
    * 判断I针中有没有sps pps
    * @param type  0==264  1==265
    * @param data
    * @return
    */
   public static boolean ISIFrameData(int type, byte[] data) {
      int nalType;
      if (type == 0) {
         int FF_H264_SPS_NAL = 7;
         int FF_H264_PPS_NAL = 8;
         nalType = data[4] & 0x1f;
         XLog.e("","h264 nalType=== "+nalType);
         if (FF_H264_SPS_NAL == nalType ||
                 FF_H264_PPS_NAL == nalType
         ) {
            return true;
         }
      } else  if (type == 1){
         int FF_HEVC_VPS_NAL = 32;
         int FF_HEVC_PPS_NAL = 34;
         int FF_HEVC_SPS_NAL = 33;
         nalType = (data[4] & 0x7e) >> 1;
         XLog.e("","h265 nalType=== "+nalType);
         if (FF_HEVC_VPS_NAL == nalType ||
                 FF_HEVC_PPS_NAL == nalType ||
                 FF_HEVC_SPS_NAL == nalType){
            return true;
         }
      }
      return false;
   }



  /**
    * 获取I帧里面的 sps pps 信息
    * @param type  0==264  1==265
    * @param frame  帧数据
    * @return
    */
   public static List<byte []> getPPSByKeyFrameData(int type , byte[] frame){
      Map<Integer,Integer> map=new HashMap<>();
      int save_x=0;
      List<byte[]> list=new ArrayList<>(2);
      byte[]sps,pps;
      if(type==0){//h264
         int nalType=frame[4]&0x1f;//判断I帧,然后根据具体情况获取sps,pps
         if(nalType==7){
            //[Start Code]:Start Code 用于标示这是一个NALU 单元的开始(也称分隔符),必须是"00 00 00 01" 或"00 00 01"。
            //关键帧是00 00 00 01
            for (int i = 0; i < frame.length-3; i++) {
               if(frame[i]==0&&frame[i+1]==0&&frame[i+2]==0&&frame[i+3]==1){
                   map.put(save_x,i);
                   save_x++;
               }
            }
            int spsLength=map.get(1)-map.get(0)-4;
            int spsOffset=map.get(0)+3;
            sps=new byte[spsLength];
            System.arraycopy(frame,spsOffset,sps,0,spsLength);

            int ppsLength=map.get(2)-map.get(1)-4;
            int ppsOffset=map.get(1)+3;
            pps=new byte[ppsLength];
            System.arraycopy(frame,ppsOffset,pps,0,ppsLength);

            list.add(sps);
            list.add(pps);
         }
      }
      return list;
   }
相关推荐
一笑的小酒馆19 分钟前
Androidiot扫地机器人设备详情页地图加载慢卡顿优化方案
android
Zhu7586 小时前
在k8s集群部署MySQL单实例,支持多个主流发行版
android·mysql·kubernetes
AFinalStone9 小时前
Android 7系统异常问题排查(四)Framework层(上)—System Server Watchdog
android·系统异常
宝杰X711 小时前
Android Room3 多平台数据库
android·数据库
huibin14785236914 小时前
热缓解学习记录
android·学习
小田的博客16 小时前
SAP MM 供应商银行主数据更新报错!message R1228!
android·java·服务器
一笑的小酒馆18 小时前
Android智能猫砂盆视频加载慢卡顿问题分析
android
只会cv的小前端19 小时前
七巧低代码服务端脚本使用方法
android·低代码·rxjava
19 小时前
Android 自定义 View 实战:从零打造工业级全向摇杆控件(OmniJoystickView)
android·kotlin·自定义view·摇杆控件
雨白19 小时前
深入理解 Kotlin 协程 (十一):以逸待劳,探秘 select 多路复用与并发安全策略
android·kotlin