Upto: Table of Contents of full book "Programming the Raspberry Pi GPU"

OpenMAX on the Raspberry Pi State

OpenMAX IL uses a state model for each component. Within each state certain operations can be performed while others must be done before the next state transition can occur.

Resources

Files

Files used are here

Component states

Every component has a state and can go through a series of state transitions. There are three "desirable" states: Loaded, Idle and Executing. There are some less desirable states such as "Wait for Resources" and "Invalid", and if a component is in one of those states except transiently then your program probably won't be working properly.

The state transition diagram is Figure 2-3 of the 1.1.2 specification:

Whenever a component changes state it will invoke the event callback handler established for that component. We ignored this in earlier chapters and will look in more detail at it later. For now, we have to give a non-NULL function to the EventHandler callback struct which is passed when getting a handle to a component. Minimally, it will look like this

	
OMX_ERRORTYPE cEventHandler(
                            OMX_HANDLETYPE hComponent,
                            OMX_PTR pAppData,
                            OMX_EVENTTYPE eEvent,
                            OMX_U32 Data1,
                            OMX_U32 Data2,
                            OMX_PTR pEventData) {
    // do nothing
    return OMX_ErrorNone;
}

OMX_CALLBACKTYPE callbacks  = { .EventHandler = cEventHandler,
                                .EmptyBufferDone = NULL,
                                .FillBufferDone = NULL
};

err = OMX_GetHandle(&handle, componentName,
        	    NULL, &callbacks);
	
      

A created component moves into the Loaded state. Requests to change to another state are done using the call (Section 3.2.2.2 of the 1.1.2 specification)

	
OMX_SendCommand(handle,
                OMX_CommandStateSet, 
                <next_state>, 
                <user_data>);
	
      

Loaded to Idle

What we want to do for any component is to move it from Loaded to Idle by

	
OMX_SendCommand(handle,
                OMX_CommandStateSet, 
                OMX_StateIdle,
                NULL);
	
      

A non-working program

This is where we now hit hurdle number one in OpenMAX programming: a request to change state only has effect if all the pre-requisites are met for the new state. To draw an analogy: if you want your car to change from stopped state to moving state, then in preparation for this you have to insert the ignition key, switch the engine on, release the parking brake, engage gears, etc. if any one of these steps is missing, the state change just won't occur.

The first major change that you want to happen will be from OMX_StateLoaded to OMX_StateIdle. The following program creates a program and requests the state change. If you run it, you will see that it just sits in the Loaded state and never makes the transition. The program is wontwork.c. I've included some convenience functions err2str and printState

/*
 * WARNING: THIS PROGRAM DOESN'T WORK
 */

#include <stdio.h>
#include <stdlib.h>

#include <OMX_Core.h>
#include <OMX_Component.h>

#include <bcm_host.h>

char *err2str(int err) {
    switch (err) {
    case OMX_ErrorInsufficientResources: return "OMX_ErrorInsufficientResources";
    case OMX_ErrorUndefined: return "OMX_ErrorUndefined";
    case OMX_ErrorInvalidComponentName: return "OMX_ErrorInvalidComponentName";
    case OMX_ErrorComponentNotFound: return "OMX_ErrorComponentNotFound";
    case OMX_ErrorInvalidComponent: return "OMX_ErrorInvalidComponent";
    case OMX_ErrorBadParameter: return "OMX_ErrorBadParameter";
    case OMX_ErrorNotImplemented: return "OMX_ErrorNotImplemented";
    case OMX_ErrorUnderflow: return "OMX_ErrorUnderflow";
    case OMX_ErrorOverflow: return "OMX_ErrorOverflow";
    case OMX_ErrorHardware: return "OMX_ErrorHardware";
    case OMX_ErrorInvalidState: return "OMX_ErrorInvalidState";
    case OMX_ErrorStreamCorrupt: return "OMX_ErrorStreamCorrupt";
    case OMX_ErrorPortsNotCompatible: return "OMX_ErrorPortsNotCompatible";
    case OMX_ErrorResourcesLost: return "OMX_ErrorResourcesLost";
    case OMX_ErrorNoMore: return "OMX_ErrorNoMore";
    case OMX_ErrorVersionMismatch: return "OMX_ErrorVersionMismatch";
    case OMX_ErrorNotReady: return "OMX_ErrorNotReady";
    case OMX_ErrorTimeout: return "OMX_ErrorTimeout";
    case OMX_ErrorSameState: return "OMX_ErrorSameState";
    case OMX_ErrorResourcesPreempted: return "OMX_ErrorResourcesPreempted";
    case OMX_ErrorPortUnresponsiveDuringAllocation: return "OMX_ErrorPortUnresponsiveDuringAllocation";
    case OMX_ErrorPortUnresponsiveDuringDeallocation: return "OMX_ErrorPortUnresponsiveDuringDeallocation";
    case OMX_ErrorPortUnresponsiveDuringStop: return "OMX_ErrorPortUnresponsiveDuringStop";
    case OMX_ErrorIncorrectStateTransition: return "OMX_ErrorIncorrectStateTransition";
    case OMX_ErrorIncorrectStateOperation: return "OMX_ErrorIncorrectStateOperation";
    case OMX_ErrorUnsupportedSetting: return "OMX_ErrorUnsupportedSetting";
    case OMX_ErrorUnsupportedIndex: return "OMX_ErrorUnsupportedIndex";
    case OMX_ErrorBadPortIndex: return "OMX_ErrorBadPortIndex";
    case OMX_ErrorPortUnpopulated: return "OMX_ErrorPortUnpopulated";
    case OMX_ErrorComponentSuspended: return "OMX_ErrorComponentSuspended";
    case OMX_ErrorDynamicResourcesUnavailable: return "OMX_ErrorDynamicResourcesUnavailable";
    case OMX_ErrorMbErrorsInFrame: return "OMX_ErrorMbErrorsInFrame";
    case OMX_ErrorFormatNotDetected: return "OMX_ErrorFormatNotDetected";
    case OMX_ErrorContentPipeOpenFailed: return "OMX_ErrorContentPipeOpenFailed";
    case OMX_ErrorContentPipeCreationFailed: return "OMX_ErrorContentPipeCreationFailed";
    case OMX_ErrorSeperateTablesUsed: return "OMX_ErrorSeperateTablesUsed";
    case OMX_ErrorTunnelingUnsupported: return "OMX_ErrorTunnelingUnsupported";
    default: return "unknown error";
    }
}

void printState(OMX_HANDLETYPE handle) {
    OMX_STATETYPE state;
    OMX_ERRORTYPE err;

    err = OMX_GetState(handle, &state);
    if (err != OMX_ErrorNone) {
        fprintf(stderr, "Error on getting state\n");
        exit(1);
    }
    switch (state) {
    case OMX_StateLoaded: printf("StateLoaded\n"); break;
    case OMX_StateIdle: printf("StateIdle\n"); break;
    case OMX_StateExecuting: printf("StateExecuting\n"); break;
    case OMX_StatePause: printf("StatePause\n"); break;
    case OMX_StateWaitForResources: printf("StateWait\n"); break;
    case OMX_StateInvalid: printf("StateInvalid\n"); break;
    default:  printf("State unknown\n"); break;
    }
}

OMX_ERRORTYPE cEventHandler(
                            OMX_HANDLETYPE hComponent,
                            OMX_PTR pAppData,
                            OMX_EVENTTYPE eEvent,
                            OMX_U32 Data1,
                            OMX_U32 Data2,
                            OMX_PTR pEventData) {

    printf("Hi there, I am in the %s callback\n", __func__);
    printf("Event is %i\n", (int)eEvent);
    printf("Param1 is %i\n", (int)Data1);
    printf("Param2 is %i\n", (int)Data2);

    return OMX_ErrorNone;
}

OMX_CALLBACKTYPE callbacks  = { .EventHandler = cEventHandler,
                                .EmptyBufferDone = NULL,
                                .FillBufferDone = NULL
};


int main(int argc, char** argv) {

    int i;
    char *componentName;
    OMX_ERRORTYPE err;
    OMX_HANDLETYPE handle;

    if (argc < 2) {
	fprintf(stderr, "Usage: %s component-name\n", argv[0]);
	exit(1);
    }
    componentName = argv[1];

    bcm_host_init();

    err = OMX_Init();
    if(err != OMX_ErrorNone) {
	fprintf(stderr, "OMX_Init() failed %s\n", err2str(err));
	exit(1);
    }
    /** Ask the core for a handle to the component
     */
    err = OMX_GetHandle(&handle, componentName,
                        // the next two fields are discussed later 
                        NULL, &callbacks);    
    if (err != OMX_ErrorNone) {
	fprintf(stderr, "OMX_GetHandle failed %s\n", err2str(err));
	exit(1);
    }

    // check our current state - should be Loaded
    printState(handle);

    // request a move to idle
    OMX_SendCommand(handle,
		    OMX_CommandStateSet, 
		    OMX_StateIdle,
		    NULL);

    int n = 0;
    while (n++ < 10) {
	sleep(1);
	// are we there yet?
	printState(handle);
    }
 
    exit(0);
}

      

The output is

	
StateLoaded
Hi there, I am in the cEventHandler callback
Event is 3
Param1 is 61
Param2 is 0
StateLoaded
StateLoaded
StateLoaded
StateLoaded
StateLoaded
StateLoaded
StateLoaded
StateLoaded
StateLoaded
StateLoaded
	
      

What's wrong with it?

The OpenMAX 1.1.2 specification in Section 3.1.1.2.1.1 "OMX_StateLoaded to OMX_StateIdle" says

If the IL client requests a state transition from OMX_StateLoaded to OMX_StateIdle, the component shall acquire all of its static resources, including buffers for all enabled ports, before completing the transition. The component does not acquire buffers for any disabled ports. Furthermore, before the transition can complete, the buffer supplier, which is always the IL client when not tunneling, shall ensure that the non-supplier possesses all of its buffers.

Clear? Well, maybe not. What it is saying is that the component must have everything right, including allocating all of the input and output buffers. Alternatively, you can do the equivalent of leaving the car on a hill and just releasing the parking brake, which is a much simpler way of moving! For a component, this means disabling all of the ports. This can get you into Idle state, and then you can worry about the next transition.

To disable all of the ports we have to use the techniques of the last chapter: find all of the video ports, all of the audio ports, all of the image ports and all of the other ports. For each one, call

	
OMX_SendCommand(handle, OMX_CommandPortDisable,
                <port_index>, NULL);
	
      

The progam is working.c

#include <stdio.h>
#include <stdlib.h>

#include <OMX_Core.h>
#include <OMX_Component.h>

#include <bcm_host.h>

char *err2str(int err) {
    switch (err) {
    case OMX_ErrorInsufficientResources: return "OMX_ErrorInsufficientResources";
    case OMX_ErrorUndefined: return "OMX_ErrorUndefined";
    case OMX_ErrorInvalidComponentName: return "OMX_ErrorInvalidComponentName";
    case OMX_ErrorComponentNotFound: return "OMX_ErrorComponentNotFound";
    case OMX_ErrorInvalidComponent: return "OMX_ErrorInvalidComponent";
    case OMX_ErrorBadParameter: return "OMX_ErrorBadParameter";
    case OMX_ErrorNotImplemented: return "OMX_ErrorNotImplemented";
    case OMX_ErrorUnderflow: return "OMX_ErrorUnderflow";
    case OMX_ErrorOverflow: return "OMX_ErrorOverflow";
    case OMX_ErrorHardware: return "OMX_ErrorHardware";
    case OMX_ErrorInvalidState: return "OMX_ErrorInvalidState";
    case OMX_ErrorStreamCorrupt: return "OMX_ErrorStreamCorrupt";
    case OMX_ErrorPortsNotCompatible: return "OMX_ErrorPortsNotCompatible";
    case OMX_ErrorResourcesLost: return "OMX_ErrorResourcesLost";
    case OMX_ErrorNoMore: return "OMX_ErrorNoMore";
    case OMX_ErrorVersionMismatch: return "OMX_ErrorVersionMismatch";
    case OMX_ErrorNotReady: return "OMX_ErrorNotReady";
    case OMX_ErrorTimeout: return "OMX_ErrorTimeout";
    case OMX_ErrorSameState: return "OMX_ErrorSameState";
    case OMX_ErrorResourcesPreempted: return "OMX_ErrorResourcesPreempted";
    case OMX_ErrorPortUnresponsiveDuringAllocation: return "OMX_ErrorPortUnresponsiveDuringAllocation";
    case OMX_ErrorPortUnresponsiveDuringDeallocation: return "OMX_ErrorPortUnresponsiveDuringDeallocation";
    case OMX_ErrorPortUnresponsiveDuringStop: return "OMX_ErrorPortUnresponsiveDuringStop";
    case OMX_ErrorIncorrectStateTransition: return "OMX_ErrorIncorrectStateTransition";
    case OMX_ErrorIncorrectStateOperation: return "OMX_ErrorIncorrectStateOperation";
    case OMX_ErrorUnsupportedSetting: return "OMX_ErrorUnsupportedSetting";
    case OMX_ErrorUnsupportedIndex: return "OMX_ErrorUnsupportedIndex";
    case OMX_ErrorBadPortIndex: return "OMX_ErrorBadPortIndex";
    case OMX_ErrorPortUnpopulated: return "OMX_ErrorPortUnpopulated";
    case OMX_ErrorComponentSuspended: return "OMX_ErrorComponentSuspended";
    case OMX_ErrorDynamicResourcesUnavailable: return "OMX_ErrorDynamicResourcesUnavailable";
    case OMX_ErrorMbErrorsInFrame: return "OMX_ErrorMbErrorsInFrame";
    case OMX_ErrorFormatNotDetected: return "OMX_ErrorFormatNotDetected";
    case OMX_ErrorContentPipeOpenFailed: return "OMX_ErrorContentPipeOpenFailed";
    case OMX_ErrorContentPipeCreationFailed: return "OMX_ErrorContentPipeCreationFailed";
    case OMX_ErrorSeperateTablesUsed: return "OMX_ErrorSeperateTablesUsed";
    case OMX_ErrorTunnelingUnsupported: return "OMX_ErrorTunnelingUnsupported";
    default: return "unknown error";
    }
}

void printState(OMX_HANDLETYPE handle) {
    OMX_STATETYPE state;
    OMX_ERRORTYPE err;

    err = OMX_GetState(handle, &state);
    if (err != OMX_ErrorNone) {
        fprintf(stderr, "Error on getting state\n");
        exit(1);
    }
    switch (state) {
    case OMX_StateLoaded: printf("StateLoaded\n"); break;
    case OMX_StateIdle: printf("StateIdle\n"); break;
    case OMX_StateExecuting: printf("StateExecuting\n"); break;
    case OMX_StatePause: printf("StatePause\n"); break;
    case OMX_StateWaitForResources: printf("StateWait\n"); break;
    case OMX_StateInvalid: printf("StateInvalid\n"); break;
    default:  printf("State unknown\n"); break;
    }
}

OMX_ERRORTYPE cEventHandler(
                            OMX_HANDLETYPE hComponent,
                            OMX_PTR pAppData,
                            OMX_EVENTTYPE eEvent,
                            OMX_U32 Data1,
                            OMX_U32 Data2,
                            OMX_PTR pEventData) {

    printf("Hi there, I am in the %s callback\n", __func__);
    printf("Event is %i\n", (int)eEvent);
    printf("Param1 is %i\n", (int)Data1);
    printf("Param2 is %i\n", (int)Data2);

    return OMX_ErrorNone;
}

OMX_CALLBACKTYPE callbacks  = { .EventHandler = cEventHandler,
                                .EmptyBufferDone = NULL,
                                .FillBufferDone = NULL
};

void disableSomePorts(OMX_HANDLETYPE handle, OMX_INDEXTYPE indexType) {
    OMX_PORT_PARAM_TYPE param;
    int startPortNumber, endPortNumber;
    int nPorts;
    int n;
    OMX_ERRORTYPE err;

    memset(&param, 0, sizeof(OMX_PORT_PARAM_TYPE));
    param.nSize = sizeof(OMX_PORT_PARAM_TYPE);
    param.nVersion.nVersion = OMX_VERSION;

    err = OMX_GetParameter(handle, indexType, &param);
    if(err != OMX_ErrorNone){
        fprintf(stderr, "Error in getting image OMX_PORT_PARAM_TYPE parameter\n"
);
        return;
    }

    startPortNumber = param.nStartPortNumber;
    nPorts = param.nPorts;
    endPortNumber = startPortNumber + nPorts;

    for (n = startPortNumber; n < endPortNumber; n++) {
	OMX_SendCommand(handle, OMX_CommandPortDisable,
			n, NULL);
    }
}

void disableAllPorts(OMX_HANDLETYPE handle) {
    disableSomePorts(handle, OMX_IndexParamVideoInit);
    disableSomePorts(handle, OMX_IndexParamImageInit);
    disableSomePorts(handle, OMX_IndexParamAudioInit);
    disableSomePorts(handle, OMX_IndexParamOtherInit);
}

int main(int argc, char** argv) {

    int i;
    char *componentName;
    OMX_ERRORTYPE err;
    OMX_HANDLETYPE handle;

    if (argc < 2) {
	fprintf(stderr, "Usage: %s component-name\n", argv[0]);
	exit(1);
    }
    componentName = argv[1];

    bcm_host_init();

    err = OMX_Init();
    if(err != OMX_ErrorNone) {
	fprintf(stderr, "OMX_Init() failed %s\n", err2str(err));
	exit(1);
    }
    /** Ask the core for a handle to the component
     */
    err = OMX_GetHandle(&handle, componentName,
                        // the next two fields are discussed later 
                        NULL, &callbacks);
    if (err != OMX_ErrorNone) {
	fprintf(stderr, "OMX_GetHandle failed %s\n", err2str(err));
	exit(1);
    }

    sleep(1);
    // check our current state - should be Loaded
    printState(handle);

    disableAllPorts(handle);

    // request a move to idle
    OMX_SendCommand(handle,
		    OMX_CommandStateSet, 
		    OMX_StateIdle,
		    NULL);

    int n = 0;
    while (n++ < 10) {
	sleep(1);
	// are we there yet?
	printState(handle);
    }
 
    exit(0);
}

      

Idle to Executing

Once a component is in Idle state, a request can be made to move it to Executing. This is actually pretty easy, and generally succeeds. Once it is in Executing state it can start filling and emptying buffers (we haven't looked at buffers yet).

When do state changes occur?

In the "working" program of an earlier section we did a loop with a sleep to print the current state. If you want to detect when a state change has occurred you could do a similar sort of 'busy wait'.

That isn't really satisfactory: OpenMAX should be able to able to tell you when a state change occurs. And it does: when a state change occurs in a component it generates an event which is given to the event handler callback.

The event handler callback is discussed in Section 3.1.2.9.1 of the 1.1.2 specification. It is defined as

	
OMX_ERRORTYPE (*EventHandler)(
    OMX_IN OMX_HANDLETYPE hComponent,
    OMX_IN OMX_PTR pAppData,
    OMX_IN OMX_EVENTTYPE eEvent,
    OMX_IN OMX_U32 nData1,
    OMX_IN OMX_U32 nData2,
    OMX_IN OMX_PTR pEventData);
	
      

The hComponent is the handle to the component. The eEvent is an enumerated data type giving possible event types. The data values contain values appropriate to the event. These are described in Table 3-7 of the 1.1.2 specification:

For now, the important event is OMX_EventCmdComplete with nData1 set to OMX_CommandStateSet The value in nData2 is then the state reached - OMX_StateIdle, etc.

The program event.c monitors events. When run on the OMX.broadcom.clock component, it reports disabling all five ports, transitioning to Idle and then Executing:

#include <stdio.h>
#include <stdlib.h>

#include <OMX_Core.h>
#include <OMX_Component.h>

#include <bcm_host.h>

char *err2str(int err) {
    switch (err) {
    case OMX_ErrorInsufficientResources: return "OMX_ErrorInsufficientResources";
    case OMX_ErrorUndefined: return "OMX_ErrorUndefined";
    case OMX_ErrorInvalidComponentName: return "OMX_ErrorInvalidComponentName";
    case OMX_ErrorComponentNotFound: return "OMX_ErrorComponentNotFound";
    case OMX_ErrorInvalidComponent: return "OMX_ErrorInvalidComponent";
    case OMX_ErrorBadParameter: return "OMX_ErrorBadParameter";
    case OMX_ErrorNotImplemented: return "OMX_ErrorNotImplemented";
    case OMX_ErrorUnderflow: return "OMX_ErrorUnderflow";
    case OMX_ErrorOverflow: return "OMX_ErrorOverflow";
    case OMX_ErrorHardware: return "OMX_ErrorHardware";
    case OMX_ErrorInvalidState: return "OMX_ErrorInvalidState";
    case OMX_ErrorStreamCorrupt: return "OMX_ErrorStreamCorrupt";
    case OMX_ErrorPortsNotCompatible: return "OMX_ErrorPortsNotCompatible";
    case OMX_ErrorResourcesLost: return "OMX_ErrorResourcesLost";
    case OMX_ErrorNoMore: return "OMX_ErrorNoMore";
    case OMX_ErrorVersionMismatch: return "OMX_ErrorVersionMismatch";
    case OMX_ErrorNotReady: return "OMX_ErrorNotReady";
    case OMX_ErrorTimeout: return "OMX_ErrorTimeout";
    case OMX_ErrorSameState: return "OMX_ErrorSameState";
    case OMX_ErrorResourcesPreempted: return "OMX_ErrorResourcesPreempted";
    case OMX_ErrorPortUnresponsiveDuringAllocation: return "OMX_ErrorPortUnresponsiveDuringAllocation";
    case OMX_ErrorPortUnresponsiveDuringDeallocation: return "OMX_ErrorPortUnresponsiveDuringDeallocation";
    case OMX_ErrorPortUnresponsiveDuringStop: return "OMX_ErrorPortUnresponsiveDuringStop";
    case OMX_ErrorIncorrectStateTransition: return "OMX_ErrorIncorrectStateTransition";
    case OMX_ErrorIncorrectStateOperation: return "OMX_ErrorIncorrectStateOperation";
    case OMX_ErrorUnsupportedSetting: return "OMX_ErrorUnsupportedSetting";
    case OMX_ErrorUnsupportedIndex: return "OMX_ErrorUnsupportedIndex";
    case OMX_ErrorBadPortIndex: return "OMX_ErrorBadPortIndex";
    case OMX_ErrorPortUnpopulated: return "OMX_ErrorPortUnpopulated";
    case OMX_ErrorComponentSuspended: return "OMX_ErrorComponentSuspended";
    case OMX_ErrorDynamicResourcesUnavailable: return "OMX_ErrorDynamicResourcesUnavailable";
    case OMX_ErrorMbErrorsInFrame: return "OMX_ErrorMbErrorsInFrame";
    case OMX_ErrorFormatNotDetected: return "OMX_ErrorFormatNotDetected";
    case OMX_ErrorContentPipeOpenFailed: return "OMX_ErrorContentPipeOpenFailed";
    case OMX_ErrorContentPipeCreationFailed: return "OMX_ErrorContentPipeCreationFailed";
    case OMX_ErrorSeperateTablesUsed: return "OMX_ErrorSeperateTablesUsed";
    case OMX_ErrorTunnelingUnsupported: return "OMX_ErrorTunnelingUnsupported";
    default: return "unknown error";
    }
}

void printState(OMX_HANDLETYPE handle) {
    OMX_STATETYPE state;
    OMX_ERRORTYPE err;

    err = OMX_GetState(handle, &state);
    if (err != OMX_ErrorNone) {
        fprintf(stderr, "Error on getting state\n");
        exit(1);
    }
    switch (state) {
    case OMX_StateLoaded: printf("StateLoaded\n"); break;
    case OMX_StateIdle: printf("StateIdle\n"); break;
    case OMX_StateExecuting: printf("StateExecuting\n"); break;
    case OMX_StatePause: printf("StatePause\n"); break;
    case OMX_StateWaitForResources: printf("StateWait\n"); break;
    case OMX_StateInvalid: printf("StateInvalid\n"); break;
    default:  printf("State unknown\n"); break;
    }
}

OMX_ERRORTYPE cEventHandler(
                            OMX_HANDLETYPE hComponent,
                            OMX_PTR pAppData,
                            OMX_EVENTTYPE eEvent,
                            OMX_U32 Data1,
                            OMX_U32 Data2,
                            OMX_PTR pEventData) {

    if(eEvent == OMX_EventCmdComplete) {
        if (Data1 == OMX_CommandStateSet) {
            printf("Component State changed to ");
            switch ((int)Data2) {
            case OMX_StateInvalid:
                printf("OMX_StateInvalid\n");
                break;
            case OMX_StateLoaded:
                printf("OMX_StateLoaded\n");
                break;
            case OMX_StateIdle:
                printf("OMX_StateIdle\n");
                break;
            case OMX_StateExecuting:
                printf("OMX_StateExecuting\n");
                break;
            case OMX_StatePause:
                printf("OMX_StatePause\n");
                break;
            case OMX_StateWaitForResources:
                printf("OMX_StateWaitForResources\n");
               break;
            }
        } else  if (Data1 == OMX_CommandPortEnable){
            printf("OMX State Port enabled %d\n", (int) Data2);
        } else if (Data1 == OMX_CommandPortDisable){
            printf("OMX State Port disabled %d\n", (int) Data2); 
        }
    } else if(eEvent == OMX_EventBufferFlag) {
        if((int)Data2 == OMX_BUFFERFLAG_EOS) {
	    printf("Event is buffer end of stream\n");
        }
    } else if(eEvent == OMX_EventError) {
      if (Data1 == OMX_ErrorSameState) {
        printf("Already in requested state\n");
      } else {
        printf("Event is Error %X\n", Data1);
      }
    } else  if(eEvent == OMX_EventMark) {
        printf("Event is Buffer Mark\n");
    } else  if(eEvent == OMX_EventPortSettingsChanged) {
        printf("Event is PortSettingsChanged\n");
    }

    return OMX_ErrorNone;
}

OMX_CALLBACKTYPE callbacks  = { .EventHandler = cEventHandler,
                                .EmptyBufferDone = NULL,
                                .FillBufferDone = NULL
};

void disableSomePorts(OMX_HANDLETYPE handle, OMX_INDEXTYPE indexType) {
    OMX_PORT_PARAM_TYPE param;
    int startPortNumber, endPortNumber;
    int nPorts;
    int n;
    OMX_ERRORTYPE err;

    //setHeader(&param, sizeof(OMX_PORT_PARAM_TYPE));

    memset(&param, 0, sizeof(OMX_PORT_PARAM_TYPE));
    param.nSize = sizeof(OMX_PORT_PARAM_TYPE);
    param.nVersion.nVersion = OMX_VERSION;

    err = OMX_GetParameter(handle, indexType, &param);
    if(err != OMX_ErrorNone){
        fprintf(stderr, "Error in getting image OMX_PORT_PARAM_TYPE parameter\n"
, 0);
        return;
    }

    startPortNumber = param.nStartPortNumber;
    nPorts = param.nPorts;
    endPortNumber = startPortNumber + nPorts;

    for (n = startPortNumber; n < endPortNumber; n++) {
	OMX_SendCommand(handle, OMX_CommandPortDisable,
			n, NULL);
    }
}

void disableAllPorts(OMX_HANDLETYPE handle) {
    disableSomePorts(handle, OMX_IndexParamVideoInit);
    disableSomePorts(handle, OMX_IndexParamImageInit);
    disableSomePorts(handle, OMX_IndexParamAudioInit);
    disableSomePorts(handle, OMX_IndexParamOtherInit);
}

int main(int argc, char** argv) {

    int i;
    char *componentName;
    OMX_ERRORTYPE err;
    OMX_HANDLETYPE handle;

    if (argc < 2) {
	fprintf(stderr, "Usage: %s component-name\n", argv[0]);
	exit(1);
    }
    componentName = argv[1];

    bcm_host_init();

    err = OMX_Init();
    if(err != OMX_ErrorNone) {
	fprintf(stderr, "OMX_Init() failed %s\n", err2str(err));
	exit(1);
    }
    /** Ask the core for a handle to the component
     */
    err = OMX_GetHandle(&handle, componentName,
                        // the next two fields are discussed later 
                        NULL, &callbacks);
    if (err != OMX_ErrorNone) {
	fprintf(stderr, "OMX_GetHandle failed %s\n", err2str(err));
	exit(1);
    }

    sleep(1);
    // check our current state - should be Loaded
    printState(handle);

    disableAllPorts(handle);

    // request a move to idle
    OMX_SendCommand(handle,
		    OMX_CommandStateSet, 
		    OMX_StateIdle,
		    NULL);

    sleep(2);
    printState(handle);
 
    // and to executing
    OMX_SendCommand(handle,
		    OMX_CommandStateSet, 
		    OMX_StateExecuting,
		    NULL);
    sleep(2);
    printState(handle);
 
   exit(0);
}

      

with output

	
$ ./event OMX.broadcom.clock
StateLoaded
OMX State Port disabled 80
OMX State Port disabled 81
OMX State Port disabled 82
OMX State Port disabled 83
OMX State Port disabled 84
OMX State Port disabled 85
Component State changed to OMX_StateIdle
StateIdle
Component State changed to OMX_StateExecuting
StateExecuting
	
      

What to do when a state change occurs?

OpneMAX is inherently an asynchronous system. Each component is a 'black box' and the client using it has no idea nor control over how a component does its work. It may be fast, it may be slow, or it may never complete the requested activity (as in the wontwork program).

Events do not occur synchronously in the client's thread. They occur in a separate thread. Calls to the event handler are, however, blocking synchronous calls to the component. That is, the component cannot do any more work until the event is handled by the client's code. OpenMAX puts a time limit on this (Section 3.1.2.9.1) of five milliseconds.

That isn't much time. What the client needs to do in this component's thread is to signal a wakeup call in its own thread. That is, the client should block in its own thread, waiting on a wakeup event from the component's thread.

The client can do this in a portable way using Posix threads. Posix threads are well documented. We would need code such as

	
pthread_mutex_t mutex;
OMX_STATETYPE currentState = OMX_StateLoaded;
pthread_cond_t stateCond;

void waitFor(OMX_STATETYPE state) {
    printf("Waiting for %p\n", state);
    pthread_mutex_lock(&mutex);
    while (currentState != state)
        pthread_cond_wait(&stateCond, &mutex);
    printf("Wait successfully completed\n");
    pthread_mutex_unlock(&mutex);
}

void wakeUp(OMX_STATETYPE newState) {
    printf("Waking up %p\n", newState);
    pthread_mutex_lock(&mutex);
    currentState = newState;
    pthread_cond_signal(&stateCond);
    pthread_mutex_unlock(&
mutex);
}
	
      

where waitFor is called in the client thread and wakeUp is called in the event handler.

We won't give any examples of using this here, but it is easy to add to the event example. Examples of this are in my Linux Sound book. The RPi's GPU has its own VCOS thread system, and has built a library, the ilclient library, around these. So it's off to this topic in the next chapter.

Conclusion

A key issue in OpenMAX programming is handling state changes for each component. This chapter has discussed the critical issues using general OpenMAX calls.


      

Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
" Programming AudioVideo on the Raspberry Pi GPU " by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/RPi/ .

If you like this book, please contribute using PayPal

Or Flattr me:
Flattr this book