從RTSP協議SDP資料中獲得二進位制的SPS、PPS

薰衣草的旋律發表於2015-06-06

在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("\n");
		}		
	}
	printf("\n\n\n");	
	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("\n");
		}		
	}
	printf("\n");	
	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] == '\0') --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 '\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';
  return result;
}

 完整demo下載(Ubuntu Linux下執行沒問題)

相關文章