Live555 C++ arm linux64 RTSP推流开发

一、首先安装Live555

1、下载源码

由于不能apt-get install,所以先官网下载源码。http://www.live555.com/liveMedia/
在这里插入图片描述
在这里插入图片描述
解压 tar -zxvf live.2023.07.24.tar.gz

安装可以看这篇博文前部
live555server环境搭建
OpenSSL必须安装

2、生成makefile

安装时如果直接在arm板子上装,生成makefile时就直接写

~/live$ ./genMakefiles linux

# 查看Makefile
~/live$ cat Makefile
##### Change the following for your environment:
COMPILE_OPTS =		$(INCLUDES) -I/usr/local/include -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C =			c
C_COMPILER =		cc
...

不能写成armlinux

~/live$ ./genMakefiles armlinux

# 查看Makefile
~/live$ cat Makefile
##### Change the following for your environment:
CROSS_COMPILE?=		arm-elf-
COMPILE_OPTS =		$(INCLUDES) -I/usr/local/include -I. -O2 -DSOCKLEN_T=socklen_t -DNO_SSTREAM=1 -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C =			c
...

如果选成了armlinux,这样make的时候用的就是交叉编译工具链,仅适用于从x86机器上给arm机器编译。

3、make

make时基本都会遇到test问题
编辑一下这个文件 /live/BasicUsageEnvironment/BasicTaskScheduler.cpp(190行左右)

if (fTriggersAwaitingHandling[i].test()) {
# 将上面这行改为
if (fTriggersAwaitingHandling[i].test_and_set()) {

4、sudo make install

编好了再运行sudo make install之后,就会把头文件放到 /usr/local/include 中,库文件放到 /usr/local/lib 中,不需要添加环境变量。

5、编译

编译demo的时候,在makefile中增加-I和-L就行。

文件名:
test_live555.cpp
编译指令:
g++ -I/usr/local/include -I/usr/local/include/groupsock -I/usr/local/include/UsageEnvironment -L/usr/local/lib test_live555.cpp -o test_live555 -lliveMedia -lBasicUsageEnvironment -lgroupsock -lUsageEnvironment -lssl -lcrypto

-lliveMedia -lBasicUsageEnvironment -lgroupsock -lUsageEnvironment 是live555的
-lssl -lcrypto 是OpenSSL的

二、运行demo

我们既然是要做rtsp推流,demo在 live/testProgs/testOnDemandRTSPServer.cpp 中,这个demo功能就是读取本地视频,然后启动RTSP推流服务器。

make的时候已经编译好了,我们可以直接运行。
不过我删减了一些,得自己编译了。拷到test_live555.cpp中,写一个makefile:

#include <liveMedia/liveMedia.hh>
#include <BasicUsageEnvironment/BasicUsageEnvironment.hh>
#include <UsageEnvironment/UsageEnvironment.hh>
#include <GroupsockHelper.hh>


void announceURL(RTSPServer* rtspServer, ServerMediaSession* sms) {
  if (rtspServer == NULL || sms == NULL) return; // sanity check

  UsageEnvironment& env = rtspServer->envir();

  env << "Play this stream using the URL ";
  if (weHaveAnIPv4Address(env)) {
    char* url = rtspServer->ipv4rtspURL(sms);
    env << "\"" << url << "\"";
    delete[] url;
    if (weHaveAnIPv6Address(env)) env << " or ";
  }
  if (weHaveAnIPv6Address(env)) {
    char* url = rtspServer->ipv6rtspURL(sms);
    env << "\"" << url << "\"";
    delete[] url;
  }
  env << "\n";
}
static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
			   char const* streamName, char const* inputFileName) {
  UsageEnvironment& env = rtspServer->envir();

  env << "\n\"" << streamName << "\" stream, from the file \""
      << inputFileName << "\"\n";
  announceURL(rtspServer, sms);
}

UsageEnvironment* env;

// To make the second and subsequent client for each stream reuse the same
// input stream as the first client (rather than playing the file from the
// start for each client), change the following "False" to "True":
Boolean reuseFirstSource = False;

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

  UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
  authDB = new UserAuthenticationDatabase;
  authDB->addUserRecord("admin", "a12345678");
#endif

  // Create the RTSP server:
#ifdef SERVER_USE_TLS
  // Serve RTSPS: RTSP over a TLS connection:
  RTSPServer* rtspServer = RTSPServer::createNew(*env, 322, authDB);
#else
  // Serve regular RTSP (over a TCP connection):
  RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
#endif
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }

  char const* descriptionString
    = "Session streamed by \"testOnDemandRTSPServer\"";

  // A H.264 video elementary stream:
  {
    char const* streamName = "h264ESVideoTest";
    char const* inputFileName = "test.264";
    ServerMediaSession* sms
      = ServerMediaSession::createNew(*env, streamName, streamName,
				      descriptionString);
    sms->addSubsession(H264VideoFileServerMediaSubsession
		       ::createNew(*env, inputFileName, reuseFirstSource));
    rtspServer->addServerMediaSession(sms);

    announceStream(rtspServer, sms, streamName, inputFileName);
  }

#ifdef SERVER_USE_TLS
  // (Attempt to) use the default HTTPS port (443) instead:
  char const* httpProtocolStr = "HTTPS";
  if (rtspServer->setUpTunnelingOverHTTP(443)) {
#else
  char const* httpProtocolStr = "HTTP";
  if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
#endif
    *env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-" << httpProtocolStr << " tunneling.)\n";
  } else {
    *env << "\n(RTSP-over-" << httpProtocolStr << " tunneling is not available.)\n";
  }

  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}


CC = g++
CFLAGS = -I/usr/local/include -I/usr/local/include/groupsock -I/usr/local/include/UsageEnvironment
LDFLAGS = -L/usr/local/lib
LIBS = -lliveMedia -lBasicUsageEnvironment -lgroupsock -lUsageEnvironment -lssl -lcrypto

TARGET = test_live555

all: $(TARGET)

$(TARGET): test_live555.cpp
	$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(LIBS)

clean:
	rm -f $(TARGET)

(删干净了其他格式,只留了.264的,不然太乱)随便拷了一个.264文件过来,运行后打印如下:

$ ./test_live555 

"h264ESVideoTest" stream, from the file "test.264"
Play this stream using the URL "rtsp://192.168.1.149:8554/h264ESVideoTest"

(We use port 8000 for optional RTSP-over-HTTP tunneling.)

这时就代表,已经开始推流了,用vlc输入这个url就可以拉了
请添加图片描述
拉流时可以看到程序打印

MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size (60972).  55319 bytes of trailing data was dropped!  Correct this by increasing "OutPacketBuffer::maxSize" to at least 115319, *before* creating this 'RTPSink'.  (Current value is 60000.)
MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size (60972).  55319 bytes of trailing data was dropped!  Correct this by increasing "OutPacketBuffer::maxSize" to at least 115319, *before* creating this 'RTPSink'.  (Current value is 60000.)
MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size (60972).  9383 bytes of trailing data was dropped!  Correct this by increasing "OutPacketBuffer::maxSize" to at least 69383, *before* creating this 'RTPSink'.  (Current value is 60000.)

同时也可以抓包看到RTSP相关网络包
请添加图片描述

三、问题

live555似乎无法对RTSP推理地址进行定制。除非改源码。
在这里插入图片描述

源码GroupsockHelper.cpp可见

ipv4AddressBits ourIPv4Address(UsageEnvironment& env) {
  if (ReceivingInterfaceAddr != INADDR_ANY) {
    // Hack: If we were told to receive on a specific interface address, then 
    // define this to be our ip address:
    _ourIPv4Address = ReceivingInterfaceAddr;
  }

  if (!_weHaveAnIPv4Address) {
    getOurIPAddresses(env);
  }

  return _ourIPv4Address;
}

...

void getOurIPAddresses(UsageEnvironment& env) {
  // We use two methods to (try to) get our IP addresses.
  // First, we use "getifaddrs()".  But if that doesn't work
  // (or if "getifaddrs()" is not defined), then we use an alternative (more old-fashioned)
  // mechanism: First get our host name, then try resolving this host name.
  struct sockaddr_storage foundIPv4Address = nullAddress(AF_INET);
  struct sockaddr_storage foundIPv6Address = nullAddress(AF_INET6);

  Boolean getifaddrsWorks = False; // until we learn otherwise
#ifndef NO_GETIFADDRS
  struct ifaddrs* ifap;

  if (getifaddrs(&ifap) == 0) {
    // Look through all interfaces:
    for (struct ifaddrs* p = ifap; p != NULL; p = p->ifa_next) {
      // Ignore an interface if it's not up, or is a loopback interface:
      if ((p->ifa_flags&IFF_UP) == 0 || (p->ifa_flags&IFF_LOOPBACK) != 0) continue;

      // Also ignore the interface if the address is considered 'bad' for us:
      if (p->ifa_addr == NULL || isBadAddressForUs(*p->ifa_addr)) continue;

      // We take the first IPv4 and first IPv6 addresses:
      if (p->ifa_addr->sa_family == AF_INET && addressIsNull(foundIPv4Address)) {
        copyAddress(foundIPv4Address, p->ifa_addr);
        getifaddrsWorks = True;
      } else if (p->ifa_addr->sa_family == AF_INET6 && addressIsNull(foundIPv6Address)) {
        copyAddress(foundIPv6Address, p->ifa_addr);
        getifaddrsWorks = True;
      }
    }
    freeifaddrs(ifap);
  }
  
  ...

在获取IP时

for (struct ifaddrs* p = ifap; p != NULL; p = p->ifa_next) {

ifap中有所有网卡的信息,但是

if (p->ifa_addr->sa_family == AF_INET && addressIsNull(foundIPv4Address)) {

这句只拿了第一个符合要求的ip。只要变量foundIPv4Address中有结果,就不会再要了。所以只能获取到一个ip,返回到上层的_ourIPv4Address中,拼接到RTSP服务器url前面。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值