#define READ_FROM_FILES_SYNCHRONOUSLY 1
READ_FROM_FILES_SYNCHRONOUSLY 1
void ByteFrameLiveVideoSource:: doGetNextFrame()
{
if(fLimitNumBytesToStream && fNumBytesToStream == 0) {
handleClosure(this);
return;
}
// printf("ByteFrameLiveVideoSource doGetNextFrame 1: \r\n");
#ifdef READ_FROM_FILES_SYNCHRONOUSLY
//从编码处获取frame!!!!
doGetNextFrameFormEncoder();
#else
if (!fHaveStartedReading) {
// Await readable data from the file:
envir().taskScheduler().turnOnBackgroundReadHandling(GETFRAME_HANDLER_ID,
(TaskScheduler::BackgroundHandlerProc*)&getFrameableHandler, this);
fHaveStartedReading = True;
}
#endif
}
if(fLimitNumBytesToStream && fNumBytesToStream == 0) {
handleClosure(this);
return;
}
// printf("ByteFrameLiveVideoSource doGetNextFrame 1: \r\n");
#ifdef READ_FROM_FILES_SYNCHRONOUSLY
//从编码处获取frame!!!!
doGetNextFrameFormEncoder();
#else
if (!fHaveStartedReading) {
// Await readable data from the file:
envir().taskScheduler().turnOnBackgroundReadHandling(GETFRAME_HANDLER_ID,
(TaskScheduler::BackgroundHandlerProc*)&getFrameableHandler, this);
fHaveStartedReading = True;
}
#endif
}
void ByteFrameLiveVideoSource:: doGetNextFrameFormEncoder(){
// Try to read as many bytes as will fit in the buffer provided (or "fPreferredFrameSize" if less)
if (fLimitNumBytesToStream && fNumBytesToStream < (u_int64_t)fMaxSize) {
fMaxSize = (unsigned)fNumBytesToStream;
}
if (fPreferredFrameSize > 0 && fPreferredFrameSize < fMaxSize) {
fMaxSize = fPreferredFrameSize;
}
else srcId =0;
fFrameSize =0;
if(getFrame != NULL){
//回调函数,外部送流给live555
fFrameSize = getFrame(chId,srcId,fTo,fMaxSize);
if(fFrameSize > fMaxSize){
///这里非常重要,如果不设置fNumTruncatedBytes这个变量,fTo无法回收
///说明没有足够的空间存放一帧,丢弃,应该请求一个IDR帧,getFrame请求IDR
fNumTruncatedBytes = fFrameSize - fMaxSize;
fFrameSize = fMaxSize;
}
else fNumTruncatedBytes = 0;
}
if (fFrameSize == 0) {
handleClosure(this);
return;
}
//fNumBytesToStream -= fFrameSize;
// Set the 'presentation time':
if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
// This is the first frame, so use the current time:
gettimeofday(&fPresentationTime, NULL);
} else {
// Increment by the play time of the previous data:
unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
fPresentationTime.tv_sec += uSeconds/1000000;
fPresentationTime.tv_usec = uSeconds%1000000;
}
// Remember the play time of this data:
fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
fDurationInMicroseconds = fLastPlayTime;
} else {
// We don't know a specific play time duration for this data,
// so just record the current time as being the 'presentation time':
gettimeofday(&fPresentationTime, NULL);
}
// Inform the reader that he has data:
#ifdef READ_FROM_FILES_SYNCHRONOUSLY
// 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);
#else
// Because the file read was done from the event loop, we can call the
// 'after getting' function directly, without risk of infinite recursion:
FramedSource::afterGetting(this);
#endif
}
重写 H264LiveVideoServerMediaSubsession 由 H264FileServerMediaSubsession修改,H264FileServerMediaSubsession createsource 来自ByteFile。。。source
H264LiveVideoServerMediaSubsession来自ByteFrameLiveVideoSource
关键代码,两个回调函数,tempCB和stopCb,tempCb填充ByteFrameLiveVideoSource::getFrame,stopCb用于RTSP接受到teardown命令执行的一个函数!
FramedSource* H264LiveVideoServerMediaSubsession ::
createNewStreamSource(unsigned clientSessionId,unsigned & estBitrate){
estBitrate = 500; // kbps, estimate
// Create the video source:
// if(tempCb != NULL) cout<<"create new stream source------------------>"< %#x\n",liveVideoSource);
if(liveVideoSource !=NULL){
cout<< "create liveVideoSource OK \n";
return H264VideoStreamFramer::createNew(envir(), liveVideoSource);
}
else return NULL;
}
最后接口阶段
class liveVideoRTSPServer: public RTSPServerSupportingHTTPStreaming {
public:
static liveVideoRTSPServer* createNew(Port ourPort,UserAuthenticationDatabase* authDatabase,
GetFrameCB cb,StopPlayCB stopCb,unsigned reclamationTestSeconds = 65);
protected:
liveVideoRTSPServer(UsageEnvironment& env, int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds);
// called only by createNew();
virtual ~liveVideoRTSPServer();
protected: // redefined virtual functions
virtual ServerMediaSession* lookupServerMediaSession(char const* streamName);
private:
GetFrameCB readFreamCb;
StopPlayCB stopPlayCb;
public:
static UsageEnvironment* s_env;
static UsageEnvironment* getEnv();
};
lookupServerMediaSession这个可以定义自己的访问规则,我这里定义了RTSP://IP:554/ch0/main 即可访问主码流。ch0对应通道0,main主码流,sub子码流
C接口:
void* liveVideoServerStart(myGetFrameCB cb,myStopPlayCB stopCb){
RTSPServer* rtspServer;
portNumBits rtspServerPortNum = 554;
// Begin by setting up our usage environment:
rtspServer = liveVideoRTSPServer::createNew(rtspServerPortNum,NULL,(GetFrameCB)cb,(StopPlayCB)stopCb);
if (rtspServer == NULL) {
rtspServerPortNum = 8554;
rtspServer = liveVideoRTSPServer::createNew(rtspServerPortNum, NULL,(GetFrameCB)cb,(StopPlayCB)stopCb);
}
if (rtspServer == NULL) {
*liveVideoRTSPServer::getEnv() << "Failed to create RTSP server: " <getResultMsg() << "\n";
exit(1);
}
char* urlPrefix = rtspServer->rtspURLPrefix();
fprintf(stdout, "use like this:%s", urlPrefix);
fprintf(stdout, "channel/srcch \n");
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
*liveVideoRTSPServer::getEnv() << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";
}
else {
*liveVideoRTSPServer::getEnv() << "(RTSP-over-HTTP tunneling is not available.)\n";
}
liveVideoRTSPServer::getEnv()->taskScheduler().doEventLoop(); // does not return
return NULL;
}
定义自己的访问规则!
static ServerMediaSession* createNewSMS(UsageEnvironment& env,
char const* streamName, GetFrameCB cb,StopPlayCB stopCb) {
// Use the file name extension to determine the type of "ServerMediaSession":
int chId,SrcId;
int i;
ServerMediaSession* sms = NULL;
Boolean const reuseSource = False;
char const* extension = strrchr(streamName, '/');
char const* pstr = streamName;
char chStr[10]={0} ;
//pstr = streamName;
if (extension == NULL) return NULL;
for(i=0;i YUV4:2:0 // allow for some possibly large H.264 frames
sms->addSubsession(H264LiveVideoServerMediaSubsession::createNew(env,cb, stopCb,chId,SrcId, reuseSource));
return sms;
}
VLC 观看:
总结:
经过修改可以完成编码器-live555 RTSP推送码流,VLC观看!
存在实时性相对较差,开多个卡,偶尔有画面花屏。待修改!