第二章、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); // 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, &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;
}
相关推荐
NuyoahC16 分钟前
算法笔记(十一)——优先级队列(堆)
c++·笔记·算法·优先级队列
FL16238631291 小时前
[C++]使用纯opencv部署yolov11-pose姿态估计onnx模型
c++·opencv·yolo
sukalot1 小时前
windows C++-使用任务和 XML HTTP 请求进行连接(一)
c++·windows
ぃ扶摇ぅ1 小时前
Windows系统编程(三)进程与线程二
c++·windows
Mr.Z.4112 小时前
【历年CSP-S复赛第一题】暴力解法与正解合集(2019-2022)
c++
Death2002 小时前
使用Qt进行TCP和UDP网络编程
网络·c++·qt·tcp/ip
郭二哈3 小时前
C++——list
开发语言·c++·list
yunhuibin3 小时前
ffmpeg面向对象——拉流协议匹配机制探索
学习·ffmpeg
黑不溜秋的3 小时前
C++ 语言特性29 - 协程介绍
开发语言·c++
一丝晨光3 小时前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby