JRTPLIB详解

简介

RTP 是目前解决流媒体实时传输问题的最好办法,如果需要在Linux平台上进行实时流媒体编程,可以考虑使用一些开放源代码的RTP库,如LIBRTP、 JRTPLIB等。 JRTPLIB是一个面向对象的RTP库,它完全遵循RFC 1889设计,在很多场合下是一个非常不错的选择,下面就以JRTPLIB为例,讲述如何在Linux平台上运用RTP协议进行实时流媒体编程。

JRTPLIB 是一个用C++语言实现的RTP库,目前已经可以运行在Windows、Linux、FreeBSD、Solaris、Unix和 VxWorks等多种操作系统上。

CMake介绍

CMake是一个工程文件生成工具。用户可以使用预定义好的CMake脚本,根据自己配置选择开发环境(像是Visual Studio, Code::Blocks, Eclipse)生成不同IDE的工程文件。
Cmake下载地址:https://cmake.org/download/
解压后直接运行cmake-gui.exe。
image

JThread编译

git clone https://github.com/j0r1/JRTPLIB.git
cd JRTPLIB
mkdir build
cd build
cmake .. && make && sudo make install

如果需要启用JThread支持,需要在编译JRTPLIB之前编译JThread

git clone https://github.com/j0r1/JThread.git
cd JRTPLIB
mkdir build
cd build
cmake .. && make && sudo make install

JRTPLIB例程

官方历程参见https://github.com/j0r1/JRTPLIB/tree/master/examples,以下是example1.cpp,添加了一些中文注释

#include "rtpsession.h"
#include "rtpudpv4transmitter.h"
#include "rtpipv4address.h"
#include "rtpsessionparams.h"
#include "rtperrors.h"
#include "rtplibraryversion.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>

using namespace jrtplib;

// 打印错误信息
void checkerror(int rtperr)
{
    if (rtperr < 0)
    {
        std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl;
        exit(-1);
    }
}

// 主循环

int main(void)
{

// windows相关,不需要可以删了
#ifdef RTP_SOCKETTYPE_WINSOCK
    WSADATA dat;
    WSAStartup(MAKEWORD(2,2),&dat);
#endif // RTP_SOCKETTYPE_WINSOCK

    RTPSession sess;
    uint16_t portbase,destport;
    uint32_t destip;
    std::string ipstr;
    int status,i,num;

    // 打印JRTPLIB版本
    std::cout << "Using version " << RTPLibraryVersion::GetVersion().GetVersionString() << std::endl;

    // 获取本地端口,用以接收
    std::cout << "Enter local portbase:" << std::endl;
    std::cin >> portbase;
    std::cout << std::endl;

    // 目标地址,本机使用127.0.0.1
    std::cout << "Enter the destination IP address" << std::endl;
    std::cin >> ipstr;
    destip = inet_addr(ipstr.c_str());
    if (destip == INADDR_NONE)
    {
        std::cerr << "Bad IP address specified" << std::endl;
        return -1;
    }

    // inet_addr函数返回的是网络字节序,需要将网络字节序转化为主机字节序
    destip = ntohl(destip);

    // 目标端口,如果发送给本机,与上面的接收端口相同即可
    std::cout << "Enter the destination port" << std::endl;
    std::cin >> destport;

    // 发包数量
    std::cout << std::endl;
    std::cout << "Number of packets you wish to be sent:" << std::endl;
    std::cin >> num;

    // 创建传输参数和会话参数
    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 sending 10 samples each second, so we'll
    // put the timestamp unit to (1.0/10.0)
    // 设置时间戳
    sessparams.SetOwnTimestampUnit(1.0/10.0);

    // 是否接收自己发送的包
    sessparams.SetAcceptOwnPackets(true);
    // 设置接收端口
    transparams.SetPortbase(portbase);
    // 创建端口
    status = sess.Create(sessparams,&transparams);
    checkerror(status);

    // 写入IPv4地址和端口
    RTPIPv4Address addr(destip,destport);

    // 添加目标地址
    status = sess.AddDestination(addr);
    checkerror(status);

    for (i = 1 ; i <= num ; i++)
    {
        printf("\nSending packet %d/%d\n",i,num);

        // 发包
        status = sess.SendPacket((void *)"1234567890",10,0,false,10);
        checkerror(status);

        sess.BeginDataAccess();

        // 检查收包
        if (sess.GotoFirstSourceWithData())
        {
            do
            {
                RTPPacket *pack;

                while ((pack = sess.GetNextPacket()) != NULL)
                {
                    // 在这里进行数据处理
                    printf("Got packet !\n");

                    // 不再需要这个包了,删除之
                    sess.DeletePacket(pack);
                }
            } while (sess.GotoNextSourceWithData());
        }

        sess.EndDataAccess();

// 这部分与JThread库相关
#ifndef RTP_SUPPORT_THREAD
        status = sess.Poll();
        checkerror(status);
#endif // RTP_SUPPORT_THREAD

        // 等待一秒,发包间隔
        RTPTime::Wait(RTPTime(1,0));
    }

    // 销毁对话
    sess.BYEDestroy(RTPTime(10,0),0,0);

// Windows相关
#ifdef RTP_SOCKETTYPE_WINSOCK
    WSACleanup();
#endif // RTP_SOCKETTYPE_WINSOCK
    return 0;
}

这里会有问题,怎么读取收到的packet中的data,需要先包含头文件

#include "rtppacket.h"

在进行数据处理的地方使用以下函数获取负载数据,和负载长度

pack->GetPayloadData();
pack->GetPayloadLength();

CMake配置

在使用CMake进行编译的时候可能会出现找不到库的情况,需要手写FindJRTPLIB.cmake

# - Find JRTPLIB library
# Once done this will define
# JRTPLIB_FOUND - system has JRTPLIB
# JRTPLIB_INCLUDE_DIR - JRTPLIB include directories
# JRTPLIB_LIBRARY - where to find the JRTPLIB library

if(JRTPLIB_INCLUDE_DIR)
    # Already in cache, be silent
    set(JRTPLIB_FIND_QUIETLY TRUE)
endif()

find_path(JRTPLIB_INCLUDE_DIR
    NAMES rtpsession.h
    PATH_SUFFIXES jrtplib jrtplib3
    DOC "JRTPLIB include directories"
    )

find_library(JRTPLIB_LIBRARY
    NAMES jrtp
    DOC "JRTPLIB library"
    )

# handle the QUIETLY and REQUIRED arguments and set JRTPLIB_FOUND to TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(JRTPLIB
                                  REQUIRED_VARS JRTPLIB_LIBRARY JRTPLIB_INCLUDE_DIR
                                  VERSION_VAR JRTPLIB_VERSION_STRING)

if(JRTPLIB_INCLUDE_DIR AND JRTPLIB_LIBRARY)
    set(JRTPLIB_FOUND TRUE)
    set(JRTPLIB_LIBRARIES ${JRTPLIB_LIBRARY})
    set(JRTPLIB_INCLUDE_DIRS ${JRTPLIB_INCLUDE_DIR})
else()
    set (JRTPLIB_FOUND FALSE)
    message(FATAL_ERROR "JRTPLIB not found")
endif()

mark_as_advanced(
    JRTPLIB_INCLUDE_DIR
    JRTPLIB_LIBRARY)

如果前面编译了JThread库,或者在编译的时候提示

This function is not available when using the RTP Poll thread feature.

则还需要将JThread库进行链接,FindJTHREAD.cmake如下:

# - Find JTHREAD library
# Once done this will define
# JTHREAD_FOUND - system has JTHREAD
# JTHREAD_INCLUDE_DIR - JTHREAD include directories
# JTHREAD_LIBRARY - where to find the JTHREAD library

if(JTHREAD_INCLUDE_DIR)
    # Already in cache, be silent
    set(JTHREAD_FIND_QUIETLY TRUE)
endif()

find_path(JTHREAD_INCLUDE_DIR
    NAMES jthread.h
    PATH_SUFFIXES jthread jthread3
    DOC "JTHREAD include directories"
    )

find_library(JTHREAD_LIBRARY
    NAMES jthread
    DOC "JTHREAD library"
    )

# handle the QUIETLY and REQUIRED arguments and set JTHREAD_FOUND to TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(JTHREAD
                                  REQUIRED_VARS JTHREAD_LIBRARY JTHREAD_INCLUDE_DIR
                                  VERSION_VAR JTHREAD_VERSION_STRING)

if(JTHREAD_INCLUDE_DIR AND JTHREAD_LIBRARY)
    set(JTHREAD_FOUND TRUE)
    set(JTHREAD_LIBRARIES ${JTHREAD_LIBRARY})
    set(JTHREAD_INCLUDE_DIRS ${JTHREAD_INCLUDE_DIR})
else()
    set (JTHREAD_FOUND FALSE)
    message(FATAL_ERROR "JTHREAD not found")
endif()

mark_as_advanced(
    JTHREAD_INCLUDE_DIR
    JTHREAD_LIBRARY)

之后在顶层的CMakeLists.txt中使用find_package(JRTPLIB)进行链接,不再赘述。

posted @ 2023-10-08 15:55  先锋之客  阅读(418)  评论(0编辑  收藏  举报