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, 裁剪文章来源:https://www.toymoban.com/news/detail-401971.html
只需留下这些目录(BasicUsageEnvironment、groupsock、liveMedia、mediaServer、UsageEnvironment),其它可删除掉。
其中liveMedia目录下有很多类型的文件,不需要的也可删除,同时修改
MediaSubsession::createSourceObjects()把相关类型的createNew也删除掉,否则编译失败。文章来源地址https://www.toymoban.com/news/detail-401971.html
到了这里,关于live555推送实时视频流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!