live555推送实时视频流

1,linux 环境:
官网上下载,下载地址:http://www.live555.com/liveMedia/public/
live555 版本:“2018.12.14”
参考:http://www.live555.com/liveMedia/faq.html 这个FAQ要仔细阅读。
2,编译
根据不同的平台来配置,并生成对应的Makefile
2.1 ARM平台:
修改交叉编译工具
cp config.armlinux config.arm
vi config.arm
CROSS_COMPILE?= arm-buildroot-linux-uclibcgnueabi-
生成Makefile: ./genMakefiles arm
2.2 Linux 64位平台(x86-64 ):
./genMakefiles linux-64bit
2.3 Linux 32位平台(x86):
./genMakefiles linux
make

生成mediaServer/live555MediaServer

3,测试
3.1,mediaServer下 会生成 live555MediaServer。
live555MediaServer test.264
如果出现Correct this by increasing “OutPacketBuffer::maxSize” to at least 186818, before creating this ‘RTPSink’. (Current value is 100000.)
在DynamicRTSPServer.cpp文件ServerMediaSession* createNewSMS()
里修改OutPacketBuffer::maxSize

if (strcmp(extension, ".264") == 0) {
    // Assumed to be a H.264 Video Elementary Stream file:
    NEW_SMS("H.264 Video");
    OutPacketBuffer::maxSize = 300000; //100000;// allow for some possibly large H.264 frames
    sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
  }


createNewSMS是在RTSP setup时调用的。
3.2,testProgs
testProgs 目录下各种测试文件,每个文件的作用和用法,官网上有详细的介绍。这些测试用例目前基本上都是以文件的形式作为输入源,下面重点介绍以实时流的形式作为输入源的2种方法。
主要是参考testH264VideoStreamer 和testOnDemandRTSPServer来修改。

4.不用读文件,使用实时视频流作为输入源

最简单的方法:将实时视频流推送到一个FIFO管道(或stdin),将文件名改为这个管道的文件名,这里不做详细介绍了。管道一次写数据4096。

4.1  方法1,rtsp_server_main.cpp

#include <liveMedia.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>
#include <rtsp_stream.h>

#include "LiveServerMediaSubsession.h"

#include <pthread.h>
#include "rtsp_server.h"


using namespace KHJ;

UsageEnvironment* env;

#ifdef RTSP_H265_SUPPORT
H265VideoStreamFramer* videoSource = NULL;
#else
H264VideoStreamFramer* videoSource = NULL;
#endif

RTPSink* videoSink;

pthread_t rtsp_server_thread_id;

void play(); // forward

void * rtsp_server_thread(void *) 
{
	// Begin by setting up our usage environment:
	TaskScheduler* scheduler = BasicTaskScheduler::createNew();
	env = BasicUsageEnvironment::createNew(*scheduler);

	OutPacketBuffer::maxSize = 512 * 1024;
	RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
	if (rtspServer == NULL) {
		*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
		exit(1);
	}
	
	
	ServerMediaSession* sms = ServerMediaSession::createNew(*env, "stream", "", 
												"Session streamed by \"testVideoStreamer\"", True);
												
	sms->addSubsession(KHJ::LiveServerMediaSubsession::createNew(*env));
	rtspServer->addServerMediaSession(sms);

	char* url = rtspServer->rtspURL(sms);
	*env << "Play this stream using the URL \"" << url << "\"\n";
	delete[] url;

	*env << "Beginning streaming...\n";
	//play();
	
	if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
		*env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";
	} else {
		*env << "(RTSP-over-HTTP tunneling is not available.)\n";
	}

	env->taskScheduler().doEventLoop(); 


}

void afterPlaying(void* /*clientData*/) {
	*env << "...done reading from file\n";
	videoSink->stopPlaying();
	if (videoSource)
	Medium::close(videoSource);
	play();
}

void play() 
{
	H265Source *fileSource = H265Source::createNew(*env);

	FramedSource* videoES = fileSource;

	#ifdef RTSP_H265_SUPPORT
	videoSource = H265VideoStreamFramer::createNew(*env, videoES);
	#else
	videoSource = H264VideoStreamFramer::createNew(*env, videoES);
	#endif

	// Finally, start playing:
	*env << "Beginning to read from file...\n";
	videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}


int rtsp_server_start()
{
    int ret = 0;

    if((ret = pthread_create(&rtsp_server_thread_id, NULL, rtsp_server_thread, NULL)) <0 )
    {
        printf("create rtsp_server_thread fail\n");
    }

    return ret;
}

int rtsp_server_stop()
{
//    delete env;
//    delete videoSource;
    return 0;
}

#if 0
int main()
{
    rtsp_server_start();
    getchar();
}
#endif



4.2 方法2,参考testOnDemandRTSPServer
1)set the variable “reuseFirstSource” to “True”
2)根据类H264VideoFileServerMediaSubsession,新建一个新类H264LiveVideoServerMediaSubsession, implementation of the two pure virtual functions “createNewStreamSource()” and “createNewRTPSink()”
在createNewStreamSource()里用上面的H264LiveVideoSource代替ByteStreamFileSource。

H264VideoRTPSink继承关系:
H264VideoRTPSink->H264or5VideoRTPSink->VideoRTPSink->MultiFramedRTPSink->RTPSink->MediaSink->Medium。
H264VideoRTPSource继承关系:
H264VideoRTPSource->MultiFramedRTPSource->RTPSource->FramedSource->MediaSource->Medium.
H264VideoStreamFramer继承关系:
H264VideoStreamFramer->H264or5VideoStreamFramer->MPEGVideoStreamFramer->FramedFilter->FramedSource ->MediaSource->Medium.

下面列出具体实现


h265source.h

#ifndef MESAI_H265_SOURCE_HH
#define MESAI_H265_SOURCE_HH

#include <FramedSource.hh>
#include <UsageEnvironment.hh>
#include <Groupsock.hh>

namespace RTSP_DEMO
{
    
  class H265Source : public FramedSource {
  public:
    static H265Source* createNew(UsageEnvironment& env);
    ~H265Source();

  private:
    H265Source(UsageEnvironment& env);
    virtual void doGetNextFrame();
    virtual void doStopGettingFrames();
    
  private:
    int fp;

  };

}

#endif



h265source.cpp文件如下:

static int sfp[MAX_CLIENT_NUM] = {-1, -1, -1, -1};

namespace RTSP_DEMO
{
    H265Source * H265Source::createNew(UsageEnvironment& env) {
		return new H265Source(env);
	}

	H265Source::H265Source(UsageEnvironment& env) : FramedSource(env)
	{
        printf("%s--->%d\n", __FUNCTION__, __LINE__);

        int i=0;

        fp = -1;
        for(i=0; i<MAX_CLIENT_NUM; i++)
        {
            if(sfp[i] == -1)
            {
                fp = i;
                sfp[i] = fp;
                break;
            }
        }

        if(fp<0)
        {
            return;
        }

        memset(&client_frame_info[fp], 0, sizeof(client_frame_info[fp]));

        printf("%s--->%d, fp is %d\n", __FUNCTION__, __LINE__, fp);
	}

	H265Source::~H265Source()
	{
        printf("%s--->%d, fp is %d\n", __FUNCTION__, __LINE__, fp);

        int i=0;

        for(i=0; i<MAX_CLIENT_NUM; i++)
        {
            if(sfp[i] == fp)
            {
                fp = -1;
                sfp[i] = fp;
                break;
            }
        }

        printf("%s--->%d, fp is %d\n", __FUNCTION__, __LINE__, fp);
	}

	void H265Source::doStopGettingFrames()
	{
        printf("%s--->%d\n", __FUNCTION__, __LINE__);

        return ;
	}


	void H265Source::doGetNextFrame()
	{
		if (!isCurrentlyAwaitingData()) return;
		
		int ret = 0;
		int len = 10 * 1024;

//        printf("fMaxSize is %d\n", fMaxSize);
		
		if (len > fMaxSize)
			len = fMaxSize;
		
		gettimeofday(&fPresentationTime, NULL);
		
		
		fNumTruncatedBytes = 0;
	agin:

         if((ret = read_packet(fp, fTo, len)) > 0){
             fFrameSize = ret;
         }else{
            fFrameSize = 0;
         }
		
		if(fFrameSize>0)
			FramedSource::afterGetting(this);
		
	}
	
}


linve555常用修改点:

1, 输入的一帧数据最大值
StreamParser.cpp
#define BANK_SIZE 1500000 //帧越大,这个值就要越大

2, rtp buffer最大值
(1)Source端使用 MultiFramedRTPSource.cpp
BufferedPacket::BufferedPacket()
定义输入Buffer的上限值,即BufferedPacket的最大值
#define MAX_PACKET_SIZE 65536
(2)Sink端使用 MultiFramedRTPSink.cpp
#define RTP_PAYLOAD_MAX_SIZE 1456 //(1500-14-20-8)/4 *4 //ethernet=14,IP=20, UDP=8, a multiple of 4 bytes
MediaSink.cpp
静态变量OutPacketBuffer::maxSize = 600000; // allow for some possibly large H.265 frames,2000000 is by default
最好是RTP_PAYLOAD_MAX_SIZE的整数倍
值小了,会不断打印信息: Correct this by increasing “OutPacketBuffer::maxSize” to at least

,3,获取IP地址失败
RTSPServer::rtspURLPrefix(){
ourIPAddress(envir())
}

GroupsockHelper.cpp

 ourIPAddress()
 {
    if (badAddressForUs(from)) {
      #if 0
      char tmp[100];
      sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val());
      env.setResultMsg(tmp);
      from = 0;
      #endif
      struct ifreq req;
      int ret = 0;
      char szIpBuf[32];

      sock = socket(AF_INET, SOCK_DGRAM, 0);
      if (-1 != sock)
      {
          memset(&req, 0, sizeof(req));
          strncpy(req.ifr_name, "eth0", sizeof(req.ifr_name));
          ret = ioctl(sock, SIOCGIFADDR, &req);
          if (-1 == ret)
          {
            close(sock);
          }
          else
              {
               memset(&szIpBuf, 0, sizeof(szIpBuf));
             strcpy(szIpBuf, inet_ntoa(((struct sockaddr_in *)&req.ifr_addr)->sin_addr));
            close(sock);
            fromAddr.sin_addr.s_addr=our_inet_addr(szIpBuf);
            from = fromAddr.sin_addr.s_addr;
              }
      }
      else
        {
             char tmp[100];
         sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val());
         env.setResultMsg(tmp);
         from = 0;
        }
 }


3,内存泄漏点
RTCPInstance::processIncomingReport
if(NULL != reason)
{
delete[] reason;
reason = NULL;
}
在申请内存时加上上面释放语句
reason = new char[reasonLength + 1];
4,fill sei data DeltaTfiDivisor

H264or5VideoStreamParser::H264or5VideoStreamParser()
{
    //according to H264 and H265 spec, if not fill sei data, then         
frame_field_info_present_flag is zero. so need to set DeltaTfiDivisor to 2.0 in H264 and                         1.0 in H265
    if(fHNumber == 264) {
    DeltaTfiDivisor = 2.0;
    } else {
    DeltaTfiDivisor = 1.0;
    }
}


5,长时间拉取拉取RTSP流
报错误"Hit limit when reading incoming packet over TCP"
可考虑提高maxRTCPPacketSize的值
RTCP.CPP
static unsigned const maxRTCPPacketSize = 1456;

6,如播放越久延时越大
MultiFramedRTPSink.cpp->MultiFramedRTPSink::sendPacketIfNecessary() 最后延时列队uSecondsToGo 每帧都有延时时间。将uSecondsToGo 值赋为0。

7, 裁剪

只需留下这些目录(BasicUsageEnvironment、groupsock、liveMedia、mediaServer、UsageEnvironment),其它可删除掉。
其中liveMedia目录下有很多类型的文件,不需要的也可删除,同时修改
MediaSubsession::createSourceObjects()把相关类型的createNew也删除掉,否则编译失败。

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠哉无忧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值