Jrtplib在Android平台上的使用

匿名 (未验证) 提交于 2019-12-03 00:02:01

由于工作中需要用到rtp协议,java暂时没有比较好的开发框架,参考了其他的一些博文,自己摸索着使用jrtplib库,在此记录一下。

  1. 关于如何编译jrtplib库,在我的上一篇有讲解jrtplib库移植到android上
  2. 可根据自身需求,参考实现的功能。

实现的功能

一、接收端

  1. 接收RTP数据,携带数据并回调通知Java端;
  2. 接收RTCP、BYE等数据,携带数据发送端IP字符串并回调通知Java端;
  3. 实现网络摄像机RTSP协议对接;
  4. 实现接收RTP数据,并转发RTP数据。

二、发送端
1、实现H264编码数据分包发送;
2、发送RTP数据;
3、接收RTCP、BYE等数据,并携带数据发送端IP字符串回调至Java。

准备工作

  • Android.mk 文件编写
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)
  • Application.mk文件编写
#对标准C++ stl库的支持 #APP_STL := stlport_static APP_STL := gnustl_static APP_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() <<std::endl;             callbackRtp(pack->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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!