当前位置: 首页 > news >正文

第二章、FFmpeg增加RTP协议外部扩展信息解析

文章目录

  • 第二章、FFmpeg增加RTP协议外部扩展信息解析
    • RTP协议
    • FFmpeg源码分析
    • 为什么要增加RTP协议外部信息解析

第二章、FFmpeg增加RTP协议外部扩展信息解析

第一章RTP协议深入原理介绍

RTP协议

RTP协议格式:
请添加图片描述

1.V:RTP协议的版本号,占2位,当前协议版本号为2。

  1. P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。

  2. X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头。

  3. CC:CSRC计数器,占4位,指示CSRC 标识符的个数。

  4. M: 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。

  5. PT: 有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。

  6. 序列号:占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); // Identifierswitch (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); // Identifierswitch (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 包头数据的缓冲区指针 bufuint8_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 extensionnew_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, &timestamp, 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;
}

相关文章:

  • 蓝桥杯嵌入式第12届真题(完成) STM32G431
  • 内核内存回收关键隐藏变量之page引用计数
  • unity学习(38)——创建(create)角色脚本(panel)--EventSystem
  • 前端不传被删记录的id怎么删除记录,或子表如何删除记录
  • 【深度学习目标检测】十九、基于深度学习的芒果计数分割系统-含数据集、GUI和源码(python,yolov8)
  • 【C语言】详解计算机二级c语言程序题
  • uni-app 经验分享,从入门到离职(四)——页面栈以及页面跳转的 API(开发经验总结)
  • R语言入门笔记2.5
  • AR汽车行业解决方案系列之2-远程汽修
  • 命令行窗口文本复制到 Word 格式保持不变
  • 信息学奥赛一本通1205:汉诺塔问题
  • 接口测试实战--自动化测试流程
  • STM32单片机基本原理与应用(八)
  • hive表中的数据导出 多种方法详细说明
  • 华为校招机试题库2024年(JAVA、Python、C++)
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • C# 免费离线人脸识别 2.0 Demo
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • css的样式优先级
  • dva中组件的懒加载
  • iOS | NSProxy
  • JS变量作用域
  • Python打包系统简单入门
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 思维导图—你不知道的JavaScript中卷
  • 小程序01:wepy框架整合iview webapp UI
  • 你对linux中grep命令知道多少?
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (算法)N皇后问题
  • (一)插入排序
  • (正则)提取页面里的img标签
  • (转)EOS中账户、钱包和密钥的关系
  • (转)setTimeout 和 setInterval 的区别
  • (转)使用VMware vSphere标准交换机设置网络连接
  • (转)项目管理杂谈-我所期望的新人
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .Net Redis的秒杀Dome和异步执行
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • @Builder用法
  • @zabbix数据库历史与趋势数据占用优化(mysql存储查询)
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [Android]Tool-Systrace
  • [asp.net core]project.json(2)
  • [BUUCTF 2018]Online Tool
  • [BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)
  • [C/C++]数据结构----顺序表的实现(增删查改)