天天看点

海思项目学习记录 -3、ORTP库传输

1、ORTP库安装编译

注意;要增加H.264的payload支持。

在src/avprofile.c中357行添加:

rtp_profile_set_payload(profile,96,&payload_type_h264);//这个96是h.264帧头的一个信息

配置和编译、安装

(1)进入ortp目录执行./autogen.sh

(2)错误1:./autogen.sh: line 44: libtoolize: command not found

解决:sudo apt-get install libtool* 注意安装的时候要保证虚拟机联网

(2)错误2:libtoolize: error: Please install GNU M4, or ‘export M4=/path/to/gnu/m4’.

解决:sudo apt-get install m4

(3)错误3:Automake - aclocal: command not found

解决:sudo apt-get install automake

(4)继续执行./configure --prefix=/tmp/ortp --host=arm-hisiv300-linux

prefix 安装目录,,host 交叉编译工具链的选择

(5)make && make install

最后;到/tmp/ortp目录下查看移植好的库和头文件,之后在使用的时候要包含ku文件和头文件才能正确使用。

注意安装的时候要保证虚拟机联网

/etc/network/interface文件

ifdown ens33

ifup ens33

2、合并到mmp的sample 编译运行

修改代码后

重新编译sample

(1)复制ortp头文件 cp /tmp/ortp/include/ortp/ ./ -rf

(2)修改venc中Makefile,添加libortp的链接支持 -lortp -L路径

$(TARGET):%:%.o $(COMM_OBJ)

$(CC) $(CFLAGS) -lpthread -lm -lortp -o [email protected] $^ $(MPI_LIBS) $(AUDIO_LIBA) $(SENSOR_LIBS) -L/tmp/ortp/lib

(3)venc下执行make

即可生成可执行程序,注意要把ortp库的.h文件和lib库部署到开发板,否则运行的时候会提示缺少库的。

3、ORTP库源码学习

ORTP库概览

(1)库本身没有main,提供一堆功能函数,都在src目录下

(2)库的使用给了案例,有main,在src/tests目录下,每个.c文件就是一个案例

(3)相关数据结构和头文件在include/ortp目录下

(4)ortp实现了rtp和rtcp协议,前者负责传输,后者rtcp是rtp库的补充负责控制和同步协调。

3.1、以rtp_send.c为案例进行了解ORTP库

第一步;主要完成对payload的注册
	av_profile_init(&av_profile)
		rtp_profile_set_payload(profile,96,&payload	type_h264);
第二步;初始化调度器
	ortp_scheduler_init();
		rtp_scheduler_new
			ortp_malloc
			rtp_scheduler_init 初始化互斥锁、条件锁,以及会话的初始化
		rtp_scheduler_start
			ortp_thread_create 互斥锁保护,条件锁等待创建线程
				rtp_scheduler_schedule 线程函数、
第三步;创建session和设置会话属性
	rtp_session_new
		ortp_malloc
		rtp_session_init
			rtp_session_set_profile 最终还是要设置会话的profile
			rtp_session_enable_jitter_buffer 配置jitter防抖
			rtp_session_set_ssrc 判断设置ssrc同步源标志
第四步;读取发送
	rtp_session_send_with_ts
		rtp_session_create_packet 给数据添加帧头
		rtp_session_sendm_with_ts 加了m表示完成格式;帧头+内容
			rtp_session_rtp_send
				rtp_sendmsg or sendto
			rtp_session_rtcp_process_send
				rtp_sendmsg or sendto
           

细节部分;

ORTP中的调度器

调度器就是一个仲裁机构,也就是一段代码,每隔一段时间执行一次决定调度谁,一般是线程或进程

ortp库里面的scheduler就是一个定时器,每隔多少时间执行一次、这里面的调度实在干嘛的?、ortp库可以同时维持多路会话也就是多路通信,那么就要需要调度运行保持分时复用

会话session ORTP库的通信都是基于会话的,但是同时可以支持多个session同时进行,在代码逻辑上

一个session最终都会体现到一段代码一个数据结构就是结构体。

SSRC 同步源标志 这个可以用于会话通信的标志,当多路会话时用于可以根据信息识别是那个会话

就是每个会话通信传输信息的唯一识别码、作用就是在多路会话的时候直接截取信息可以根据ssrc知道是哪路会话的否则不知道。

jitter buffer防抖

jitter buffer技术是ip 音视频通信里相对比较高级的主题, jitter buffer模块好坏通常是衡量一个voip客户端/服务器好坏的技术点之一,尤其是在网络抖动比较严重,如3g,wifi环境,数据包的rtt值不均衡往往会导致语音卡顿,丢字等现象,jitter buffer 模块通过缓存一段数据包,把数据包重排,并均匀的送给播放端,一个好的jitter buffer实现通长是动态调整缓存大小的,在网络延迟大,抖动严重时会动态增加缓存大小,在网络恢复时动态减小缓存大小以减少端到端的播放延迟。

3.2、ORTP库中create_and_bind函数

static ortp_socket_t create_and_bind

涉及到

struct addrinfo解释

https://www.cnblogs.com/likui360/p/6128938.html

AF_XX 与PF_XX 地址族和协议族

setsockopt设置SO_REUSEADDR

https://blog.csdn.net/u010144805/article/details/78579528

3.3、RTP发送实验源码分析

首先要在common/sample_common_venc.c中添加ortp库的初始化函数和发送函数;

然后再就是修改venc/sample_venc.c;mmp初始化编码的步骤都不需要变,只需要修改之前存放到文件里面去,现在是使用ortp包发送。

venc/sample_venc.c

//venc/sample_venc.c
HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    HI_S32 i;

    
    for (i = 0; i < pstStream->u32PackCount; i++)
    {
    //只需要修改这里
    	#if ORTP_ENABLE
		rtpSend(pRtpSession,pstStream->pstPack[i].pu8Addr, pstStream->pstPack[i].u32Len);
       #else
	   //文件则需要考虑偏移量,而ortp库则是网络流每次放到原地即可
        	fwrite(pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,
               pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset, 1, fpH264File);

        	fflush(fpH264File);
        #endif
    }
           

sample_common_venc.c中

sample_common_venc.c中
//定义一个宏来管理。
#if ORTP_ENABLE
#include <ortp/ortp.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#define Y_PLOAD_TYPE 96 //H.264
#define MAX_RTP_PKT_LENGTH 1400
#define DefaultTimestampIncrement 3600 //(90000/25)
uint32_t g_userts=0;
RtpSession *pRtpSession = NULL;

#define LOCAL_HOST_IP  "192.168.1.20"

/**  初始化   
 *     
 *   主要用于对ortp以及其它参数进行初始化   
 *   @param:  char * ipStr 目的端IP地址描述串   
 *   @param:  int port 目的端RTP监听端口   
 *   @return:  RtpSession * 返回指向RtpSession对象的指针,如果为NULL,则初始化失败   
 *   @note:      这个注意是从ortp-master\src\tests\rtpsend.c抄过来的
 */   
RtpSession * rtpInit( char  * ipStr, int  port)
{
    RtpSession *session; 
    char  *ssrc;
    printf("********oRTP for H.264 Init********\n");

    ortp_init();
    ortp_scheduler_init();
    ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);
    session=rtp_session_new(RTP_SESSION_SENDONLY);	

    rtp_session_set_scheduling_mode(session,1);
    rtp_session_set_blocking_mode(session,0);
    //rtp_session_set_connected_mode(session,TRUE);
    rtp_session_set_remote_addr(session,ipStr,port);//设置远程IP和端口号 端口号就要两边一样可以通信就行了
    rtp_session_set_payload_type(session,Y_PLOAD_TYPE);

    ssrc=getenv("SSRC");
    if (ssrc!=NULL) {
        printf("using SSRC=%i.\n",atoi(ssrc));
        // 设置输出流的SSRC。不做此步的话将会给个随机值 
        rtp_session_set_ssrc(session,atoi(ssrc));
    }
    return  session;
}
/**  结束ortp的发送,释放资源   
 *     
 *   @param:  RtpSession *session RTP会话对象的指针   
 *   @return:  0表示成功   
 *   @note:       
 */    
int  rtpExit(RtpSession *session)   
{ 
    printf("********oRTP for H.264 Exit********\n");  
    g_userts = 0;   
       
    rtp_session_destroy(session);   
    ortp_exit();   
    ortp_global_stats_display();   
  
     return  0;   
}   
/**  发送rtp数据包   
 *     
 *   主要用于发送rtp数据包   
 *   @param:  RtpSession *session RTP会话对象的指针   
 *   @param:  const char *buffer 要发送的数据的缓冲区地址   
  *   @param: int len 要发送的数据长度   
 *   @return:  int 实际发送的数据包数目   
 *   @note:     如果要发送的数据包长度大于BYTES_PER_COUNT,本函数内部会进行分包处理   
 */   
int  rtpSend(RtpSession *session, char  *buffer,  int  len)
{  
    int  sendBytes = 0; 
    int status;       
    uint32_t valid_len=len-4;
    unsigned char NALU=buffer[4];
     //分包发送,并且还涉及到一点H.264帧头的信息
    //如果数据小于MAX_RTP_PKT_LENGTH字节,直接发送:单一NAL单元模式
    if(valid_len <= MAX_RTP_PKT_LENGTH)
    {
        sendBytes = rtp_session_send_with_ts(session,
                                             &buffer[4],  //注意从第四个字节开始 的H.264格式问题
                                             valid_len,
                                             g_userts);
    }
    else if (valid_len > MAX_RTP_PKT_LENGTH)
    {
        //切分为很多个包发送,每个包前要对头进行处理,如第一个包
        valid_len -= 1;
        int k=0,l=0;
        k=valid_len/MAX_RTP_PKT_LENGTH;
        l=valid_len%MAX_RTP_PKT_LENGTH;
        int t=0;
        int pos=5;
        if(l!=0)
        {
            k=k+1;
        }
        while(t<k)//||(t==k&&l>0))
        {
            if(t<(k-1))//(t<k&&l!=0)||(t<(k-1))&&(l==0))//(0==t)||(t<k&&0!=l))
            {
                buffer[pos-2]=(NALU & 0x60)|28;//把包的帧类型设置为RTP类型
                buffer[pos-1]=(NALU & 0x1f);
                if(0==t)
                {
                    buffer[pos-1]|=0x80;
                }
                sendBytes = rtp_session_send_with_ts(session,
                                                     &buffer[pos-2],
                                                     MAX_RTP_PKT_LENGTH+2,
                                                     g_userts);
                t++;
                pos+=MAX_RTP_PKT_LENGTH;
            }
            else //if((k==t&&l>0)||((t==k-1)&&l==0))
            {
                int iSendLen;
                if(l>0)
                {
                    iSendLen=valid_len-t*MAX_RTP_PKT_LENGTH;
                }
                else
                    iSendLen=MAX_RTP_PKT_LENGTH;
                buffer[pos-2]=(NALU & 0x60)|28;
                buffer[pos-1]=(NALU & 0x1f);
                buffer[pos-1]|=0x40;//重要等级提1
                sendBytes = rtp_session_send_with_ts(session,
                                                     &buffer[pos-2],
                                                     iSendLen+2,
                                                     g_userts);
                t++;
            }
        }
    }

    g_userts += DefaultTimestampIncrement;//timestamp increase
    return  len;
}
#endif
           

4、VLC播放器的配置文件sdp

配置文件sdp;是一种格式,到时候VCL来以什么格式来解析。

m=video 8080 RTP/AVP 96 m表示内容 8080端口 RTP传输协议 96传输内容的协议

a=rtpmap:96 H264

a=framerate:25 帧率

c=IN IP4 192.168.1.20 IPV4的格式化,和本地的IP地址

继续阅读