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;
   }
相关推荐
LSL666_2 小时前
5 Repository 层接口
android·运维·elasticsearch·jenkins·repository
alexhilton6 小时前
在Jetpack Compose中创建CRT屏幕效果
android·kotlin·android jetpack
2501_940094028 小时前
emu系列模拟器最新汉化版 安卓版 怀旧游戏模拟器全集附可运行游戏ROM
android·游戏·安卓·模拟器
下位子8 小时前
『OpenGL学习滤镜相机』- Day9: CameraX 基础集成
android·opengl
参宿四南河三10 小时前
Android Compose SideEffect(副作用)实例加倍详解
android·app
火柴就是我11 小时前
mmkv的 mmap 的理解
android
没有了遇见11 小时前
Android之直播宽高比和相机宽高比不支持后动态获取所支持的宽高比
android
shenshizhong11 小时前
揭开 kotlin 中协程的神秘面纱
android·kotlin
vivo高启强12 小时前
如何简单 hack agp 执行过程中的某个类
android
沐怡旸12 小时前
【底层机制】 Android ION内存分配器深度解析
android·面试