ffmpeg播放rtp流,为了降低首开延迟,需要在SDP文件中指定PPS、SPS、VPS信息。抓包后发现wireshark无法解析AP包。需要自己进行AP包解析。RTP协议AP包格式如下:
根据如上信息,我们可以解析AP包,效果如下
- 40 01,type=32,VPS(视频参数集)
- 42 01,type=33,SPS(序列参数集)
- 44 01,type=34,PPS(图像参数集)
- 4E 01, type=39,SEI(补充增强信息)
- 26 01,type=19,可能有RADL图像的IDR图像的SS编码数据 IDR
- 02 01, type=01,被参考的后置图像,且非TSA、非STSA的SS编码数据
- 46 01,type=35,分隔符,没用。
下图中红色部分是分隔符,橙色是VPS,黑色是SPS,黄色为PPS
SDP中的VPS等信息需要转换成base64。使用下面的函数可转换
#include <stdio.h>
#include <inttypes.h>
#include <math.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <string>
#include <string.h>
#include <iostream>
#define AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1)
# define AV_RB32(x) \
(((uint32_t)((const uint8_t*)(x))[0] << 24) | \
(((const uint8_t*)(x))[1] << 16) | \
(((const uint8_t*)(x))[2] << 8) | \
((const uint8_t*)(x))[3])
using namespace std;
char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
{
static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *ret, *dst;
unsigned i_bits = 0;
int i_shift = 0;
int bytes_remaining = in_size;
if (in_size >= UINT_MAX / 4 ||
out_size < AV_BASE64_SIZE(in_size))
return NULL;
ret = dst = out;
while (bytes_remaining > 3) {
i_bits = AV_RB32(in);
in += 3; bytes_remaining -= 3;
*dst++ = b64[ i_bits>>26 ];
*dst++ = b64[(i_bits>>20) & 0x3F];
*dst++ = b64[(i_bits>>14) & 0x3F];
*dst++ = b64[(i_bits>>8 ) & 0x3F];
}
i_bits = 0;
while (bytes_remaining) {
i_bits = (i_bits << 8) + *in++;
bytes_remaining--;
i_shift += 8;
}
while (i_shift > 0) {
*dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
i_shift -= 6;
}
while ((dst - ret) & 3)
*dst++ = '=';
*dst = '\0';
return ret;
}
static int doLog = 0;
void hexDump(uint8_t* data,int len){
if(doLog == 0)
return;
printf("[%d]",len);
int pos = 0;
uint8_t d = 0;
while(pos<len){
d = *(data+pos);
printf("0x%02x,",(int)d);
pos++;
}
printf("[%d]\n",len);
}
//sprop-vps=QAEMAf//AWAAAAMAkAAAAwAAAwBalZgJ; sprop-sps=QgEBAWAAAAMAkAAAAwAAAwBaoAeCAIh95ZWaSTK8BacIAAADAAgAAAMA8EA=; sprop-pps=RAHBcrRiQA==
//sprop-vps=DAH//wFgAAADALAAAAMAAAMAWqwJ; sprop-sps=AQFgAAADALAAAAMAAAMAWqAPCAPBY2uSRS9NQEBAQCA=; sprop-pps=wPLOcDs0
void getXPSfromAP(uint8_t* ap){
int pos = 0;
while(true){
uint16_t hdr = (ap[pos+2]<<8)|(ap[pos+3]);
int typ = (hdr>>9)&0x3f;
int siz = (ap[pos+0]<<8)|(ap[pos+1])-2;
switch(typ){
case 32:
{
char str_vps[100]={0};
uint8_t* vps = (uint8_t*)malloc(siz);
memcpy(vps,ap+pos+4,siz);
av_base64_encode(str_vps,100,vps,siz);
cout<<"sprop-vps="<<str_vps;
hexDump(vps,siz);
free(vps);
break;
}
case 33:
{
char str_sps[100]={0};
uint8_t* sps = (uint8_t*)malloc(siz);
memcpy(sps,ap+pos+4,siz);
av_base64_encode(str_sps,100,sps,siz);
cout<<"; sprop-sps="<<str_sps;
hexDump(sps,siz);
free(sps);
break;
}
case 34:
{
char str_pps[100]={0};
uint8_t* pps = (uint8_t*)malloc(siz);
memcpy(pps,ap+pos+4,siz);
av_base64_encode(str_pps,100,pps,siz);
cout<<"; sprop-pps="<<str_pps<<endl;
hexDump(pps,siz);
free(pps);
return;
break;
}
default:
break;
}
pos += 4;
pos += siz;
}
}
int main(int argc, char *argv[]) {
if(argc>=2){
doLog = atoi(argv[1]);
}
uint8_t in[]={0x67, 0x42,0xc0,0x14,0xf4,0x0b,0x04,0xb4,0x20,0x00,0x00,0x03,0x00,0x20,0x00,0x00,0x03,0x03,0xd1,0xe2,0x85,0x54};
char str_sps[100]={0};
av_base64_encode(str_sps,100,in,22);
cout<<"sps'sbasecode:"<<str_sps<<endl;
uint8_t pps[]={0x68 ,0xce ,0x04 ,0xf2};
char str_sps2[100]={0};
av_base64_encode(str_sps2,100,pps,4);
cout<<"pps'sbasecode:"<<str_sps2<<endl;
uint8_t ap[] = {0x00,0x17,0x40,0x01,0x0c,0x01,0xff,0xff,0x01,0x60,0x00,0x00,0x03,0x00,0xb0,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x5a,0xac,0x09,0x00,0x22,0x42,
0x01,0x01,0x01,0x60,0x00,0x00,0x03,0x00,0xb0,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x5a,0xa0,0x0f,0x08,0x03,0xc1,0x63,0x6b,0x92,0x45,0x2f,
0x4d,0x40,0x40,0x40,0x40,0x20,0x00,0x08,0x44,0x01,0xc0,0xf2,0xce,0x70,0x3b,0x34,0x00,0x09,0x4e,0x01,0xe5,0x04,0x00,0x00,0x70,0x00,0x80,0x00,
0x1e,0x4e,0x01,0x89,0x18,0x3a,0x98,0x75,0x30,0x1d,0x4c,0x0b,0xb8,0x7d,0x00,0x40,0x74,0x3d,0x13,0x40,0x42,0x00,0x00,0x04,0xb0,0x00,0x00,0x03,0x00,0xc8,0x80};
getXPSfromAP(ap);
return 0;
}