apple / HomeKitADK

Apache License 2.0
2.55k stars 232 forks source link

Creating nested TLV8 structures #79

Open nanosonde opened 3 years ago

nanosonde commented 3 years ago

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 commented 3 years ago

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.

joebelford commented 3 years ago

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".