代码其实很简单,主要是要知道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” 这两个参数一定要和接收到的帧对应上。
/*** 判断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){//h264int nalType=frame[4]&0x1f;//判断I帧,然后根据具体情况获取sps,ppsif(nalType==7){//[Start Code]:Start Code 用于标示这是一个NALU 单元的开始(也称分隔符),必须是”00 00 00 01” 或”00 00 01”。//关键帧是00 00 00 01for (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;}