由于工作中需要用到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  := 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

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. Android之手机出现"已安装了存在签名冲突的同名数据包"的原因及
  4. Android(安卓)IO性能分析及排查
  5. Android实现社交应用中的评论和回复功能
  6. Android与物联网设备通信-网络模型分层
  7. Android实现通过浏览器点击链接打开本地应用(APP)并拿到浏览器传递
  8. Android(安卓)Jetpack - paging(分页库)
  9. Android中直播视频技术探究之---桌面屏幕视频数据源采集功能分析

随机推荐

  1. 我的第一个Android应用程序HelloWorld
  2. Cursor与Adapter结合使用
  3. Xamarin的Mono for Android目前可运行于M
  4. Ubuntu14.04 Android(安卓)源码编译
  5. Samsung Galaxy Nexus 正式發佈,4.65 吋 H
  6. Android中矢量图形的那些事 - SVG or Vec
  7. Android(安卓)反射机制
  8. Android集结号
  9. flutter项目的打包和发布 主要是Android
  10. [置顶] android 自己创建一个注释模板