cgo+gSoap+onvif学习总结:5、获取profileToken、rtsp流地址、快照地址及cgo偶发signal SIGSEGV: segmentation violation分割违例解决方法
1. 前言
理论上我们搜索到设备并获取设备信息后,根据要筛选的设备信息条件筛选出我们需要的ipc后,我们还可以分别对其进行网络信息配置,这个之前go的方式我们已经使用过了,没有问题,gSoap方式应该问题也不大,所以这里我们就略过这块了,我们直接来实验获取设备能力之后,获取profileToken并获取rtsp流地址和快照地址,这部分获取到后就相当于可以从摄像头拉流了,AI处理很多时候都是只需要获取rtsp流,然后分析音视频流来做进一步的人、车、非等识别判断,所以这块虽然相对不是很难,但是使用上是非常广泛的。
根据之前的开发经验,这块从代码层面看还是分三步:1、gSoap生成c/c++代码框架;2、完成c/c++代码;3、完成cgo代码;从onvif协议角度看,流程稍微复杂一些:设备搜索、鉴权、获取设备信息、筛选设备、网络设置(网络设置一般不需要,但有时候根据需求需要自动修改出厂网络设置)、获取设备能力、选择媒体能力并获取ProfileToken、根据ProfileToken获取对应视频流的rtsp流地址、获取快照地址等,其它的一些ptz、预置点等都是针对获取到的设备能力来做的。
2. gSoap生成c/c++代码框架
网络接口规范地址:https://www.onvif.org/profiles/specifications/
cd ./soap/
//注意这里的typemap.dat是上节我们修改过的,不然还会出现duration.c编译报错
wsdl2h -c -t ./typemap.dat -o onvif.h http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/ver10/events/wsdl/event.wsdl https://www.onvif.org/ver10/media/wsdl/media.wsdl
vim onvif.h
添加
//需要依赖如下文件,从gSoap源码拷贝到我们的工程中(拷贝一次后续就可以一直用了)
dom.c、dom.h、wsseapi.c、wsseapi.h、smdevp.c、smdevp.h、mecevp.c、mecevp.h、threads.c、threads.h、wsaapi.c、wsaapi.h
//注意下面的文件需要custom文件夹,否则路径不对
struct_timeval.h、struct_timeval.c
//duration.c以及duration.h对于生成框架代码和编译c代码路径可能会有差异,根据实际情况可能需要两个位置都有
//只生成客户端源文件,我们只实现客户端,不添加-C会多生成服务端代码
soapcpp2 -x -L -C onvif.h
整个过程截图:
可以看到上一节我们跑通后,后续再生成框架代码就很流畅了。
3. 实现c代码并运行测试
我们还是根据生成的c框架代码写出获取profileToken、rtsp流地址、抓拍地址的c实例代码,并写简单的main函数做下测试,保证c实例代码编译运行正常。onvif流程如下:获取设备能力-》获取profileToken-》获取rtsp流地址和抓拍地址。
3.1 c代码
如下我们还是简单做下封装并写main函数:
#include <string.h>
#include "soap/soapStub.h"
#include "soap/wsdd.nsmap"
#include "soap/soapH.h"
#include "soap/wsseapi.h"
#include "client.h"
struct soap *new_soap(struct soap *soap) {
soap = soap_new();
if (soap == NULL) {
printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
return NULL;
}
soap_set_namespaces(soap, namespaces);
soap->recv_timeout = 5;
printf("func:%s,line:%d.new soap success!\n", __FUNCTION__, __LINE__);
return soap;
}
void del_soap(struct soap *soap) {
soap_end(soap);
soap_free(soap);
}
int discovery(struct soap *soap) {
struct wsdd__ProbeType req;
struct __wsdd__ProbeMatches resp;
struct wsdd__ScopesType sScope;
struct SOAP_ENV__Header header;
int count = 0;
int res;
char uuid_string[64];
printf("func:%s,line:%d.discovery dev!\n", __FUNCTION__, __LINE__);
if (soap == NULL) {
printf("func:%s,line:%d.soap is nil.\n", __FUNCTION__, __LINE__);
return -1;
}
sprintf(uuid_string, "464A4854-4656-5242-4530-110000000000");
printf("func:%s,line:%d.uuid = %s\n", __FUNCTION__, __LINE__, uuid_string);
soap_default_SOAP_ENV__Header(soap, &header);
header.wsa5__MessageID = uuid_string;
header.wsa5__To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
header.wsa5__Action = "http://schemas.xmllocal_soap.org/ws/2005/04/discovery/Probe";
soap->header = &header;
soap_default_wsdd__ScopesType(soap, &sScope);
sScope.__item = "onvif://www.onvif.org";
soap_default_wsdd__ProbeType(soap, &req);
req.Scopes = &sScope;
req.Types = "ns1:NetworkVideoTransmitter";
res = soap_send___wsdd__Probe(soap, "soap.udp://239.255.255.250:3702/", NULL, &req);
if (res == -1) {
printf("func:%s,line:%d.soap error: %d, %s, %s \n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
res = soap->error;
} else {
do {
printf("func:%s,line:%d.begin receive probe match, find dev count:%d.... \n", __FUNCTION__, __LINE__,
count);
res = soap_recv___wsdd__ProbeMatches(soap, &resp);
printf("func:%s,line:%d.result=%d \n", __FUNCTION__, __LINE__, res);
if (res == -1) {
break;
} else {
printf("soap_recv___wsdd__Probe: __sizeProbeMatch = %d \n", resp.wsdd__ProbeMatches->__sizeProbeMatch);
printf("Target EP Address : %s \n",
resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);
printf("Target Type : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Types);
printf("Target Service Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);
printf("Target Metadata Version: %d \n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);
printf("Target Scope Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Scopes->__item);
count++;
}
} while (1);
}
return res;
}
int set_auth_info(struct soap *soap, const char *username, const char *password) {
if (NULL == username) {
printf("func:%s,line:%d.username is null.\n", __FUNCTION__, __LINE__);
return -1;
}
if (NULL == password) {
printf("func:%s,line:%d.password is nil.\n", __FUNCTION__, __LINE__);
return -2;
}
int result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);
return result;
}
int get_device_info(struct soap *soap, const char *username, const char *password, char *xAddr) {
if (NULL == xAddr) {
printf("func:%s,line:%d.dev addr is nil.\n", __FUNCTION__, __LINE__);
return -1;
}
if (soap == NULL) {
printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
return -2;
}
struct _tds__GetDeviceInformation deviceInformation;
struct _tds__GetDeviceInformationResponse deviceInformationResponse;
set_auth_info(soap, username, password);
int res = soap_call___tds__GetDeviceInformation(soap, xAddr, NULL, &deviceInformation, &deviceInformationResponse);
if (NULL != soap) {
printf("Manufacturer:%s\n", deviceInformationResponse.Manufacturer);
printf("Model:%s\n", deviceInformationResponse.Model);
printf("FirmwareVersion:%s\n", deviceInformationResponse.FirmwareVersion);
printf("SerialNumber:%s\n", deviceInformationResponse.SerialNumber);
printf("HardwareId:%s\n", deviceInformationResponse.HardwareId);
}
return res;
}
int get_capabilities(struct soap *soap, const char *username, const char *password, char *xAddr, char *mediaAddr) {
struct _tds__GetCapabilities capabilities;
struct _tds__GetCapabilitiesResponse capabilitiesResponse;
set_auth_info(soap, username, password);
int res = soap_call___tds__GetCapabilities(soap, xAddr, NULL, &capabilities, &capabilitiesResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
if (capabilitiesResponse.Capabilities == NULL) {
printf("func:%s,line:%d.GetCapabilities failed! result=%d \n", __FUNCTION__, __LINE__, res);
} else {
printf("func:%s,line:%d.Media->XAddr=%s \n", __FUNCTION__, __LINE__,
capabilitiesResponse.Capabilities->Media->XAddr);
strcpy(mediaAddr, capabilitiesResponse.Capabilities->Media->XAddr);
}
return res;
}
int get_profiles(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetProfiles profiles;
struct _trt__GetProfilesResponse profilesResponse;
set_auth_info(soap, username, password);
int res = soap_call___trt__GetProfiles(soap, xAddr, NULL, &profiles, &profilesResponse);
if (res == -1)
{
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
if (profilesResponse.Profiles != NULL) {
if (profilesResponse.Profiles->Name != NULL) {
printf("func:%s,line:%d.Profiles Name:%s\n", __FUNCTION__, __LINE__, profilesResponse.Profiles->Name);
}
if (profilesResponse.Profiles->token != NULL) {
printf("func:%s,line:%d.Profiles Taken:%s\n", __FUNCTION__, __LINE__, profilesResponse.Profiles->token);
strcpy(profileToken, profilesResponse.Profiles->token);
}
} else {
printf("func:%s,line:%d.Profiles Get inner Error\n", __FUNCTION__, __LINE__);
}
return res;
}
int get_rtsp_uri(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetStreamUri streamUri;
struct _trt__GetStreamUriResponse streamUriResponse;
streamUri.StreamSetup = (struct tt__StreamSetup *) soap_malloc(soap, sizeof(struct tt__StreamSetup));
streamUri.StreamSetup->Stream = 0;
streamUri.StreamSetup->Transport = (struct tt__Transport *) soap_malloc(soap, sizeof(struct tt__Transport));
streamUri.StreamSetup->Transport->Protocol = 0;
streamUri.StreamSetup->Transport->Tunnel = 0;
streamUri.StreamSetup->__size = 1;
streamUri.StreamSetup->__any = NULL;
streamUri.StreamSetup->__anyAttribute = NULL;
strcpy(streamUri.ProfileToken, profileToken);
set_auth_info(soap, username, password);
int res = soap_call___trt__GetStreamUri(soap, xAddr, NULL, &streamUri, &streamUriResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
printf("func:%s,line:%d.RTSP uri is :%s \n", __FUNCTION__, __LINE__, streamUriResponse.MediaUri->Uri);
return res;
}
int get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetSnapshotUri snapshotUri;
struct _trt__GetSnapshotUriResponse snapshotUriResponse;
set_auth_info(soap, username, password);
snapshotUri.ProfileToken = profileToken;
int res = soap_call___trt__GetSnapshotUri(soap, xAddr, NULL, &snapshotUri, &snapshotUriResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
printf("func:%s,line:%d.Snapshot uri is :%s \n", __FUNCTION__, __LINE__,
snapshotUriResponse.MediaUri->Uri);
return res;
}
int main() {
struct soap *soap = NULL;
soap = new_soap(soap);
const char username[] = "admin";
const char password[] = "admin";
char serviceAddr[] = "http://40.40.40.101:80/onvif/device_service";
discovery(soap);
get_device_info(soap, username, password, serviceAddr);
char mediaAddr[200] = {'\0'};
get_capabilities(soap, username, password, serviceAddr, mediaAddr);
char profileToken[200] = {'\0'};
get_profiles(soap, username, password, profileToken, mediaAddr);
get_rtsp_uri(soap, username, password, profileToken, mediaAddr);
get_snapshot(soap, username, password, profileToken, mediaAddr);
del_soap(soap);
}
3.2 camke
cmake_minimum_required(VERSION 3.0)
project(onvif-cgo)
set(CMAKE_C_FLAGS "-DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACES")
aux_source_directory(./soap/ SRC_LIST)
include_directories(./ /usr/include/ ./soap/custom)
link_directories(~/ /usr/local/ /usr/lib/)
add_executable(gsoap-onvif ${SRC_LIST} client.c client.h ./soap/custom)
#add_executable(gsoap-onvif ${SRC_LIST} ./soap/custom device.c)
#add_executable(discovery_onvif ${SRC_LIST} main.c ./soap/custom)
target_link_libraries(gsoap-onvif -lpthread -ldl -lssl -lcrypto)
#ADD_LIBRARY(c_onvif SHARED ${SRC_LIST} client.c)
#ADD_LIBRARY(c_onvif_static STATIC ${SRC_LIST} client.c client.h ./soap/custom)
3.3 运行结果
结果如下:
rtsp地址测试结果:
抓拍地址测试结果:
4. 实现cgo代码并运行测试
cgo代码由于需要收发字符串内容,所以需要先了解以下cgo中c和go如何进行字符数组的交互,这块找一下示例之后照猫画虎即可。
4.1 修改后的c代码
client.c
#include <string.h>
#include "soap/soapStub.h"
#include "soap/wsdd.nsmap"
#include "soap/soapH.h"
#include "soap/wsseapi.h"
#include "client.h"
struct soap *new_soap(struct soap *soap) {
soap = soap_new();
if (soap == NULL) {
printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
return NULL;
}
soap_set_namespaces(soap, namespaces);
soap->recv_timeout = 5;
printf("func:%s,line:%d.new soap success!\n", __FUNCTION__, __LINE__);
return soap;
}
void del_soap(struct soap *soap) {
soap_end(soap);
soap_free(soap);
}
int discovery(struct soap *soap) {
struct wsdd__ProbeType req;
struct __wsdd__ProbeMatches resp;
struct wsdd__ScopesType sScope;
struct SOAP_ENV__Header header;
int count = 0;
int res;
char uuid_string[64];
printf("func:%s,line:%d.discovery dev!\n", __FUNCTION__, __LINE__);
if (soap == NULL) {
printf("func:%s,line:%d.soap is nil.\n", __FUNCTION__, __LINE__);
return -1;
}
sprintf(uuid_string, "464A4854-4656-5242-4530-110000000000");
printf("func:%s,line:%d.uuid = %s\n", __FUNCTION__, __LINE__, uuid_string);
soap_default_SOAP_ENV__Header(soap, &header);
header.wsa5__MessageID = uuid_string;
header.wsa5__To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
header.wsa5__Action = "http://schemas.xmllocal_soap.org/ws/2005/04/discovery/Probe";
soap->header = &header;
soap_default_wsdd__ScopesType(soap, &sScope);
sScope.__item = "onvif://www.onvif.org";
soap_default_wsdd__ProbeType(soap, &req);
req.Scopes = &sScope;
req.Types = "ns1:NetworkVideoTransmitter";
res = soap_send___wsdd__Probe(soap, "soap.udp://239.255.255.250:3702/", NULL, &req);
if (res == -1) {
printf("func:%s,line:%d.soap error: %d, %s, %s \n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
res = soap->error;
} else {
do {
printf("func:%s,line:%d.begin receive probe match, find dev count:%d.... \n", __FUNCTION__, __LINE__,
count);
res = soap_recv___wsdd__ProbeMatches(soap, &resp);
printf("func:%s,line:%d.result=%d \n", __FUNCTION__, __LINE__, res);
if (res == -1) {
break;
} else {
printf("soap_recv___wsdd__Probe: __sizeProbeMatch = %d \n", resp.wsdd__ProbeMatches->__sizeProbeMatch);
printf("Target EP Address : %s \n",
resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);
printf("Target Type : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Types);
printf("Target Service Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);
printf("Target Metadata Version: %d \n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);
printf("Target Scope Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Scopes->__item);
count++;
}
} while (1);
}
return res;
}
int set_auth_info(struct soap *soap, const char *username, const char *password) {
if (NULL == username) {
printf("func:%s,line:%d.username is null.\n", __FUNCTION__, __LINE__);
return -1;
}
if (NULL == password) {
printf("func:%s,line:%d.password is nil.\n", __FUNCTION__, __LINE__);
return -2;
}
int result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);
return result;
}
int get_device_info(struct soap *soap, const char *username, const char *password, char *xAddr) {
if (NULL == xAddr) {
printf("func:%s,line:%d.dev addr is nil.\n", __FUNCTION__, __LINE__);
return -1;
}
if (soap == NULL) {
printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
return -2;
}
struct _tds__GetDeviceInformation deviceInformation;
struct _tds__GetDeviceInformationResponse deviceInformationResponse;
set_auth_info(soap, username, password);
int res = soap_call___tds__GetDeviceInformation(soap, xAddr, NULL, &deviceInformation, &deviceInformationResponse);
if (NULL != soap) {
printf("Manufacturer:%s\n", deviceInformationResponse.Manufacturer);
printf("Model:%s\n", deviceInformationResponse.Model);
printf("FirmwareVersion:%s\n", deviceInformationResponse.FirmwareVersion);
printf("SerialNumber:%s\n", deviceInformationResponse.SerialNumber);
printf("HardwareId:%s\n", deviceInformationResponse.HardwareId);
}
return res;
}
int get_capabilities(struct soap *soap, const char *username, const char *password, char *xAddr, char *mediaAddr) {
struct _tds__GetCapabilities capabilities;
struct _tds__GetCapabilitiesResponse capabilitiesResponse;
set_auth_info(soap, username, password);
int res = soap_call___tds__GetCapabilities(soap, xAddr, NULL, &capabilities, &capabilitiesResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
if (capabilitiesResponse.Capabilities == NULL) {
printf("func:%s,line:%d.GetCapabilities failed! result=%d \n", __FUNCTION__, __LINE__, res);
} else {
printf("func:%s,line:%d.Media->XAddr=%s \n", __FUNCTION__, __LINE__,
capabilitiesResponse.Capabilities->Media->XAddr);
strcpy(mediaAddr, capabilitiesResponse.Capabilities->Media->XAddr);
}
return res;
}
int get_profiles(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetProfiles profiles;
struct _trt__GetProfilesResponse profilesResponse;
set_auth_info(soap, username, password);
int res = soap_call___trt__GetProfiles(soap, xAddr, NULL, &profiles, &profilesResponse);
if (res == -1)
{
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
if (profilesResponse.Profiles != NULL) {
if (profilesResponse.Profiles->Name != NULL) {
printf("func:%s,line:%d.Profiles Name:%s\n", __FUNCTION__, __LINE__, profilesResponse.Profiles->Name);
}
if (profilesResponse.Profiles->token != NULL) {
printf("func:%s,line:%d.Profiles Taken:%s\n", __FUNCTION__, __LINE__, profilesResponse.Profiles->token);
strcpy(profileToken, profilesResponse.Profiles->token);
}
} else {
printf("func:%s,line:%d.Profiles Get inner Error\n", __FUNCTION__, __LINE__);
}
return res;
}
int get_rtsp_uri(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetStreamUri streamUri;
struct _trt__GetStreamUriResponse streamUriResponse;
streamUri.StreamSetup = (struct tt__StreamSetup *) soap_malloc(soap, sizeof(struct tt__StreamSetup));
streamUri.StreamSetup->Stream = 0;
streamUri.StreamSetup->Transport = (struct tt__Transport *) soap_malloc(soap, sizeof(struct tt__Transport));
streamUri.StreamSetup->Transport->Protocol = 0;
streamUri.StreamSetup->Transport->Tunnel = 0;
streamUri.StreamSetup->__size = 1;
streamUri.StreamSetup->__any = NULL;
streamUri.StreamSetup->__anyAttribute = NULL;
streamUri.ProfileToken = profileToken;
set_auth_info(soap, username, password);
int res = soap_call___trt__GetStreamUri(soap, xAddr, NULL, &streamUri, &streamUriResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
printf("func:%s,line:%d.RTSP uri is :%s \n", __FUNCTION__, __LINE__, streamUriResponse.MediaUri->Uri);
return res;
}
int get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetSnapshotUri snapshotUri;
struct _trt__GetSnapshotUriResponse snapshotUriResponse;
set_auth_info(soap, username, password);
snapshotUri.ProfileToken = profileToken;
int res = soap_call___trt__GetSnapshotUri(soap, xAddr, NULL, &snapshotUri, &snapshotUriResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
printf("func:%s,line:%d.Snapshot uri is :%s \n", __FUNCTION__, __LINE__,
snapshotUriResponse.MediaUri->Uri);
return res;
}
client.h
#ifndef ONVIF_CGO_CLIENT_H
#define ONVIF_CGO_CLIENT_H
typedef struct soap *P_Soap;
extern struct soap *new_soap(struct soap *soap);
extern void del_soap(struct soap *soap);
extern int discovery(struct soap *soap);
extern int get_device_info(struct soap *soap, const char *username, const char *password, char *xAddr);
extern int get_capabilities(struct soap *soap, const char *username, const char *password, char *xAddr, char *mediaAddr);
extern int
get_profiles(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr);
extern int
get_rtsp_uri(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr);
extern int
get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr);
#endif
4.2 修改后的cmake
cmake_minimum_required(VERSION 3.0)
project(onvif-cgo)
set(CMAKE_C_FLAGS "-DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACES")
aux_source_directory(./soap/ SRC_LIST)
include_directories(./ /usr/include/ ./soap/custom)
link_directories(~/ /usr/local/ /usr/lib/)
#add_executable(gsoap-onvif ${SRC_LIST} client.c client.h ./soap/custom)
#add_executable(gsoap-onvif ${SRC_LIST} ./soap/custom device.c)
#add_executable(discovery_onvif ${SRC_LIST} main.c ./soap/custom)
#target_link_libraries(gsoap-onvif -lpthread -ldl -lssl -lcrypto)
#ADD_LIBRARY(c_onvif SHARED ${SRC_LIST} client.c)
ADD_LIBRARY(c_onvif_static STATIC ${SRC_LIST} client.c client.h ./soap/custom)
4.3 cgo代码
package main
import "C"
import (
"fmt"
"unsafe"
)
func main() {
var soap C.P_Soap
soap = C.new_soap(soap)
username := C.CString("admin")
password := C.CString("admin")
serviceAddr := C.CString("http://40.40.40.101:80/onvif/device_service")
C.discovery(soap)
C.get_device_info(soap, username, password, serviceAddr)
mediaAddr := [200]C.char{}
C.get_capabilities(soap, username, password, serviceAddr, &mediaAddr[0])
fmt.Println(C.GoString(&mediaAddr[0]))
profileToken := [200]C.char{}
C.get_profiles(soap, username, password, &profileToken[0], &mediaAddr[0])
fmt.Println(C.GoString(&profileToken[0]))
C.get_rtsp_uri(soap, username, password, &profileToken[0], &mediaAddr[0])
C.get_snapshot(soap, username, password, &profileToken[0], &mediaAddr[0])
C.del_soap(soap)
C.free(unsafe.Pointer(username))
C.free(unsafe.Pointer(password))
C.free(unsafe.Pointer(serviceAddr))
}
GOOS=linux GOARCH=amd64 CGO_ENABLE=1 go build -o onvif_cgo main.go
4.4 运行结果
5. cgo偶发的fatal error: unexpected signal during runtime execution问题
在运行期间我们发现:编译出的cgo可执行程序运行时会随机性出现错误异常退出,错误信息类似:"fatal error: unexpected signal during runtime execution
signal SIGSEGV: segmentation violation xxx"
zy@LS2-R910CQQT:/mnt/d/code/onvif_cgo$ ./onvif_cgo
func:new_soap,line:23.new soap success!
func:discovery,line:48.discovery dev!
func:discovery,line:55.uuid = 464A4854-4656-5242-4530-110000000000
func:discovery,line:89.begin receive probe match, find dev count:0....
func:discovery,line:94.result=-1
Manufacturer:UNIVIEW
Model:IPC-S632-IR@P-X33-VG
FirmwareVersion:CIPC-B2302.3.65.L02.211207
SerialNumber:210235C52Q321A000109
HardwareId:IPC-S632-IR@P-X33-VG
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x2 addr=0xe78000 pc=0x61a051]
runtime stack:
runtime.throw(0x9a7e10, 0x2a)
/usr/local/go/src/runtime/panic.go:1117 +0x72
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:718 +0x2e5
goroutine 1 [syscall]:
runtime.cgocall(0x5b4430, 0xc00004dee8, 0xc000000000)
/usr/local/go/src/runtime/cgocall.go:154 +0x5b fp=0xc00004deb8 sp=0xc00004de80 pc=0x552f5b
main._Cfunc_get_capabilities(0x7f06a87d0010, 0xe58680, 0xe586a0, 0xe57f70, 0x0)
_cgo_gotypes.go:96 +0x48 fp=0xc00004dee8 sp=0xc00004deb8 pc=0x5b3ce8
main.main.func4(0xc00004df50, 0xe58680, 0xe586a0, 0xe57f70, 0xc000000000)
/mnt/d/code/onvif_cgo/main.go:31 +0x77 fp=0xc00004df28 sp=0xc00004dee8 pc=0x5b4317
main.main()
/mnt/d/code/onvif_cgo/main.go:31 +0x145 fp=0xc00004df88 sp=0xc00004df28 pc=0x5b40a5
runtime.main()
/usr/local/go/src/runtime/proc.go:225 +0x256 fp=0xc00004dfe0 sp=0xc00004df88 pc=0x581736
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc00004dfe8 sp=0xc00004dfe0 pc=0x5ad221
如下图所示(运行几次可能就会出现,也可能第一次出现,后续又好了,弄了很久):
在搜索一些资料后发现目前似乎go的高版本解决了该问题,如果使用cgo就很可能出现类似问题,之前的go的dns库等很多借助cgo的库都出现过类似问题,似乎是glibc相关的内存栈分割和go本身存在差异导致的。
原文:
https://marcan.st/2017/12/debugging-an-evil-go-runtime-bug/
csdn转载:
https://blog.csdn.net/dev_csdn/article/details/78813904
github上的修改点(可以看出tag版本为1.18rc1):
https://github.com/golang/go/commit/a158382b1c9c0b95a7d41865a405736be6bc585f
然后我在wsl上安装1.18rc1做了下测试(截至到我处理该问题时稳定版本只到1.17.x,1.18.x目前还是不稳定版本):
go下载地址:https://golang.google.cn/dl/
sudo wget https://golang.google.cn/dl/go1.18rc1.linux-amd64.tar.gz
sudo tar -zxvf go1.18rc1.linux-amd64.tar.gz -C /usr/local/
vim ~/.bashrc
export PATH=$PATH:/usr/local/go/bin
export GOPATH=~/work/goStudy
export PATH=$PATH:$GOPATH/bin
export GO111MODULE=on
export GOPROXY=https://goproxy.io
source ~/.bashrc
go version
go env
然后我用1.18版本重新编译测试,运行很多次都是正常的,没有再出现上述分割违例错误。
6. 整体项目结构
zy@LS2-R910CQQT:/mnt/d/code/onvif_cgo$ tree -a -I ".idea|build|.git"
.
├── CMakeLists.txt
├── client.c
├── client.h
├── onvif_cgo
└── soap
├── DeviceBinding.nsmap
├── MediaBinding.nsmap
├── PullPointSubscriptionBinding.nsmap
├── RemoteDiscoveryBinding.nsmap
├── custom
│ ├── README.txt
│ ├── chrono_duration.cpp
│ ├── chrono_duration.h
│ ├── chrono_time_point.cpp
│ ├── chrono_time_point.h
│ ├── duration.c
│ ├── duration.h
│ ├── float128.c
│ ├── float128.h
│ ├── int128.c
│ ├── int128.h
│ ├── long_double.c
│ ├── long_double.h
│ ├── long_time.c
│ ├── long_time.h
│ ├── qbytearray_base64.cpp
│ ├── qbytearray_base64.h
│ ├── qbytearray_hex.cpp
│ ├── qbytearray_hex.h
│ ├── qdate.cpp
│ ├── qdate.h
│ ├── qdatetime.cpp
│ ├── qdatetime.h
│ ├── qstring.cpp
│ ├── qstring.h
│ ├── qtime.cpp
│ ├── qtime.h
│ ├── struct_timeval.c
│ ├── struct_timeval.h
│ ├── struct_tm.c
│ ├── struct_tm.h
│ ├── struct_tm_date.c
│ └── struct_tm_date.h
├── dom.c
├── dom.h
├── duration.c
├── duration.h
├── mecevp.c
├── mecevp.h
├── onvif.h
├── smdevp.c
├── smdevp.h
├── soapC.c
├── soapClient.c
├── soapH.h
├── soapStub.h
├── stdsoap2.h
├── stdsoap2_ssl.c
├── struct_timeval.c
├── struct_timeval.h
├── threads.c
├── threads.h
├── typemap.dat
├── wsaapi.c
├── wsaapi.h
├── wsdd.nsmap
├── wsseapi.c
└── wsseapi.h
2 directories, 69 files
7. 最后
获取到rtsp流数据后,我们可以结合ffmpeg做一些对rtsp流的编解码,之后可以针对根据获取到的音频、视频流等做AI处理。在一些嵌入式环境下还可结合live555做音视频直播。
|