由于工作中需要用到rtp协议,java暂时没有比较好的开发框架,参考了其他的一些博文,自己摸索着使用jrtplib库,在此记录一下。
一、接收端
二、发送端
1、实现H264编码数据分包发送;
2、发送RTP数据;
3、接收RTCP、BYE等数据,并携带数据发送端IP字符串回调至Java。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jthread
LOCAL_SRC_FILES := libjthread.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := jrtp
LOCAL_SRC_FILES := libjrtplib.a
LOCAL_STATIC_LIBRARIES := jthread
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := rtp-handle
LOCAL_SRC_FILES := RtpHandle.cpp RtpReceiver.cpp RtpSender.cpp RtpCommon.cpp
LOCAL_STATIC_LIBRARIES := jthread jrtp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
#对标准C++ stl库的支持
#APP_STL := stlport_static
APP_STL := gnustl_static
APP_ABI := armeabi-v7a
public class RtpHandle {
static {
System.loadLibrary("rtp-handle");
}
/**
* 只发送数据,不做接收处理
*/
public static native boolean initSendHandle(int localport, String desthost, int destport, RtpListener listener);
/**
* 只接收数据,不做发送处理
*/
public static native boolean initReceiveHandle(String localhost,int localport, RtpListener listener);
/**
* 接收数据,并转发
*/
public static native boolean initReceiveAndSendHandle(String localhost,int localport,String desthost, int destport, RtpListener listener);
/**
* 发送数据
*/
public static native boolean sendH264Byte(byte[] src, int byteLength,boolean isSpsOrPps);
/**
* 发送数据
*/
public static native boolean sendRtpByte(byte[] src, int byteLength,boolean isMarker);
/**
* 销毁资源
*/
public static native boolean finiRtp();
}
BeginDataAccess();
// check incoming packets
if (GotoFirstSourceWithData()) {
do {
RTPPacket *rtppack = NULL;
while ((rtppack = GetNextPacket()) != NULL) {
processRtpPacket(rtppack);
DeletePacket(rtppack);
}
} while (GotoNextSourceWithData());
}
EndDataAccess();
if (pack->GetPayloadType() == H264) {
//std::cout<<"Got H264 packet:êo " << rtppack.GetExtendedSequenceNumber() << " from SSRC " << srcdat.GetSSRC() <GetPayloadData(), pack->GetPayloadLength(), pack->HasMarker(),
hid_callback);
}
//转发rtp数据
void CRTPSender::SendRtpData(unsigned char *m_rtpBuf, int buflen, bool isMarker) {
if (buflen < 4) {
return;
}
if (mCount % 100 == 0) {
logd("SendRtpData packet length %d \n", buflen);
mCount = 0;
}
int status;
this->SetMaximumPacketSize(1500);
if (isMarker) {
status = this->SendPacket((void *) m_rtpBuf, buflen, H264, isMarker, 3600);
CheckError(status);
} else {
status = this->SendPacket((void *) m_rtpBuf, buflen, H264, isMarker, 0);
CheckError(status);
}
}
void CRTPSender::SendH264Nalu(unsigned char *m_h264Buf, int buflen, bool isSpsOrPps) {
if (buflen < 4) {
return;
}
unsigned char *pSendbuf; //发送数据指针
pSendbuf = m_h264Buf;
//去除前导码0x000001 或者0x00000001
if (FindStartCode2(m_h264Buf) == 1) {
pSendbuf = &m_h264Buf[3];
buflen -= 3;
} else if (FindStartCode3(m_h264Buf) == 1) {
pSendbuf = &m_h264Buf[4];
buflen -= 4;
}
char sendbuf[SEND_LENGTH];
memset(sendbuf, 0, SEND_LENGTH);
int status;
if (mCount % 100 == 0) {
logd("SendH264Nalu packet length %d \n", buflen);
mCount = 0;
}
mCount++;
if (isSpsOrPps) {
this->SetDefaultMark(false);
memcpy(sendbuf, pSendbuf, buflen);
status = this->SendPacket((void *) sendbuf, buflen);
CheckError(status);
return;
}
if (buflen <= MAX_RTP_PKT_LENGTH) {
this->SetDefaultMark(true);
//设置NALU HEADER,并将这个HEADER填入sendbuf[0]
nalu_hdr = (NALU_HEADER *) &sendbuf[0]; //将sendbuf[0]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入sendbuf中;
nalu_hdr->F = (char) (pSendbuf[0] & 0x80);
nalu_hdr->NRI = (char) ((pSendbuf[0] & 0x60)
>> 5);//有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。
nalu_hdr->TYPE = (char) (pSendbuf[0] & 0x1f);
//NALU头已经写到sendbuf[0]中,接下来则存放的是NAL的第一个字节之后的数据。所以从r的第二个字节开始复制
memcpy(sendbuf + 1, pSendbuf, buflen);
status = this->SendPacket((void *) sendbuf, buflen);
CheckError(status);
} else if (buflen >= MAX_RTP_PKT_LENGTH) {
this->SetDefaultMark(false);
//得到该nalu需要用多少长度为1400字节的RTP包来发送
int k = 0, l = 0;
k = buflen / MAX_RTP_PKT_LENGTH;//需要k个1400字节的RTP包,这里为什么不加1呢?因为是从0开始计数的。
l = buflen % MAX_RTP_PKT_LENGTH;//最后一个RTP包的需要装载的字节数
int t = 0;//用于指示当前发送的是第几个分片RTP包
while (t < k || (t == k && l > 0)) {
memset(sendbuf, 0, SEND_LENGTH);
if (t == 0) {
//第一片
//设置FU INDICATOR,并将这个HEADER填入sendbuf[0]
fu_ind = (FU_INDICATOR *) &sendbuf[0]; //将sendbuf[0]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F = (char) (pSendbuf[0] & 0x80);
fu_ind->NRI = (char) ((pSendbuf[0] & 0x60) >> 5);
fu_ind->TYPE = 28; //FU-A类型。
//设置FU HEADER,并将这个HEADER填入sendbuf[1]
fu_hdr = (FU_HEADER *) &sendbuf[1];
fu_hdr->E = 0;
fu_hdr->R = 0;
fu_hdr->S = 1;
fu_hdr->TYPE = (char) (pSendbuf[0] & 0x1f);
memcpy(sendbuf + 2, &pSendbuf[t * MAX_RTP_PKT_LENGTH + 1], MAX_RTP_PKT_LENGTH);
status = this->SendPacket((void *) sendbuf, MAX_RTP_PKT_LENGTH + 2, H264, false, 0);
CheckError(status);
t++;
} else if (t < k)//既不是第一片,也不是最后一片
{
//设置FU INDICATOR,并将这个HEADER填入sendbuf[0]
fu_ind = (FU_INDICATOR *) &sendbuf[0]; //将sendbuf[0]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F = (char) (pSendbuf[0] & 0x80);
fu_ind->NRI = (char) ((pSendbuf[0] & 0x60) >> 5);
fu_ind->TYPE = 28;
//设置FU HEADER,并将这个HEADER填入sendbuf[1]
fu_hdr = (FU_HEADER *) &sendbuf[1];
fu_hdr->R = 0;
fu_hdr->S = 0;
fu_hdr->E = 0;
fu_hdr->TYPE = (char) (pSendbuf[0] & 0x1f);
memcpy(sendbuf + 2, &pSendbuf[t * MAX_RTP_PKT_LENGTH + 1], MAX_RTP_PKT_LENGTH);
status = this->SendPacket((void *) sendbuf, MAX_RTP_PKT_LENGTH + 2, H264, false, 0);
CheckError(status);
t++;
}
//最后一包
else if (k == t) {
this->SetDefaultMark(true);
int iSendLen;
if (l > 0) {
iSendLen = buflen - t * MAX_RTP_PKT_LENGTH;
} else {
iSendLen = MAX_RTP_PKT_LENGTH;
}
//设置FU INDICATOR,并将这个HEADER填入sendbuf[0]
fu_ind = (FU_INDICATOR *) &sendbuf[0]; //将sendbuf[0]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F = (char) (pSendbuf[0] & 0x80);
fu_ind->NRI = (char) ((pSendbuf[0] & 0x60) >> 5);
fu_ind->TYPE = 28; //FU-A类型。
//设置FU HEADER,并将这个HEADER填入sendbuf[1]
fu_hdr = (FU_HEADER *) &sendbuf[1];
fu_hdr->R = 0;
fu_hdr->S = 0;
fu_hdr->TYPE = (char) (pSendbuf[0] & 0x1f);
fu_hdr->E = 1;
memcpy(sendbuf + 2, &pSendbuf[t * MAX_RTP_PKT_LENGTH + 1], iSendLen - 1);
status = this->SendPacket((void *) sendbuf, iSendLen - 1 + 2, H264, true, 3600);
CheckError(status);
t++;
}
}
}
}
具体的代码就不在这里贴出来了,如有需要,可相互学习。
详细代码已上传至GitHub,Jrtplib4Android