用live555將內網攝像機視訊推送到外網伺服器,附原始碼
再換一個應用場景,當我們的所有IP攝像機都部署在一個沒有有線網路的環境裡面,所有的流媒體資料都要通過3G/4G網路釋出出去。那麼就必須有這麼一個服務單元,能夠通過先拉後推的方式,將內網的流媒體資料,推送併發布到外網的流媒體伺服器上去:
在實現先拉後推式轉發之前,我們先熟悉下live555的運轉模式,live555主要運轉的是一個source與sink的迴圈,sink想要資料,就呼叫source的getNextFrame,source獲取到資料後,再呼叫afterGettingFrame回撥,返回給sink資料,sink處理完後,再呼叫source的getNextFrame,如此迴圈。那麼我們這裡要實現從攝像機獲取資料,那麼我們的source就是一個RTPSource,我們又需要將資料以RTP的方式傳送給流媒體伺服器,那麼我們的sink就是一個RTPSink,我們需要打通的就是一個RTPSource到一個RTPSink的過程。
ok,live555已經幫我們實現了大部分的功能,我們只需要將已有的部分組合起來就行了,這裡我們主要用到的就是live555的ProxyServerMediaSession類和DarwinInjector類,我們用ProxyServerMediaSession從攝像機獲取流媒體,再用DarwinInjector推送到Darwin Streaming Server,主要實現流程在下面程式碼註釋中:
/*
功能描述: 一個簡單的RTSP/RTP對接功能,從RTSP源通過基本的RTSPClient流程,獲取到RTP流媒體資料
再通過標準RTSP推送過程(ANNOUNCE/SETUP/PLAY),將獲取到RTP資料推送給Darwin流媒體
分發伺服器。
此Demo只演示了單個源的轉換、推送功能!
Author: sunpany@qq.com
時間: 2014/06/25
*/
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "RTSPCommon.hh"
char* server = "www.easydss.com";//RTSP流媒體轉發伺服器地址,<請修改為自己搭建的流媒體伺服器地址>
int port = 8554; //RTSP流媒體轉發伺服器埠,<請修改為自己搭建的流媒體伺服器埠>
char* streamName = "live.sdp"; //流名稱,推送到Darwin的流名稱必須以.sdp結尾
char* src = "rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp";//源端URL
UsageEnvironment* env = NULL; //live555 global environment
TaskScheduler* scheduler = NULL;
char eventLoopWatchVariable = 0;
DarwinInjector* injector = NULL; //DarwinInjector
FramedSource* vSource = NULL; //Video Source
FramedSource* aSource = NULL; //Audio Source
RTPSink* vSink = NULL; //Video Sink
RTPSink* aSink = NULL; //Audio Sink
Groupsock* rtpGroupsockVideo = NULL;//Video Socket
Groupsock* rtpGroupsockAudio = NULL;//Audio Socket
ProxyServerMediaSession* sms = NULL;//proxy session
// 流轉發過程
bool RedirectStream(char const* ip, unsigned port);
// 流轉發結束後處理回撥
void afterPlaying(void* clientData);
// 實現等待功能
void sleep(void* clientSession)
{
char* var = (char*)clientSession;
*var = ~0;
}
// Main
int main(int argc, char** argv)
{
// 初始化基本的live555環境
scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// 新建轉發SESSION
sms = ProxyServerMediaSession::createNew(*env, NULL, src);
// 迴圈等待轉接程式與源端連線成功
while(sms->numSubsessions() <= 0 )
{
char fWatchVariable = 0;
env->taskScheduler().scheduleDelayedTask(2*1000000,(TaskFunc*)sleep,&fWatchVariable);
env->taskScheduler().doEventLoop(&fWatchVariable);
}
// 開始轉發流程
RedirectStream(server, port);
env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
return 0;
}
// 推送視訊到流媒體伺服器
bool RedirectStream(char const* ip, unsigned port)
{
// 轉發SESSION必須保證存在
if( sms == NULL) return false;
// 判斷sms是否已經連線上源端
if( sms->numSubsessions() <= 0 )
{
*env << "sms numSubsessions() == 0\n";
return false;
}
// DarwinInjector主要用於向Darwin推送RTSP/RTP資料
injector = DarwinInjector::createNew(*env);
struct in_addr dummyDestAddress;
dummyDestAddress.s_addr = 0;
rtpGroupsockVideo = new Groupsock(*env, dummyDestAddress, 0, 0);
struct in_addr dummyDestAddressAudio;
dummyDestAddressAudio.s_addr = 0;
rtpGroupsockAudio = new Groupsock(*env, dummyDestAddressAudio, 0, 0);
ServerMediaSubsession* subsession = NULL;
ServerMediaSubsessionIterator iter(*sms);
while ((subsession = iter.next()) != NULL)
{
ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession;
unsigned streamBitrate;
FramedSource* source = proxySubsession->createNewStreamSource(1, streamBitrate);
if (strcmp(proxySubsession->mediumName(), "video") == 0)
{
// 用ProxyServerMediaSubsession建立Video的RTPSource
vSource = source;
unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat();
// 建立Video的RTPSink
vSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source);
// 將Video的RTPSink賦值給DarwinInjector,推送視訊RTP給Darwin
injector->addStream(vSink,NULL);
}
else
{
// 用ProxyServerMediaSubsession建立Audio的RTPSource
aSource = source;
unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat();
// 建立Audio的RTPSink
aSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source);
// 將Audio的RTPSink賦值給DarwinInjector,推送音訊RTP給Darwin
injector->addStream(aSink,NULL);
}
}
// RTSP ANNOUNCE/SETUP/PLAY推送過程
if (!injector->setDestination(ip, streamName, "live555", "LIVE555", port))
{
*env << "injector->setDestination() failed: " << env->getResultMsg() << "\n";
return false;
}
// 開始轉發視訊RTP資料
if((vSink != NULL) && (vSource != NULL))
vSink->startPlaying(*vSource,afterPlaying,vSink);
// 開始轉發音訊RTP資料
if((aSink != NULL) && (aSource != NULL))
aSink->startPlaying(*aSource,afterPlaying,aSink);
*env << "\nBeginning to get camera video...\n";
return true;
}
// 停止推送,釋放所有變數
void afterPlaying(void* clientData)
{
if( clientData == NULL ) return;
if(vSink != NULL)
vSink->stopPlaying();
if(aSink != NULL)
aSink->stopPlaying();
if(injector != NULL)
{
Medium::close(*env, injector->name());
injector == NULL;
}
ServerMediaSubsession* subsession = NULL;
ServerMediaSubsessionIterator iter(*sms);
while ((subsession = iter.next()) != NULL)
{
ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession;
if (strcmp(proxySubsession->mediumName(), "video") == 0)
proxySubsession->closeStreamSource(vSource);
else
proxySubsession->closeStreamSource(aSource);
}
if(vSink != NULL)
Medium::close(vSink);<pre name="code" class="html">
if(aSink != NULL)Medium::close(aSink);if(vSource != NULL)Medium::close(vSource);if(aSource != NULL)Medium::close(aSource);delete rtpGroupsockVideo;rtpGroupsockVideo = NULL;delete rtpGroupsockAudio;rtpGroupsockAudio = NULL;}
程式還有許多要完善的地方,只是一個簡單的實現。
原始碼下載:
http://pan.baidu.com/s/1sj6Ue4l
非常感謝感謝6樓 Boris_Cao_2015 5天前 的回覆,是這樣的!
“按著這個程式碼不同時支援音視訊,要修改LIVE555裡面DarwinInjector原始碼, stream channel id記得加1,因為RTCP instance不存在,所以RTP流的stream channel id必須自動加1, 否則跟RTCP的stream channel id重合,這就是原因。跟樓主和大家分享。嘻嘻!”
------------------------------------------------------------
相關文章
- WebRTC網頁開啟攝像頭並錄製視訊Web網頁
- 網路攝像機-ISP基礎一
- 智慧攝像機的網路安全如何保障?
- 兒童攝影網-網頁原始碼全網頁原始碼
- 搭建一個攝像頭應用程式 應用程式內部攝像頭
- 訊息推送介面設計(內含原始碼)原始碼
- OpenGL 攝像機視角詳解
- 網路攝像機—影象壓縮方式—JPEG、MJPEG
- 社交網路、手機視訊迅速崛起 將再造網際網路
- win10 2004怎樣新增網路攝像機_win10 2004新增網路攝像機步驟Win10
- PHP視訊解析網站原始碼+後臺PHP網站原始碼
- 詳解網路攝像機中的IR-CUT
- 海康威視攝像頭-chrome谷歌瀏覽器高版本區域網下海康攝像頭web網頁實時播放Chrome谷歌瀏覽器Web網頁
- 攝像頭、麥克風、揚聲器測試程式(附原始碼)原始碼
- 視訊直播app原始碼,將內容推薦給平臺內的好友APP原始碼
- Android 呼叫攝像頭功能【拍照與視訊】Android
- 3、Opencv播放視訊、儲存、暫停視訊,開啟攝像頭OpenCV
- NTP校時(時間同步伺服器)IPC網路攝像機時鐘問題排查伺服器
- 1000個網路攝像頭如何組網?
- shodan秒破網路攝像頭
- 婚紗攝影網站模版原始碼案例網站原始碼
- 兩步實現安卓手機秒變網路攝像頭安卓
- 網路攝像機畫面卡頓的主要原因有哪些?
- 二開蘋果CMS視訊網站雙端原始碼蘋果網站原始碼
- RTSP網路攝像頭網頁無外掛直播視訊平臺EasyNVR自定義標題和CopyRight步驟網頁VR
- MacBook PyCharm啟用碼(附視訊)MacPyCharm
- 海康威視攝像頭重置密碼問題密碼
- opencv python 從攝像頭獲取視訊/從檔案獲取視訊 /儲存視訊OpenCVPython
- iOS 儲存攝像頭H264視訊流iOS
- 樂訊通雲通訊:為什麼監控攝像頭要用物聯網路卡
- 3D遊戲引擎之遊戲攝像機的使用 公開課直播視訊3D遊戲引擎
- 攝像機控制軟體CaptureGRID更好的幫您管理攝像機APT
- C#推流RTMP,攝像頭、麥克風、桌面、音效卡(附原始碼)C#原始碼
- 將海康大華等網路攝像機RTSP流進行網頁Flash rtmp和H5 hls直播的技術方案網頁H5
- 黑客黑掉監控攝像機並公佈荷蘭女子手球隊裸體視訊黑客
- 樹莓派上安裝USB網路攝像頭樹莓派
- 基於C#的內網穿透學習筆記(附原始碼)C#內網穿透筆記原始碼
- 視訊直播原始碼+一對一交友原始碼+短視訊原始碼,抓住5G就是抓住機遇原始碼