参考文章
https://blog.csdn.net/zhizhengguan/article/details/109387400
https://blog.csdn.net/benkaoya/article/details/72486624
一、简介
ONVIF规范中设备管理和控制部分所定义的接口均以Web Services的形式提供,而音视频流则通过RTP/RTSP进行。简单粗暴的理解:IPC的各种参数获取/配置都是通过ONVIF协议接口实现,而音视频流多媒体传输采用的是RTP/RTSP协议实现。要读取IPC的音视频流,概况起来是,先通过ONVIF接口获取IPC主/辅码流的RTSP地址,再利用FFmpeg接口(或其他开源方案)读取音视频流数据。
ONVIF协议接口在media.wsdl,文件地址为https://www.onvif.org/ver10/media/wsdl/media.wsdl
Onvif协议对前端摄像头的取流是通过标准RTSP协议,因此我们就需要通过Onvif协议获取到需要预览摄像头的RTSP的地址,将拿到的RTSP通过VLC或者其他的RTSP客户端取流预览前端摄像头。实现的功能如下图所示:
形如rtsp://192.168.1.137:554/stream1
二、获取实时预览url地址的流程
- 通过「设备发现」,得到 「设备服务地址」。
- 使用「设备服务地址」调用ONVIF_GetMediaCapabilities接口,得到「媒体服务地址」。
- 使用「媒体服务地址」调用ONVIF_GetMediaProfilesToken接口,得到主次码流的「媒体配置信息」,其中包含ProfileToken。
- 根据获取到的token调用对应的接口获取到RTSP地址。
- 获取到RTSP地址之后我们就可以用opencv或者ffmeg之类的看实时视频了。
注:获取的token包含了主码流、辅码流的token已经各种配置的token,可以根据自己需要自取;根据主、辅码流的toekn获取到的是对应主、辅码流的RTSP地址。主、辅码流的RTSP地址不同,但是差异可能不大。
VLC播放RTSP视频
RTSP是很成熟的多媒体传输协议,用于传输音视频流数据。我们不需要自己写代码来实现RTSP协议,有很多现成的开源方案可供我们使用,比如强大的FFmpeg。为了让大家对RTSP有个更好的认识,下面使用VLC media player播放RTSP视频流。
打开VLC media player播放器(我的版本是VLC media player 2.1.3)。
选择菜单【媒体】>【打开网络串流】,输入RTSP地址,点击【播放】,即可实时播放视频流媒体,如下图所示。
如果提示认证失败,那么URL就要加上用户名、密码,格式如:rtsp://username:password@100.100.100.5:554/av0_0
三、数据结构和函数
生成gSOAP框架的代码,项目文件结构和解决编译报错可以参考佬的这篇文章:
https://blog.csdn.net/zhizhengguan/article/details/109387400
1.数据结构
# 数据类型定义在client\application\soapStub.h
tt__StreamSetup ttStreamSetup;
tt__Transport ttTransport;
_trt__GetStreamUri req;
_trt__GetStreamUriResponse rep;
/* ../onvif_head/onvif.h:996 */
#ifndef SOAP_TYPE_tt__StreamSetup
#define SOAP_TYPE_tt__StreamSetup (734)
/* complex XML schema type 'tt:StreamSetup': */
class SOAP_CMAC tt__StreamSetup : public soap_dom_element {
public:
/// Required element 'tt:Stream' of XML schema type 'tt:StreamType'
enum tt__StreamType Stream;
/// Required element 'tt:Transport' of XML schema type 'tt:Transport'
tt__Transport *Transport;
public:
/// Return unique type id SOAP_TYPE_tt__StreamSetup
virtual long soap_type(void) const { return SOAP_TYPE_tt__StreamSetup; }
/// (Re)set members to default values
virtual void soap_default(struct soap*);
/// Serialize object to prepare for SOAP 1.1/1.2 encoded output (or with SOAP_XML_GRAPH) by analyzing its (cyclic) structures
virtual void soap_serialize(struct soap*) const;
/// Output object in XML, compliant with SOAP 1.1 encoding style, return error code or SOAP_OK
virtual int soap_put(struct soap*, const char *tag, const char *type) const;
/// Output object in XML, with tag and optional id attribute and xsi:type, return error code or SOAP_OK
virtual int soap_out(struct soap*, const char *tag, int id, const char *type) const;
/// Get object from XML, compliant with SOAP 1.1 encoding style, return pointer to object or NULL on error
virtual void *soap_get(struct soap*, const char *tag, const char *type);
/// Get object from XML, with matching tag and type (NULL matches any tag and type), return pointer to object or NULL on error
virtual void *soap_in(struct soap*, const char *tag, const char *type);
/// Return a new object of type tt__StreamSetup, default initialized and not managed by a soap context
virtual tt__StreamSetup *soap_alloc(void) const { return SOAP_NEW_UNMANAGED(tt__StreamSetup); }
public:
/// Constructor with default initializations
tt__StreamSetup() : Stream(), Transport() { }
/// Destructor
virtual ~tt__StreamSetup() { }
/// Friend allocator used by soap_new_tt__StreamSetup(struct soap*, int)
friend SOAP_FMAC1 tt__StreamSetup * SOAP_FMAC2 soap_instantiate_tt__StreamSetup(struct soap*, int, const char*, const char*, size_t*);
};
#endif
/* ../onvif_head/onvif.h:998 */
#ifndef SOAP_TYPE_tt__Transport
#define SOAP_TYPE_tt__Transport (735)
/* Type tt__Transport is a recursive data type, (in)directly referencing itself through its (base or derived class) members */
/* complex XML schema type 'tt:Transport': */
class SOAP_CMAC tt__Transport : public soap_dom_element {
public:
/// Required element 'tt:Protocol' of XML schema type 'tt:TransportProtocol'
enum tt__TransportProtocol Protocol;
/// Optional element 'tt:Tunnel' of XML schema type 'tt:Transport'
tt__Transport *Tunnel;
public:
/// Return unique type id SOAP_TYPE_tt__Transport
virtual long soap_type(void) const { return SOAP_TYPE_tt__Transport; }
/// (Re)set members to default values
virtual void soap_default(struct soap*);
/// Serialize object to prepare for SOAP 1.1/1.2 encoded output (or with SOAP_XML_GRAPH) by analyzing its (cyclic) structures
virtual void soap_serialize(struct soap*) const;
/// Output object in XML, compliant with SOAP 1.1 encoding style, return error code or SOAP_OK
virtual int soap_put(struct soap*, const char *tag, const char *type) const;
/// Output object in XML, with tag and optional id attribute and xsi:type, return error code or SOAP_OK
virtual int soap_out(struct soap*, const char *tag, int id, const char *type) const;
/// Get object from XML, compliant with SOAP 1.1 encoding style, return pointer to object or NULL on error
virtual void *soap_get(struct soap*, const char *tag, const char *type);
/// Get object from XML, with matching tag and type (NULL matches any tag and type), return pointer to object or NULL on error
virtual void *soap_in(struct soap*, const char *tag, const char *type);
/// Return a new object of type tt__Transport, default initialized and not managed by a soap context
virtual tt__Transport *soap_alloc(void) const { return SOAP_NEW_UNMANAGED(tt__Transport); }
public:
/// Constructor with default initializations
tt__Transport() : Protocol(), Tunnel() { }
/// Destructor
virtual ~tt__Transport() { }
/// Friend allocator used by soap_new_tt__Transport(struct soap*, int)
friend SOAP_FMAC1 tt__Transport * SOAP_FMAC2 soap_instantiate_tt__Transport(struct soap*, int, const char*, const char*, size_t*);
};
#endif
/* ../onvif_head/onvif.h:2146 */
#ifndef SOAP_TYPE__trt__GetStreamUri
#define SOAP_TYPE__trt__GetStreamUri (1309)
/* complex XML schema type 'trt:GetStreamUri': */
class SOAP_CMAC _trt__GetStreamUri {
public:
/// Required element 'trt:StreamSetup' of XML schema type 'tt:StreamSetup'
tt__StreamSetup *StreamSetup;
/// Required element 'trt:ProfileToken' of XML schema type 'tt:ReferenceToken'
char *ProfileToken;
/// Context that manages this object
struct soap *soap;
public:
/// Return unique type id SOAP_TYPE__trt__GetStreamUri
virtual long soap_type(void) const { return SOAP_TYPE__trt__GetStreamUri; }
/// (Re)set members to default values
virtual void soap_default(struct soap*);
/// Serialize object to prepare for SOAP 1.1/1.2 encoded output (or with SOAP_XML_GRAPH) by analyzing its (cyclic) structures
virtual void soap_serialize(struct soap*) const;
/// Output object in XML, compliant with SOAP 1.1 encoding style, return error code or SOAP_OK
virtual int soap_put(struct soap*, const char *tag, const char *type) const;
/// Output object in XML, with tag and optional id attribute and xsi:type, return error code or SOAP_OK
virtual int soap_out(struct soap*, const char *tag, int id, const char *type) const;
/// Get object from XML, compliant with SOAP 1.1 encoding style, return pointer to object or NULL on error
virtual void *soap_get(struct soap*, const char *tag, const char *type);
/// Get object from XML, with matching tag and type (NULL matches any tag and type), return pointer to object or NULL on error
virtual void *soap_in(struct soap*, const char *tag, const char *type);
/// Return a new object of type _trt__GetStreamUri, default initialized and not managed by a soap context
virtual _trt__GetStreamUri *soap_alloc(void) const { return SOAP_NEW_UNMANAGED(_trt__GetStreamUri); }
public:
/// Constructor with default initializations
_trt__GetStreamUri() : StreamSetup(), ProfileToken(), soap() { }
/// Destructor
virtual ~_trt__GetStreamUri() { }
/// Friend allocator used by soap_new__trt__GetStreamUri(struct soap*, int)
friend SOAP_FMAC1 _trt__GetStreamUri * SOAP_FMAC2 soap_instantiate__trt__GetStreamUri(struct soap*, int, const char*, const char*, size_t*);
};
#endif
/* ../onvif_head/onvif.h:2148 */
#ifndef SOAP_TYPE__trt__GetStreamUriResponse
#define SOAP_TYPE__trt__GetStreamUriResponse (1310)
/* complex XML schema type 'trt:GetStreamUriResponse': */
class SOAP_CMAC _trt__GetStreamUriResponse {
public:
/// Required element 'trt:MediaUri' of XML schema type 'tt:MediaUri'
tt__MediaUri *MediaUri;
/// Context that manages this object
struct soap *soap;
public:
/// Return unique type id SOAP_TYPE__trt__GetStreamUriResponse
virtual long soap_type(void) const { return SOAP_TYPE__trt__GetStreamUriResponse; }
/// (Re)set members to default values
virtual void soap_default(struct soap*);
/// Serialize object to prepare for SOAP 1.1/1.2 encoded output (or with SOAP_XML_GRAPH) by analyzing its (cyclic) structures
virtual void soap_serialize(struct soap*) const;
/// Output object in XML, compliant with SOAP 1.1 encoding style, return error code or SOAP_OK
virtual int soap_put(struct soap*, const char *tag, const char *type) const;
/// Output object in XML, with tag and optional id attribute and xsi:type, return error code or SOAP_OK
virtual int soap_out(struct soap*, const char *tag, int id, const char *type) const;
/// Get object from XML, compliant with SOAP 1.1 encoding style, return pointer to object or NULL on error
virtual void *soap_get(struct soap*, const char *tag, const char *type);
/// Get object from XML, with matching tag and type (NULL matches any tag and type), return pointer to object or NULL on error
virtual void *soap_in(struct soap*, const char *tag, const char *type);
/// Return a new object of type _trt__GetStreamUriResponse, default initialized and not managed by a soap context
virtual _trt__GetStreamUriResponse *soap_alloc(void) const { return SOAP_NEW_UNMANAGED(_trt__GetStreamUriResponse); }
public:
/// Constructor with default initializations
_trt__GetStreamUriResponse() : MediaUri(), soap() { }
/// Destructor
virtual ~_trt__GetStreamUriResponse() { }
/// Friend allocator used by soap_new__trt__GetStreamUriResponse(struct soap*, int)
friend SOAP_FMAC1 _trt__GetStreamUriResponse * SOAP_FMAC2 soap_instantiate__trt__GetStreamUriResponse(struct soap*, int, const char*, const char*, size_t*);
};
#endif
2.函数
函数声明:client\application\soapStub.h
/** Web service synchronous operation 'soap_call___trt__GetStreamUri' to the specified endpoint and SOAP Action header, returns SOAP_OK or error code */
SOAP_FMAC5 int SOAP_FMAC6 soap_call___trt__GetStreamUri(struct soap *soap, const char *soap_endpoint, const char *soap_action, _trt__GetStreamUri *trt__GetStreamUri, _trt__GetStreamUriResponse &trt__GetStreamUriResponse);
函数定义:client\application\soapClient.cpp
SOAP_FMAC5 int SOAP_FMAC6 soap_call___trt__GetStreamUri(struct soap *soap, const char *soap_endpoint, const char *soap_action, _trt__GetStreamUri *trt__GetStreamUri, _trt__GetStreamUriResponse &trt__GetStreamUriResponse)
{ if (soap_send___trt__GetStreamUri(soap, soap_endpoint, soap_action, trt__GetStreamUri) || soap_recv___trt__GetStreamUri(soap, trt__GetStreamUriResponse))
return soap->error;
return SOAP_OK;
}
四、功能实现(c++)
int ONVIF_GetMediaCapabilities(const std::string& deviceXAddr, std::string *mediaXAddr)
{
int result = 0;
struct soap *soap = nullptr;
_tds__GetCapabilities devinfo_req;
_tds__GetCapabilitiesResponse devinfo_resp;
SOAP_ASSERT(!deviceXAddr.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
result = soap_call___tds__GetCapabilities(soap, deviceXAddr.c_str(), NULL, &devinfo_req, devinfo_resp);
SOAP_CHECK_ERROR(result, soap, "GetCapabilities");
if(devinfo_resp.Capabilities->Media != nullptr)
{
*mediaXAddr=devinfo_resp.Capabilities->Media->XAddr;
}
else
{
*mediaXAddr="null";
}
#if DEBUG
std::cout << "---------------------ONVIF_GetPTZCapabilities------------------" << "\n";
std::cout << "Media:" << *mediaXAddr << "\n";
std::cout << "-------------------------------------------------------------" << "\n";
#endif
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
}
bool ONVIF_GetMediaProfilesToken(const std::string& mediaXAddr, std::string *mediaprofilesToken)
{
int result = 0;
struct soap *soap = nullptr;
_trt__GetProfiles devinfo_req;
_trt__GetProfilesResponse devinfo_resp;
SOAP_ASSERT(!mediaXAddr.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
result = soap_call___trt__GetProfiles(soap, mediaXAddr.c_str(), nullptr, &devinfo_req, devinfo_resp);
SOAP_CHECK_ERROR(result, soap, "ONVIF_GetProfiles");
SOAP_ASSERT(devinfo_resp.__sizeProfiles > 0);
*mediaprofilesToken = (*devinfo_resp.Profiles)->token;
#if DEBUG
std::cout << "-----------------ONVIF_GetMediaProfilesToken-----------------" << "\n";
std::cout << *mediaprofilesToken << "\n";
std::cout << "-------------------------------------------------------------" << "\n";
#endif
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
}
/************************************************************************
**函数:ONVIF_GetStreamUri
**功能:获取设备码流地址(RTSP)
**参数:
[in] MediaXAddr - 媒体服务地址
[in] ProfileToken - the media profile token
**返回:
0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_GetStreamUri(const std::string&MediaXAddr, const std::string&ProfileToken, std::string *streamUri)
{
int result = 0;
struct soap *soap = nullptr;
tt__StreamSetup ttStreamSetup;
tt__Transport ttTransport;
_trt__GetStreamUri req;
_trt__GetStreamUriResponse rep;
SOAP_ASSERT(!MediaXAddr.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
ttStreamSetup.Stream = tt__StreamType__RTP_Unicast;
ttStreamSetup.Transport = &ttTransport;
ttStreamSetup.Transport->Protocol = tt__TransportProtocol__RTSP;
ttStreamSetup.Transport->Tunnel = nullptr;
req.StreamSetup = &ttStreamSetup;
req.ProfileToken = const_cast<char *>(ProfileToken.c_str());
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
result = soap_call___trt__GetStreamUri(soap, MediaXAddr.c_str(), nullptr, &req, rep);
SOAP_CHECK_ERROR(result, soap, "GetServices");
if (nullptr != rep.MediaUri) {
if (nullptr != rep.MediaUri->Uri) {
*streamUri = rep.MediaUri->Uri;
#if DEBUG
std::cout << "----------------------ONVIF_GetStreamUri---------------------" << "\n";
std::cout << "RTSP URI:\t"<< *streamUri << "\n";
std::cout << "-------------------------------------------------------------" << "\n";
#endif
}
}
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
}