jekeyhui99 发表于 2016-8-19 17:25:17

调用live555接收RTSP直播流,转换RTMP直播协议

调用live555接收RTSP直播流,转换RTMP直播协议RTSP协议也是广泛使用的直播/点播流媒体协议,最近实现了一个RTSP协议转换RTMP直播协议的程序,为的是可以接收远端设备或服务器的多路RTSP直播数据,实时转换为RTMP直播协议,推送到FMS、Red5、wowza server等RTMP服务器,以实现flash观看RTSP直播源的需求。程序同时也具备从FLV文件获取输入数据并转换RTMP直播。实现的思路分享如下。要点分析  首先,程序的主要目的,是从多路RTSP输入源中提取AAC编码的音频和H.264编码视频数据,并生成RTMP数据包,然后组装RTMP推送协议,并发往RTMP服务器。在发送的过程中,要求可以从RTSP数据源切换到具有相同h.264和aac编码的FLV文件中,并不影响RTMP直播。因此,本程序的关键点有以下部分:
[*]RTSP直播流的读取
[*]H.264和AAC编码数据的分析、处理
[*]FLV文件数据的提取及与RTSP直接的切换和衔接
[*]RTMP数据包封装
[*]RTMP推送协议
  有了关键点,就可以一项一项的去分析。
设计思路  根据上面分析的要点,首先要选择RTSP直播协议的读取。我们不需要从零做起,网络上有很多和RTSP相关的开源项目可以使用或借鉴,我选择了Live555。  Live555是一个跨平台的流媒体解决方案,主要支持RTSP协议,好像也支持SIP(这个也是我马上研究的重点,之后会写文章研究SIP相关的技术实现)。Live555实现了RTSP包括服务器-客户端的整套结构,是很知名的一个开源项目。网上有很多关于Live555学习和使用的文章,我就不具体介绍了。  H.264和AAC数据的分析处理,这个对于从没做过相关项目开发的人来说,应该是一个难点,主要是相关概念的理解。好在我一直在做这块,也比较好弄。  第4和第5点,可以参照我之前的文章“RTMP协议发送H.264编码及AAC编码的音视频,实现摄像头直播”的技术方法,来加以实现。因此,主要需要处理的就是RTSP直播流数据的获取,以及对其中H.264和AAC编码数据的处理。  于是可以画出大体结构如下:  http://www.java123.net/uploads/201311/21/607454211013251.jpg
逻辑与实现1. 程序框架和模块说明  http://www.java123.net/uploads/201311/21/607454211013252.jpg
2. 主要接口  http://www.java123.net/uploads/201311/21/607454211013253.jpg  RtspCapture是我的程序里管理RTSP直播数据流和分析处理的类,接口很重要,基本上这个设计就可以。(当然,全部代码是不会放上来的,有兴趣的可以和我谈,这里只罗列关键的地方。)  http://www.java123.net/uploads/201311/21/607454211013254.jpg  他所使用的live555变量,这里ourRTSPClient实际就是RTSPClient的简单继承  http://www.java123.net/uploads/201311/21/607454211013255.jpg3.RtspCapture调用live555的主要流程  1. 首先,在RtspCapture构造函数初始化  http://www.java123.net/uploads/201311/21/607454211013256.jpg  2. 在StartRtsp函数中,创建RtspClient,并发送"describe"命令,开始获取sdp。回调函数就是continueAfterDESCRIBE。在这里创建MediaSession,之后再发送"setup"命令,等,这些都可以在live555的例子以及网上的说明中看到。  http://www.java123.net/uploads/201311/21/607454211013257.jpg  3. 还有一个关键点,就是要在自己的线程循环中,调用live555 environment的事件循环,就像这样  http://www.java123.net/uploads/201311/21/607454211013258.jpg4. 对rtsp回调h264数据的分析处理  这里演示了,如何从rtsp回调的h264数据中,提取sps和pps信息。里面的parse函数,是live555自带的。  http://www.java123.net/uploads/201311/21/607454211013259.jpg调用方式

jekeyhui99 发表于 2016-8-19 17:27:07

通过live555实现H264 RTSP直播
http://blog.csdn.net/firehood_/article/details/16844397
用live555做PC音视频直播,附源码
在分别做了基于live555与Darwin两种开源服务器的转发服务器后,不得不说Darwin确实在架构以及性能方面较live555略胜一筹,不过没关系,以live555的更新速度,作者的负责,相信在客户端开发以及ipC等方面会给大家带来不少帮助,不罗嗦,今天要给大家带来的是基于live555的本地视频实时采集与转发的介绍(有代码噢~).。在对live555做二次开发时,最好的方式就是尽量多地去继承自live555,而不要去改动开源代码本身,尤其是一开始接触live555,这样在live555官方升级后,我们再对本地代码进行升级时,就可以比较少地去考虑自己对live555的修改了,省时省力,效果还不错。不论是做远端采集转发还是本地采集转发,我们首先要做的就是继承live555中的OnDemandServerMediaSubsession类来实现自己需求的OnDemand类,按照类名Subsession,表示的只是一种类型媒体的会话,如果有多种类型媒体需要转发(比如音频、视频),那么就需要实现多种OnDemandServerMediaSubsession的继承,来个性化对不同媒体的转发,那么今天我们只对H264视频进行本地采集和转发,我们实现的类命名为:H264LiveVideoServerMediaSubsession,主要重写的方法有private: // redefined virtual functions
virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
unsigned char rtpPayloadTypeIfDynamic,
FramedSource* inputSource);
protected:
virtual char const* sdpLines();


其中 CreateNewRTPSink类似于H264VideoFileServerMediaSubsession直接返回H264VideoRTPSink对象就行了

RTPSink* H264LiveVideoServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock,
unsigned char rtpPayloadTypeIfDynamic,
FramedSource* /*inputSource*/) {
return H264VideoRTPSink::createNew(envir(), rtpGroupsock, 96, 0, "H264");
}


关键部分就是createNewStreamSource函数,创建自定义的Source来采集视频,提供H264VideoRTPSink基类MultiFramedRTPSink通过packFrame()调用fSource->getNextFrame(...),一次获取一个完整帧进行转发。那么我们先实现的就是这个Source: 同样,我们实现的自定义source类继承自H264VideoStreamFramer,重写virtual void doGetNextFrame();方法实现本地采集数据的获取:

void MyH264VideoStreamFramer::doGetNextFrame()
{
TNAL* pNal = NULL;//TNAL自定义的存储单帧数据的结构体
unsigned char* pOrgImg;

//获取NAL,如果m_pNalArray还有未取完的,先发送完,如果发送完了,从pH264Enc中获取最新数据帧,存入m_pNalArray链表
if((m_pNalArray != NULL) && (m_iCurNal < m_iCurNalNum))
{
pNal = &m_pNalArray;//m_pNalArray存储TNAL的链表,存储本地采集的数据链表
}
else
{
m_pH264Enc->CleanNAL(m_pNalArray, m_iCurNalNum);//清空m_pNalArray链表
m_iCurNal = 0;

pOrgImg = m_pCamera->QueryFrame();
gettimeofday(&fPresentationTime, NULL);//同一帧的NAL具有相同的时间戳

m_pH264Enc->Encode(pOrgImg, m_pNalArray, m_iCurNalNum);
pNal = &m_pNalArray;
}
m_iCurNal++;

unsigned char* realData = pNal->data;//转发的数据指针
unsigned int realLen = pNal->size;//转发的数据长度

if(realLen < fMaxSize)
{
memcpy(fTo, realData, realLen);//复制到fTo中,fTo为转发的中转地址
}
else
{
memcpy(fTo, realData, fMaxSize);
fNumTruncatedBytes = realLen - fMaxSize;
}

fDurationInMicroseconds = 40000;//控制播放速度
//gettimeofday(&fPresentationTime, NULL);

fFrameSize = realLen;
afterGetting(this); //通知RTPSink,数据获取完成
}

那么再回到H264LiveVideoServerMediaSubsession类中,CreateNewSource返回的为MyH264VideoStreamFramer对象

return MyH264VideoStreamFramer::createNew(envir(), NULL);

于是整个live555从source到sink的连接流程就通了,那么为什么要重写sdpLines函数呢?这里只是一种简单形式的转发实现,其sdp信息并未真实构造,所以就写成了固定的格式,大家也可以按照自己的方式去重写

char const* H264LiveVideoServerMediaSubsession::sdpLines()
{
return fSDPLines =
"m=video 0 RTP/AVP 96\r\n"
"c=IN IP4 0.0.0.0\r\n"
"b=AS:96\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=fmtp:96 packetization-mode=1;profile-level-id=000000;sprop-parameter-sets=H264\r\n"
"a=control:track1\r\n";
}

至于本地Camera视频的采集以及H264 Encode,因人而异,对不同的设备也有不同的样式,附上的代码中实现的是windows本地的camera YUV视频采集以及264编码,感谢分享!源码下载地址:http://pan.baidu.com/s/1hqIHMj6http://www.easydarwin.org/article/Streaming/11.html

页: [1]
查看完整版本: 调用live555接收RTSP直播流,转换RTMP直播协议