H.264编解码小结

编码的流程:采集--> 获取到视频帧--> 对视频帧进行编码 --> 获取到视频帧信息 --> 将编码后的数据以NALU方式写入到文件

1.NAL的封装方式:

  • NAL是将每一帧数据写入到一个或者多个NALU单元中,进行传输或存储的
  • NALU分为NAL头和NAL体
  • NALU头通常为00 00 00 01,作为一个新的NALU的起始标识

2.封装过程:

  • I帧、P帧、B帧都是被封装成一个或者多个NALU进行传输或者存储的
  • I帧开始之前也有非VCL的NAL单元,用于保存其他信息,比如:PPS、SPS
  • PPS(Picture Parameter Sets):图像参数集
  • SPS(Sequence Parameter Set):序列参数集
  • 在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧,后续是B帧、P帧等数据

3.如何获取SPS和PPS

使用RTP传输H264的时候,需要用到sdp协议描述,其中有两项:Sequence Parameter Sets (SPS) 和Picture Parameter Set (PPS)需要用到,那么这两项从哪里获取呢?答案是从H264码流中获取.在H264码流中,都是以"0x00 0x00 0x01"或者"0x00 0x00 0x00 0x01"为开始码的,找到开始码之后,使用开始码之后的第一个字节的低5位判断是否为7(sps)或者8(pps), 及data[4] & 0x1f == 7 || data[4] & 0x1f == 8,得到的信息就可以用于sdp.sps和pps需要用逗号分隔开来.

typedef enum
{
    NALUTypeSliceNoneIDR = 1,
    NALUTypeSliceIDR = 5,
    NALUTypeSPS = 7,
    NALUTypePPS = 8
} NALUType;

- (int)getNALUType:(NSData *)NALU
{
    uint8_t * bytes = (uint8_t *) NALU.bytes;
    return bytes[4] & 0x1F;
}

-(void)logType:(NSData *)h264Data
{
    int type = [self getNALUType: h264Data];
    switch (type) {
        case NALUTypeSliceNoneIDR:
            NSLog(@"NALUTypeSliceNoneIDR");
            break;
        case NALUTypeSliceIDR:
            NSLog(@"_______________NALUTypeSliceIDR");
            break;
        case NALUTypeSPS:
            NSLog(@"~~~~~~NALUTypeSPS");
            break;
        case NALUTypePPS:
            NSLog(@"~~~~~~NALUTypePPS");
            break;
            
        default:
            NSLog(@"type = %i",type);
            break;
    }
}

你可能感兴趣的:(H.264编解码小结)