ONVIF協議網絡攝像機(IPC)客戶端程序開發(14):修改分辨率

版權聲明:本文爲博主原創文章,如要轉載請標明出處。 https://blog.csdn.net/benkaoya/article/details/72490332

1 專欄導讀

本專欄第一篇文章「專欄開篇」列出了專欄的完整目錄,按目錄順序閱讀,有助於你的理解,專欄前面文章講過的知識點(或代碼段),後面文章不會贅述。爲了節省篇幅,突出重點,在文章中展示的示例代碼僅僅是關鍵代碼,你可以在「專欄開篇」中獲取完整代碼。

如有錯誤,歡迎你的留言糾正!讓我們共同成長!你的「點贊」「打賞」是對我最大的支持和鼓勵!

2 原理簡介

IPC有關多媒體的參數,都是由媒體配置文件(media profile)來管理。想通過ONVIF協議修改IPC諸如分辨率這樣的媒體參數,就得先弄懂媒體配置文件。

媒體配置文件(media profile)是用於管理音視頻流相關的一系列配置的集合,一個配置文件由一系列相關聯的配置類實體構成。配置類包括:

  • Video source configuration(視頻源配置)
  • Audio source configuration(音頻源配置)
  • Video encoder configuration(視頻編碼器配置)
  • Audio encoder configuration(視頻編碼器配置)
  • PTZ configuration(PTZ配置)
  • Video analytics configuration(視頻分析配置)
  • Metadata configuration(元數據配置)
  • Audio output configuration(音頻輸出配置)
  • Audio decoder configuration(音頻解碼器配置)

對應的結構體爲:

struct tt__Profile {
        char *Name;
        struct tt__VideoSourceConfiguration *VideoSourceConfiguration;
        struct tt__AudioSourceConfiguration *AudioSourceConfiguration;
        struct tt__VideoEncoderConfiguration *VideoEncoderConfiguration;
        struct tt__AudioEncoderConfiguration *AudioEncoderConfiguration;
        struct tt__VideoAnalyticsConfiguration *VideoAnalyticsConfiguration;
        struct tt__PTZConfiguration *PTZConfiguration;
        struct tt__MetadataConfiguration *MetadataConfiguration;
        struct tt__ProfileExtension *Extension;
        char *token;
        enum xsd__boolean *fixed;
};

一個tt__Profile結構體就是一個媒體配置文件,一個配置文件由全部的或部分的配置類實體組成,“部分”的意思是,對於不支持的功能(如PTZ),允許其配置信息爲空(PTZConfiguration爲NULL)。

這裏有一個概念一定要理清楚,配置類和實體的區別:

  1. 就某個配置類而言,一個設備可以有多個實體。如「視頻編碼器配置」類,一個IPC設備至少包含兩個「視頻編碼器配置」實體,分別關聯主碼流和輔碼流,這兩個「視頻編碼器配置」實體參數有所區別,如分辨率不同,碼率不同等等。
  2. 一個媒體配置文件由一不同配置類的實體組成,同一配置類的不同實體,只能有一個實體跟媒體配置文件關聯。
  3. 一個設備可以有多個媒體配置文件,如主碼流、輔碼流就是兩個不同的媒體配置文件。

爲了唯一標識某個配置實體,每個配置實體都有對應的唯一標識符token,很多的ONVIF媒體接口也是通過這些token來訪問(修改)這些配置的。比如,視頻源配置token、音頻源配置token、視頻編碼器配置token,甚至連媒體配置文件本身都有token。如下圖紅色矩形框所示(GetProfiles接口):


圖1

3 函數接口

對於某個具體的配置類,ONVIF都提供了一套完整的函數接口,類似如下:

  • AddXXXConfiguration:新增XXX配置
  • RemoveXXXConfiguration:刪除指定的XXX配置
  • GetXXXConfigurations:獲取所有的XXX配置
  • GetXXXConfiguration:獲取指定的XXX配置
  • GetXXXConfigurationOptions:獲取XXX配置選項集
  • GetCompatibleXXXConfigurations:獲取所有兼容的XXX配置
  • SetXXXConfiguration:修改指定的XXX配置

將XXX替換成VideoEncoder,就得到了「視頻編碼器配置」類相關的ONVIF接口,其他配置類也如此:

  • AddVideoEncoderConfiguration:新增視頻編碼器配置
  • RemoveVideoEncoderConfiguration:刪除指定的視頻編碼器配置
  • GetVideoEncoderConfigurations:獲取所有的視頻編碼器配置
  • GetVideoEncoderConfiguration:獲取指定的視頻編碼器配置
  • GetVideoEncoderConfigurationOptions:獲取視頻編碼器配置選項集
  • GetCompatibleVideoEncoderConfigurations:獲取所有兼容的視頻編碼器配置
  • SetVideoEncoderConfiguration:修改指定的視頻編碼器配置

「媒體配置文件」與「視頻編碼器配置」的關係:

  ONVIF對應的函數接口 備註
一個設備可以有多個「媒體配置文件」 GetProfile GetProfiles 比如主碼流、輔碼流,是兩個相互獨立的媒體配置文件。每個媒體配置文件的token不同。
一個設備可以有多個「視頻編碼器配置」實體 GetVideoEncoderConfiguration GetVideoEncoderConfigurations  
一個「媒體配置文件」只能關聯一個「視頻編碼器配置」實體 AddVideoEncoderConfiguration  
每個「視頻編碼器配置」實體獨享屬於自己的一套參數選項集 GetVideoEncoderConfigurationOptions 比如IPC的主碼流,視頻分辨率只能設置爲預先設定好的幾個分辨率(選項集)中的一種,不能隨意配置。可選擇的分辨率是IPC出廠時就與設定好的,沒法通過ONVIF接口增/刪/改。 “獨享”的意思,就是視頻編碼器配置1的分辨率,只能在視頻編碼器配置1中使用,不能在視頻編碼器配置2中使用。比如主碼流的1080P分辨率,不能配置到輔碼流中。

4 編碼流程

IPC客戶端通過ONVIF修改分辨率的步驟如下:

  1. 先通過GetProfiles獲取所有媒體配置文件,可得知主/輔碼流的視頻編碼器配置token;
  2. 由視頻編碼器配置token通過SetVideoEncoderConfiguration修改視頻編碼器配置(如修改分辨率、幀率、碼率等);
  3. 修改的參數必須在GetVideoEncoderConfigurationOptions中的選項集範圍內(即IPC出廠時預設定好的那幾個選項集),如分辨率只能設置爲那幾種中的一種,不能隨意設置。否則SetVideoEncoderConfiguration會返回失敗。
  4. 每個視頻編碼器配置的分辨率可選集,只能自個使用,不能用於其他視頻編碼器配置中。比如主碼流的1080P分辨率,不能配置到輔碼流中,SetVideoEncoderConfiguration會調用失敗。

5 示例代碼

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "onvif_comm.h"
#include "onvif_dump.h"

/************************************************************************
**函數:ONVIF_GetVideoEncoderConfigurationOptions
**功能:獲取指定視頻編碼器配置的參數選項集
**參數:
        [in] MediaXAddr   - 媒體服務地址
        [in] ConfigurationToken - 視頻編碼器配置的令牌字符串,如果爲NULL,則獲取所有視頻編碼器配置的選項集(會雜在一起,無法區分選項集是歸屬哪個視頻編碼配置器的)
**返回:
        0表明成功,非0表明失敗
**備註:
    1). 有兩種方式可以獲取指定視頻編碼器配置的參數選項集:一種是根據ConfigurationToken,另一種是根據ProfileToken
************************************************************************/
int ONVIF_GetVideoEncoderConfigurationOptions(const char *MediaXAddr, char *ConfigurationToken)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _trt__GetVideoEncoderConfigurationOptions          req;
    struct _trt__GetVideoEncoderConfigurationOptionsResponse  rep;

    SOAP_ASSERT(NULL != MediaXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    req.ConfigurationToken = ConfigurationToken;

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___trt__GetVideoEncoderConfigurationOptions(soap, MediaXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetVideoEncoderConfigurationOptions");

    dump_trt__GetVideoEncoderConfigurationOptionsResponse(&rep);

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

/************************************************************************
**函數:ONVIF_GetVideoEncoderConfiguration
**功能:獲取設備上指定的視頻編碼器配置信息
**參數:
        [in] MediaXAddr - 媒體服務地址
        [in] ConfigurationToken - 視頻編碼器配置的令牌字符串
**返回:
        0表明成功,非0表明失敗
**備註:
************************************************************************/
int ONVIF_GetVideoEncoderConfiguration(const char *MediaXAddr, char *ConfigurationToken)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _trt__GetVideoEncoderConfiguration          req;
    struct _trt__GetVideoEncoderConfigurationResponse  rep;

    SOAP_ASSERT(NULL != MediaXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    req.ConfigurationToken = ConfigurationToken;
    result = soap_call___trt__GetVideoEncoderConfiguration(soap, MediaXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetVideoEncoderConfiguration");

    dump_trt__GetVideoEncoderConfigurationResponse(&rep);

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

/************************************************************************
**函數:ONVIF_SetVideoEncoderConfiguration
**功能:修改指定的視頻編碼器配置信息
**參數:
        [in] MediaXAddr - 媒體服務地址
        [in] venc - 視頻編碼器配置信息
**返回:
        0表明成功,非0表明失敗
**備註:
    1). 所設置的分辨率必須是GetVideoEncoderConfigurationOptions返回的“分辨率選項集”中的一種,否則調用SetVideoEncoderConfiguration會失敗。
************************************************************************/
int ONVIF_SetVideoEncoderConfiguration(const char *MediaXAddr, struct tagVideoEncoderConfiguration *venc)
{
    int result = 0;
    struct soap *soap = NULL;

    struct _trt__GetVideoEncoderConfiguration           gVECfg_req;
    struct _trt__GetVideoEncoderConfigurationResponse   gVECfg_rep;

    struct _trt__SetVideoEncoderConfiguration           sVECfg_req;
    struct _trt__SetVideoEncoderConfigurationResponse   sVECfg_rep;

    SOAP_ASSERT(NULL != MediaXAddr);
    SOAP_ASSERT(NULL != venc);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    memset(&gVECfg_req, 0x00, sizeof(gVECfg_req));
    memset(&gVECfg_rep, 0x00, sizeof(gVECfg_rep));
    gVECfg_req.ConfigurationToken = venc->token;

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___trt__GetVideoEncoderConfiguration(soap, MediaXAddr, NULL, &gVECfg_req, &gVECfg_rep);
    SOAP_CHECK_ERROR(result, soap, "GetVideoEncoderConfiguration");

    if (NULL == gVECfg_rep.Configuration) {
        SOAP_DBGERR("video encoder configuration is NULL.\n");
        goto EXIT;
    }

    memset(&sVECfg_req, 0x00, sizeof(sVECfg_req));
    memset(&sVECfg_rep, 0x00, sizeof(sVECfg_rep));

    sVECfg_req.ForcePersistence = xsd__boolean__true_;
    sVECfg_req.Configuration    = gVECfg_rep.Configuration;

    if (NULL != sVECfg_req.Configuration->Resolution) {
        sVECfg_req.Configuration->Resolution->Width  = venc->Width;
        sVECfg_req.Configuration->Resolution->Height = venc->Height;
    }

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___trt__SetVideoEncoderConfiguration(soap, MediaXAddr, NULL, &sVECfg_req, &sVECfg_rep);
    SOAP_CHECK_ERROR(result, soap, "SetVideoEncoderConfiguration");

EXIT:

    if (SOAP_OK == result) {
        SOAP_DBGLOG("\nSetVideoEncoderConfiguration success!!!\n");
    }

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

void cb_discovery(char *DeviceXAddr)
{
    int stmno = 0;                                                              // 碼流序號,0爲主碼流,1爲輔碼流
    int profile_cnt = 0;                                                        // 設備配置文件個數
    struct tagProfile *profiles = NULL;                                         // 設備配置文件列表
    struct tagCapabilities capa;                                                // 設備能力信息

    ONVIF_GetCapabilities(DeviceXAddr, &capa);                                  // 獲取設備能力信息(獲取媒體服務地址)

    profile_cnt = ONVIF_GetProfiles(capa.MediaXAddr, &profiles);                // 獲取媒體配置信息(主/輔碼流配置信息)

    if (profile_cnt > stmno) {
        struct tagVideoEncoderConfiguration venc;
        char *vencToken = profiles[stmno].venc.token;

        ONVIF_GetVideoEncoderConfigurationOptions(capa.MediaXAddr, vencToken);  // 獲取該碼流支持的視頻編碼器參數選項集

        ONVIF_GetVideoEncoderConfiguration(capa.MediaXAddr, vencToken);         // 獲取該碼流當前的視頻編碼器參數

        venc = profiles[stmno].venc;
        venc.Height = 960;
        venc.Width  = 1280;
        ONVIF_SetVideoEncoderConfiguration(capa.MediaXAddr, &venc);             // 設置該碼流當前的視頻編碼器參數

        ONVIF_GetVideoEncoderConfiguration(capa.MediaXAddr, vencToken);         // 觀察是否修改成功
    }

    if (NULL != profiles) {
        free(profiles);
        profiles = NULL;
    }
}

int main(int argc, char **argv)
{
    ONVIF_DetectDevice(cb_discovery);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章