通过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)’…

计算机分数的简便运算,分数的简便运算和分数的解方程

问题描述&#xff1a;分数的简便运算和分数的解方程简便运算四又十五分之八一又二十分之十三三又四分之一十-二又十二分之七-四又六分之五五又五分之二-(1.8二又四分之九)二又三分之一-1.5三又十二分之五六又十三分之五-2.375-一又八分之五3.825二又九分之四一又九分之五四又三…

OpenGL 4.0 Tutorials 第三章:初始化 OpenGL 4.0

原文地址&#xff1a; http://www.rastertek.com/gl40tut03.html Tutorial 3: Initializing OpenGL 4.0 第三章&#xff1a;初始化 OpenGL 4.0 This tutorial will be the first real introduction to working with OpenGL 4.0. We will address three main things which are …

客户机和服务器在s7通信中各有什么作用,哪些通信口可以进行 Modbus TCP 通讯,作为 Modbus 服务器的 SIMATIC S7 CPU 可以...

以下通信端口可用于 Modbus/TCP 协议&#xff1a;在默认情况下&#xff0c;端口号 502 作为 Modbus 服务器的本地端口。可以在 Modbus 客户机中设置需要的本地端口&#xff0c;通常使用从 2000 开始的端口号。如果通信伙伴具有为服务器设置端口号的功能&#xff0c;那么也可以使…

移位操作符

移位操作符操作运算对象是 位(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;机器…

(转)你的团队需要一个领袖,而不是一个主管

作为这个社会的一员&#xff0c;你有你自己想要追随的领袖&#xff0c;他们是一种超人的存在&#xff0c;在各自的领域出类拔萃&#xff0c;拥有强大的人格力量。你不由自主的追随着精神领袖 Steve Jobs、王阳明、默罕默德&#xff1b;音乐领袖崔健、Bruce Springsteen、Michae…

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

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

ExecuteScalar

ExecuteScalar运行查询&#xff0c;并返回查询所返回的结果集中第一行的第一列或空引用&#xff08;假设结果集为空).忽略其它列或行. 使用 ExecuteScalar 方法从数据库中检索单个值。 由于不用创建行集、查找值并关闭行集&#xff0c;所以产生的系统开销很小与使用 ExecuteRea…

计算机启动软件,计算机软件及应用启动会-20210703001237.pptx-原创力文档

晨操劲舞;业绩播报;昨天--今天--明天;昨天;遇到的困惑;有时我们感觉辛苦、累&#xff01;;入行的初衷;1、为了收入2、为了改变环境与现状3、为了找个事情做4、为了锻炼自己5、为了生存&#xff01; 试一试 ……;忆往昔&#xff0c;峥嵘岁月稠……;今 天;;先往 三只锅里倒入一些…

文件夹没有安全选项-文件上传下载-路径访问被拒绝

在文件的下载和上传中&#xff0c;有时候会出现“路径。。。访问被拒绝”&#xff0c;这是由于权限问题引起&#xff0c;只要给文件所在的文件夹设置权限为everyone就可以解决了&#xff0c;但是有时候文件夹属性没有“安全”选项卡&#xff0c;解决方法如下&#xff1a; 第一种…

css3 背景等比例,纯 CSS,不用背景,实现图片等比例展示

最简单的等比例&#xff1a;div img { max-width:100%; max-height:100%; }如上显示效果是&#xff1a;图片等比例缩放&#xff0c;不变形&#xff1b;图片所有区域都会显示。但是&#xff0c;如上代码有可能会造成横向、纵向白边&#xff0c;当我们在做图片列表时&#xff0c;…

emctl start dbconsole OC4J_dbconsole*** not found

C:\windows\system32>emctl start dbconsole OC4J Configuration issue. D:\app\product\11.1.0\db_1/oc4j/j2ee/OC4J_DBConsole_ghost1_orcl not found. 查看监听状态&#xff0c;环境变量 C:\windows\system32>echo %ORACLE_HOME% D:\app\product\11.1.0\db_1C:\window…

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

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

高德地图 android 调用 amap.clear()后定位蓝点消失 如何重新显示定位

您好&#xff01;AMap.clear()方法&#xff0c;将地图上全部的覆盖物都清除&#xff0c;包括定位的小蓝点。如果不想清除定位的图标&#xff0c;有两种方法&#xff1a;方法一&#xff1a;将自定以的marker存好&#xff0c;清除时&#xff0c;调用marker.remove()方法&#xff…

易票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&…

so打包进APK

问题描述我本身有个现成的SO文件&#xff0c;想通过编译方式打包进APK里&#xff0c;不知道该怎么做&#xff1f; 解决方案1libs/armeabi/是在项目根目录中&#xff0c;应该可以 解决方案2大哥&#xff0c;so放到lib目录中&#xff0c;打包时&#xff0c;会自动编入APK&#xf…