2 Using the MME API. STMicroelectronics Multicom 4
Annonce
Annonce
UM1112
2 Using the MME API
Using the MME API
2.1 Overview
The MME API provides a means for an application program running on the host processor to control and manipulate a codec or similar media transformer running either on the same processor or on a different companion processor. The aim of a companion processor is to assist the host in transforming data in real time and it communicates with the host using the
ICS communication interface, see
Chapter 5: Overview of the inter-core system (ICS) on page 110
. Both host and companion transformers may, optionally, make use of hardware accelerators to off-load some or all of the work. The MME API remains the same independent of the location or type of the transformer, effectively hiding the (potentially complex) structure of the system from the application. The MME API is intended to form part of the driver layer of typical multimedia software stacks. See
2.1.1 Transformers and transformer instances
Within the MME system the division of roles between the host and companion processors is logically described by what are known as transformers. Transformers are named software modules that normally reside in the companion processors. A companion CPU will register a transformer for each codec or media transformation it can perform (see
Registering transformers on page 21
). The host will then use the symbolic name of each of the transformers it requires, to lookup and instantiate them, see
Figure 2. Transformer instances - single host
Host (ST40)
Video_decode
Companion (ST231)
Transformer_name_1
Audio_decode
Transformer_name_2
Companion (ST231)
Transformer_name_3
Transformer_name_4
8182595 Rev 5 15/224
33
Using the MME API UM1112
When a transformer is instantiated, the abstract transformer is combined with parametric and state information; it is then capable of processing data. This is called a transformer
instance.
Typically, transformers that rely on hardware accelerators can only have one instance at a single point in time due to there only being one accelerator. However, for software transformers, it is unusual for anything other than available memory to limit the number of instances of a particular transformer.
This version of Multicom also supports a usage model known as Multi-hosting.
Traditionally in an MME system there would have been one host CPU (for example, an
ST40) and multiple companion CPUs (for example, ST231s), however newer SoCs now incorporate multiple ST40 CPUs. Multi-hosting means that any CPU in the MME system can act as a host and hence instantiate and issue commands to transformers. So in practise any
CPU in an MME system can act either as a host or a companion or both simultaneously. The only exception to this is that a program in Linux user space cannot register transformers.
Multi-hosting can be used to build complex systems where decode and compute are offloaded to one or more of the companions CPUs. Here are a couple of example usage scenarios.
On a multi ST40 SoC such as the STi7141 both the ST40 CPUs could act as hosts and issue commands to any one of the three ST231 CPUs simultaneously.
On an SoC such as the STi7200, which has four ST231s, the Audio decode could be
partitioned and off-loaded to both Audio CPUs, as shown in
. This can be done transparently to the application running on the ST40 host by 'nesting' transformations.
For example, the host CPU issues an Audio decode transformation command to the
Audio codec which is registered on the Audio0 CPU, this transformer has in turn instantiated a transformer registered on the Audio1 CPU. As each command arrives, the Audio codec running on Audio0 issues transformation commands to Audio1 to offload some of the compute. Once these complete it can then complete the original transformation command from the host.
16/224 8182595 Rev 5
UM1112
Host (ST40)
Video_decode
Audio_decode
Figure 3. Transformer instances: multi-hosting
Using the MME API
Companion (ST231)
Transformer_name_1
Transformer_name_2
Companion (ST231)
Transformer_name_3
Companion acting as host
Transformer_name_4
Companion (ST231)
Transformer_name_5
Transformer_name_6
8182595 Rev 5 17/224
33
Using the MME API UM1112
2.1.3
Note:
Commands and events
Transformer instances are controlled by sending them commands. Each command is a selfcontained unit of work consisting of a due time, a command code, some transformer specific parametric information and the data buffers to be transformed, by the command. All commands of the same priority are executed in due time order.
The priority of a command is inherited from the transformer instance with which it is associated. The priority of a transformer instance is supplied by the application when the transformer is instantiated.
Because commands are executed on different processors and, potentially, can be deferred for execution by different hardware accelerators, this does not imply that across the system as a whole, all commands will be issued or complete in due time order.
Each command is associated with a status structure that, among other things, provides the unique identifier by which the command can be managed together with an indication of the command’s current state.
Commands are submitted for execution asynchronously, that is, the function to issue the command completes successfully before the command has completed.
The MME can generate events when a command completes or fails. Event notification may be optionally enabled by the application programmer when a command is submitted. Events are delivered to the application by using callbacks.
2.1.4 Callbacks
A callback function and application-specific callback data is associated with a transformer when a transformer is instantiated. When a command is sent to a transformer, the application can choose whether or not it will be notified of any events associated with the command, by the associated callback.
Note:
The due time is used by the MME implementation to determine in what order to process commands. The command queue for each transformer is maintained in due time order and when a command is dispatched all the queues are examined and the one with the lowest due time is selected for execution.
The due time is only relevant to MME_SET_GLOBAL_TRANSFORM_PARAMS and
MME_TRANSFORM
; MME_SEND_BUFFERS commands are executed in strict FIFO order and
can pre-empt currently running commands. See
Section 2.8: Fault detection and recovery on page 29 .
Neither the MME host nor the companion is aware of the current system time. This leaves the choice of what time unit to use entirely at the application designers discretion. In most cases using the host processor’s system clock is recommended. Although the MME implementation does not know the unit of time, it does know that as time progresses the due time will eventually reach 0xffffffff and overflow. For this reason when due times are compared it is not a simple magnitude comparison. Instead the times are arranged, such that t
after
- t
before
is less than 0x7fffffff.
shows what this comparison means in practice by showing how t
a
against all possible 32-bit values
.
will be compared
18/224 8182595 Rev 5
UM1112 Using the MME API
Figure 4. Time arithmetic x
ticks
0x7FFF FFFF
0x8000 0000
t a
Past
Future
Before Now
After Now
y
ticks
0 0xFFFF FFFF
When due times are exactly equal then the least recently issued command will be executed first. This permits commands to be executed in strict FIFO order if their due times are always the same value; zero is a good candidate value in this case although any value can be used.
There are three obvious ways an application may choose to utilize the due time:
As constant value across all transformers. This results in FIFO scheduling within a transformer and round robin scheduling among transformers.
As unique constant values. This results in FIFO scheduling within a transformer and prioritized scheduling among transformers. This differs from normal prioritized scheduling because low priority transforms will not be pre-empted. This may yield slightly better utilization of processor bandwidth at the expense of latency.
As true due time. This results in due time scheduling within all commands irrespective of which transformer queue they appear on.
The due time mechanism allows commands to be executed in a deterministic sequence.
However, an application may require short-duration transforms (such as a series of audio frame decodes) to complete while a lengthy transform operation (such as a JPEG decode) is being handled by another transformer instance.
To facilitate this MME supports five transformer priorities. A priority is assigned to a transformer instance when the instance is created. Transformer priorities are mapped onto the underlying operating system thread priorities; an execution thread is created for each priority for which a transformer is instantiated.
8182595 Rev 5 19/224
33
Using the MME API UM1112
Therefore a transform command executing on a high priority transformer instance takes precedence over a command executing on a lower priority transformer instance.
Commands at a particular priority are submitted sequentially to their transformer instances in due time order.
2.2
The MME API uses MME structures to pass data. Typically there is a size field StructSize which must be set to the size of the structure in bytes, see
Section 4.2: MME constants, enums and types on page 79
.
Summary of MME facilities
The following is a summary of the main facilities provided by the MME API.
MME provides API calls to allow the application to set up MME and to register transformers.
Managing transformer lifetimes, see
Transformer instances can be created or destroyed using the MME API. It is also possible to examine the capabilities of a transformer before it is instantiated.
Buffer and cache management, see
Data buffers are used extensively by MME to transport unstructured data between the application and the transformers. MME provides API calls to allocate and manage data buffers as well as subdividing them. Flags are provided to influence cache management of data buffers.
Application and transformer specific data, see
MME provides a mechanism for passing application-specific or transformer data to and from the transformer.
Issuing commands, see
The central function of the MME API is to perform transformations. Three types of
commands may be sent to a transformer, as described in
.
describes the difference between a frame-based transformer and a stream-based transformer.
The chapter finishes with a description of linking and loading issues for different operating
.
2.3 Initialization
Initialization of a system is divided into two stages:
1.
Initializing MME (which presupposes that the ICS is initialized.)
2. Registering transformers, if they are being used
20/224 8182595 Rev 5
UM1112
Note:
Using the MME API
It is essential that all host and companion processors in a system are initialized. However, some stages of the initialization may be omitted for particular operating environments supported by MME:
For multi-processor OS21 systems, all steps are required for companion processors.
Registering transformers is optional for host processors.
For Linux kernel space operation, MME is initialized automatically when the MME module is loaded. Registering local transformers is optional.
Section 2.11.2: Linux on page 32
contains further details about loading Linux kernel
modules.
It is not possible to register transformers in Linux user space.
Note:
Note:
The MME library is initialized using the following function:
MME_ERROR MME_Init(void)
The MME library must be loaded and initialized on each processor and user space process in the system.
The Linux kernel MME implementation automatically calls MME_Init during module load.
No other API can be called until the library is initialized.
In a system where multiple threads use MME, it is permissible for each thread to call
MME_Init
. The first call performs initialization, returning MME_SUCCESS if no error occurs.
Any subsequent calls simply return MME_ALREADY_INITIALIZED. That is, a second call to
MME_Init
does not re-initialize MME.
Calls to MME_Init are not counted. Thus particular care must be taken de-initializing MME
when sharing the MME between multiple threads.
A transformer is registered with a name and associated function pointers by using the following function:
MME_ERROR MME_RegisterTransformer( const char *name,
MME_AbortCommand_t abortFunc,
MME_GetTransformerCapability_t getTransformerCapabilityFunc,
MME_InitTransformer_t initTransformerFunc,
MME_ProcessCommand_t processCommandFunc,
MME_TermTransformer_t termTransformerFunc)
Each of the functions pointed to is described in detail in
Chapter 3: Writing an MME transformer
2.3.3 Example
Note:
This section provides examples of how MME is started on a host CPU and on a companion
CPU. The examples illustrate the startup sequence for the host application and for the companion. It is assumed that the operating system has been started on each CPU.
For brevity, return code checks have been omitted from the examples.
8182595 Rev 5 21/224
33
Using the MME API UM1112
Host-side example
/* Initialize ICS */ err = ICS_cpu_init(0);
/* Intitialize the MME system for a host */ res = MME_Init();
/* Init transformer */ res = MME_InitTransformer("com.st.mcdt.mme.test_transformer",
&initParams, &transformerHandle);
/* Send a transform command */ res = MME_SendCommand(transformerHandle, MME_TRANSFORM, ...); res = MME_TermTransformer(transformerHandle);
Companion-side example
/* Initialize ICS */ err = ICS_cpu_init(0);
/* Intitialize the MME system for a companion */ res = MME_Init();
/* Register the transformers active on this CPU */ res = MME_RegisterTransformer("com.st.mcdt.mme.test_transformer", abortFunc, getCapabilityFunc, initFunc, processCommandFunc, termFunc);
2.4 Managing transformer lifetimes
Transformer instances can be created and destroyed using the following functions:
MME_ERROR MME_InitTransformer( const char *name,
MME_TransformerInitParams_t *params_p,
MME_TransformerHandle_t *handle_p)
MME_ERROR MME_TermTransformer(MME_TransformerHandle_t handle_p)
The name argument specifies the name of the previously registered transformer.
params_p
is used to specify one of five priority levels for the transformer together with details of the callback function used to communicate any events associated with this transformer and its commands. Additionally the parameter structure may contain a pointer to transformer specific parameters containing any initial state the transformer may require.
See
Section 2.6: Application and transformer specific data on page 27
If MME_InitTransformer returns successfully then handle_p is supplied with a handle used to issue commands and terminate the transformer. Once initialized, a transformer can execute an arbitrary number of commands before finally being terminated.
MME_TermTransformer
is used to destroy a transformer instance thus freeing any resources used by the transformer.
22/224 8182595 Rev 5
UM1112
Note:
Using the MME API
It is not possible to terminate a transformer if there are any outstanding commands pending.
If this is attempted an error is returned.
2.5
Note:
It is sometimes useful to examine the capabilities of a transformer before it is instantiated, either for error checking or to ensure the correct transformer is being used. MME allows transformers to publish their capabilities without requiring a handle. This enables transformers to be examined before any calls to MME_InitTransformer.
The following function is used for this purpose:
MME_ERROR MME_GetTransformerCapability( const char *transformerName,
MME_TransformerCapability_t *transformerCapability)
The transformer is able to describe its preferred input and output formats together with its version number. Transformer specific details can also be copied into a user-supplied buffer.
Buffer and cache management
Data buffers are used throughout MME to transport unstructured data between the application and transformers. In this case, unstructured is used to mean that data has identical representation on all processors regardless of endianness or similar concerns; it is a simple stream of bytes. A data buffer describes a logical group of memory locations that contain or are intended to contain media data. A data buffer is represented by the structure
MME_DataBuffer_t
.
Each data buffer is composed of one or more scatter pages. A scatter page describes a single sequential group of memory locations, or more specifically, a base pointer and a size.
A data buffer comprised of a single scatter page is a linear buffer while a data buffer consisting of multiple scatter pages is a scattered buffer.
A scatter page is represented by the structure MME_ScatterPage_t.
Figure 5. A scattered data buffer
Completely-filled
4096 byte scatter page
Half-filled
12288 byte data buffer
Half-filled
4096 byte scatter page
Empty
4096 byte scatter page
Although both linear and scattered buffers are properly handled by the MME API, some transformers are not able to efficiently support scattered buffers. For example, using
8182595 Rev 5 23/224
33
Using the MME API
2.5.1
UM1112
scattered buffers makes it difficult to delegate work to accelerators that only support linear
DMA.
Allocating data buffers
Data buffers can be allocated and freed using the following functions:
MME_ERROR MME_AllocDataBuffer(
MME_TransformerHandle_t Handle,
MME_UINT Size,
MME_AllocationFlags_t Flags,
MME_DataBuffer_t **DataBuffer_pp)
2.5.2
Note:
Note:
MME_ERROR MME_FreeDataBuffer(MME_DataBuffer_t *DataBuffer_p)
Buffers allocated using MME_AllocDataBuffer come from a common buffer pool which is of a fixed size. The size of this buffer pool can be varied by adjusting the tuneable value
MME_TUNEABLE_BUFFER_POOL_SIZE
before calling MME_Init.
All buffers allocated from the common buffer pool are guaranteed to be accessible by all transformers, and both cached and uncached translations can be requested by making use of the Flags argument.
If the user is intending to manually manage the MME data buffers then the default buffer pool size can be set to a small value. However, even in this case, the common buffer pool will be used for the MME command meta-data and hence should be sized accordingly.
Manually managing data buffers
MME permits any memory locations accessible by the host to be used in data buffers and scatter pages.
Linux User Mode applications are the exception to this, see
.
This allows most applications to manage memory for themselves and construct data buffers and scatter pages as required.
All user managed buffer memory must first be registered with MME by using the
MME_RegisterMemory
function. Failure to do this causes the MME_SendCommand operation to fail. If cached and uncached translations of this buffer memory are required, then memory registration calls with both cached and uncached translations of the memory region must be made.
Memory can be registered and deregistered using the following functions:
MME_ERROR MME_RegisterMemory (MME_TransformerHandle_t Handle,
void *Base,
MME_SIZE Size,
MME_MemoryHandle_t *Handle_p)
MME_ERROR MME_DeregisterMemory (MME_MemoryHandle_t Handle)
Cached buffers that are not aligned to the largest cache line size in the system pose significant problems because this makes writes by the companion CPU to those addresses unsafe.
24/224 8182595 Rev 5
UM1112
2.5.3
Using the MME API
Subdividing a data buffer
The application may divide the scatter pages returned by MME_AllocDataBuffer() into application-oriented scatter pages, so long as the divided pages reside entirely within the allocated pages.
An application must not make assumptions about the number of scatter pages returned by
MME_AllocDataBuffer
unless the flag MME_ALLOCATION_PHYSICAL is specified, in which case a single page is returned.
A simplified example of dividing a physical scatter page is shown below. This example takes the scatter page returned by MME_AllocDataBuffer and divides it into
NUM_SCATTER_PAGES
scatter pages:
MME_DataBuffer_t* dataBuffer;
MME_ScatterPage_t* origPage;
MME_ScatterPage_t scatterPage[NUM_SCATTER_PAGES]; int newPageSize; unsigned char* pageBase;
/* Allocate a buffer of ’size’ bytes */
MME_AllocDataBuffer(hdl, size, MME_ALLOCATION_PHYSICAL, &dataBuffer);
/* Keep a record of the original scatter page array */ origPage = &dataBuffer->ScatterPages_p;
/* Calculate size of each new scatter page - ignore the remainder bytes */ newPageSize = origPage->Size/NUM_SCATTER_PAGES; pageBase = origPage->Page_p;
/* Set the data buffer to use the new array of scatter pages */ dataBuffer->ScatterPages_p = scatterPage; for (i=0; i<NUM_SCATTER_PAGES; i++) {
dataBuffer->ScatterPages_p[i].Page_p = pageBase;
dataBuffer->ScatterPages_p[i].Size = newPageSize;
dataBuffer->ScatterPages_p[i].BytesUsed = newPageSize;
pageBase += newPageSize;
}
/* Now use the data buffer with the scatter pages */
...
/* Free the data buffer when no longer required */ dataBuffer->ScatterPages_p = origPage;
MME_FreeDataBuffer(dataBuffer);
8182595 Rev 5 25/224
33
Using the MME API
2.5.4 Data buffers in Linux user mode
UM1112
A Linux user application writer should endeavor to use MME_AllocDataBuffer() to allocate a data buffer for use by MME_SendCommand(). If this is not feasible because a data buffer has been allocated in kernel space by another agent (for example a video driver), the corresponding user space address of this buffer may be used in the Page_p field of a scatter page (MME_ScatterPage_t).
MME_SendCommand
ensures that the physical pages that comprise the buffer:
belong to the calling process
are physically contiguous
If either of these criteria are not met, MME_SendCommand returns MME_INTERNAL_ERROR.
The cacheability of these contiguous pages is determined from the cacheability flag within the Virtual Memory Area (VMA), in which the pages reside.
When a data buffer is held in cached memory, MME is forced to make a pessimistic assumption regarding whether it is held in a particular processor’s cache, in order to guarantee correctness. In many cases, the application is in a position to provide hints that can reduce this pessimistic behavior. These hints have no effect if memory is uncached, and can therefore be applied by an application even for affinity-allocated memory (see
Section 2.5.1: Allocating data buffers on page 24
For example, a buffer populated by an incoherent DMA peripheral (and not subsequently read by the CPU) is known not to be in a processor’s cache. It is therefore wasteful to spend time flushing such a buffer from memory.
For this reason, each MME scatter page can be marked with the cache management hints
, prior to being made available to the host.
Flag
Table 2. MME_ScatterPage_t FlagsIn and FlagsOut
FlagsIn
(1)
FlagsOut
(2)
Description
MME_DATA_CACHE_COHERENT
MME_DATA_TRANSIENT
3
3
3
For a host this means that all input buffers are coherent with memory and the output buffers are not present in the cache.
For a companion this means that
no buffers are present in the cache. This directs MME to avoid any unnecessary cache invalidations or cache flushes.
Data is reused by the companion without being read by another processor or hardware accelerator.
This directs MME not to flush the companion’s data cache.
1. The FlagsIn field is set by the host application before the command is issued to the MME.
2. The FlagsOut field is set by the companion transformer before notifying the host that the transform is complete.
26/224 8182595 Rev 5
UM1112 Using the MME API
2.6 Application transformer specific data
MME provides a mechanism for passing application-specific or transformer data to and from the transformer. When such data is to be passed it is specified by an address (of type
MME_GenericParams_t
) at which the data starts and a length in bytes. A mechanism for managing the portability of such data is described in
Section 3.7: Parameter passing on page 44
Commands are issued using the following function:
MME_ERROR MME_SendCommand(
MME_TransformerHandle_t Handle,
MME_Command_t *CmdInfo_p)
MME_SendCommand
is asynchronous; it returns before the command has completed processing. For this reason it is possible to examine the state of the command before it has completed.
The application must fill in several fields of the MME_Command_t structure:
StructSize
Section 2.1.7: Structure size on page 20
CmdCode
- with the command to perform - see
Section 2.4: Managing transformer lifetimes on page 22
CmdEnd
- to specify whether events such as a “command completion” cause the callback function to be called
Due time
Section 2.1.5: Due time on page 18
(zero for all commands ensures FIFO processing of commands)
NumberInputBuffers
- the number of input data buffers
NumberOutputBuffers
- the number of output data buffers
DataBuffers_p
- a pointer to an array of pointers to the input buffers and output buffers. The input buffer pointers must precede the output buffer pointers in this array
Param_p
and ParamSize - with command specific parameters. These should be set
to NULL and zero respectively if there are no parameters. See
Section 2.6: Application and transformer specific data on page 27
Section 3.7: Parameter passing on page 44
The state is held in the MME_CommandStatus_t structure within the MME_Command_t structure. The command changes from state to state as shown in
.
8182595 Rev 5 27/224
33
Using the MME API
Figure 6. Command state diagram
Start state
Submit command
MME_COMMAND_PENDING
Command de-queued
MME_COMMAND_EXECUTING
Command succeeded
Command deferred
Deferred state
Command failed
MME_COMMAND_COMPLETED MME_COMMAND_FAILED
UM1112
Note:
Stop state
When a command is issued it will be in the MME_COMMAND_PENDING state for as long as it is enqueued, waiting for processing time to become available.
The command’s transition to the MME_COMMAND_EXECUTING state, occurs when it is allocated processor time.
When a command is scheduled for execution on a companion processor, it is not possible to
distinguish between the MME_COMMAND_PENDING and MME_COMMAND_EXECUTING states
due to the requirement of minimizing communication between the host and companion. All commands that are scheduled on a companion processor appear to the application to be in
the MME_COMMAND_EXECUTING state.
If execution has terminated, either by normal completion of processing or due to an error, the state changes to one of the final states: MME_COMMAND_COMPLETED, or
MME_COMMAND_FAILED
. Callbacks occur whenever execution of a command completes or blocks due to command overflow or underflow conditions, which are described in
Section 2.10.2: Stream-based and hybrid operation on page 31
Executing commands enter the deferred state when a transformer delegates processing to the hardware block. (The deferred state is a conceptual state which is not observable by the host). When a command is deferred, subsequent commands are executed by the processor while the original command is executed by an independent hardware accelerator. A
28/224 8182595 Rev 5
UM1112
Note:
Using the MME API
deferred command can either proceed directly to one of the final states or re-enter the
MME_COMMAND_PENDING
state and wait for processor time.
Applications should never repeatedly poll the state of a command as this has the potential to deny other threads the use of the processor. Instead of polling, the application should utilize callbacks.
Note:
It is possible for the application to request that a command is aborted. This is achieved using the following function:
MME_ERROR MME_AbortCommand(MME_CommandId_t CmdId)
This function is asynchronous, it returns successfully before the request for abortion has been passed on.
An abort is, effectively, a request for a command to immediately enter the
MME_COMMAND_FAILED
state. As such the application will be notified if an abort was possible by the command entering this state. The application will receive a callback, assuming these are requested. If the command could not be aborted it will complete as normal entering either the MME_COMMAND_COMPLETED state or the MME_COMMAND_FAILED state with a different error code.
Although a command in the MME_COMMAND_PENDING state can always be aborted, it is not always possible for commands in the MME_COMMAND_EXECUTING or deferred
(a)
states to be aborted, as support for abort from these states is transformer-specific.
Since commands can asynchronously move from state to state, it is possible for a command to change state between the application observing that command in the
MME_COMMAND_PENDING
state and issuing the abort. As such the application should always consult the state of the aborted command before assuming success.
MME and the underlying ICS system provide simple facilities to aid the device driver writer in coping with unexpected failures of the companion CPUs.
In particular, CPU crashes are detected, and any outstanding commands and transformers that are hosted on the failed CPU are terminated. Further facilities are provided to allow the
programmer to ‘Ping’ the remote transformers periodically (See
MME_PingTransformer on page 70
), allowing them to check that the target transformer has not hung or got stuck in an
infinite loop.
Using these facilities the device driver writer should be able to detect and recover from an unexpected companion CPU failure. Generally this involves tidying up all the device driver state associated with the failed CPU and transformer instances, and then reloading and restarting the code on the companion CPU.
Multicom cannot recover from a failure of the host CPU or errors which cause a system wide lockup. Also, if a companion failure results in corruption of memory belonging to the host
CPU, then it may fail subsequently.
a. The technicalities of aborting a deferred command is discussed in detail in
Section 3.4.2: Deferred commands on page 40
.
8182595 Rev 5 29/224
33
Using the MME API
2.9 Types of commands
UM1112
There are three types of command that can be issued through a call to MME_SendCommand:
transform data
provide additional data buffers
alter global parameters
A command type is selected by setting the MME_CommandCode_t field in the
MME_Command_t
structure.
These types are described in
through to
2.9.2
2.9.3
Data transformations are at the heart of the MME API. All the other API calls and transformer commands exist solely to assist with data transformations.
The command code for data transformations is MME_TRANSFORM.
Wherever possible, data transformations are supplied with both input and output buffers as part of the transform command.
Most complex transformers also take some transformer specific command parameters.
Typically this parametric information is purely local affecting only the current transformation although it is quite legitimate for this to affect some global state. For example, when changing channel in a set-top box application a transformer reset might be requested.
Providing supplementary buffers
In some cases, it is not possible for a data transformation to be supplied with all the data buffers required for the transformation to complete. The reasons for this are outlined in
. If a transformation cannot be supplied with all the data buffers initially then it is necessary to supply supplementary buffers to the transformer. This is achieved using one or more MME_SEND_BUFFERS commands.
MME_SEND_BUFFERS
commands do not complete, that is, enter one of the final states, at the point the buffers are supplied to the transformer. They complete only when the buffers have been consumed by the transformer. This allows the application to know whether a buffer contains valid or in-use data without having to identify which transform request was responsible for filling it. Should such information be required, it is possible for transformer specific command status structures to be filled in by the transformer.
Altering global parameters
Global parameters form part of each instantiated transformer and are manipulated using the
MME_SET_GLOBAL_TRANSFORM_PARAMS
command code. Examples of global parameters include the chosen output format for data, gain for each channel of a mixer or the magnitude of reverb effects.
Global parameters can also be used to change less obvious items of global transformer state. For example, the transformer could be directed to move any command in the deferred state to the MME_COMMAND_FAILED state. By effectively abandoning any commands it has deferred it will then be possible for a transformer instance to be terminated.
When altering the global parameters, no data buffers are passed into or out of the transformer. All information required by the transformer should be passed using the
30/224 8182595 Rev 5
UM1112 Using the MME API
transformer specific command parameters. Similarly any information returned by the transformer should be contained in the transformer specific command status parameters.
2.10 Common types of transformer
This section distinguishes between types of transformer, based on how the input and output buffers are managed, in order to discuss the advantages and disadvantages of each approach.
A transformer is considered frame-based if its entire input and output buffers can be determined at the point the transform MME_TRANSFORM command is issued. Any transformer that does not require the use of MME_SEND_BUFFERS can therefore be considered to be frame-based.
A transformer is considered streaming if both its input and output buffers require the use of
MME_SEND_BUFFERS
.
Pure streaming transformers, while perfectly legitimate, are quite rare. Much more common are hybrid transformers consisting of frame-based input buffers and streaming output buffers or vice versa.
Frame-based operation is considered to be the default within MME. Although other modes of operation are possible, if ever there is a choice between frame-based or streaming operation then the frame-based approach is recommended.
By adopting frame-based operation, the amount of interrupt activity on both CPUs can be minimized. Particularly for media processors this maximizes processing bandwidth.
Because all buffers are available before the transformation begins, there is no risk of the transformer blocking, which has the potential to seriously degrade performance.
Frame-based operation is a particularly good approach for decoding multiplexed audio/video streams common in embedded multimedia processing. For both audio and video decode, the size of the output frame or picture is known in advance of processing. It is also fairly easy to identify complete input frames, because the device performing the demultiplex can readily identify end-of-frame markers.
If a frame-based transformer is not supplied with sufficient buffers, it does not block, instead it moves to the MME_COMMAND_FAILED state and sets the appropriate error code in the command status structure.
When a frame-based transformer completes, it issues a MME_COMMAND_COMPLETED_EVT event and the application receives a callback.
2.10.2 Stream-based and hybrid operation
Unfortunately there are number of reasons why it may not be possible for a transformer to be wholly frame-based:
transformations create an unknown quantity of output
transformations consume an unknown quantity of input
transformations are required to start before all available buffers are ready
The first situation is common in variable or average rate encoders. From a given input it is computationally unfeasible to estimate how much output will be created.
8182595 Rev 5 31/224
33
Using the MME API
Note:
UM1112
The second situation occurs when a stream format makes it difficult to identify end-of-frame markers or simply when a stream is not divided into frames.
The final situation is comparatively rare. One example is that of decoding a large JPEG image stored on ‘slow’ media, such as disk. It is desirable that the time between opening and displaying the image is minimized by starting to decode data as it becomes available.
As such, once the first part of the JPEG is available, it is useful to initiate the transform, knowing that the remaining part of the image will be provided by the application when (or before) the input buffer underflows.
Unlike frame-based transformers, stream-based transformers (or the stream-based side of a hybrid transformer) will not return an error during data underflow or overflow. Instead a
MME_DATA_UNDERFLOW_EVT
or MME_NOT_ENOUGH_MEMORY_EVT event is issued and the command suspends its execution waiting for further data.
Both underflow and overflow are exceptional events and should not occur during normal operation of a streaming transformer. They exist only to allow these circumstances to be handled gracefully. When underflow or overflow occurs, all processing at the same priority is halted on that processor. This has the potential to waste significant processor bandwidth, particularly in single-purpose companion processors. Other transformer priorities are not
effected. See Section 3.4.3: Streaming and hybrid transformers on page 41 .
In order to prevent these problems, the application should normally buffer sufficient input data or output space for this situation to be avoided. Where buffering exposes latency problems when changing streams (for example, in trick modes or during channel change)
then MME_AbortCommand provides a means to mitigate this.
When a data buffer has been consumed or filled completely, the MME_SEND_BUFFER command completes.
If an input data buffer is only partially consumed, the command will not complete; instead it remains pending until the next MME_TRANSFORM command consumes it.
Whether a partially filled output data buffer completes, before it is full, is transformerdependant. If the output consists of variable length frames, it is normal to complete a partially-filled frame and move on to the next one. Where the output does not contain frame markers, the buffer will not complete until it is completely filled.
This section describes linking and loading issues for different operating systems.
2.11.1 OS21
On OS21, the main application must link against both the ICS and MME libraries. Assuming the correct library search path is defined, this should be achieved by adding the following to the link command line:
-lmme -lics -lics_bsp
2.11.2 Linux
The MME API can be used on a host processor running Linux, in either kernel mode, user mode or both concurrently. There is a single kernel module that must be loaded and a single library that must be linked with a user application.
32/224 8182595 Rev 5
UM1112 Using the MME API
However, the MME kernel module must be loaded after the ICS kernel module is loaded.
Also, the user application must be linked against the MME user library. Assuming the correct library search path is defined, this should be achieved by adding the following to the link command line:
-lmme
It is not valid for a Linux kernel module to call MME_Init or MME_Term because initialization and de-initialization occur automatically when then module is loaded and unloaded.
Note:
Multicom also supports relocatable libraries (also known as dynamic modules) which can be loaded and unloaded during application runtime. Relocatable libraries are only supported on the CPUs that run OS21.
For example, in order to compile an ST231 compatible ELF module, a command similar to the following can be used; st200cc -mcore=st231 -mruntime=os21\
-fpic --rlib -fvisibility=protected\ dyn.c -o dyn.out
It is recommended that ST200 Micro Toolset R6.4.0 or later is used when building relocatable libraries.
8182595 Rev 5 33/224
33
Download
Annonce