live555很多人用来做rtsp server或者rtsp client,其实也可以单独用作通用的rtp库。笔者就是对其进行稍作封装,命名为CRtpRecevier和CRtpSender类。这样就可应付很多rtp处理。
封装后基本可以一劳永逸的移植到各个平台上使用。live555支持的payload type比较全面。各种流行的媒体rtp封装都支持,不需要自己进行分帧和特殊处理,省很多事情。
代码性能和延时都有保证。音视频可小于80毫秒。
1. 用作rtp接收,可以参考我以前的博文
发送的核心逻辑是构造媒体的sdp字符串,然后live555内部会分析这个文本创建不同的解析类实例
Live555通过SDP文本信息实现对RTP的接收
2.用作rtp发送,基本逻辑如下
a. 构建一个数据源类
我这里命名为ByteStreamQueueBufferSource
实现一个以队列为数据源的通用类, 从FramedSource派生,可供其他类使用,用作音视频直播的数据源。
注意,该类参考live555原有的类ByteStreamMemoryBufferSource和ByteStreamFileSource的实现.
注意ByteStreamMemoryBufferSource内部不可以用阻塞算法,不然多个rtp发送就不流畅了。
b.构造发送逻辑
以下以音频为例子,构造发送rtp逻辑。对于h264视频也是类似的,就是ByteStreamQueueBufferSource还要增加一个H264VideoStreamFramer分析类处理rtp分包逻辑。
bool CRtpSender::CreatePcmRtpSink(int chanNum, int sampleRate)
{
UsageEnvironment& env = envir();
frame_source_ = ByteStreamQueueBufferSource::createNew(env, 0, 0, 0);//frame_source_为FramedSource*
if(frame_source_ == NULL)
{
RTP_LOGE("ByteStreamQueueBufferSource::createNew failed\n");
return false;
}
struct in_addr dummyDestAddress;
dummyDestAddress.s_addr = remote_ip_;//远程IPV4地址,网络字节序
Groupsock *rtpGroupsock = new Groupsock(env, dummyDestAddress, local_port_, 0);//local_port_本地端口,主机字节序
#ifdef ENABLE_RTCP_SEND
Groupsock *rtcpGroupsock = new Groupsock(env, dummyDestAddress, local_port_ + 1, 0);
#endif
Port destPort(remote_port_);//remote_port_为目的rtp端口
rtpGroupsock->changeDestinationParameters(dummyDestAddress, destPort, ~0);
#ifdef ENABLE_RTCP_SEND
Port destRtcpPort(remote_port_ + 1);
rtcpGroupsock->changeDestinationParameters(dummyDestAddress, destRtcpPort, ~0);
#endif
rtp_sink_ = SimpleRTPSink::createNew(env, rtpGroupsock,
payload_type_, sampleRate,
"audio", (mime_type_ == MT_PCMU ? "PCMU" : "PCMA"), 1, False/*每个包一个rtp*/);
if(rtp_sink_ == NULL)
{
RTP_LOGE("SimpleRTPSink::createNew failed\n");
delete rtpGroupsock;
#ifdef ENABLE_RTCP_SEND
delete rtcpGroupsock;
#endif
return false;
}
#ifdef ENABLE_RTCP_SEND
unsigned char CNAME[32];
sprintf((char*)CNAME, "HTTEC_RTP_%d", remote_port_ % 1000);
const unsigned estimatedSessionBandwidthVideo = 128; // in kbps; for RTCP b/w share
rtcp_inst_ = RTCPInstance::createNew(env, rtcpGroupsock,
estimatedSessionBandwidthVideo, CNAME,
rtp_sink_, NULL /* we're a server */);
if(rtcp_inst_ == NULL)
{
RTP_LOGE("RTCPInstance::createNew failed\n");
Medium::close(rtp_sink_);
rtp_sink_ = NULL;
delete rtpGroupsock;
delete rtcpGroupsock;
return false;
}
sock_rtcp_ = rtcpGroupsock;
#endif
sock_rtp_ = rtpGroupsock;
RTP_LOGD("CreatePcmRtpSink ok \n");
return true;
}
{
UsageEnvironment& env = envir();
frame_source_ = ByteStreamQueueBufferSource::createNew(env, 0, 0, 0);//frame_source_为FramedSource*
if(frame_source_ == NULL)
{
RTP_LOGE("ByteStreamQueueBufferSource::createNew failed\n");
return false;
}
struct in_addr dummyDestAddress;
dummyDestAddress.s_addr = remote_ip_;//远程IPV4地址,网络字节序
Groupsock *rtpGroupsock = new Groupsock(env, dummyDestAddress, local_port_, 0);//local_port_本地端口,主机字节序
#ifdef ENABLE_RTCP_SEND
Groupsock *rtcpGroupsock = new Groupsock(env, dummyDestAddress, local_port_ + 1, 0);
#endif
Port destPort(remote_port_);//remote_port_为目的rtp端口
rtpGroupsock->changeDestinationParameters(dummyDestAddress, destPort, ~0);
#ifdef ENABLE_RTCP_SEND
Port destRtcpPort(remote_port_ + 1);
rtcpGroupsock->changeDestinationParameters(dummyDestAddress, destRtcpPort, ~0);
#endif
rtp_sink_ = SimpleRTPSink::createNew(env, rtpGroupsock,
payload_type_, sampleRate,
"audio", (mime_type_ == MT_PCMU ? "PCMU" : "PCMA"), 1, False/*每个包一个rtp*/);
if(rtp_sink_ == NULL)
{
RTP_LOGE("SimpleRTPSink::createNew failed\n");
delete rtpGroupsock;
#ifdef ENABLE_RTCP_SEND
delete rtcpGroupsock;
#endif
return false;
}
#ifdef ENABLE_RTCP_SEND
unsigned char CNAME[32];
sprintf((char*)CNAME, "HTTEC_RTP_%d", remote_port_ % 1000);
const unsigned estimatedSessionBandwidthVideo = 128; // in kbps; for RTCP b/w share
rtcp_inst_ = RTCPInstance::createNew(env, rtcpGroupsock,
estimatedSessionBandwidthVideo, CNAME,
rtp_sink_, NULL /* we're a server */);
if(rtcp_inst_ == NULL)
{
RTP_LOGE("RTCPInstance::createNew failed\n");
Medium::close(rtp_sink_);
rtp_sink_ = NULL;
delete rtpGroupsock;
delete rtcpGroupsock;
return false;
}
sock_rtcp_ = rtcpGroupsock;
#endif
sock_rtp_ = rtpGroupsock;
RTP_LOGD("CreatePcmRtpSink ok \n");
return true;
}