Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Extending data structures in XDM

From Texas Instruments Wiki
Jump to: navigation, search

Overview

In XDM, structures such as codec creation's *_Params, process()'s *_InArgs and *_OutArgs, and control()'s *_DynamicParams and *_Status are all extensible, meaning users can redefine the structure to include additional fields beyond the base ones. To support this extensibility, XDM structures contain size as its first field. This field is used:

  • By the framework to determine how big the structure is.
  • By the codec to determine how to interpret the fields.

The advantage of support for extensions is:

  • Flexibility. This allows users to reuse existing interfaces while augmenting their data structures with extra fields that they need beyond what is initially supported.

The disadvantages are:

  • Extra memory needed to support size field.
  • An application written to use an algorithm that implements the extended interface/data structures might not necessarily be able to use an algorithm that supports strictly the base interface, i.e. algorithms of the same base class are not necessarily interchangeable without modification of the application code.

Example

Given a base parameter structure:

typedef struct IVIDDEC_DynamicParams {
    XDAS_Int32 size;
    XDAS_Int32 decodeHeader;
    XDAS_Int32 displayWidth;
    XDAS_Int32 frameSkipMode;
} IVIDDEC_DynamicParams;

We can extend it with new fields as follows:

typedef struct MYIVIDDEC_DynamicParams {
    IVIDDEC_DynamicParams base;
    XDAS_Int32 newField1;
    XDAS_Int32 newField2;
} MYIVIDDEC_DynamicParams;

When using this new structure, we need to ensure that base.size field is set to the size of the extended structure to ensure correct operation in frameworks (e.g. Codec Engine):

    MYIVIDDEC_DynamicParams dParams;
 
    dParams.base.size = sizeof(MYIVIDDEC_DynamicParams);   // Must be set to size of extended structure
    dParams.base.displayWidth = 1024;
    dParams.newField1 = 13;
    ...
 
    /* If using Codec Engine, API call might look like this.  Note the cast */
    VIDDEC_control(handle, id, (VIDDEC_DynamicParams *)&dParams, &status);

Constraints

  • When using the Codec Engine, because it's RPC based, you have the following constraints when utilizing remote codecs (when using CE with local codecs, these constraints don't apply):
    • No pointers in the extended fields. Because of address translation (GPP-side addresses may not match the DSP-side address) and cache maintenance (DSP is cached, which needs maintained for coherence), pointers to data are non-trivial to manage. The default VISA RPC stubs/skeletons will manage pointers defined in the base class, but it's impossible to know about pointers in proprietary extensions.
      • If you must pass pointers across cores, consider using the inBufs and outBufs arguments already present in most of the process() calls, or the *_Status.data field for control() calls. Note that this technique applies to pointers to data buffers - pointers to functions on different devices are not supported.
      • Another alternative if the pointers are to 'small' structs containing scalar fields, you may be able to unroll these scalar elements of the pointed-to-struct into extended scalar fields and avoid the pointer difficulties altogether.
    • Field alignment. Both the base data structure and the extended structure have to be 32-bit aligned for remote algorithm execution support. If not, extra padding should be added to the structure. E.g.
typedef struct MYIVIDDEC_DynamicParams {
    IVIDDEC_DynamicParams base;
    XDAS_Int32 newField1;
    XDAS_Int16 newField2;
    XDAS_Int16 padding;  // Needed for 32-bit alignment
} MYIVIDDEC_DynamicParams;
    • Max size. Fixed size messages are used to marshal arguments for, and unmarshal replies from, remote processors. These messages contain 1) internal headers (< 128 bytes), input structs (e.g. "InArgs" or "DynamicParams") and output structs (e.g. "OutArgs" or "Status"). The sum of these contents must fit in a message, so the message size imposes a max size constraint on extended args.
      • In CE releases before 2.25, there was an artificial limit imposed; regardless of how big the actual message is, no more than 512 bytes were used.
      • In current CE releases (2.25 and later), the entire size of the message is available to marshal/unmarshal arguments. You can configure the size of a message in the app's config script:
/*
 * Increase the size of messages from the default 4kB to 8kB.  To keep the same memory
 * map, we also halve the number of messages from the default 64 to 32.
 */
var osalGlobal = xdc.useModule('ti.sdo.ce.osal.Global');
osalGlobal.armCommMsgSize = 8 * 1024;  // use 8 kB msgs rather than the default 4kB
osalGlobal.armCommNumMsgs = 32;   // create 32 msgs rather than the default 64

Naming Conventions

Because anyone can extend these XDM structs, it's important to attempt to define some conventions on the names of the extended fields. This enables integrators using many different codecs to not have to wonder whether the extended field is .MyField or .myfield or
.MYFIELD
. This section describes guidelines for naming extended structs.
Naming Conventions
Convention Examples
Field names begin with a lowercase letter. Camel case is used where needed to delimit multiword names. myExtendedField
Fields that indicate boolean flags end with the word "Flag" and are documented to accept XDAS_TRUE or XDAS_FALSE. enableMyFeatureFlag

Distribution

Because these extended structs are codec-specific, they should be provided with the codec, and included in the codec's documentation. Typically, this involves simply adding a header with these definitions in the codec's package.

Notes

  • These structs are independent of the framework (e.g. Codec Engine), and therefore belong in the base codec release, not in any framework-specific package.
  • If extended parameters are used, codecs will try to interpret all of the extended settings. If you use extended parameters, make sure you initialize all members of the extended parameter structure -- not just the ones you are interested in. Codec documentation should provide reasonable defaults for all extended parameter settings.

The perils of extending inArgs, outArgs

As noted above there are (at least) two ways to extend XDM / VISA interfaces. Extending via dynamic params is fine - for example on DM6467 we do the following: - (code snippet at time of DM6467 Beta)

/* Use extended dynamic parameters to allow tweaking of the QP value */
    IH264VENC_DynamicParams extDynParams = {
        Venc1_DynamicParams_DEFAULT,
        0,    /* ChromaQPOffset */
        18,   /* QPISlice */
        20,   /* QPSlice */
        0,    /* LfDisableIdc */
        0,    /* LFAlphaC0Offset */
        0,    /* LFBetaOffset */
        0,    /* LFParametersFlag */
        51,   /* RateCtrlQpMax */
        0,    /* RateCtrlQpMin */
        0,
    };
 
....
....
 
    extDynParams.videncDynamicParams.size = sizeof(IH264VENC_DynamicParams);
    extDynParams.videncDynamicParams.targetBitRate = params.maxBitRate;
    extDynParams.videncDynamicParams.inputWidth = params.maxWidth;
    extDynParams.videncDynamicParams.inputHeight = params.maxHeight;
 
    /* Create the video encoder */
    hVe1 = Venc1_create(hEngine, "h264enc", &params,
                        (VIDENC1_DynamicParams *) &extDynParams);

Observe that we're calling a generic API, Venc1_create() - there's nothing specific to H264 encode. The only thing we're doing is extending the data fields, in this case mainly to tweak the Quality Parameters (QPISlice, QPSlice). No logic in the code changes.

Now look at what we have to do (again as of DM6467 Beta) for AAC High End Encode.

Int Aac_process(Aenc_Handle hAe, Buffer_Handle hInBuf, Buffer_Handle hOutBuf)
{
    XDAS_Int32              inBufSizeArray[1];
    XDAS_Int32              outBufSizeArray[1];
    XDM_BufDesc             inBufDesc;
    XDM_BufDesc             outBufDesc;
    XDAS_Int32              status;
    IAACENC_InArgs          inArgs;
    IAACENC_OutArgs         outArgs;
    XDAS_Int8              *inPtr;
    XDAS_Int8              *outPtr;
 
    inPtr                   = Buffer_getUserPtr(hInBuf);
    outPtr                  = Buffer_getUserPtr(hOutBuf);
 
    inBufSizeArray[0]       = Buffer_getNumBytesUsed(hInBuf);
    outBufSizeArray[0]      = Buffer_getSize(hOutBuf);
 
    inBufDesc.bufSizes      = inBufSizeArray;
    inBufDesc.bufs          = &inPtr;
    inBufDesc.numBufs       = 1;
 
    outBufDesc.bufSizes     = outBufSizeArray;
    outBufDesc.bufs         = &outPtr;
    outBufDesc.numBufs      = 1;
 
    outArgs.audenc_outArgs.size = sizeof(IAACENC_OutArgs);
 
    /* Special AAC inArgs needed, hence custom Aac_process() required */
    inArgs.audenc_inArgs.size   = sizeof(IAACENC_InArgs);
    inArgs.numAncBytes          = 0;
    inArgs.ancData              = NULL;
 
    /* Stereo 16-bit */
    inArgs.numInSamples         = Buffer_getNumBytesUsed(hInBuf) / 4;
 
    /* Encode the audio buffer */
    status = AUDENC_process(Aenc_getVisaHandle(hAe), &inBufDesc, &outBufDesc,
                            (AUDENC_InArgs *) &inArgs,
                            (AUDENC_OutArgs *) &outArgs);

That's completely different from the extended parameters scenario - we now have Aac_process() instead of just AUDENC_process() (or Aenc_process() to be analogous with naming convention in previous h264 scenario). This is because this particular codec implementation requires inArgs.numInSamples to be buffer size / 4 (the 4 is number of channels * number of bytes per sample). That difference is not comprehended in the AUDENC XDM 0.9 interface.

What would happen if we now added another Audio Encoder that customized inArgs/outArgs? We'd inevitably have to load up the code with ugly #ifdef AACENC #else guards. Worse still there's no plug and play - we couldn't swap out one vendor's AACENC for another since the extensions are, by definition, custom.

All applications (not just the DVSDK demos) suffer - TI's Digital Video Test Bench (DVTB) copies all of the header constants and structures from the AAC Encoder (as of DM6467 Beta) into its application file dvevmStAACEnc.h, in order to 'see' the extensions - this results in increased maintenance.

The good news in the above case is that it may be possible in future to use 'normal VISA' because the number of channels is passed in through AUDENC_DynamicParams.numChannels, and the number of bytes per sample can be derived from AUDENC_DynamicParams.inputBitsPerSample. This is being considered.

However, the custom code logic above clearly shows that inArgs/outArgs extensions equals custom application code which defeats the original intent of the VISA APIs. You simply cannot write generic application code in such cases.

If you believe you need such extensions we strongly encourage you to look at the newer XDM interfaces - perhaps AUDENC1 or VIDDEC2 solves the problem. This XDM FAQ article shows some XDM version details.

If after that, you still believe you need to extend inArgs/outArgs, please make sure you supply test cases that clearly exercise all of the extended modes - this is essential because test suites such as the DVTB will not be able to find problems in this area. Make sure such modes also get tested from an Arm-side application on an SoC platform so that any corner cases with cache-management (see above re address translation etc) are caught.

In summary, extending interfaces via DynamicParams is fine, however doing so via inArgs/outArgs requires custom application logic and is strongly discouraged.