通过live555实现H264 RTSP直播

前面的文章中介绍了《H264视频通过RTMP流直播》,下面将介绍一下如何将H264实时视频通过RTSP直播。

      实现思路是将视频流发送给live555, 由live555来实现H264数据流直播。

      视频采集模块通过FIFO队列将H264数据帧发送给live555. live555 在收到客户端的RTSP播放请求后,开始从FIFO中读取H264视频数据并通过RTSP直播出去。整个流程如下图所示:

调整和修改Live555 MediaServer

        下载live555源码,在media目录下增加四个文件并修改文件live555MediaServer.cpp。增加的四个文件如下:

WW_H264VideoServerMediaSubsession.h

WW_H264VideoServerMediaSubsession.cpp

 WW_H264VideoSource.h

WW_H264VideoSource.cpp

        下面附上四个文件的源码:

WW_H264VideoServerMediaSubsession.h

#pragma once#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"#include "OnDemandServerMediaSubsession.hh"
#include "WW_H264VideoSource.h"class WW_H264VideoServerMediaSubsession : public OnDemandServerMediaSubsession
{
public:WW_H264VideoServerMediaSubsession(UsageEnvironment & env, FramedSource * source);~WW_H264VideoServerMediaSubsession(void);public:virtual char const * getAuxSDPLine(RTPSink * rtpSink, FramedSource * inputSource);virtual FramedSource * createNewStreamSource(unsigned clientSessionId, unsigned & estBitrate); // "estBitrate" is the stream's estimated bitrate, in kbpsvirtual RTPSink * createNewRTPSink(Groupsock * rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource * inputSource);static WW_H264VideoServerMediaSubsession * createNew(UsageEnvironment & env, FramedSource * source);static void afterPlayingDummy(void * ptr);static void chkForAuxSDPLine(void * ptr);void chkForAuxSDPLine1();private:FramedSource * m_pSource;char * m_pSDPLine;RTPSink * m_pDummyRTPSink;char m_done;
};

WW_H264VideoServerMediaSubsession.cpp

#include "WW_H264VideoServerMediaSubsession.h"WW_H264VideoServerMediaSubsession::WW_H264VideoServerMediaSubsession(UsageEnvironment & env, FramedSource * source) : OnDemandServerMediaSubsession(env, True)
{m_pSource = source;m_pSDPLine = 0;
}WW_H264VideoServerMediaSubsession::~WW_H264VideoServerMediaSubsession(void)
{if (m_pSDPLine){free(m_pSDPLine);}
}WW_H264VideoServerMediaSubsession * WW_H264VideoServerMediaSubsession::createNew(UsageEnvironment & env, FramedSource * source)
{return new WW_H264VideoServerMediaSubsession(env, source);
}FramedSource * WW_H264VideoServerMediaSubsession::createNewStreamSource(unsigned clientSessionId, unsigned & estBitrate)
{return H264VideoStreamFramer::createNew(envir(), new WW_H264VideoSource(envir()));
}RTPSink * WW_H264VideoServerMediaSubsession::createNewRTPSink(Groupsock * rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource * inputSource)
{return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
}char const * WW_H264VideoServerMediaSubsession::getAuxSDPLine(RTPSink * rtpSink, FramedSource * inputSource)
{if (m_pSDPLine){return m_pSDPLine;}m_pDummyRTPSink = rtpSink;//mp_dummy_rtpsink->startPlaying(*source, afterPlayingDummy, this);m_pDummyRTPSink->startPlaying(*inputSource, 0, 0);chkForAuxSDPLine(this);m_done = 0;envir().taskScheduler().doEventLoop(&m_done);m_pSDPLine = strdup(m_pDummyRTPSink->auxSDPLine());m_pDummyRTPSink->stopPlaying();return m_pSDPLine;
}void WW_H264VideoServerMediaSubsession::afterPlayingDummy(void * ptr)
{WW_H264VideoServerMediaSubsession * This = (WW_H264VideoServerMediaSubsession *)ptr;This->m_done = 0xff;
}void WW_H264VideoServerMediaSubsession::chkForAuxSDPLine(void * ptr)
{WW_H264VideoServerMediaSubsession * This = (WW_H264VideoServerMediaSubsession *)ptr;This->chkForAuxSDPLine1();
}void WW_H264VideoServerMediaSubsession::chkForAuxSDPLine1()
{if (m_pDummyRTPSink->auxSDPLine()){m_done = 0xff;}else{double delay = 1000.0 / (FRAME_PER_SEC);  // msint to_delay = delay * 1000;  // usnextTask() = envir().taskScheduler().scheduleDelayedTask(to_delay, chkForAuxSDPLine, this);}
}

WW_H264VideoSource.h

#ifndef _WW_H264VideoSource_H
#define _WW_H264VideoSource_H#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#include "FramedSource.hh"#define FRAME_PER_SEC 25class WW_H264VideoSource : public FramedSource
{
public:WW_H264VideoSource(UsageEnvironment & env);~WW_H264VideoSource(void);public:virtual void doGetNextFrame();virtual unsigned int maxFrameSize() const;static void getNextFrame(void * ptr);void GetFrameData();private:void *m_pToken;char *m_pFrameBuffer;int  m_hFifo;
};#endif

WW_H264VideoSource.cpp

#include "WW_H264VideoSource.h"
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#endif#define FIFO_NAME     "/tmp/H264_fifo"
#define BUFFER_SIZE   PIPE_BUF
#define REV_BUF_SIZE  (1024*1024)#ifdef WIN32
#define mSleep(ms)    Sleep(ms)
#else
#define mSleep(ms)    usleep(ms*1000)
#endifWW_H264VideoSource::WW_H264VideoSource(UsageEnvironment & env) : 
FramedSource(env),
m_pToken(0),
m_pFrameBuffer(0),
m_hFifo(0)
{m_hFifo = open(FIFO_NAME,O_RDONLY);printf("[MEDIA SERVER] open fifo result = [%d]\n",m_hFifo);if(m_hFifo == -1){return;}m_pFrameBuffer = new char[REV_BUF_SIZE];if(m_pFrameBuffer == NULL){printf("[MEDIA SERVER] error malloc data buffer failed\n");return;}memset(m_pFrameBuffer,0,REV_BUF_SIZE);
}WW_H264VideoSource::~WW_H264VideoSource(void)
{if(m_hFifo){::close(m_hFifo);}envir().taskScheduler().unscheduleDelayedTask(m_pToken);if(m_pFrameBuffer){delete[] m_pFrameBuffer;m_pFrameBuffer = NULL;}printf("[MEDIA SERVER] rtsp connection closed\n");
}void WW_H264VideoSource::doGetNextFrame()
{// 根据 fps,计算等待时间double delay = 1000.0 / (FRAME_PER_SEC * 2);  // msint to_delay = delay * 1000;  // usm_pToken = envir().taskScheduler().scheduleDelayedTask(to_delay, getNextFrame, this);
}unsigned int WW_H264VideoSource::maxFrameSize() const
{return 1024*200;
}void WW_H264VideoSource::getNextFrame(void * ptr)
{((WW_H264VideoSource *)ptr)->GetFrameData();
}void WW_H264VideoSource::GetFrameData()
{gettimeofday(&fPresentationTime, 0);fFrameSize = 0;int len = 0;unsigned char buffer[BUFFER_SIZE] = {0};while((len = read(m_hFifo,buffer,BUFFER_SIZE))>0){memcpy(m_pFrameBuffer+fFrameSize,buffer,len);fFrameSize+=len;}//printf("[MEDIA SERVER] GetFrameData len = [%d],fMaxSize = [%d]\n",fFrameSize,fMaxSize);// fill frame datamemcpy(fTo,m_pFrameBuffer,fFrameSize);if (fFrameSize > fMaxSize){fNumTruncatedBytes = fFrameSize - fMaxSize;fFrameSize = fMaxSize;}else{fNumTruncatedBytes = 0;}afterGetting(this);
}

修改live555MediaServer.cpp文件如下

#include "stdafx.h"#include <BasicUsageEnvironment.hh>  
//#include "DynamicRTSPServer.hh"  
//#include "version.hh"  
#include "WW_H264VideoSource.h"  
#include "WW_H264VideoServerMediaSubsession.h"  int main(int argc, char** argv) {// Begin by setting up our usage environment:  TaskScheduler* scheduler = BasicTaskScheduler::createNew();UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL  // To implement client access control to the RTSP server, do the following:  authDB = new UserAuthenticationDatabase;authDB->addUserRecord("username1", "password1"); // replace these with real strings  // Repeat the above with each <username>, <password> that you wish to allow  // access to the server.  
#endif  // Create the RTSP server:  RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);if (rtspServer == NULL) {*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";exit(1);}// Add live stream  WW_H264VideoSource * videoSource = 0;ServerMediaSession * sms = ServerMediaSession::createNew(*env, "h264ESVideoTest", "h264ESVideoTest", "ww live test");sms->addSubsession(WW_H264VideoServerMediaSubsession::createNew(*env, NULL));rtspServer->addServerMediaSession(sms);char * url = rtspServer->rtspURL(sms);*env << "using url \"" << url << "\"\n";delete[] url;// Run loop  env->taskScheduler().doEventLoop();rtspServer->removeServerMediaSession(sms);Medium::close(rtspServer);env->reclaim();delete scheduler;return 1;
}

发送H264视频流的RTSPStream

/******************************************************************** 
filename:   RTSPStream.h
created:    2013-08-01
author:     firehood 
purpose:    通过live555实现H264 RTSP直播
*********************************************************************/ 
#pragma once
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif#ifdef WIN32
typedef HANDLE       ThreadHandle;
#define mSleep(ms)   Sleep(ms)
#else
typedef unsigned int SOCKET;
typedef pthread_t    ThreadHandle;
#define mSleep(ms)   usleep(ms*1000)
#endif#define FILEBUFSIZE (1024 * 1024) class CRTSPStream
{
public:CRTSPStream(void);~CRTSPStream(void);
public:// 初始化bool Init();// 卸载void Uninit();// 发送H264文件bool SendH264File(const char *pFileName);// 发送H264数据帧int SendH264Data(const unsigned char *data,unsigned int size);
};
/******************************************************************** 
filename:   RTSPStream.cpp
created:    2013-08-01
author:     firehood 
purpose:    通过live555实现H264 RTSP直播
*********************************************************************/ 
#include "RTSPStream.h"
#ifdef WIN32
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#endif#define FIFO_NAME    "/tmp/H264_fifo"
#define BUFFERSIZE   PIPE_BUFCRTSPStream::CRTSPStream(void)
{}CRTSPStream::~CRTSPStream(void)
{}bool CRTSPStream::Init()
{if(access(FIFO_NAME,F_OK) == -1){int res = mkfifo(FIFO_NAME,0777);if(res != 0){printf("[RTSPStream] Create fifo failed.\n");return false;}}return true;
}void CRTSPStream::Uninit()
{}bool CRTSPStream::SendH264File(const char *pFileName)
{if(pFileName == NULL){return false;}FILE *fp = fopen(pFileName, "rb");  if(!fp)  {  printf("[RTSPStream] error:open file %s failed!",pFileName);}  fseek(fp, 0, SEEK_SET);unsigned char *buffer  = new unsigned char[FILEBUFSIZE];int pos = 0;while(1){int readlen = fread(buffer+pos, sizeof(unsigned char), FILEBUFSIZE-pos, fp);if(readlen<=0){break;}readlen+=pos;int writelen = SendH264Data(buffer,readlen);if(writelen<=0){break;}memcpy(buffer,buffer+writelen,readlen-writelen);pos = readlen-writelen;mSleep(25);}fclose(fp);delete[] buffer;return true;
}// 发送H264数据帧
int CRTSPStream::SendH264Data(const unsigned char *data,unsigned int size)
{if(data == NULL){return 0;}// open pipe with non_block modeint pipe_fd = open(FIFO_NAME, O_WRONLY|O_NONBLOCK);//printf("[RTSPStream] open fifo result = [%d]\n",pipe_fd);if(pipe_fd == -1){return 0;}int send_size = 0;int remain_size = size;while(send_size < size){int data_len = (remain_size<BUFFERSIZE) ? remain_size : BUFFERSIZE;int len = write(pipe_fd,data+send_size,data_len);if(len == -1){static int resend_conut = 0;if(errno == EAGAIN && ++resend_conut<=3){printf("[RTSPStream] write fifo error,resend..\n");continue;}resend_conut = 0;printf("[RTSPStream] write fifo error,errorcode[%d],send_size[%d]\n",errno,send_size);break;}else{  send_size+= len;remain_size-= len;}}close(pipe_fd);//printf("[RTSPStream] SendH264Data datalen[%d], sendsize = [%d]\n",size,send_size);return 0;
}

测试程序代码

#include <stdio.h>
#include "RTSPStream.h"int main(int argc,char* argv[])
{CRTSPStream rtspSender;bool bRet = rtspSender.Init();rtspSender.SendH264File("E:\\测试视频\\test.264");system("pause");  
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432259.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络设置端口转发,网件NETGEAR几款路由器端口转发功能设置方法

WPN824, RP614v2&#xff0c;MR814v2&#xff0c;WGR614&#xff0c;WGT624 端口转发设置实例。(以 RP614v2 为例)1. WPN824, RP614v2&#xff0c;MR814v2&#xff0c;WGR614&#xff0c;WGT624 如何设置端口转发&#xff1f; 先登陆到设备的配置截面 在‘高级选项(Advanced)’…

移位操作符

移位操作符操作运算对象是 位(bit) 它处理的数据类型只能是 整数类型(int) 先大概看一下图,了解它是怎么移动的,下面会解释规则 移位的时候关注两点, 1.移动的方向 2.空缺位置的填补形式. "有符号"左移位操作符(<<) 将二进制数据左移(在低位补0). int i 124…

【OpenGL】详解第一个OpenGL程序

写在前面 OpenGL能做的事情太多了&#xff01;很多程序也看起来很复杂。很多人感觉OpenGL晦涩难懂&#xff0c;原因大多是被OpenGL里面各种语句搞得头大&#xff0c;一会gen一下&#xff0c;一会bind一下&#xff0c;一会又active一下。搞到最后都不知道自己在干嘛&#xff0c;…

基于 Editor.js 开发富文本编辑器库

开始 Editor.js 提供了简单而直观的用户界面&#xff0c;根据需求可以灵活添加自定义的编辑工具&#xff0c;通过插件扩展功能 Editorjs 使用 js 开发&#xff0c;脱离框架依赖&#xff0c;因此可以基于它封装富文本编辑器&#xff0c;用于 Vue 和 React 项目 editor-js-com…

dell服务器从硬盘引导,就是折腾 篇三:戴尔H710 mini(D1版本)阵列卡刷直通模式 附硬盘引导和还原IR模式办法...

就是折腾 篇三&#xff1a;戴尔H710 mini(D1版本)阵列卡刷直通模式 附硬盘引导和还原IR模式办法2021-07-24 10:00:201点赞13收藏12评论首先断开电池&#xff0c;确保阵列卡牢牢插入主板&#xff0c;没有松动。否则可能像我一样启动后识别不了raid卡。经实际测试&#xff0c;机器…

硬件服务器采购指南,硬件组装_服务器采购指南_太平洋电脑网PConline

这个机箱不支持普通大光驱&#xff0c;要用超薄光驱&#xff0c;超薄光驱是不可以直接用IDE数据线连接&#xff0c;必须用一个很小光驱转接卡&#xff0c;当然电源接口是和软驱电源接口通用的。光驱转接板这次我们采用的电源&#xff0c;也比较突出。电源是一个不能马虎的东西&…

360剑灵洪门崛起服务器维护,剑灵洪门崛起————【维护】8月1日更新维护公告...

亲爱的玩家&#xff1a;大家好&#xff01;为了更新游戏内容&#xff0c;提升游戏体验&#xff0c;7k7k《剑灵洪门崛起》将于8月1日7:00-8:00对所有服务器进行更新维护&#xff0c;维护期间无法登陆游戏&#xff0c;维护时间预计1小时。如果在维护期间无法完成维护相关事宜&…

易票365显示连接服务器失败,易票365服务器地址参数

易票365服务器地址参数 内容精选换一换查看指定VPC通道的弹性云服务器列表。您可以在API Explorer中调试该接口。GET /v2/{project_id}/apic/instances/{instance_id}/vpc-channels/{vpc_channel_id}/members状态码&#xff1a; 200状态码&#xff1a; 400状态码&#xff1a; 4…

语言统计学中的几个定律,可作为设计检索的参考

30定律&#xff1a;出现频率最高的30个词占全文本总词数的30&#xff05;如果剔除150个最高频率的词&#xff08;由于df过大被认为是停用词&#xff09;&#xff1a;倒排表记录总个数会减少25&#xff0d;30&#xff05;Zipf定律&#xff1a; 在自然语料库中所有term的freq&…

Linux makefile 教程 很具体,且易懂

近期在学习Linux下的C编程&#xff0c;买了一本叫《Linux环境下的C编程指南》读到makefile就越看越迷糊&#xff0c;可能是我的理解能不行。 于是google到了下面这篇文章。通俗易懂。然后把它贴出来&#xff0c;方便学习。 后记&#xff0c;看完发现这篇文章和《Linux环境下的C…

如何改善虚幻引擎中的游戏线程CPU性能表现

您游戏中的帧频率是不是太低&#xff1f; 您了解为什么会发生这种现象吗&#xff1f; 这是不是由于您同时生成了太多敌人&#xff1f;还是由于某个特定敌人过于消耗系统资源&#xff1f; 是由于您设置了过多的视觉特效&#xff0c;还是由于您所设计的战斗系统所造成的&#xff…

UE 光影参数

平行光的光影效果参数 天光的光影效果参数 让材质不反射光&#xff0c;也就是材质本身的颜色不起作用&#xff0c;只能使用自发光 去掉模型光影效果

《BI项目笔记》多维数据集中度量值设计时的聚合函数

Microsoft SQL Server Analysis Services 提供了几种函数&#xff0c;用来针对包含在度量值组中的维度聚合度量值。默认情况下&#xff0c;度量值按每个维度进行求和。但是&#xff0c;通过 AggregateFunction 属性&#xff0c;您可以修改此行为。聚合函数的累加性可确定度量值…

零基础Unreal Engine 4(UE4)图文笔记之粒子系统

1.我们需要创建两个东西&#xff0c;一个材质一个粒子。先打开材质&#xff0c;在制作粒子之前&#xff0c;我们首先需要自己创建一个粒子效果能用的材质 在材质编辑器中&#xff0c;修改细节中Blend Mode类型为Translucent&#xff0c;Shading Model 为Unit&#xff0c;这一步…

[UE4]性能优化指南(美术向)

参考自官方文档&#xff1a; Performance Guidelines for Artists and Designershttps://docs.unrealengine.com/en-us/Engine/Performance/Guidelines 但是官方文档写的太粗燥&#xff0c;对UE4没有一定了解&#xff0c;很难理解文档的意图。这里我在官方文档的基础上&#x…

UE4 Fix – “Lighting build failed. Swarm failed to kick off.”

Hello! Have you encountered the “Swarm Failed to Kick Off” error on an Unreal Engine project when trying to build a level? I did, after we switched to a custom engine build. Since most of the resources on the web were not helpful. Here’s a really simpl…

贪吃蛇 WPF

贪吃蛇 WPF using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Wind…

阿旺wifi智能系统源码

系统简介本系统适合DD-WRT固件路由器和OoenWrt固件路由器或者任何带有WIFIDOG插件的路由器。系统基于ThinkPHP框架PHPMySQL的技术开发。系统主要功能: 1.无密码认证&#xff1a;只点击按钮或强制看广告1.验证码认证&#xff1a;招待券认证、一次性账号、指定时间限制3.用户名密…

warning C4828问题的处理

warning C4828: 文件包含在偏移 0x215 处开始的字符&#xff0c;该字符在当前源字符集中无效(代码页 65001)。 (编译源文件 XXXXXXcpp) 这提示是由于字符集的问题导致&#xff0c;解决方案如下 点击VS2017 文件->另存为->编码保存->65001 然后重新编译,警告问题解决…