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;
   }
相关推荐
Winston Wood23 分钟前
Android Parcelable和Serializable的区别与联系
android·序列化
清风徐来辽28 分钟前
Android 项目模型配置管理
android
帅得不敢出门1 小时前
Gradle命令编译Android Studio工程项目并签名
android·ide·android studio·gradlew
problc2 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
帅得不敢出门12 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡
我又来搬代码了14 小时前
【Android】使用productFlavors构建多个变体
android
德育处主任15 小时前
Mac和安卓手机互传文件(ADB)
android·macos
芦半山15 小时前
Android“引用们”的底层原理
android·java
迃-幵16 小时前
力扣:225 用队列实现栈
android·javascript·leetcode
大风起兮云飞扬丶16 小时前
Android——从相机/相册获取图片
android