Onvif服务端开发

实现了Onvif服务端的设备搜索和RTSP流的功能。用 ONVIF Device Manager 测试工具可以成功搜索到设备和获取到RTSP流,RTSP服务端我是直接用的 live555, 完整代码我已上传,不需要积分就能下载。主要是看onvif_server.c 和 onvif_server_interface.c 文件,onvif_server.c 主要是创建socket服务,onvif_server_interface.c 主要是实现相关的onvif服务接口。相关代码如下:

live555MediaServer:

cpp 复制代码
#include <pthread.h>
#include "soapH.h"
#include "macro.h"

void *onvif_discovered_server(void *arg)
{
    struct soap soap_udp;
    int socked_fd;

    // 初始化 SOAP 服务器对象
    soap_init1(&soap_udp, SOAP_IO_UDP | SOAP_XML_IGNORENS);
    soap_udp.bind_flags = SO_REUSEADDR; // 允许重复绑定
    soap_udp.port = 3702;

    soap_set_namespaces(&soap_udp, namespaces);

    // 绑定端口
    socked_fd = soap_bind(&soap_udp, NULL, soap_udp.port, 10);
    if (socked_fd < 0)
    {
        printf("%d soap_bind failed\n", __LINE__);
        soap_print_fault(&soap_udp, stderr);
        goto end;
    }

    // 加入组播
    struct ip_mreq mreqcon;
    mreqcon.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
    mreqcon.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(soap_udp.master, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreqcon, sizeof(mreqcon)) < 0)
    {
        printf("setsockopt error, error message: %s\n", strerror(errno));
        goto end;
    }

    while (1)
    {
        socked_fd = soap_serve(&soap_udp);
        if (socked_fd != SOAP_OK)
        {
            printf("%d soap_serve failed\n", __LINE__);
            soap_print_fault(&soap_udp, stderr);
        }

        soap_destroy(&soap_udp);
        soap_end(&soap_udp);
    }

end:
    soap_done(&soap_udp);
    return NULL;
}

int http_get(struct soap *soap)
{
    FILE *fd = NULL;
    static char buf[1024 * 5] = {0};

    fd = fopen(PATH_HTML, "rb");
    if (!fd)
        return 404;
    if (soap_response(soap, SOAP_HTML) == SOAP_OK) // HTTP response header with text/html
    {
        size_t r = fread(buf, 1, sizeof(buf), fd);
        if (r > 0)
            soap_send(soap, buf);
    }

    soap_end_send(soap);
    fclose(fd);

    return SOAP_OK;
}

void *onvif_http_server(void *arg)
{
    struct soap soap_tcp;
    int socked_fd;
    fd_set readfds;
    struct timeval timeout;

    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    soap_init1(&soap_tcp, SOAP_XML_INDENT);
    soap_tcp.port = DEVICE_PORT;
    soap_tcp.bind_flags = SO_REUSEADDR;
    soap_tcp.send_timeout = 3; // send timeout is 3s
    soap_tcp.recv_timeout = 3; // receive timeout is 3s
    soap_tcp.fget = http_get;

    soap_set_namespaces(&soap_tcp, namespaces);

    socked_fd = soap_bind(&soap_tcp, DEVICE_IP, soap_tcp.port, 10);
    if (socked_fd < 0)
    {
        printf("%d soap_bind failed\n", __LINE__);
        soap_print_fault(&soap_tcp, stderr);
        goto end;
    }

    int socked_fd_new = -1;
    while (1)
    {
        socked_fd_new = soap_accept(&soap_tcp);
        if (!soap_valid_socket(socked_fd_new))
        {
            printf("soap_accept failed\n");
            soap_print_fault(&soap_tcp, stderr);
            goto end;
        }

        FD_ZERO(&readfds);
        FD_SET(socked_fd_new, &readfds);

        int activity = select(socked_fd_new + 1, &readfds, NULL, NULL, &timeout);
        if (activity < 0)
        {
            printf("select error\n");
            goto end;
        }

        if (FD_ISSET(socked_fd_new, &readfds))
        {
            if (soap_serve(&soap_tcp) != SOAP_OK)
            {
                printf("%d soap_serve failed, error: %d\n", __LINE__, soap_tcp.error);
                soap_print_fault(&soap_tcp, stderr);
            }
        }

        soap_destroy(&soap_tcp);
        soap_end(&soap_tcp);
    }

end:
    soap_done(&soap_tcp);
    return NULL;
}

int create_onvif_server()
{
    pthread_t thread1, thread2;
    pthread_attr_t attr1, attr2;

    pthread_attr_init(&attr1);
    pthread_attr_init(&attr2);

    pthread_attr_setdetachstate(&attr1, PTHREAD_CREATE_DETACHED);
    pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_DETACHED);

    if (pthread_create(&thread1, &attr1, onvif_discovered_server, NULL) != 0)
        return -1;
    if (pthread_create(&thread2, &attr2, onvif_http_server, NULL) != 0)
        return -1;

    pthread_attr_destroy(&attr1);
    pthread_attr_destroy(&attr2);

    return 0;
}

onvif_server_interface.c,这里只写出已经实现了的接口函数:

cpp 复制代码
#include "wsaapi.h"
#include "soapH.h"
#include "soapStub.h"
#include "macro.h"
#include "wsseapi.h"

// 该函数从 soapClient.c 文件中拷贝过来的
SOAP_FMAC5 int SOAP_FMAC6 soap_send___wsdd__ProbeMatches(struct soap *soap, const char *soap_endpoint, const char *soap_action, struct wsdd__ProbeMatchesType *wsdd__ProbeMatches)
{
    struct __wsdd__ProbeMatches soap_tmp___wsdd__ProbeMatches;
    if (soap_action == NULL)
        soap_action = "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/ProbeMatches";
    soap_tmp___wsdd__ProbeMatches.wsdd__ProbeMatches = wsdd__ProbeMatches;
    soap_begin(soap);
    soap_set_version(soap, 2);  /* use SOAP1.2 */
    soap->encodingStyle = NULL; /* use SOAP literal style */
    soap_serializeheader(soap);
    soap_serialize___wsdd__ProbeMatches(soap, &soap_tmp___wsdd__ProbeMatches);
    if (soap_begin_count(soap))
        return soap->error;
    if ((soap->mode & SOAP_IO_LENGTH))
    {
        if (soap_envelope_begin_out(soap) || soap_putheader(soap) || soap_body_begin_out(soap) || soap_put___wsdd__ProbeMatches(soap, &soap_tmp___wsdd__ProbeMatches, "-wsdd:ProbeMatches", "") || soap_body_end_out(soap) || soap_envelope_end_out(soap))
            return soap->error;
    }
    if (soap_end_count(soap))
        return soap->error;
    if (soap_connect(soap, soap_endpoint, soap_action) || soap_envelope_begin_out(soap) || soap_putheader(soap) || soap_body_begin_out(soap) || soap_put___wsdd__ProbeMatches(soap, &soap_tmp___wsdd__ProbeMatches, "-wsdd:ProbeMatches", "") || soap_body_end_out(soap) || soap_envelope_end_out(soap) || soap_end_send(soap))
        return soap_closesock(soap);
    return SOAP_OK;
}

// 鉴权
int onvif_access_control(struct soap *soap)

{
    const char *username = soap_wsse_get_Username(soap);
    if (!username)
    {
        soap_wsse_delete_Security(soap); // remove old security headers before returning!
        return soap->error;              // no username: return FailedAuthentication (from soap_wsse_get_Username)
    }

    if (strcmp(username, USERNAME) != 0)
    {
        printf("username error\n");
        soap_wsse_delete_Security(soap);
        return 401;
    }

    if (soap_wsse_verify_Password(soap, PASSWORD))
    {
        soap_wsse_delete_Security(soap); // remove old security headers before returning!
        return soap->error;              // no username: return FailedAuthentication (from soap_wsse_verify_Password)
    }

    return 0;
}

// 实现 __wsdd__Probe 函数,用于处理 WS-Discovery Probe 请求
SOAP_FMAC5 int SOAP_FMAC6 __wsdd__Probe(struct soap *soap, struct wsdd__ProbeType *wsdd__Probe)
{
    printf("-------- %s --------\n", __func__);

    // 定义一个 ProbeMatches 结构,用于存储多个 ProbeMatch 条目
    struct wsdd__ProbeMatchesType ProbeMatches;

    // 假设设备仅有一个匹配项 (一个服务)
    int MatchSize = 1;

    // 初始化 ProbeMatches 结构,清零内存
    memset(&ProbeMatches, 0, sizeof(ProbeMatches));

    // 分配内存给 ProbeMatch 数组
    // 使用 soap_malloc 分配内存,这样 gSOAP 可以自动管理其生命周期
    ProbeMatches.ProbeMatch = (struct wsdd__ProbeMatchType *)soap_malloc(soap, sizeof(struct wsdd__ProbeMatchType) * MatchSize);
    if (!ProbeMatches.ProbeMatch)
        return SOAP_FAULT; // 内存分配失败,返回 SOAP Fault
    // 初始化 ProbeMatches.ProbeMatch
    soap_default_wsdd__ProbeMatchType(soap, ProbeMatches.ProbeMatch);

    // 设置实际的 ProbeMatch 数量
    ProbeMatches.__sizeProbeMatch = MatchSize;

    // 获取第一个(也是唯一一个) ProbeMatch 条目
    struct wsdd__ProbeMatchType *ProbeMatch = &ProbeMatches.ProbeMatch[0];

    // ----------------------------
    // 填充 EndpointReference 信息
    // ----------------------------
    /*
     * EndpointReference 用于标识设备的唯一地址 (URI)。
     * 通常使用设备的 UUID 来生成一个 URN 格式的地址。
     */
    ProbeMatch->wsa__EndpointReference.Address = (char *)soap_malloc(soap, 256);
    if (!ProbeMatch->wsa__EndpointReference.Address)
        return SOAP_FAULT;
    // 格式化地址为 "urn:uuid:device_uuid"
    snprintf(ProbeMatch->wsa__EndpointReference.Address, 256, "urn:uuid:%s", "12345678-1234-1234-1234-1234567890ab");

    // -------------------
    // 填充 Types 信息
    // -------------------
    /*
     * Types 用于描述设备的类型,根据 ONVIF 规范,通常包括设备提供的服务类型。
     * 例如,"tdn:NetworkVideoTransmitter" 表示设备是一个网络视频传输器。
     */
    ProbeMatch->Types = (char *)soap_malloc(soap, 256);
    if (!ProbeMatch->Types)
        return SOAP_FAULT;
    strcpy(ProbeMatch->Types, "tdn:NetworkVideoTransmitter");

    // -------------------
    // 填充 Scopes 信息
    // -------------------
    /*
     * Scopes 用于描述设备的作用域信息,提供更详细的设备分类和属性。
     * 例如,设备类型、制造商、型号、位置等。
     * 作用域以空格分隔的 URI 形式表示。
     */
    ProbeMatch->Scopes = (struct wsdd__ScopesType *)soap_malloc(soap, sizeof(struct wsdd__ScopesType));
    if (!ProbeMatch->Scopes)
        return SOAP_FAULT;
    ProbeMatch->Scopes->__item = (char *)soap_malloc(soap, 512);
    if (!ProbeMatch->Scopes->__item)
        return SOAP_FAULT;
    // 示例作用域信息,根据实际情况调整
    snprintf(ProbeMatch->Scopes->__item, 512,
             "onvif://www.onvif.org/type/video_encoder "
             "onvif://www.onvif.org/type/audio_encoder "
             "onvif://www.onvif.org/hardware/%s "
             "onvif://www.onvif.org/name/%s",
             "MyDeviceHardware", "MyONVIFDevice");

    // -------------------
    // 填充 XAddrs 信息
    // -------------------
    /*
     * XAddrs 提供设备服务的访问地址 (URL),通常包括 HTTP 或 HTTPS 地址。
     * 该地址用于访问设备的 Onvif 服务,如设备管理、媒体服务等。
     */
    ProbeMatch->XAddrs = (char *)soap_malloc(soap, 256);
    if (!ProbeMatch->XAddrs)
        return SOAP_FAULT;
    // 格式化服务访问地址
    snprintf(ProbeMatch->XAddrs, 256, "http://%s:%d/onvif/device_service", DEVICE_IP, DEVICE_PORT);

    // ---------------------------
    // 填充 MetadataVersion 信息
    // ---------------------------
    /*
     * MetadataVersion 表示设备元数据的版本号,用于跟踪设备描述的变化。
     * 每当设备的元数据(如服务列表、配置等)发生变化时,应递增此版本号。
     */
    ProbeMatch->MetadataVersion = 1;

    // Build SOAP Header
    soap->header->wsa__RelatesTo = (struct wsa__Relationship *)soap_malloc(soap, sizeof(struct wsa__Relationship));
    if (!soap->header->wsa__RelatesTo)
        return SOAP_FAULT;
    // 初始化 soap->header->wsa__RelatesTo
    soap_default__wsa__RelatesTo(soap, soap->header->wsa__RelatesTo);

    soap->header->wsa__RelatesTo->__item = soap->header->wsa__MessageID;
    soap->header->wsa__Action = soap_strdup(soap, "http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches");
    soap->header->wsa__To = soap_strdup(soap, "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous");

    // 发送 ProbeMatches 响应
    return soap_send___wsdd__ProbeMatches(soap, "http://", NULL, &ProbeMatches);
}

SOAP_FMAC5 int SOAP_FMAC6 __tds__GetCapabilities(struct soap *soap, struct _tds__GetCapabilities *tds__GetCapabilities, struct _tds__GetCapabilitiesResponse *tds__GetCapabilitiesResponse)
{
    printf("-------- %s --------\n", __func__);

    /*
    int ret = onvif_access_control(soap);
    if (ret != 0)
        return ret;
    */

    tds__GetCapabilitiesResponse->Capabilities = (struct tt__Capabilities *)soap_malloc(soap, sizeof(struct tt__Capabilities));
    memset(tds__GetCapabilitiesResponse->Capabilities, 0, sizeof(struct tt__Capabilities));

    // Media
    if (tds__GetCapabilities->Category[0] == tt__CapabilityCategory__Media ||
        tds__GetCapabilities->Category[0] == tt__CapabilityCategory__All)
    {
        tds__GetCapabilitiesResponse->Capabilities->Media = (struct tt__MediaCapabilities *)soap_malloc(soap, sizeof(struct tt__MediaCapabilities));
        memset(tds__GetCapabilitiesResponse->Capabilities->Media, 0, sizeof(struct tt__MediaCapabilities));
        tds__GetCapabilitiesResponse->Capabilities->Media->XAddr = (char *)soap_malloc(soap, sizeof(char) * 256);
        memset(tds__GetCapabilitiesResponse->Capabilities->Media->XAddr, 0, sizeof(char) * 256);
        sprintf(tds__GetCapabilitiesResponse->Capabilities->Media->XAddr, "http://%s:%d/onvif/media_service", DEVICE_IP, DEVICE_PORT);
        //<Media><StreamingCapabilities>
        tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities = (struct tt__RealTimeStreamingCapabilities *)soap_malloc(soap, sizeof(struct tt__RealTimeStreamingCapabilities));
        memset(tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities, 0, sizeof(struct tt__RealTimeStreamingCapabilities));
        tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities->RTPMulticast = (enum xsd__boolean *)soap_malloc(soap, sizeof(enum xsd__boolean));
        *(tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities->RTPMulticast) = xsd__boolean__false_; // 表示设备不支持RTP的多播功能
        tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities->RTP_USCORERTSP_USCORETCP = (enum xsd__boolean *)soap_malloc(soap, sizeof(enum xsd__boolean));
        *(tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities->RTP_USCORERTSP_USCORETCP) = xsd__boolean__true_; // 表示设备支持通过RTSP的TCP传输方式
        tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities->RTP_USCORETCP = (enum xsd__boolean *)soap_malloc(soap, sizeof(enum xsd__boolean));
        *(tds__GetCapabilitiesResponse->Capabilities->Media->StreamingCapabilities->RTP_USCORETCP) = xsd__boolean__true_; // 表示设备支持RTP协议的TCP传输方式
    }

    // 返回成功
    return SOAP_OK;
}

SOAP_FMAC5 int SOAP_FMAC6 __trt__GetVideoSources(struct soap *soap, struct _trt__GetVideoSources *trt__GetVideoSources, struct _trt__GetVideoSourcesResponse *trt__GetVideoSourcesResponse)
{
    printf("-------- %s --------\n", __func__);

    int size = 1;
    trt__GetVideoSourcesResponse->__sizeVideoSources = size;
    trt__GetVideoSourcesResponse->VideoSources = (struct tt__VideoSource *)soap_malloc(soap, sizeof(struct tt__VideoSource) * size);
    memset(trt__GetVideoSourcesResponse->VideoSources, 0, sizeof(struct tt__VideoSource) * trt__GetVideoSourcesResponse->__sizeVideoSources);

    trt__GetVideoSourcesResponse->VideoSources->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetVideoSourcesResponse->VideoSources->token, 0, sizeof(char) * 32);
    strcpy(trt__GetVideoSourcesResponse->VideoSources->token, "vs_SourceToken");

    trt__GetVideoSourcesResponse->VideoSources->Resolution = (struct tt__VideoResolution *)soap_malloc(soap, sizeof(struct tt__VideoResolution));
    memset(trt__GetVideoSourcesResponse->VideoSources->Resolution, 0, sizeof(struct tt__VideoResolution));
    trt__GetVideoSourcesResponse->VideoSources->Resolution->Width = VIDEO_WIDTH;
    trt__GetVideoSourcesResponse->VideoSources->Resolution->Height = VIDEO_HEIGHT;
    trt__GetVideoSourcesResponse->VideoSources->Framerate = FRAME_RATE;

    return SOAP_OK;
}

SOAP_FMAC5 int SOAP_FMAC6 __trt__GetProfile(struct soap *soap, struct _trt__GetProfile *trt__GetProfile, struct _trt__GetProfileResponse *trt__GetProfileResponse)
{
    printf("-------- %s --------\n", __func__);

    trt__GetProfileResponse->Profile = (struct tt__Profile *)soap_malloc(soap, sizeof(struct tt__Profile));
    memset(trt__GetProfileResponse->Profile, 0, sizeof(struct tt__Profile));

    trt__GetProfileResponse->Profile->Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->Name, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->Name, "MyProfile");
    trt__GetProfileResponse->Profile->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->token, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->token, "ProfileToken");
    trt__GetProfileResponse->Profile->fixed = (enum xsd__boolean *)soap_malloc(soap, sizeof(enum xsd__boolean));
    *(trt__GetProfileResponse->Profile->fixed) = xsd__boolean__true_;

    // <VideoSourceConfiguration><name>和<VideoSourceConfiguration><token>
    trt__GetProfileResponse->Profile->VideoSourceConfiguration = (struct tt__VideoSourceConfiguration *)soap_malloc(soap, sizeof(struct tt__VideoSourceConfiguration));
    memset(trt__GetProfileResponse->Profile->VideoSourceConfiguration, 0, sizeof(struct tt__VideoSourceConfiguration));
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->VideoSourceConfiguration->Name, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->VideoSourceConfiguration->Name, "vs_name");
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->VideoSourceConfiguration->token, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->VideoSourceConfiguration->token, "vs_token");
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->SourceToken = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->VideoSourceConfiguration->SourceToken, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->VideoSourceConfiguration->SourceToken, "vs_SourceToken");
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->UseCount = 1;
    // <VideoSourceConfiguration><Bounds>
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->Bounds = (struct tt__IntRectangle *)soap_malloc(soap, sizeof(struct tt__IntRectangle));
    memset(trt__GetProfileResponse->Profile->VideoSourceConfiguration->Bounds, 0, sizeof(struct tt__IntRectangle));
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->Bounds->x = 0;
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->Bounds->y = 0;
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->Bounds->width = VIDEO_WIDTH;
    trt__GetProfileResponse->Profile->VideoSourceConfiguration->Bounds->height = VIDEO_HEIGHT;

    // <VideoEncoderConfiguration>
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration = (struct tt__VideoEncoderConfiguration *)soap_malloc(soap, sizeof(struct tt__VideoEncoderConfiguration));
    memset(trt__GetProfileResponse->Profile->VideoEncoderConfiguration, 0, sizeof(struct tt__VideoEncoderConfiguration));
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Name, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Name, "ve_name");
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->token, 0, sizeof(char) * 32);
    strcpy(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->token, "ve_token");
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->UseCount = 1;
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Encoding = tt__VideoEncoding__H264;
    // <VideoEncoderConfiguration><Resolution>、<RateControl>
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Resolution = (struct tt__VideoResolution *)soap_malloc(soap, sizeof(struct tt__VideoResolution));
    memset(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Resolution, 0, sizeof(struct tt__VideoResolution));
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Resolution->Width = VIDEO_WIDTH;
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Resolution->Height = VIDEO_HEIGHT;
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->Quality = 10;
    // <VideoEncoderConfiguration><RateControl>
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->RateControl = (struct tt__VideoRateControl *)soap_malloc(soap, sizeof(struct tt__VideoRateControl));
    memset(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->RateControl, 0, sizeof(struct tt__VideoRateControl));
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->RateControl->FrameRateLimit = FRAME_RATE;
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->RateControl->EncodingInterval = 1;
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->RateControl->BitrateLimit = 6000;
    // <VideoEncoderConfiguration><H264>
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->H264 = (struct tt__H264Configuration *)soap_malloc(soap, sizeof(struct tt__H264Configuration));
    memset(trt__GetProfileResponse->Profile->VideoEncoderConfiguration->H264, 0, sizeof(struct tt__H264Configuration));
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->H264->GovLength = 120;
    trt__GetProfileResponse->Profile->VideoEncoderConfiguration->H264->H264Profile = tt__H264Profile__High;

    return SOAP_OK;
}

SOAP_FMAC5 int SOAP_FMAC6 __trt__GetProfiles(struct soap *soap, struct _trt__GetProfiles *trt__GetProfiles, struct _trt__GetProfilesResponse *trt__GetProfilesResponse)
{
    printf("-------- %s --------\n", __func__);

    trt__GetProfilesResponse->__sizeProfiles = 1;
    trt__GetProfilesResponse->Profiles = (struct tt__Profile *)soap_malloc(soap, sizeof(struct tt__Profile) * trt__GetProfilesResponse->__sizeProfiles);
    memset(trt__GetProfilesResponse->Profiles, 0, sizeof(struct tt__Profile) * trt__GetProfilesResponse->__sizeProfiles);

    int i = 0;
    trt__GetProfilesResponse->Profiles[i].Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].Name, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].Name, "MyProfile");
    trt__GetProfilesResponse->Profiles[i].token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].token, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].token, "ProfileToken");

    // <VideoSourceConfiguration><name>
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration = (struct tt__VideoSourceConfiguration *)soap_malloc(soap, sizeof(struct tt__VideoSourceConfiguration));
    memset(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration, 0, sizeof(struct tt__VideoSourceConfiguration));
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Name, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Name, "vs_name");
    // <VideoSourceConfiguration><token>
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->token, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->token, "vs_token");
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->SourceToken = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->SourceToken, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->SourceToken, "vs_SourceToken");
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->UseCount = 1; // 表示该视频源配置的使用次数
    // <VideoSourceConfiguration><Bounds>
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Bounds = (struct tt__IntRectangle *)soap_malloc(soap, sizeof(struct tt__IntRectangle));
    memset(trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Bounds, 0, sizeof(struct tt__IntRectangle));
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Bounds->x = 0;
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Bounds->y = 0;
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Bounds->width = VIDEO_WIDTH;
    trt__GetProfilesResponse->Profiles[i].VideoSourceConfiguration->Bounds->height = VIDEO_HEIGHT;

    // <VideoEncoderConfiguration>
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration = (struct tt__VideoEncoderConfiguration *)soap_malloc(soap, sizeof(struct tt__VideoEncoderConfiguration));
    memset(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration, 0, sizeof(struct tt__VideoEncoderConfiguration));
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Name, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Name, "ve_name");
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->token, 0, sizeof(char) * 32);
    strcpy(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->token, "ve_token");
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->UseCount = 1;                       // 表示视频编码配置的使用次数
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Encoding = tt__VideoEncoding__H264; // 视频编码格式
    // <VideoEncoderConfiguration><Resolution>
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Resolution = (struct tt__VideoResolution *)soap_malloc(soap, sizeof(struct tt__VideoResolution));
    memset(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Resolution, 0, sizeof(struct tt__VideoResolution));
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Resolution->Width = VIDEO_WIDTH;
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Resolution->Height = VIDEO_HEIGHT;
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->Quality = 10; //  视频质量
    // <VideoEncoderConfiguration><RateControl>
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->RateControl = (struct tt__VideoRateControl *)soap_malloc(soap, sizeof(struct tt__VideoRateControl));
    memset(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->RateControl, 0, sizeof(struct tt__VideoRateControl));
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->RateControl->FrameRateLimit = FRAME_RATE; // 帧率
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->RateControl->EncodingInterval = 1;        // 编码间隔
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->RateControl->BitrateLimit = 3000;         // 比特率限制
    // <VideoEncoderConfiguration><H264>
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->H264 = (struct tt__H264Configuration *)soap_malloc(soap, sizeof(struct tt__H264Configuration));
    memset(trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->H264, 0, sizeof(struct tt__H264Configuration));
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->H264->GovLength = 120;                     // GOP长度
    trt__GetProfilesResponse->Profiles[i].VideoEncoderConfiguration->H264->H264Profile = tt__H264Profile__High; // H.264 Profile

    return SOAP_OK;
}

SOAP_FMAC5 int SOAP_FMAC6 __trt__GetVideoSourceConfiguration(struct soap *soap, struct _trt__GetVideoSourceConfiguration *trt__GetVideoSourceConfiguration, struct _trt__GetVideoSourceConfigurationResponse *trt__GetVideoSourceConfigurationResponse)
{
    printf("-------- %s --------\n", __func__);

    trt__GetVideoSourceConfigurationResponse->Configuration = (struct tt__VideoSourceConfiguration *)soap_malloc(soap, sizeof(struct tt__VideoSourceConfiguration));
    memset(trt__GetVideoSourceConfigurationResponse->Configuration, 0, sizeof(struct tt__VideoSourceConfiguration));

    trt__GetVideoSourceConfigurationResponse->Configuration->UseCount = 1;
    trt__GetVideoSourceConfigurationResponse->Configuration->Name = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetVideoSourceConfigurationResponse->Configuration->Name, 0, sizeof(char) * 32);
    trt__GetVideoSourceConfigurationResponse->Configuration->token = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetVideoSourceConfigurationResponse->Configuration->token, 0, sizeof(char) * 32);
    strcpy(trt__GetVideoSourceConfigurationResponse->Configuration->Name, "vs_name");
    strcpy(trt__GetVideoSourceConfigurationResponse->Configuration->token, "vs_token");

    trt__GetVideoSourceConfigurationResponse->Configuration->SourceToken = (char *)soap_malloc(soap, sizeof(char) * 32);
    memset(trt__GetVideoSourceConfigurationResponse->Configuration->SourceToken, 0, sizeof(char) * 32);
    strcpy(trt__GetVideoSourceConfigurationResponse->Configuration->SourceToken, "vs_SourceToken");

    trt__GetVideoSourceConfigurationResponse->Configuration->Bounds = (struct tt__IntRectangle *)soap_malloc(soap, sizeof(struct tt__IntRectangle));
    memset(trt__GetVideoSourceConfigurationResponse->Configuration->Bounds, 0, sizeof(struct tt__IntRectangle));
    trt__GetVideoSourceConfigurationResponse->Configuration->Bounds->x = 0;
    trt__GetVideoSourceConfigurationResponse->Configuration->Bounds->y = 0;
    trt__GetVideoSourceConfigurationResponse->Configuration->Bounds->width = VIDEO_WIDTH;
    trt__GetVideoSourceConfigurationResponse->Configuration->Bounds->height = VIDEO_HEIGHT;

    return SOAP_OK;
}

SOAP_FMAC5 int SOAP_FMAC6 __trt__GetStreamUri(struct soap *soap, struct _trt__GetStreamUri *trt__GetStreamUri, struct _trt__GetStreamUriResponse *trt__GetStreamUriResponse)
{
    printf("-------- %s --------\n", __func__);

    trt__GetStreamUriResponse->MediaUri = (struct tt__MediaUri *)soap_malloc(soap, sizeof(struct tt__MediaUri));
    memset(trt__GetStreamUriResponse->MediaUri, 0, sizeof(struct tt__MediaUri));

    trt__GetStreamUriResponse->MediaUri->Uri = (char *)soap_malloc(soap, sizeof(char) * 256);
    memset(trt__GetStreamUriResponse->MediaUri->Uri, 0, sizeof(char) * 256);
    sprintf(trt__GetStreamUriResponse->MediaUri->Uri, RTSP_URL);
    trt__GetStreamUriResponse->MediaUri->InvalidAfterConnect = xsd__boolean__true_;
    trt__GetStreamUriResponse->MediaUri->InvalidAfterReboot = xsd__boolean__true_;
    // 超时时间
    trt__GetStreamUriResponse->MediaUri->Timeout = 200;

    return 0;
}

参考了这个项目中的源码:onvif-server-with-rtsp

我的另一篇博客:Ubuntu 编译gSOAP库,并生成ONVIF代码框架_gsoap 生成-CSDN博客

相关资料:一个比较完善的Onvif服务端 ,不过源码要钱:happytimesoft

相关推荐
野蛮的大西瓜5 小时前
BigBlueButton目前支持哪些操作系统和浏览器
人工智能·机器人·自动化·音视频·信息与通信
特立独行的猫a5 小时前
HarmonyOS ArkTS中视频播放Video组件实现竖屏到横屏切换
华为·音视频·harmonyos
Lunar*7 小时前
使用 NVIDIA DALI 计算视频的光流
python·音视频
orangapple9 小时前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
haibo214413 小时前
Ruyi-Mini-7B:开源的图像生成视频模型
音视频
ts小陈18 小时前
多音轨视频使用FFmpeg删除不要音轨方法
ffmpeg·音视频
engchina19 小时前
音频数据采样入门详解 - 给Python初学者的简单解释
python·音视频
benben04421 小时前
Audiocraft智能音频和音乐生成工具部署及使用
音视频
haha_qasim1 天前
音频声音太小怎么调大?调大音频声音的几种方法
人工智能·音视频·语音识别