从RTSP协议SDP数据中获得H264中的的SPS、PPS

来源:互联网 发布:物联网与java 编辑:程序博客网 时间:2024/05/22 04:49

1. 如何解析SDP中包含的H.264的SPS和PPS串

SDP中的H.264的SPS和PPS串,包含了初始化H.264解码器所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等。
由于SDP中的SPS和PPS都是BASE64编码形式的,不容易理解,附件有一个工具软件可以对SDP中的SPS和PPS进行解析。
用法是在命令行中输入:
spsparser sps.txt pps.txt output.txt

例如sps.txt中的内容为:
Z0LgFNoFglE=
pps.txt中的内容为:
aM4wpIA=

最终解析的到的结果为:

Start dumping SPS:
  profile_idc = 66
  constrained_set0_flag = 1
  constrained_set1_flag = 1
  constrained_set2_flag = 1
  constrained_set3_flag = 0
  level_idc = 20
  seq_parameter_set_id = 0
  chroma_format_idc = 1
  bit_depth_luma_minus8 = 0
  bit_depth_chroma_minus8 = 0
  seq_scaling_matrix_present_flag = 0
  log2_max_frame_num_minus4 = 0
  pic_order_cnt_type = 2
  log2_max_pic_order_cnt_lsb_minus4 = 0
  delta_pic_order_always_zero_flag = 0
  offset_for_non_ref_pic = 0
  offset_for_top_to_bottom_field = 0
  num_ref_frames_in_pic_order_cnt_cycle = 0
  num_ref_frames = 1
  gaps_in_frame_num_value_allowed_flag = 0
  pic_width_in_mbs_minus1 = 21
  pic_height_in_mbs_minus1 = 17
  frame_mbs_only_flag = 1
  mb_adaptive_frame_field_flag = 0
  direct_8x8_interence_flag = 0
  frame_cropping_flag = 0
  frame_cropping_rect_left_offset = 0
  frame_cropping_rect_right_offset = 0
  frame_cropping_rect_top_offset = 0
  frame_cropping_rect_bottom_offset = 0
  vui_parameters_present_flag = 0

Start dumping PPS:
  pic_parameter_set_id = 0
  seq_parameter_set_id = 0
  entropy_coding_mode_flag = 0
  pic_order_present_flag = 0
  num_slice_groups_minus1 = 0
  slice_group_map_type = 0
  num_ref_idx_l0_active_minus1 = 0
  num_ref_idx_l1_active_minus1 = 0
  weighted_pref_flag = 0
  weighted_bipred_idc = 0
  pic_init_qp_minus26 = 0
  pic_init_qs_minus26 = 0
  chroma_qp_index_offset = 10
  deblocking_filter_control_present_flag = 1
  constrained_intra_pred_flag = 0
  redundant_pic_cnt_present_flag = 0
  transform_8x8_mode_flag = 0
  pic_scaling_matrix_present_flag = 0
  second_chroma_qp_index_offset = 10

/////////////////////////////////////////////////////////////////////////////////////////////////
这里需要特别提一下这两个参数
pic_width_in_mbs_minus1 = 21
  pic_height_in_mbs_minus1 = 17
分别表示图像的宽和高,以宏块(16x16)为单位的值减1
因此,实际的宽为 (21+1)*16 = 352

经包括SPS&PPS,码流中完全可以不用传输SPS&PPS
2. profile-level-id=42A01E,这是SPS的开头几个字节,剩下的在sprop-parameter- ets=Z0IACpZTBYmI, aMljiA==中,BASE64编码,把“Z0IACpZTBYmI, aMljiA==”反BASE64转换回去,应该刚好是SPS&PPS的内容


3.   在RTSP协议的交互过程中,第二步客户端发送DESCRIBE请求之后,服务端会返回SDP内容,该SDP内容中有关于媒体和会话的描述,本篇文章主要给出如何从SDP字符串中得到H264视频信息中的sps、pps的二进制数据。

我们知道,在RTSP协议中DESCRIBE请求回复内容的SDP部分中,如果服务端的直播流的视频是H264的编码格式的话,那么在SDP中会将H264的sps、pps信息通过Base64编码成字符串发送给客户端(也就是解码器端),sps称为序列参数集,pps称为图形参数集。这两个参数中包含了初始化H.264解码器所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等。这样解码器就可以在DESCRIBE阶段,利用这些参数初始化解码器的设置了。那么如何将SDP中的字符串还原成sps、pps的二进制数据呢。下面的部分代码是从live555项目中取出来的,可以作为小功能独立使用,如果大家有用的着,可以直接拿去使用在项目中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//main.cpp的内容
 
#include <stdio.h>
#include "Base64.h"
 
intmain()
{
    /*
        RTSP 响应的SDP的内容中sprop-parameter-sets键值:
        sprop-parameter-sets=Z2QAKq2wpDBSAgFxQWKQPQRWFIYKQEAuKCxSB6CKwpDBSAgFxQWKQPQRTDoUKQNC4oJHMGIemHQoUgaFxQSOYMQ9MOhQpA0LigkcwYh6xEQmIVilsQRWUURJsogxOU4QITKUIEVlCCTYQVhBMJQhMIjGggWQJFaIGBJZBAaEnaMIDwsSWQQKCwsrRBQYOWQweO0YEBZASNAogszlAUAW7/wcFBwMQAABdwAAr8g4AAADAL68IAAAdzWU//+MAAADAF9eEAAAO5rKf//CgA==,aP48sA==;
        其中逗号前面的内容是sps的二进制数据被base64之后的结果
        而逗号后面的内容(不要分号,分号是sdp中键值对的分隔符),是pps的内容
        使用live555中的base64Decode函数分别对这两部分进行反base64解码得到的二进制数据就是h264中的sps pps 的二进制内容
        分别是以67 和 68 开头
    */
    char* sps_sdp = "Z2QAKq2wpDBSAgFxQWKQPQRWFIYKQEAuKCxSB6CKwpDBSAgFxQWKQPQRTDoUKQNC4oJHMGIemHQoUgaFxQSOYMQ9MOhQpA0LigkcwYh6xEQmIVilsQRWUURJsogxOU4QITKUIEVlCCTYQVhBMJQhMIjGggWQJFaIGBJZBAaEnaMIDwsSWQQKCwsrRBQYOWQweO0YEBZASNAogszlAUAW7/wcFBwMQAABdwAAr8g4AAADAL68IAAAdzWU//+MAAADAF9eEAAAO5rKf//CgA==";
    char* pps_sdp = "aP48sA==";
    unsignedintresult_size=0;
    unsignedchar* p = base64Decode(sps_sdp,result_size);
    for(inti =0;i<result_size;i++)
    {
        printf("%02X ",p[i]);
        if((i+1)%16==0)
        {
            printf("\n");
        }      
    }
    printf("\n\n\n");  
    p = base64Decode(pps_sdp,result_size);
    for(inti =0;i<result_size;i++)
    {
        printf("%02X ",p[i]);
        if((i+1)%16==0)
        {
            printf("\n");
        }      
    }
    printf("\n");  
    return0 ;
}<br>
/*
程序的解码输出如下,得到的分别是3500的sps和pps内容:
 
67 64 00 2A AD B0 A4 30 52 02 01 71 41 62 90 3D
04 56 14 86 0A 40 40 2E 28 2C 52 07 A0 8A C2 90
C1 48 08 05 C5 05 8A 40 F4 11 4C 3A 14 29 03 42
E2 82 47 30 62 1E 98 74 28 52 06 85 C5 04 8E 60
C4 3D 30 E8 50 A4 0D 0B 8A 09 1C C1 88 7A C4 44
26 21 58 A5 B1 04 56 51 44 49 B2 88 31 39 4E 10
21 32 94 20 45 65 08 24 D8 41 58 41 30 94 21 30
88 C6 82 05 90 24 56 88 18 12 59 04 06 84 9D A3
08 0F 0B 12 59 04 0A 0B 0B 2B 44 14 18 39 64 30
78 ED 18 10 16 40 48 D0 28 82 CC E5 01 40 16 EF
FC 1C 14 1C 0C 40 00 01 77 00 00 AF C8 38 00 00
03 00 BE BC 20 00 00 77 35 94 FF FF 8C 00 00 03
00 5F 5E 10 00 00 3B 9A CA 7F FF C2 80
 
68 FE 3C B0
*/

 

 其中用到的一个主要函数 base64Decode 的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include "Base64.h"
#include "strDup.h"
#include <string.h>
 
staticcharbase64DecodeTable[256];
 
staticvoidinitBase64DecodeTable() {
  inti;
  for(i = 0; i < 256; ++i) base64DecodeTable[i] = (char)0x80;
      // default value: invalid
 
  for(i = 'A'; i <= 'Z'; ++i) base64DecodeTable[i] = 0 + (i - 'A');
  for(i = 'a'; i <= 'z'; ++i) base64DecodeTable[i] = 26 + (i - 'a');
  for(i = '0'; i <= '9'; ++i) base64DecodeTable[i] = 52 + (i - '0');
  base64DecodeTable[(unsignedchar)'+'] = 62;
  base64DecodeTable[(unsignedchar)'/'] = 63;
  base64DecodeTable[(unsignedchar)'='] = 0;
}
 
unsignedchar* base64Decode(charconst* in, unsigned& resultSize,
                Boolean trimTrailingZeros) {
  staticBoolean haveInitedBase64DecodeTable = False;
  if(!haveInitedBase64DecodeTable) {
    initBase64DecodeTable();
    haveInitedBase64DecodeTable = True;
  }
 
  unsignedchar* out = (unsigned char*)strDupSize(in);// ensures we have enough space
  intk = 0;
  intconstjMax = strlen(in) - 3;
     // in case "in" is not a multiple of 4 bytes (although it should be)
  for(intj = 0; j < jMax; j += 4) {
    charinTmp[4], outTmp[4];
    for(inti = 0; i < 4; ++i) {
      inTmp[i] = in[i+j];
      outTmp[i] = base64DecodeTable[(unsigned char)inTmp[i]];
      if((outTmp[i]&0x80) != 0) outTmp[i] = 0; // pretend the input was 'A'
    }
 
    out[k++] = (outTmp[0]<<2) | (outTmp[1]>>4);
    out[k++] = (outTmp[1]<<4) | (outTmp[2]>>2);
    out[k++] = (outTmp[2]<<6) | outTmp[3];
  }
 
  if(trimTrailingZeros) {
    while(k > 0 && out[k-1] == '\0') --k;
  }
  resultSize = k;
  unsignedchar* result = newunsignedchar[resultSize];
  memmove(result, out, resultSize);
  delete[] out;
 
  returnresult;
}
 
staticconstcharbase64Char[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
char* base64Encode(charconst* origSigned, unsigned origLength) {
  unsignedcharconst* orig = (unsigned charconst*)origSigned;// in case any input bytes have the MSB set
  if(orig == NULL) returnNULL;
 
  unsignedconstnumOrig24BitValues = origLength/3;
  Boolean havePadding = origLength > numOrig24BitValues*3;
  Boolean havePadding2 = origLength == numOrig24BitValues*3 + 2;
  unsignedconstnumResultBytes = 4*(numOrig24BitValues + havePadding);
  char* result = newchar[numResultBytes+1];// allow for trailing '\0'
 
  // Map each full group of 3 input bytes into 4 output base-64 characters:
  unsigned i;
  for(i = 0; i < numOrig24BitValues; ++i) {
    result[4*i+0] = base64Char[(orig[3*i]>>2)&0x3F];
    result[4*i+1] = base64Char[(((orig[3*i]&0x3)<<4) | (orig[3*i+1]>>4))&0x3F];
    result[4*i+2] = base64Char[((orig[3*i+1]<<2) | (orig[3*i+2]>>6))&0x3F];
    result[4*i+3] = base64Char[orig[3*i+2]&0x3F];
  }
 
  // Now, take padding into account.  (Note: i == numOrig24BitValues)
  if(havePadding) {
    result[4*i+0] = base64Char[(orig[3*i]>>2)&0x3F];
    if(havePadding2) {
      result[4*i+1] = base64Char[(((orig[3*i]&0x3)<<4) | (orig[3*i+1]>>4))&0x3F];
      result[4*i+2] = base64Char[(orig[3*i+1]<<2)&0x3F];
    }else{
      result[4*i+1] = base64Char[((orig[3*i]&0x3)<<4)&0x3F];
      result[4*i+2] = '=';
    }
    result[4*i+3] = '=';
  }
 
  result[numResultBytes] = '\0';
  returnresult;
}

 完整demo下载(Ubuntu Linux下运行没问题)

0 0
原创粉丝点击