HomeKitADK icon indicating copy to clipboard operation
HomeKitADK copied to clipboard

Creating nested TLV8 structures

Open nanosonde opened this issue 3 years ago • 2 comments

Is there any example on how to easily create nested TLV8 structures with the given API?

Maybe similiar to this implementation? https://github.com/maximkulkin/esp32-homekit-camera/blob/261363ce0a9e0a561a1804213294a24737881480/main/accessory.c#L695-L711

nanosonde avatar Dec 16 '20 11:12 nanosonde

This is an incomplete example which shows how I could do it. Is this correct? How could I improve the .valueOffset calculation?

UPDATE: Got it working. I was not aware of the C builtin command offsetof() after all those years. 😄 This makes the offset calculation a lot better.

bool isValid(void *unsused HAP_UNUSED)
{
    return true;
}

typedef struct
{
    struct
    {
        uint8_t videoCodecType;
        struct
        {
            uint8_t profileID;
            uint8_t level;
            uint8_t packetizationMode;
        } videoCodecParams;
    } videoConfigCodec;
} supportedVideoConfigStruct;

supportedVideoConfigStruct supportedVideoConfigValue =
{
    {
        .videoCodecType = 0,
        {
            .profileID = 0,
            .level = 0,
            .packetizationMode = 0
        }
    }
};

HAP_STRUCT_TLV_SUPPORT(void, supportedVideoConfigFormat)
HAP_STRUCT_TLV_SUPPORT(void, videoCodecConfigFormat)
HAP_STRUCT_TLV_SUPPORT(void, videoCodecParamsFormat)
HAP_STRUCT_TLV_SUPPORT(void, videoAttributesFormat)

const HAPUInt8TLVFormat videoCodecParamsProfileID = {
    .type = kHAPTLVFormatType_UInt8,
    .constraints = { .minimumValue = 0, .maximumValue = 2},
    .callbacks = { .getDescription = NULL }
};
const HAPStructTLVMember videoCodecParamsProfileIDMember = {
    .valueOffset = offsetof(supportedVideoConfigStruct, videoConfigCodec.videoCodecParams.profileID),
    .isSetOffset = 0,
    .tlvType = 1,
    .debugDescription = "Video Config Config Params Profile ID",
    .format = &videoCodecParamsProfileID,
    .isOptional = false,
    .isFlat = false
};

const HAPUInt8TLVFormat videoCodecParamsLevel = {
    .type = kHAPTLVFormatType_UInt8,
    .constraints = { .minimumValue = 0, .maximumValue = 2},
    .callbacks = { .getDescription = NULL }
};
const HAPStructTLVMember videoCodecParamsLevelMember = {
    .valueOffset = offsetof(supportedVideoConfigStruct, videoConfigCodec.videoCodecParams.level),
    .isSetOffset = 0,
    .tlvType = 2,
    .debugDescription = "Video Config Config Params Level",
    .format = &videoCodecParamsLevel,
    .isOptional = false,
    .isFlat = false
};

const HAPUInt8TLVFormat videoCodecParamsPacketizationMode = {
    .type = kHAPTLVFormatType_UInt8,
    .constraints = { .minimumValue = 0, .maximumValue = 2},
    .callbacks = { .getDescription = NULL }
};
const HAPStructTLVMember videoCodecParamsPacketizationModeMember = {
    .valueOffset = offsetof(supportedVideoConfigStruct, videoConfigCodec.videoCodecParams.packetizationMode),
    .isSetOffset = 0,
    .tlvType = 3,
    .debugDescription = "Video Config Config Packetization Mode",
    .format = &videoCodecParamsPacketizationMode,
    .isOptional = false,
    .isFlat = false
};

/* ---------------------------------------------------------------------------------------------*/

const HAPUInt8TLVFormat videoCodecType = {
    .type = kHAPTLVFormatType_UInt8,
    .constraints = { .minimumValue = 0, .maximumValue = 1},
    .callbacks = { .getDescription = NULL }
};
const HAPStructTLVMember videoCodecTypeMember = {
    .valueOffset = offsetof(supportedVideoConfigStruct, videoConfigCodec.videoCodecType),
    .isSetOffset = 0,
    .tlvType = 1,
    .debugDescription = "Video Codec Type",
    .format = &videoCodecType,
    .isOptional = false,
    .isFlat = false
};


const videoCodecConfigFormat videoCodecParams = {
    .type = kHAPTLVFormatType_Struct,
    .members = (const HAPStructTLVMember* const[]) { &videoCodecParamsProfileIDMember,
                                                     &videoCodecParamsLevelMember,
                                                     &videoCodecParamsPacketizationModeMember,
                                                     NULL},
    .callbacks = { .isValid = isValid }
};
const HAPStructTLVMember videoCodecParamsMember = {
    .valueOffset = offsetof(supportedVideoConfigStruct, videoConfigCodec.videoCodecParams),
    .isSetOffset = 0,
    .tlvType = 2,
    .debugDescription = "Video Codec Parameters",
    .format = &videoCodecParams,
    .isOptional = false,
    .isFlat = false
};

const videoCodecConfigFormat videoCodecConfig = {
    .type = kHAPTLVFormatType_Struct,
    .members = (const HAPStructTLVMember* const[]) { &videoCodecTypeMember,
                                                     &videoCodecParamsMember,
                                                     NULL},
    .callbacks = { .isValid = isValid }
};
const HAPStructTLVMember videoCodecConfigMember = {
    .valueOffset = offsetof(supportedVideoConfigStruct, videoConfigCodec),
    .isSetOffset = 0,
    .tlvType = 1,
    .debugDescription = "Video Config Config",
    .format = &videoCodecConfig,
    .isOptional = false,
    .isFlat = false
};

const supportedVideoConfigFormat supported_video_config = {
    .type = kHAPTLVFormatType_Struct,
    .members = (const HAPStructTLVMember* const[]) { &videoCodecConfigMember, NULL},
    .callbacks = { .isValid = isValid }
};

/**
 * Handle read request to the 'SupportedVideoStreamConfiguration' characteristic of the Camera RTP Stream Management service.
 */
HAP_RESULT_USE_CHECK
HAPError HandleSupportedVideoStreamConfigurationRead(
        HAPAccessoryServerRef* server HAP_UNUSED,
        const HAPTLV8CharacteristicReadRequest* request HAP_UNUSED,
        HAPTLVWriterRef* responseWriter,
        void* _Nullable context HAP_UNUSED) {
    HAPPrecondition(responseWriter);

    HAPLogInfo(&kHAPLog_Default, "%s", __func__);

    HAPError err;

    err = HAPTLVWriterEncode(responseWriter, &supported_video_config, &supportedVideoConfigValue);
    if (err) {
        HAPAssert(err == kHAPError_OutOfResources);
        return err;
    }

    return kHAPError_None;
}

Of course there are more things missing. The attributes and so on. But at least it shows how to create nested TLV8 structures from C structs.

nanosonde avatar Dec 16 '20 17:12 nanosonde

Have you expanded on this any more to include an array of structs possibly? I'm working through something similar but trying to support multiple ".videoAttributes".

joebelford avatar Jan 11 '21 14:01 joebelford