1、ORTP库概览
(1)库本身没有main,提供一堆功能函数,都在src目录下
(2)库的使用给了案例,有main,在src/tests目录下
(3)相关数据结构和头文件在include/ortp目录下
(4)ortp实现了rtp和rtcp协议,前者负责传输,后者负责控制和同步协调
2、ORTP库的使用案例
下面分析主要对 src/tests/rtpsend.c 进行分析
3、rtp的session
(1)rtp通过会话来管理数据发送和接收,会话的本质是一个结构体,管理很多信息
(2)创建会话用rtp_session_new
(3)rtp发送用rtp_session_send_with_ts
(4)底层真正干活的还是socket接口那一套,参考rtpsession_inet.c
4、ORTP的一些小细节
(1)port.c中对OS的常用机制(任务创建和销毁、进程管理和信号量等)进行了封装,便于将ortp移植到不同平台中
(2)utils.c中实现了一个双向链表
(3)str_util.c中实现了一个队列管理
(4)rtpparse.c和rtcpparse.c文件实现了解析收到的rtp数据包的部分
(5)jitterctl.c中实现了jitter buffer来防抖。jitter buffer技术是ip 音视频通信里相对比较高级的主题,jitter buffer模块好坏通常是衡量一个voip客户端/服务器好坏的技术点之一,尤其是在网络抖动比较严重,如3g, wifi环境,数据包的rtt值不均衡往往会导致语音卡顿,丢字等现象,jitter buffer 模块通过缓存一段数据包,把数据包重排,并均匀的送给播放端,一个好的jitter buffer实现通长是动态调整缓存大小的,在网络延迟大,抖动严重时会动态增加缓存大小,在网络恢复时动态减小缓存大小以减少端到端的播放延迟。
5.Jitter buffer
如果网络是理想的,即无丢包、无抖动、低延时,那么接收到一帧完整数据就直接播放,效果一定会非常好。但是实际的网络往往很复杂,尤其是无线网络。如果还是这样直接播放,网络稍微变差,视频就会卡顿,出现马赛克等异常情况。所以,在接收端对接收的数据做一个缓冲是很有必要的。
缓冲一定是以延时作为代价的,延时越大,对抖动的过滤效果越好。一个优秀的视频jitterbuffer,不仅要能够对丢包、乱序、延时到达等异常情况进行处理,而且还要能够让视频平稳的播放,尽可能的避免出现明显的加速播放和缓慢播放。
主流的实时音视频框架基本都会实现jitterbuffer功能,诸如WebRTC、doubango等。WebRTC的jitterbuffer相当优秀,按照功能分类的话,可以分为jitter和buffer。buffer主要对丢包、乱序、延时到达等异常情况进行处理,还会和NACK、FEC、FIR等QOS相互配合。jitter主要根据当前帧的大小和延时评估出jitter delay,再结合decode delay、render delay以及音视频同步延时,得到render time,来控制平稳的渲染视频帧。
下面将分别对jitter和buffer做介绍。
源码的调用函数图:
ortp_init();//ortp的初始化
av_profile_init(&av_profile); //初始化profile,使得ortp能够支持不同格式音视频的传输
rtp_profile_set_payload(profile,96,&payload_type_h264);
ortp_global_stats_reset(); //初始化全局变量(置零)
init_random_number_generator(); //产生随机数种子
srandom(t.tv_usec+t.tv_sec);
ortp_scheduler_init(); //初始化调度器(里面是一个定时器),决定资源分配,调度任务里面的多个会话,一个会话就是一个通信。
//通过会话来管理数据发送和接收,
__ortp_scheduler=rtp_scheduler_new();
rtp_scheduler_start(__ortp_scheduler);//启动调度任务
ortp_thread_create(&sched->thread, NULL, //调度任务的执行体
rtp_scheduler_schedule,(void*)sched);
ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR); //记录log信息
session=rtp_session_new(RTP_SESSION_SENDONLY); //创建一个会话,本质是一个结构体,管理很多信息
rtp_session_init (session, mode);
session->snd.ssrc=uint32_t_random();//获取一个随机的SSRC 标识符
/***** 设置会话属性 *********/
rtp_session_set_scheduling_mode(session,1);//设置rtp会话的调度模式
rtp_session_set_flag (session, RTP_SESSION_SCHEDULED);//调度程序控制此会话
rtp_scheduler_add_session (sched, session);//将会话添加注册到调度器管理的会话集合上
rtp_session_set_blocking_mode(session,1);//设置是否使用阻塞模式
rtp_session_set_scheduling_mode(session,TRUE);//设置调度器阻塞模式
rtp_session_set_flag (session, RTP_SESSION_BLOCKING_MODE);//设置会话阻塞
rtp_session_set_connected_mode(session,TRUE);//是否启用connect() syscall
rtp_session_set_remote_addr(session,argv[2],atoi(argv[3]));//设置rtp会话的远程地址,即rtp包所在的目标地址发送
rtp_session_set_remote_addr_full (session, addr, port, addr, port+1);
rtp_session_set_local_addr (session, "0.0.0.0", -1, -1);//设置本地rtp数据监听地址
rtp_session_set_payload_type(session,0);//设置RTP发送数据的负载类型
rtp_session_set_send_payload_type(session,pt);//设置发送的负载类型
rtp_session_set_recv_payload_type(session,pt);//设置接收的负载类型
/*******************************/
while( ((i=fread(buffer,1,160,infile))>0) && (runcond) )//发送数据
{
rtp_session_send_with_ts(session,buffer,i,user_ts);//发送RTP数据包
rtp_session_create_packet_with_data(session,(uint8_t*)buffer,len,NULL); //准备包,给数据添加包
rtp_session_sendm_with_ts(session,m,userts); //发送数据包
}
fclose(infile);
rtp_session_destroy(session);
ortp_exit();
ortp_global_stats_display();
//所有线程函数
void * rtp_scheduler_schedule(void * psched);
参考:https://blog.csdn.net/qq_40732350/article/details/88370782#t2
ortp库API说明:https://blog.csdn.net/heanyu/article/details/6185914
https://blog.csdn.net/bjrxyz/article/details/56494535
https://blog.csdn.net/wangdapao12138/article/details/82535113
https://blog.csdn.net/jsn_ze/article/details/50780425