一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(十)使用JRTPLIB传输RTP数据

myRtspClient通过简单修改JRTPLIB的官方例程作为其RTP传输层实现。因为JRTPLIB使用的是CMAKE编译工具,这就是为什么编译myRtspClient时需要预装CMAKE。

该部分所有代码均集中在myRtpSession.cpp中,接下来将对其进行分析。

一、获取RTP数据

此处GetMyRTPData获取数据的方式主要是轮询,即每隔USLEEP_UNIT个微秒轮询一次直到获取到一包数据或超时,超时时间为timeout_ms,单位是微秒。

GetMyRTPPacket的逻辑与之相同,但是会比前者多获取RTP消息头,对于myRtspClient的用户来说,这同样也就是RtspClient::GetMediaPacket和RtspClient::GetMediaData的区别(逻辑相同,GetMyRTPPacket的代码就不再此附上了)。

RtspClient::GetMediaPacket和RtspClient::GetMediaData最终获取RTP数据就是通过调用这两个函数完成的。

uint8_t * MyRTPSession::GetMyRTPData(uint8_t * data_buf, size_t * size, unsigned long timeout_ms)
{
    if(!data_buf) {
        fprintf(stderr, "%s: Invalide argument('data_buf==NULL')", __func__);
        return NULL;
    }

    if(!size) {
        fprintf(stderr, "%s: Invalide argument('size==NULL')", __func__);
        return NULL;
    }

    unsigned long UsleepTimes = (timeout_ms + USLEEP_UNIT - 1) / USLEEP_UNIT; // floor the 'timeout_ms / USLEEP_UNIT'

    do {
#ifndef RTP_SUPPORT_THREAD
        int status = Poll();
        if(!IsError(status)) return NULL;
#endif

        BeginDataAccess();

        // check incoming packets
        if (!GotoFirstSourceWithData()) {
            EndDataAccess();
            usleep(USLEEP_UNIT);
            UsleepTimes--;
            continue;
            // return NULL;
        }
        RTPPacket *pack;

        if(!(pack = GetNextPacket()))
        {
            EndDataAccess();
            usleep(USLEEP_UNIT);
            UsleepTimes--;
            continue;
            // return NULL;
        }

        size_t PacketSize = 0;
        uint8_t * Packet = NULL;
        Packet = pack->GetPayloadData();
        PacketSize = pack->GetPayloadLength();
        // printf("data length: %lu\n", PacketSize);

        *size = PacketSize;
        memcpy(data_buf, Packet, PacketSize);

        // we don't longer need the packet, so
        // we'll delete it
        DeletePacket(pack);
        EndDataAccess();
        UsleepTimes = 0; // Got the data. So not need to sleep any more.
    } while(UsleepTimes > 0);

    return data_buf;
}

二、会话建立、结束等接口

此处的MyRTP_SetUp的作用是建立会话,它会确定RTP/RTCP的UDP端口,建立通信socket。每当RTSP的SETUP命令设置成功后,都会调用此函数。
int MyRTPSession::MyRTP_SetUp(MediaSession * media_session)
{
    if(!media_session) {
        fprintf(stderr, "%s: Invalid media session\n", __func__);
        return RTP_ERROR;
    }
    if(0 == media_session->TimeRate) {
        fprintf(stderr, "%s: Invalid MediaSession::TimeRate\n", __func__);
        return RTP_ERROR;
    }
    if(0 == media_session->RTPPort) {
        fprintf(stderr, "%s: Invalid MediaSession::RTPPort\n", __func__);
        return RTP_ERROR;
    }

    int status;

    // Now, we'll create a RTP session, set the destination
    // and poll for incoming data.

    RTPUDPv4TransmissionParams transparams;
    RTPSessionParams sessparams;

    // IMPORTANT: The local timestamp unit MUST be set, otherwise
    //            RTCP Sender Report info will be calculated wrong
    // In this case, we'll be just use 8000 samples per second.
    sessparams.SetOwnTimestampUnit(1.0/media_session->TimeRate);

    sessparams.SetAcceptOwnPackets(true);
    transparams.SetPortbase(media_session->RTPPort);
    status = Create(sessparams,&transparams);
    return IsError(status);
}

客户端通过MyRTP_Teardown发起销毁会话,每当RTSP的TEARDOWN命令设置成功后,都会调用此函数。

void MyRTPSession::MyRTP_Teardown(MediaSession * media_session, struct timeval * tval)
{
    struct timeval Timeout;

    if(!tval) {
        Timeout.tv_sec = 1;
        Timeout.tv_usec = 0;
    } else {
        Timeout.tv_sec = tval->tv_sec;
        Timeout.tv_usec = tval->tv_usec;
    }

    media_session->RTPPort = 0;
    BYEDestroy(RTPTime(Timeout.tv_sec, Timeout.tv_usec), 0, 0);
}

客户端通过OnBYEPacket被动销毁会话,当服务器向客户端发送BYE的RTP数据包时(比如当一段媒体流播放完的时候),该函数就会被调用。其中DestroiedClbk是myRtspClient提供给用户的回调接口。用户可以通过调用RtspClient::SetAudioByeFromServerClbk/RtspClient::SetVideoByeFromServerClbk来设置该函数。(逻辑相同,OnRemoveSource的代码就不再此附上了)。

void MyRTPSession::OnBYEPacket(RTPSourceData *dat)
{
    if (dat->IsOwnSSRC())
        return;

    uint32_t ip;
    uint16_t port;

    if (dat->GetRTPDataAddress() != 0)
    {
        const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
        ip = addr->GetIP();
        port = addr->GetPort();
    }
    else if (dat->GetRTCPDataAddress() != 0)
    {
        const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
        ip = addr->GetIP();
        port = addr->GetPort()-1;
    }
    else
        return;

    RTPIPv4Address dest(ip,port);
    DeleteDestination(dest);

    struct in_addr inaddr;
    inaddr.s_addr = htonl(ip);
    std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
    if(DestroiedClbk) {
        DestroiedClbk();
    }
}

每当新加入一个RTP数据源,OnNewSource就会被调用

void MyRTPSession::OnNewSource(RTPSourceData *dat)
{
    if (dat->IsOwnSSRC())
        return;

    uint32_t ip;
    uint16_t port;

    if (dat->GetRTPDataAddress() != 0)
    {
        const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
        ip = addr->GetIP();
        port = addr->GetPort();
    }
    else if (dat->GetRTCPDataAddress() != 0)
    {
        const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
        ip = addr->GetIP();
        port = addr->GetPort()-1;
    }
    else
        return;

    RTPIPv4Address dest(ip,port);
    AddDestination(dest);

    struct in_addr inaddr;
    inaddr.s_addr = htonl(ip);
    std::cout << "Adding destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值