文章目录
第二章、FFmpeg增加RTP协议外部扩展信息解析
RTP协议
RTP协议格式:
1.V:RTP协议的版本号,占2位,当前协议版本号为2。
-
P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
-
X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头。
-
CC:CSRC计数器,占4位,指示CSRC 标识符的个数。
-
M: 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
-
PT: 有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。
-
序列号:占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。
应用场景:对视频进行无变形缩放选定要裁剪的坐标点、对应的宽高进行裁
RTP头部协议,前12位为基础字节,对应第一个结构体,如果extension为1时候,才有外部扩展信息,外部扩展信息结构对应第二个结构体,分别对应profile、length、data。
FFmpeg源码分析
第一步打开ffmpeg-5.1源码,搜素rtpdec.c文件找对应的方法
static int rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt,
const uint8_t *buf, int len)
从上图说明过滤的RTP协议外部扩展信息
为什么要增加RTP协议外部信息解析
1、因为一般直播、视频通话、转拉流视频选择角度会放在外部协议
判断是否有外部协议的语句
int extension = (buf[0] & 0x10) >> 4;
下面是我修改方式,主要判断是否视频角度,saveRtpData方法是我自己写的,经过验证上线过可以的,有提供rtp_parse_packet_internal源码参考
修改源码增加修改的部分可以参考,我思路把获取rtp协议的的视频角度pkt->pos传递出去,再传给上下文,后面的代码要自己去写
static int parse_rtp_extension(const uint8_t* buf,int ext_len,int len,int id){
int offset=0;
int extension_data=-1;
int identifier = (buf[4] >> 4) & 0x0f;
if(identifier==id){
int extension_length = AV_RB16(buf +2);
if(extension_length==1){
extension_data = (buf[5] & 0x0f); // Identifier
switch (extension_data) {
case 0:
extension_data=0;
break;
case 1:
extension_data=90;
break;
case 2:
extension_data=180;
break;
case 3:
extension_data=270;
break;
default:
extension_data=-1;
break;
}
if (extension_data!=-1) {
return extension_data;
}
}
}
while (1) {
int profile = AV_RB16(buf+offset);
int length = AV_RB16(buf +offset+2);
if (profile==id) {
extension_data= AV_RB16(buf+offset+4);
break;
}
offset+=4 + (length << 1);
if (offset>=ext_len||offset>=len) {
break;
}
}
switch (extension_data) {
case -1:
extension_data=-4;
break;
case 0:
extension_data=0;
break;
case 1:
extension_data=90;
break;
case 2:
extension_data=180;
break;
case 3:
extension_data=270;
break;
default:
extension_data=-3;
break;
}
return extension_data;
}
static int parse_rtp_extensionOne(const uint8_t* buf,int ext_len,int len,int id,int*identifier_id,int*extension_len){
int offset=0;
int extension_data=-1;
int identifier = (buf[16] >> 4) & 0x0f;
*identifier_id=identifier;
if(identifier==id){
int extension_length = AV_RB16(buf +14);
*extension_len=extension_length;
if(extension_length==1){
extension_data = (buf[17] & 0x0f); // Identifier
switch (extension_data) {
case 0:
extension_data=0;
break;
case 1:
extension_data=90;
break;
case 2:
extension_data=180;
break;
case 3:
extension_data=270;
break;
default:
extension_data=-2;
break;
}
}else{
extension_data=-3;
}
}else{
extension_data=-4;
}
return extension_data;
}
static int saveRtpData(const uint8_t *buf,int len,int sdp_video_id){
int size=0;
int ext =0;
int identifier_id=-1;
int extension_len=-1;
int rtp_video_data=-1;
int extension = (buf[0] & 0x10) >> 4;
if (extension) {
if(sdp_video_id>0){
ext = (AV_RB16(buf + 14) + 1) << 2;
rtp_video_data= parse_rtp_extensionOne(buf,ext,len,sdp_video_id,&identifier_id,&extension_len);
}
if (access("/data/logs/prod/mg-5gcall/ffmpeg-rtp-have-extension_app.log", F_OK) != -1) {
// av_log(NULL, AV_LOG_ERROR,"====yrs=====\n" );
} else {
// av_log(NULL, AV_LOG_ERROR,"====yrs=no====\n" );
if (len<14) {
return rtp_video_data;
}
FILE *fp;
fp = fopen("/data/logs/prod/mg-5gcall/ffmpeg-rtp-have-extension_app.log", "wb"); //以二进制格式打开文件
if (fp != NULL) {
// av_log(NULL, AV_LOG_ERROR,"====extension_app======len:%d\n", len);
if (len>30) {
size=30;
}else{
size=len;
}
for (int i = 0; i < size; i++) {
fprintf(fp, "%02x ", buf[i]);
}
fprintf(fp, " sdp_video_id=%d ",sdp_video_id);
fprintf(fp, " rtp_video_data=%d ",rtp_video_data);
fprintf(fp, " identifier_id=%d ",identifier_id);
fprintf(fp, " extension_len=%d ",extension_len);
fclose(fp);
}
}
}
return rtp_video_data;
}
static void saveFile(int sdp_video_id,int len,int ext){
int size=0;
if (access("/data/logs/prod/mg-5gcall/file_app.log", F_OK) != -1) {
// av_log(NULL, AV_LOG_ERROR,"====yrs=====\n" );
} else {
FILE *fp;
fp = fopen("/data/logs/prod/mg-5gcall/file_app.log", "wb"); //以二进制格式打开文件
if (fp != NULL) {
fprintf(fp, "%d ", sdp_video_id);
fprintf(fp, "%d ", len);
fprintf(fp, "%d ", ext);
fclose(fp);
}
}
}
static int rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt,
const uint8_t *buf, int len)
{
int rtp_video_data=-1;
int rtp_angle=-1;
unsigned int ssrc;
int payload_type, seq, flags = 0;
int ext, csrc;
AVStream *st;
uint32_t timestamp;
int rv = 0;
int sdp_video_id=-1;
sdp_video_id=pkt->pos;
pkt->pos=-1;
csrc = buf[0] & 0x0f;
ext = buf[0] & 0x10;
payload_type = buf[1] & 0x7f;
if (buf[1] & 0x80)
flags |= RTP_FLAG_MARKER;
seq = AV_RB16(buf + 2);
timestamp = AV_RB32(buf + 4);
ssrc = AV_RB32(buf + 8);
/* store the ssrc in the RTPDemuxContext */
s->ssrc = ssrc;
if(sdp_video_id<0){
sdp_video_id=7;
}
rtp_angle=saveRtpData(buf,len,sdp_video_id);
pkt->pos=rtp_angle;
#if 0
/* Example data */
uint8_t data[] = {0x90,0xfb, 0x34,0x3c,0xe8,0x83, 0x82, 0x80, 0x98,
0x30, 0x59, 0x00, 0xbe, 0xde, 0x00, 0x01, 0x70, 0x01};
int rtp_extension_data=-1;
int new_ext;
// 定义 RTP 扩展字段数据
// sdp_video_id = 7;//id值
// 计算 RTP 扩展字段字节数
int ext_total_len =6;
int new_len=len+ext_total_len;
// 定义和初始化指向 RTP 包头数据的缓冲区指针 buf
uint8_t *new_buf = (uint8_t *) malloc(len+ext_total_len);
memcpy(new_buf, data, 12+ext_total_len);
memcpy(new_buf+12+ext_total_len, buf, len-12);
saveRtpData(new_buf,new_len,sdp_video_id);
rtp_angle=saveRtpData(new_buf,new_len,sdp_video_id);
pkt->pos=rtp_angle;
av_log(NULL, AV_LOG_ERROR,"==========sdp_video_id:%d\n", sdp_video_id);
av_log(NULL, AV_LOG_ERROR,"==========rtp_angle:%d\n", rtp_angle);
int new_payload_type = new_buf[1] & 0x7f;
/* NOTE: we can handle only one payload type */
// if (s->payload_type != new_payload_type)
// return -1;
int new_seq = AV_RB16(new_buf + 2);
st = s->st;
new_ext = new_buf[0] & 0x10;
av_log(NULL, AV_LOG_ERROR,"==========new_len:%d\n", new_len);
new_len -= 12;
new_buf += 12;
new_len -= 4 * csrc;
new_buf += 4 * csrc;
if (new_len < 0)
return AVERROR_INVALIDDATA;
av_log(NULL, AV_LOG_ERROR,"====22======new_len:%d\n", new_len);
if (new_ext) {
av_log(NULL, AV_LOG_ERROR, "==============new=ext===yes=====\n");
if (new_len < 4)
return -1;
/* calculate the header extension length (stored as number
* of 32-bit words) */
new_ext = (AV_RB16(new_buf + 2) + 1) << 2;
rtp_extension_data= parse_rtp_extension(new_buf,new_ext,new_len,7);
av_log(NULL, AV_LOG_ERROR,"========rtp_extension_data: %d\n", rtp_extension_data);
if (new_len < new_ext)
return -1;
// skip past RTP header extension
new_len -= new_ext;
new_buf += new_ext;
}else{
av_log(NULL, AV_LOG_ERROR, "============new==ext====no====\n");
}
#endif
/* NOTE: we can handle only one payload type */
if (s->payload_type != payload_type)
return -1;
st = s->st;
// only do something with this if all the rtp checks pass...
if (!rtp_valid_packet_in_sequence(&s->statistics, seq)) {
av_log(s->ic, AV_LOG_ERROR,
"RTP: PT=%02x: bad cseq %04x expected=%04x\n",
payload_type, seq, ((s->seq + 1) & 0xffff));
return -1;
}
if (buf[0] & 0x20) {
int padding = buf[len - 1];
if (len >= 12 + padding)
len -= padding;
}
s->seq = seq;
len -= 12;
buf += 12;
len -= 4 * csrc;
buf += 4 * csrc;
if (len < 0)
return AVERROR_INVALIDDATA;
/* RFC 3550 Section 5.3.1 RTP Header Extension handling */
if (ext) {
if (len < 4){
// saveFile(sdp_video_id,-1,-1);
return -1;
}
/* calculate the header extension length (stored as number
* of 32-bit words) */
ext = (AV_RB16(buf + 2) + 1) << 2;
// saveFile(sdp_video_id,len,ext);
if (len < ext)
return -1;
// skip past RTP header extension
// if(sdp_video_id>0){
// rtp_video_data= parse_rtp_extension(buf,ext,len,sdp_video_id);
// }
len -= ext;
buf += ext;
}
if (s->handler && s->handler->parse_packet) {
rv = s->handler->parse_packet(s->ic, s->dynamic_protocol_context,
s->st, pkt, ×tamp, buf, len, seq,
flags);
//pkt->pos=rtp_video_data;
pkt->pos=rtp_angle;
// pkt->pos=rtp_extension_data;
} else if (st) {
if ((rv = av_new_packet(pkt, len)) < 0)
return rv;
memcpy(pkt->data, buf, len);
pkt->stream_index = st->index;
// pkt->pos=rtp_video_data;
pkt->pos=rtp_angle;
// pkt->pos=rtp_extension_data;
} else {
return AVERROR(EINVAL);
}
// now perform timestamp things....
finalize_packet(s, pkt, timestamp);
return rv;
}