• 从RTSP协议SDP数据中获得二进制的SPS、PPS


    在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项目中取出来的,可以作为小功能独立使用,如果大家有用的着,可以直接拿去使用在项目中:

    //main.cpp的内容
    
    #include <stdio.h>
    #include "Base64.h"
    
    int main()
    {
    	/*
    	    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==";
    	unsigned int result_size=0;
    	unsigned char * p = base64Decode(sps_sdp,result_size);//解码sps字符串
    	for(int i =0;i<result_size;i++)
    	{
    		printf("%02X ",p[i]);
    		if((i+1)%16==0)
    		{
    			printf("
    ");
    		}		
    	}
    	printf("
    
    
    ");	
    	p = base64Decode(pps_sdp,result_size);//解码pps字符串
    	for(int i =0;i<result_size;i++)
    	{
    		printf("%02X ",p[i]);
    		if((i+1)%16==0)
    		{
    			printf("
    ");
    		}		
    	}
    	printf("
    ");	
    	return 0 ;
    }
    /* 程序输出如下: //这里是sps的二进制数据 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 //这里是pps的二进制数据 68 FE 3C B0 */

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

    #include "Base64.h"
    #include "strDup.h"
    #include <string.h>
    
    static char base64DecodeTable[256];
    
    static void initBase64DecodeTable() {
      int i;
      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[(unsigned char)'+'] = 62;
      base64DecodeTable[(unsigned char)'/'] = 63;
      base64DecodeTable[(unsigned char)'='] = 0;
    }
    
    unsigned char* base64Decode(char const* in, unsigned& resultSize,
    			    Boolean trimTrailingZeros) {
      static Boolean haveInitedBase64DecodeTable = False;
      if (!haveInitedBase64DecodeTable) {
        initBase64DecodeTable();
        haveInitedBase64DecodeTable = True;
      }
    
      unsigned char* out = (unsigned char*)strDupSize(in); // ensures we have enough space
      int k = 0;
      int const jMax = strlen(in) - 3;
         // in case "in" is not a multiple of 4 bytes (although it should be)
      for (int j = 0; j < jMax; j += 4) {
        char inTmp[4], outTmp[4];
        for (int i = 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] == '') --k;
      }
      resultSize = k;
      unsigned char* result = new unsigned char[resultSize];
      memmove(result, out, resultSize);
      delete[] out;
    
      return result;
    }
    
    static const char base64Char[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    char* base64Encode(char const* origSigned, unsigned origLength) {
      unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set
      if (orig == NULL) return NULL;
    
      unsigned const numOrig24BitValues = origLength/3;
      Boolean havePadding = origLength > numOrig24BitValues*3;
      Boolean havePadding2 = origLength == numOrig24BitValues*3 + 2;
      unsigned const numResultBytes = 4*(numOrig24BitValues + havePadding);
      char* result = new char[numResultBytes+1]; // allow for trailing ''
    
      // 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] = '';
      return result;
    }
    

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

  • 相关阅读:
    CentOS 7基于Containerd部署Kubernetes v1.20.5
    【k8s记录】二进制安装kubernetes高可用集群全过程完整版v1.20
    前台后台$.psot交互
    $.ajax与$.post、$.get的一点区别
    layui与jQuery一起使用
    用Emmet写前端代码
    栅格布局的实现过程,容器到列的划分
    bootstrap环境
    2个爬虫
    think PHP5中,模板、控制器、JavaScript的url跳转重定向方法
  • 原文地址:https://www.cnblogs.com/wangqiguo/p/4556391.html
Copyright © 2020-2023  润新知