FFmpeg常用指令记录&sdp中提取sps/pps信息&sps/pps(STAP-A解析)


Author:Peter
Location:FZU
Date: 2022-04-15

:如人饮水,冷暖自知。


ffmpeg编码/推流指令,ffprobe分析H264码流指令。win10系统,git bash终端操作(可使用linux相关指令)。


1.ffmpeg利用x264编码YUV文件为H264

  • 将yuv420压缩成h264 -r:帧率,-g:GOPsize,-bf:B帧数量,-b:v 编码速率,-bufsize:码率控制缓冲区大小
./ffmpeg.exe -s 1920x1080 -i ParkScene_1920x1080.yuv -c:v libx264 -r 25 -g 50 -bf 0 -b:v 2000k -bufsize 2500k ParkScene_in.h264

2.ffmpeg通过RTP封包推送H264码流

注意事项,ffmpeg推送rtp流,IDR帧前不像Live555会采用两个packet封装sps/pps,而是直接一个STAP-A打包方式将sps及pps一同并入一个packet,即一个IDR帧前只含有一个sps&pps信息的packet,如下图抓包显示。

./ffmpeg.exe -re -i ParkScene_in.h264  -an -c copy -f rtp -ssrc 10 rtp://127.0.0.1:9900?pkt_size=1316 > 1.sdp
  • ffmpeg 推RTP流,IDR帧中sps/pps抓包信息

IDR帧sps/pps信息
  开头十六进制18的后五位二进制为11000(十进制为24,属于STAP-A封包),即00 1a为第一个nalu(sps)[67 64… 92 40]的长度,00 05为第二个nalu(pps)[68 eb cc b2 2c]的长度。固接收端解析STAP-A包时,需要将18 00 1a 替换成00 00 00 01,00 05替换成00 00 00 01,记00 00 00 01为一个nalu的起始码。

  • 接收端解析STAP-A(sps/pps)
#include<iostream>
#include<string>
#include<vector>

using namespace std;

union LENGTH  //可以采用联合体,共用内存空间,length与byte[2]
{
	uint16_t length;
	uint8_t  byte[2];
};



//0x00 0x1a   0000 0000 0001 1010 = 26 
void testSTAP_A()
{
	char sps_pps[] = { 0x18,0x00,0x1a,0x67,0x64,0x00,0x28,
						 0xac,0xb2,0x00,0xf0,0x04,0x4f,0xcb,0x08,
						 0x00,0x00,0x03,0x00,0x08,0x00,0x00,0x03,
						 0x01,0x90,0x78,0xc1,0x92,0x40,0x00,0x05,
						 0x68,0xeb,0xcc,0xb2,0x2c
	};
	int sps_length = 0;
	int pps_length = 0;
	//电脑是小端存储,地址低位存放数字低位,这样转过来为0x1a,0x00,0x00,0x00 十进制为26
	for (int i = 2, j = 0; i >= 1; --i, ++j)
		memcpy((char*)&sps_length + j, sps_pps + i, 1);
	cout << "sps_length=" << sps_length << endl;

#if 1  //采用联合体计算sps长度,也要考虑到小端存储0x00 0x1a (0x1a在低地址)
	LENGTH sps;
	for (int i = 0; i < 2; ++i)
	{
		sps.byte[i] = sps_pps[2 - i];
	}
	cout << sps.length << endl;

#endif 

	pps_length = sizeof(sps_pps) - sps_length - 5;
	//电脑是小端存储,地址低位存放数字低位,这样转过来为00 00 00 01
	uint32_t start_code = 0x01000000;
	vector<char> parse_sps_pps(sizeof(sps_pps) + 2 * 4 - 5);
	memcpy(parse_sps_pps.data(), &start_code, 4);
	memcpy(parse_sps_pps.data() + 4, sps_pps + 3, sps_length);
	memcpy(parse_sps_pps.data() + 4 + sps_length, &start_code, 4);
	memcpy(parse_sps_pps.data() + 8 + sps_length, sps_pps + 5 + sps_length, pps_length);
}

int main()
{

	testSTAP_A();
	
	return 0;
}
  • 指令生成的sdp信息:
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 58.76.100
m=video 9900 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKyyAPAET8sIAAADAAgAAAMBkHjBkkA=,aOvMsiw=; profile-level-id=640028
  • 提取sdp中的sps/pps信息(base64编码,逗号是分割sps,pps)

sprop-parameter-sets=Z2QAKKyyAPAET8sIAAADAAgAAAMBkHjBkkA=,aOvMsiw=

  其中sps信息: Z2QAKKyyAPAET8sIAAADAAgAAAMBkHjBkkA=
    pps信息: aOvMsiw=

  • C/C++代码提取sps/pps
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
	ifstream inf("..\\ffmpeg_bin\\1.sdp");
	string oneline;
	string sps_sdp;
	string pps_sdp;
	string sps_pps_sdp;

	int sps_start,pps_start,pps_end;

	while (true)
	{
		getline(inf, oneline);
		if (inf.eof() == true)
			break;
		cout << oneline << "\n";
		if ((sps_start = oneline.find("-sets=")) >0 && (pps_end = oneline.find("; profile")) >0 )
		{
			sps_start += 6;
			sps_pps_sdp.assign(oneline.data() + sps_start, oneline.data() + pps_end);
		}

	}
	if ((pps_start = sps_pps_sdp.find(",")) > 0)
	{
		sps_sdp.assign(sps_pps_sdp.data(), sps_pps_sdp.data() + pps_start);
		pps_sdp.assign(sps_pps_sdp.data() + pps_start + 1, sps_pps_sdp.data() + sps_pps_sdp.size());
		cout << "sps:"<<sps_sdp << "\t" <<"pps:"<< pps_sdp << endl;
	}
	return 0;
}

//----------------运行结果如下:----------------------
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 58.76.100
m=video 9900 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKyyAPAET8sIAAADAAgAAAMBkHjBkkA=,aOvMsiw=; profile-level-id=640028

sps:Z2QAKKyyAPAET8sIAAADAAgAAAMBkHjBkkA=    pps:aOvMsiw=

3.ffprobe分析h264文件指令

  • 查看h264码流I/B/P帧分布
./ffprobe.exe -v error -show_frames ParkScene_in.h264 | grep "pict_type"
  • 统计h264码流总帧数
./ffprobe.exe -v error -count_frames -select_streams v:0  -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 ParkScene_in.h264
  • 查看h264码流信息
./ffprobe.exe -v error -show_streams ParkScene_in.h264

  has_b_frames=0意味着264码流中无B帧

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPS(Sequence Parameter Set)和PPS(Picture Parameter Set)是H.264视频编码标准的两个重要参数集,包含了视频编码的一些基本参数信息,比如视频的分辨率、码率、帧率、GOP大小等等。 在使用ffmpeg进行视频推流时,如果服务器报告“没有SPSPPS”错误,通常是因为推送的视频流没有包含SPSPPS参数集。这种情况下,需要在代码手动添加SPSPPS参数集。 具体的做法是: 1. 在代码获取到视频编码器的AVCodecContext结构体。 2. 从AVCodecContext获取到AVCodecParameters结构体。 3. 从AVCodecParameters获取SPSPPS参数集的数据。 4. 将SPSPPS数据分别打包成NALU单元并发送给服务器。 以下是参考代码: ``` AVCodecContext* codec_ctx = ...;//获取到编码器的AVCodecContext结构体 AVCodecParameters* codec_params = codec_ctx->codecpar;//获取到编码器的AVCodecParameters结构体 //获取SPSPPS数据 uint8_t* sps_data = codec_params->extradata + 4; uint32_t sps_size = (codec_params->extradata[0] << 8) | codec_params->extradata[1]; uint8_t* pps_data = sps_data + sps_size + 1; uint32_t pps_size = ((pps_data[0] << 8) | pps_data[1]) & 0x00FFFFFF; //打包SPS数据 uint8_t* sps_nalu = new uint8_t[4 + sps_size]; sps_nalu[0] = 0x00; sps_nalu[1] = 0x00; sps_nalu[2] = 0x00; sps_nalu[3] = 0x01; memcpy(sps_nalu + 4, sps_data, sps_size); //打包PPS数据 uint8_t* pps_nalu = new uint8_t[4 + pps_size]; pps_nalu[0] = 0x00; pps_nalu[1] = 0x00; pps_nalu[2] = 0x00; pps_nalu[3] = 0x01; memcpy(pps_nalu + 4, pps_data, pps_size); //发送SPSPPS数据 send_to_server(sps_nalu, 4 + sps_size); send_to_server(pps_nalu, 4 + pps_size); delete[] sps_nalu; delete[] pps_nalu; ``` 其,send_to_server函数用于将数据发送给服务器,具体实现可以根据自己的需求进行编写。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值