Jrtplib4Android,RTP在Android平台上的使用
16lz
2021-01-26
由于工作中需要用到rtp协议,java暂时没有比较好的开发框架,参考了其他的一些博文,自己摸索着使用jrtplib库,在此记录一下。
- 关于如何编译jrtplib库,在我的上一篇有讲解jrtplib库移植到android上
- 可根据自身需求,参考实现的功能。
实现的功能
一、接收端
- 接收RTP数据,携带数据并回调通知Java端;
- 接收RTCP、BYE等数据,携带数据发送端IP字符串并回调通知Java端;
- 实现网络摄像机RTSP协议对接;
- 实现接收RTP数据,并转发RTP数据。
二、发送端
1、实现H264编码数据分包发送;
2、发送RTP数据;
3、接收RTCP、BYE等数据,并携带数据发送端IP字符串回调至Java。
准备工作
- Android.mk 文件编写
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := jthreadLOCAL_SRC_FILES := libjthread.ainclude $(PREBUILT_STATIC_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := jrtpLOCAL_SRC_FILES := libjrtplib.aLOCAL_STATIC_LIBRARIES := jthreadinclude $(PREBUILT_STATIC_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := rtp-handleLOCAL_SRC_FILES := RtpHandle.cpp RtpReceiver.cpp RtpSender.cpp RtpCommon.cppLOCAL_STATIC_LIBRARIES := jthread jrtpLOCAL_LDLIBS := -lloginclude $(BUILD_SHARED_LIBRARY)
- Application.mk文件编写
#对标准C++ stl库的支持#APP_STL := stlport_staticAPP_STL := gnustl_staticAPP_ABI := armeabi-v7a
- 编写JniHandle操作对象
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
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- 一句话锁定MySQL数据占用元凶
- Android之手机出现"已安装了存在签名冲突的同名数据包"的原因及
- Android(安卓)IO性能分析及排查
- Android实现社交应用中的评论和回复功能
- Android与物联网设备通信-网络模型分层
- Android实现通过浏览器点击链接打开本地应用(APP)并拿到浏览器传递
- Android(安卓)Jetpack - paging(分页库)
- Android中直播视频技术探究之---桌面屏幕视频数据源采集功能分析