本文是Linux c++ onvif客户端开发系列文章之一:
- Linux c++ onvif客户端开发(1): 根据wsdl生成cpp源文件
- Linux c++ onvif客户端开发(2): 获取摄像头H264/H265 RTSP地址
- Linux c++ onvif客户端开发(3): 扫描设备
- Linux c++ onvif客户端开发(4): 扫描某个设备是否支持onvif
- Linux c++ onvif客户端开发(5):gsoap内存管理
-
Linux c++ onvif客户端开发(6):获取设备信息
-
Linux c++ onvif客户端开发(7):struct soap包装类
-
Linux c++ onvif客户端开发(8):GetServices
1. 什么是Profile
这个接口的Profile可以理解为一条通道。 主码流、主码流是不同的通道,因此它们有不同的Profle。
每个Profile有独立的VideoSource,VideoEncoder,Analytics,Metadata等。
每个Profle的属性包括Profile token和Profile name。其中Profile token是很重要的属性,访问这个Profle属性的时候都会要求传这个数据。
2. Media2
Media2是https://www.onvif.org/onvif/ver20/media/wsdl/media.wsdl,这个规范支持H265。本文所讲解的也是这个规范。
3. GetProfiles
接口有两个可选参数Token和Type
ONVIF Device Test Tool 随手掏
4. 获取所有 Profile token和name
发送
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetProfiles xmlns="http://www.onvif.org/ver20/media/wsdl">
<Token>Profile000</Token>
</GetProfiles>
</s:Body>
</s:Envelope>
接收
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:sc="http://www.w3.org/2003/05/soap-encoding" xmlns:tr2="http://www.onvif.org/ver20/media/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema">
<s:Body>
<tr2:GetProfilesResponse>
<tr2:Profiles token="Profile000" fixed="true">
<tr2:Name>Profile000</tr2:Name>
</tr2:Profiles>
<tr2:Profiles token="Profile001" fixed="true">
<tr2:Name>Profile001</tr2:Name>
</tr2:Profiles>
</tr2:GetProfilesResponse>
</s:Body>
</s:Envelope>
token是很重要的数据,后面要考的。
C++实现
先声明
struct Media20Profile {
std::string token;
std::string name;
};
class Device {
public:
Device(const std::string &ip, const std::string &user,
const std::string &passwd, int default_timeout = 2);
~Device();
int Probe(int timeout = 1);
std::string xaddr() const { return xaddr_; }
int GetDeviceInformation(DeviceInformation &device_info,
Fault *fault = nullptr);
// 获取服务地址集
int GetServices(std::map<std::string, Service> &services,
Fault *fault = nullptr);
// 获取Profiles 名称和token
int GetProfiles(std::vector<Media20Profile> &profiles,
Fault *fault = nullptr,
const std::string &media_xaddr = std::string());
// 根据profile token 获取source 和video encoder配置
int GetProfiles(ProfileConfigurations &profile_configurations,
const std::string &profile_token,
const std::string &media_xaddr = std::string());
private:
std::string ip_;
std::string username_;
std::string password_;
int default_timeout_;
std::string xaddr_;
};
实现
int Device::GetProfiles(std::vector<Media20Profile> &profiles, Fault *fault,
const std::string &media_xaddr) {
// xmlns:trt="http://www.onvif.org/ver10/media/wsdl
// xmlns:ns1="http://www.onvif.org/ver20/media/wsdl"
std::unique_ptr<Soap> soap(new Soap(default_timeout_));
soap_wsse_add_UsernameTokenDigest(soap->soap(), nullptr, username_.data(),
password_.data());
// 支持H265
struct _ns1__GetProfiles media2_req;
struct _ns1__GetProfilesResponse media2_resp;
int result = 0;
result = soap_call___ns1__GetProfiles(
soap->soap(), media_xaddr.empty() ? xaddr_.data() : media_xaddr.data(),
NULL, &media2_req, media2_resp);
if (SOAP_OK != result) {
soap->FillFault(fault);
} else {
for (auto &i : media2_resp.Profiles) {
Media20Profile prof{.token = i->token, .name = i->Name};
profiles.emplace_back(prof);
}
}
return result;
}
media_xaddr 是通过GetServices拿到的数据,对应命名空间http://www.onvif.org/ver20/media/wsdl的 xaddr。一般也可以直接用设备的xaddr。
5. 获取Profles详细信息
如果请求里面指定 Token和Type,那么接口也会返回这个Profile的详细信息。比如视频源信息、视频编码器信息等。
发送
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetProfiles xmlns="http://www.onvif.org/ver20/media/wsdl">
<Token>Profile000</Token>
<Type>VideoEncoder</Type>
</GetProfiles>
</s:Body>
</s:Envelope>
接收
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:sc="http://www.w3.org/2003/05/soap-encoding" xmlns:tr2="http://www.onvif.org/ver20/media/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema">
<s:Body>
<tr2:GetProfilesResponse>
<tr2:Profiles token="Profile000" fixed="true">
<tr2:Name>Profile000</tr2:Name>
<tr2:Configurations>
<tr2:VideoEncoder token="VideoEncoder000" GovLength="16" Profile="Main">
<tt:Name>VideoEncoder000</tt:Name>
<tt:UseCount>1</tt:UseCount>
<tt:Encoding>H265</tt:Encoding>
<tt:Resolution>
<tt:Width>1280</tt:Width>
<tt:Height>960</tt:Height>
</tt:Resolution>
<tt:RateControl ConstantBitRate="false">
<tt:FrameRateLimit>10</tt:FrameRateLimit>
<tt:BitrateLimit>2304</tt:BitrateLimit>
</tt:RateControl>
<tt:Multicast>
<tt:Address>
<tt:Type>IPv4</tt:Type>
<tt:IPv4Address>224.1.2.4</tt:IPv4Address>
</tt:Address>
<tt:Port>40000</tt:Port>
<tt:TTL>64</tt:TTL>
<tt:AutoStart>false</tt:AutoStart>
</tt:Multicast>
<tt:Quality>3.000000</tt:Quality>
</tr2:VideoEncoder>
</tr2:Configurations>
</tr2:Profiles>
</tr2:GetProfilesResponse>
</s:Body>
</s:Envelope>
C++实现
声明
这里我只定义了VideoSource,VideoEncoder相关配置。
struct Resolution {
int width = 0;
int height = 0;
};
struct RateControl {
bool constant_bit_rate = false;
float frame_rate_limit = 0;
int bitrate_limit = 0;
};
struct VideoSourceConfiguration {
std::string token;
std::string source_token;
std::string name;
};
struct VideoEncoderConfiguration {
// GetVideoEncoderConfigurations 中的token和name,不是profile token。示例:
// token="VideoEncoder000" <tt:Name>VideoEncoder000</tt:Name>
std::string token;
int gov_length = 0;
// Baseline Main High
std::string profile;
std::string name;
std::string encoding;
Resolution resolution;
RateControl rate_control;
float quality = 0;
};
struct ProfileConfigurations {
Media20Profile profile;
VideoSourceConfiguration video_source_config;
VideoEncoderConfiguration video_encoder_config;
};
实现
int Device::GetProfiles(ProfileConfigurations &profile_configurations,
const std::string &token,
const std::string &media_xaddr) {
std::unique_ptr<Soap> soap(new Soap(default_timeout_));
soap_wsse_add_UsernameTokenDigest(soap->soap(), nullptr, username_.data(),
password_.data());
// 支持H265
struct _ns1__GetProfiles media2_req;
struct _ns1__GetProfilesResponse media2_resp;
std::string profile_token(token);
media2_req.Token = &profile_token;
media2_req.Type.push_back(soap_ns1__ConfigurationEnumeration2s(
soap->soap(), ns1__ConfigurationEnumeration::VideoSource));
media2_req.Type.push_back(soap_ns1__ConfigurationEnumeration2s(
soap->soap(), ns1__ConfigurationEnumeration::VideoEncoder));
int result = 0;
result = soap_call___ns1__GetProfiles(
soap->soap(), media_xaddr.empty() ? xaddr_.data() : media_xaddr.data(),
NULL, &media2_req, media2_resp);
if (SOAP_OK != result) {
return result;
}
for (auto &i : media2_resp.Profiles) {
profile_configurations.profile.name = i->Name;
profile_configurations.profile.token = i->token;
if (i->Configurations) {
if (i->Configurations->VideoSource) {
auto vs = i->Configurations->VideoSource;
profile_configurations.video_source_config.token = vs->token;
profile_configurations.video_source_config.source_token =
vs->SourceToken;
profile_configurations.video_source_config.name = vs->Name;
}
if (i->Configurations->VideoEncoder) {
auto ve = i->Configurations->VideoEncoder;
profile_configurations.video_encoder_config.token = ve->token;
profile_configurations.video_encoder_config.gov_length =
*ve->GovLength;
profile_configurations.video_encoder_config.profile =
*ve->Profile;
profile_configurations.video_encoder_config.name = ve->Name;
profile_configurations.video_encoder_config.encoding =
ve->Encoding;
profile_configurations.video_encoder_config.resolution.width =
ve->Resolution->Width;
profile_configurations.video_encoder_config.resolution.height =
ve->Resolution->Height;
profile_configurations.video_encoder_config.rate_control
.constant_bit_rate = *ve->RateControl->ConstantBitRate;
profile_configurations.video_encoder_config.rate_control
.bitrate_limit = ve->RateControl->BitrateLimit;
profile_configurations.video_encoder_config.rate_control
.frame_rate_limit = ve->RateControl->FrameRateLimit;
profile_configurations.video_encoder_config.quality =
ve->Quality;
}
}
}
return result;
}
6. 后记
1. 其他功能比如AudioSource, AudioEncoder自己去扩展
2. Soap,Fault 的定义参考前面文章 Linux c++ onvif客户端开发(7):struct soap包装类