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

OpenMAX IL Client Library on the Raspberry Pi

The IL Client library is designed to make it easier to use OpenMAX on the RPi. It is not portable, but if you only want to build RPi applications (or for other Broadcom GPU systems) it can reduce the effort and frustration of using OpenMAX.

Resources

Files

Files used are here

The IL client library

The IL client library is not a standard library on the RPi. it is actually in the hello_pi directory, in /opt/vc/src/hello_pi/libs/ilclient/. You may have to build this yourself, using the Makefile in that directory.

Once built, you will have to include both the header file and the library in building applications.

	
cc -g -DRASPBERRY_PI  -DSTANDALONE -D__STDC_CONSTANT_MACROS \
   -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC \
   -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \
   -U_FORTIFY_SOURCE -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT \
   -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST\
   -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -I /opt/vc/include/IL \
   -I /opt/vc/include -I /opt/vc/include/interface/vcos/pthreads \
   -I/opt/vc/include/interface/vmcs_host/linux/ \
   -I/opt/vc/src/hello_pi/libs/ilclient  -o il_working il_working.c \
   -L /opt/vc/lib -Wl,--whole-archive -L/opt/vc/lib/ \
   -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread \
   -lrt -L/opt/vc/src/hello_pi/libs/ilclient -lilclient \
   -L../libs/vgfont -Wl,--no-whole-archive -rdynamic
	
      

Public Functions

These functions are documented in the file /opt/vc/src/hello_pi/libs/ilclient/ilclient.h. The types ILCLIENT_T and COMPONENT_T are also defined in this file, but you don't really need to look at them, just use them as though they were opaque types.

Creating a client

In the last chapter we created a handle to a component using OMX_GetHandle and then promptly disabled all ports. The function ilclient_create_component will do this for us by

	
ILCLIENT_T  *handle;
COMPONENT_T *component;
int         ret;

char *component_name = "image_decode"; // for example
handle = ilclient_init();

ret = ilclient_create_component(handle,
                                &component,
                                component_name,
                                ILCLIENT_DISABLE_ALL_PORTS);
if (ret < 0) {
    fprintf(stderr, "Error initialising componentn");
    exit(1);
}  
	
      

Please note that the names used for the component are not the full names; this library prefixes "OMX.broadcom." to the component name, so you just specify the last part of the name: "clock", "image_decode" etc.

We don't need this yet, but this is the right point to mention it: components use buffers, and this function can also be used to set which type of buffers are allowed. The default is no buffers. Input and output buffers can be allowed by extending the flags of the last argument:

	
ret = ilclient_create_component(handle,
                                &component,
                                component_name,
                                ILCLIENT_DISABLE_ALL_PORTS
                                |
                                ILCLIENT_ENABLE_INPUT_BUFFERS
                                |
                                ILCLIENT_ENABLE_OUTPUT_BUFFERS);
	
      

Changing component state

The function ilclient_change_component_state should now be used to ask for a change of state rather than OMX_SendCommand. Not only does it call for a state change, but also blocks, waiting for the change to occur. For example, to change a component to Idle state we call

	
ret = ilclient_change_component_state(component,
                                      OMX_StateIdle);
if (ret < 0) {
    // error
}
	
      

In the last chapter we monitored state changes by installing an event handler on the component. This library installs its own handlers so you don't need to do that. (If you do try, you will probably mess up the library.) The IL client library looks after event handling.

The equivalent of the event.c program to change state of the last chapter is much simpler now. It is il_working.c

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

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

#include <bcm_host.h>
#include <ilclient.h>

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;
    }
}

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

    int i;
    char *componentName;
    int err;
    ILCLIENT_T  *handle;
    COMPONENT_T *component;

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

    bcm_host_init();

    handle = ilclient_init();
    if (handle == NULL) {
	fprintf(stderr, "IL client init failed\n");
	exit(1);
    }

   if (OMX_Init() != OMX_ErrorNone) {
        ilclient_destroy(handle);
        fprintf(stderr, "OMX init failed\n");
	exit(1);
    }

    err = ilclient_create_component(handle,
                                &component,
                                componentName,
                                ILCLIENT_DISABLE_ALL_PORTS
                                );
    if (err == -1) {
	fprintf(stderr, "Component create failed\n");
	exit(1);
    }
    printState(ilclient_get_handle(component));

    err = ilclient_change_component_state(component,
					  OMX_StateIdle);
    if (err < 0) {
	fprintf(stderr, "Couldn't change state to Idle\n");
	exit(1);
    }
    printState(ilclient_get_handle(component));

    err = ilclient_change_component_state(component,
					  OMX_StateExecuting);
    if (err < 0) {
	fprintf(stderr, "Couldn't change state to Executing\n");
	exit(1);
    }
    printState(ilclient_get_handle(component));

    exit(0);
}

      

with output

	
StateLoaded
StateIdle
StateExecuting
	
      

Waiting for events

Although the library generally takes care of events, there are times when your application needs to ensure that certain events have occurred. The function to do this is ilclient_wait_for_event

	
int ilclient_wait_for_event(COMPONENT_T *comp, OMX_EVENTTYPE event,
                            OMX_U32 nData1, int ignore1, 
                            OMX_IN OMX_U32 nData2, int ignore2,
                            int event_flag, int suspend)
	
      

We shall see uses of this later.

Debugging clients

OpenMAX is not a friendly environment when things go wrong. If you are lucky, OpenMAX will generate an error. More often expected state changes won't occur, buffers won't be consumed or produced, etc and you don't get any indication apart from a hung application. The IL Client library can help isolate where these problems occur.

OpenMAX errors are generally handled silently by the IL Client library - i.e. you aren't told about them. However, you can set an error callback function by ilclient_set_error_callback and in this you can try to handle the error.

The simplest handler is just to print the error:

	
ilclient_set_error_callback(handle,
			    error_callback,
			    NULL);

void error_callback(void *userdata, COMPONENT_T *comp, OMX_U32 data) {
    fprintf(stderr, "OMX error %s\n", err2str(data));
}

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

Many IL Client functions return zero on success, a negative value on failure. Sometimes this will tell you what the failure is, but anyway it tells you where it occurs. Combined with the error callback, this can help pin down problems. For example, the command ilclient_wait_for_command_complete returns zero if the command succesfully completed (or returned OMX_ErrorSameState which doesn't really mean an error), or -1 if a different error occurred.

Some functions have a timeout parameter. These functions return zero on success while the failure value will show that the timeout has occurred. This is a strong indication that, for example, an expected change has not occurred. For example, the command ilclient_setup_tunnel returns -1 on timeout and other negative values for other errors.

Conclusion

We have briefly introduced the IL Client library. This will be used extensively in the following chapters.


      

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