前言:
现在市面上大多数标准的IPC(网络摄像机)对外都能输出标准的RTSP协议,要想获取IPC的标准音视频数据,必须使用RTSP协议,比较通用的方法就是live555,可以访问 http://www.live555.com/ 了解更过详情。本篇文章重点介绍如何在vs2010下面进行编译,封装,使用live555。
源码下载:
CSDN:https://download.csdn.net/download/haoyitech/10364641
源码说明:
开发工具:下载后,请用 VS2010 打开。
总体思路:先将live555编译成静态库,创建一个outRTSPClient封装类,再使用一个线程类CRtspThread封装,最后,准备rtsp:// 链接地址,拉取RTSP协议传递过来的音视频数据帧内容。默认使用RTSP的TCP模式拉取数据。
源码目录:
sample_live555\bin => 编译输出目录
sample_live555\common => 用到的公共库
sample_live555\librtsp => live555的源码
编译开关:为了避免预编译冲突,需要对live555的所有源码进行预编译开关的关闭,具体操作如下:
选中源码(可多选) => 右键 => 属性 => C/C++ => 预编译头 => 选择(不使用预编译头)
这样就可以避免stdafx.h的编译。
关键代码:
ourRTSPClient对live555的核心过程进行了封装调用,RTSP的几个基本过程简介:
OPTIONS:这个过程可以返回RTSP服务器支持的相关信息。
DESCRIBE:这个过程可以返回RTSP服务器反馈的SDP
SETUP:这个过程可以返回音视频的格式信息,音频和视频格式会各自单独执行一次
PLAY:这个过程表示RTSP协议链接是否完全成功,如果成功,可以开始接收音视频数据内容了。
音视频数据帧是通过 DummySink::afterGettingFrame 来反馈获取,每当一个数据帧到达时,就会自动调用这个函数,有关音视频的数据帧、时间戳,都在这里进行处理,可以将获取到的音视频数据帧向上层反馈。这个例子工程只对音视频数据帧进行了信息打印。
CRtspThread 是一个线程类,展示了如何将 ourRTSPClient 进行封装,实现获取RTSP数据过程中的不阻塞。
BOOL CRtspThread::InitThread(BOOL bUsingTCP, LPCTSTR lpszRtspUrl)
{
// 判断并保存rtsp拉流地址...
if( lpszRtspUrl == NULL || strlen(lpszRtspUrl) <= 0 )
return false;
m_strRtspUrl = lpszRtspUrl;
// 创建rtsp链接环境...
m_scheduler_ = BasicTaskScheduler::createNew();
m_env_ = BasicUsageEnvironment::createNew(*m_scheduler_);
m_rtspClient_ = ourRTSPClient::createNew(*m_env_, m_strRtspUrl.c_str(), 1, "rtspThread", bUsingTCP);
// 有些服务器必须先发OPTIONS...
// 发起第一次rtsp握手 => 先发起 OPTIONS 命令...
m_rtspClient_->sendOptionsCommand(continueAfterOPTIONS);
// 启动线程...
this->Start();
return true;
}
void CRtspThread::Entry()
{
// 进行任务循环检测,修改 m_rtspEventLoopWatchVariable 可以让任务退出...
ASSERT( m_env_ != NULL && m_rtspClient_ != NULL );
m_env_->taskScheduler().doEventLoop(&m_rtspEventLoopWatchVariable);
// 设置数据结束标志...
m_bFinished = true;
// 任务退出之后,再释放rtsp相关资源...
// 只能在这里调用 shutdownStream() 其它地方不要调用...
if( m_rtspClient_ != NULL ) {
m_rtspClient_->shutdownStream();
m_rtspClient_ = NULL;
}
// 释放任务对象...
if( m_scheduler_ != NULL ) {
delete m_scheduler_;
m_scheduler_ = NULL;
}
// 释放环境变量...
if( m_env_ != NULL ) {
m_env_->reclaim();
m_env_ = NULL;
}
}
为了启动CRTSPThread和ourRTSPClient,还需要在启动前做一些准备:
// 初始化网络、线程、套接字...
WORD wsVersion = MAKEWORD(2, 2);
WSADATA wsData = {0};
(void)::WSAStartup(wsVersion, &wsData);
OSThread::Initialize();
SocketUtils::Initialize();
例子程序的启动按钮:
void Csample_live555Dlg::OnBnClickedButtonRtsp()
{
// 释放rtsp线程资源...
if( m_lpRtspThread != NULL ) {
delete m_lpRtspThread;
m_lpRtspThread = NULL;
}
// 准备rtsp地址,创建新的线程资源...
CString strUrl = "rtsp://admin:[email protected]/Streaming/Channels/101";
m_lpRtspThread = new CRtspThread();
m_lpRtspThread->InitThread(true, strUrl);
}
使用完毕,在程序退出前,需要做释放操作:
void Csample_live555Dlg::OnDestroy()
{
CDialogEx::OnDestroy();
// 释放rtsp线程资源...
if( m_lpRtspThread != NULL ) {
delete m_lpRtspThread;
m_lpRtspThread = NULL;
}
// 释放一些公共资源...
OSThread::UnInitialize();
SocketUtils::UnInitialize();
}
更多信息:
************************************************************************************************************************