修改live555支持从缓冲区读取h264帧 (纠正下是 h264 slice)

(2019-03-25 纠正下,下面所有h264帧概念,其实是h264slice, 并非一帧图像,h264中每一个slice可以单独解码,所以有时候会看到 花屏,因为一帧数据被分成很多单独的slice,如果网络帧丢了部分slice,则一帧图像会出现只解码一部分的情况)

现有本地已知的h264数据帧缓存,想对接到live555 使用rtsp发送。

live555源码中是支持直接读h264文件的demo, 想改为从自己的dev获取数据,官方也有相关FAQ:http://www.live555.com/liveMedia/faq.html  :3. The "test*Streamer" test programs read from a file. Can I modify them so that they take input from a H.264, H.265 or MPEG encoder instead, so I can stream live (rather than prerecorded) video and/or audio?

原意还是自行查看原语,有一句:(Even simpler, if your operating system represents the encoder device as a file, then you can just use the name of this file (instead of "test.*")

先贴上最终成果:(具体分析流程后续贴上)

先说明:(以下方式对接,修改是比较少的,但是效率可是有折扣的,数据在传到发送模块前先在framepaser中走了一圈,而这个framepaser中很大的一部分工作在于从这一串流数据中解析出每一帧的起始和结束,当然也有sps,pps等信息,而我们已知的本地缓冲区已经是知道帧开头结束的,这一部分的工作重复,还多了framepaser内部使用的内存空间 ,要解决这些问题,那就需要自行实现H264VideoStreamFramer这一层了)

参照testOnDemandRTSPServer.cpp, 在其中添加自己的一个模块(尽量保持原代码的完整性,建议所有的修改另行添加一个目录)。

main函数添加自己的模块:

#if 1
  // A H.264 live elementary stream:
  {//这里是自己的ServerMediaSubssion
  
        //这个是输出缓冲区的大小,要设置到比任意一帧h264 大
 	 OutPacketBuffer::maxSize = 2000000;
    char const* streamName = "h264Live";
	
    char const* inputFileName = "live";
    ServerMediaSession* sms
      = ServerMediaSession::createNew(*env, streamName, streamName,
				      descriptionString);
    sms->addSubsession(H264LiveVideoServerMediaSubssion
		       ::createNew(*env,reuseFirstSource));
    rtspServer->addServerMediaSession(sms);

    announceStream(rtspServer, sms, streamName, inputFileName);
  }
#endif
#if 1
   // H.264原demo对比
   // 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);
	   }
#endif

下面要实现自己的

class H264LiveVideoServerMediaSubssion

H264FramedLiveSource.hh

#ifndef _H264_LIVE_VIDEO_SERVER_MEDIA_SUBSESSION_HH

#define _H264_LIVE_VIDEO_SERVER_MEDIA_SUBSESSION_HH

#include "OnDemandServerMediaSubsession.hh"

 

class H264LiveVideoServerMediaSubssion : public OnDemandServerMediaSubsession 
{
 
public:
	static H264LiveVideoServerMediaSubssion* createNew(UsageEnvironment& env, Boolean reuseFirstSource);

protected:

	H264LiveVideoServerMediaSubssion(UsageEnvironment& env,Boolean reuseFirstSource);
	~H264LiveVideoServerMediaSubssion();

protected: // redefined virtual functions
	FramedSource* createNewStreamSource(unsigned clientSessionId,unsigned& estBitrate);
	RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
                                    unsigned char rtpPayloadTypeIfDynamic,
				    FramedSource* inputSource);
public:

};

#endif

H264FramedLiveSource.cpp


#include "H264LiveVideoServerMediaSubssion.hh"
#include "H264FramedLiveSource.hh"
#include "H264VideoStreamFramer.hh"
#include "H264VideoRTPSink.hh"
 
H264LiveVideoServerMediaSubssion* H264LiveVideoServerMediaSubssion::createNew(UsageEnvironment& env, Boolean reuseFirstSource)
{
	return new H264LiveVideoServerMediaSubssion(env, reuseFirstSource);
}

H264LiveVideoServerMediaSubssion::H264LiveVideoServerMediaSubssion(UsageEnvironment& env,Boolean reuseFirstSource)
: OnDemandServerMediaSubsession(env,reuseFirstSource)
{

}

H264LiveVideoServerMediaSubssion::~H264LiveVideoServerMediaSubssion()
{

}

FramedSource* H264LiveVideoServerMediaSubssion::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)
{
	//创建视频源,参照H264VideoFileServerMediaSubsession
	H264FramedLiveSource* liveSource = H264FramedLiveSource::createNew(envir());
	if (liveSource == NULL)
	{
		return NULL;
	}
	// Create a framer for the Video Elementary Stream:
	return H264VideoStreamFramer::createNew(envir(), liveSource);

}

RTPSink* H264LiveVideoServerMediaSubssion
::createNewRTPSink(Groupsock* rtpGroupsock,
		   unsigned char rtpPayloadTypeIfDynamic,
		   FramedSource* /*inputSource*/) {
  return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
}

下面是关键,创建H264FramedLiveSource,自己的输入源。也是对比H264VideoFileServerMediaSubsession

里面用的ByteStreamFileSource 来分析。

H264FramedLiveSource

class H264FramedLiveSource : public FramedSource
{

public:
	static H264FramedLiveSource* createNew(UsageEnvironment& env);
	// redefined virtual functions
	virtual unsigned maxFrameSize() const;

protected:

	H264FramedLiveSource(UsageEnvironment& env);

	 virtual ~H264FramedLiveSource();
 
private:
	virtual void doGetNextFrame();

protected:
	
	static TestFromFile * pTest;

};

H264FramedLiveSource.cpp

H264FramedLiveSource::H264FramedLiveSource(UsageEnvironment& env)
: FramedSource(env)
{

}


H264FramedLiveSource* H264FramedLiveSource::createNew(UsageEnvironment& env)
{
	H264FramedLiveSource* newSource = new H264FramedLiveSource(env);
	return newSource;
}

H264FramedLiveSource::~H264FramedLiveSource()
{

}
unsigned H264FramedLiveSource::maxFrameSize() const
{
	//printf("wangmaxframesize %d %s\n",__LINE__,__FUNCTION__);
	//这里返回本地h264帧数据的最大长度
	return 1024*120;
}

void H264FramedLiveSource::doGetNextFrame()
{
        //这里读取本地的帧数据,就是一个memcpy(fTo,XX,fMaxSize),要确保你的数据不丢失,即fMaxSize要大于等于本地帧缓存的大小,关键在于上面的maxFrameSize() 虚函数的实现
        fFrameSize = XXXgeth264Frame(fTo,  fMaxSize);
        printf("read dat befor %d %s fMaxSize %d ,fFrameSize %d  \n",__LINE__,__FUNCTION__,fMaxSize,fFrameSize);
		
	if (fFrameSize == 0) {
		handleClosure();
		return;
	  }


	  //设置时间戳
	  gettimeofday(&fPresentationTime, NULL);

	  // Inform the reader that he has data:
	  // To avoid possible infinite recursion, we need to return to the event loop to do this:
	  nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
					(TaskFunc*)FramedSource::afterGetting, this);

	}
	
	return;
}

以上,关键在于doGetNextFrame 虚函数(用java里面的 接口 称谓,感觉更为妥当)的实现,明确该接口必须要做哪些工作:

参照ByteStreamFileSource类中的实现,可以列出这个虚函数需要做的工作:

1.0 读数据到 fto

2.0 实际读到的数据个数设置到 fFrameSize

3.0 设置时间戳到 fPresentationTime

4.0 读完数据,通知调用者 Inform the reader that he has data

不过有一点问题,你会发现 读数据时用到的 表示能读多少数据的输入参数 fMaxSize 是不得而知的,这个就只能去查看调用者怎么设置的了,就是要明确它的调用逻辑,分析过程后续补充。 要控制这个参数,实现上面的H264FramedLiveSource::maxFrameSize(),返回一个帧的最大长度。这个虚函数在ByteStreamFileSource是没有覆盖实现的,ByteStreamFileSource 中是一个文件流,上面想读多少数据,就能直接中这个文件中读多少数据,下次还可以从上一次的读取位置接着读取,回想官方的FAQ:if your operating system represents the encoder device as a file, 而我这个实现的是,我有一帧数据在缓冲区,要求调用者一次性读走,不然下次可就指不定去哪里了,整个要实现的功能就是这样

(其实这样对接,修改是比较少的,但是效率可是有折扣的,数据在传到发送模块前先在framepaser中走了一圈,而这个framepaser中很大的一部分工作在于从这一串流数据中解析出每一帧的起始和结束,当然也有sps,pps等信息,而我们已知的本地缓冲区已经是知道帧开头结束的,这一部分的工作重复,还多了framepaser内部使用的内存空间 ,要解决这些问题,那就需要自行实现H264VideoStreamFramer这一层了)

图解:https://blog.csdn.net/u012459903/article/details/86597475 图四

猜你喜欢

转载自blog.csdn.net/u012459903/article/details/86676702