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

OpenMAX Buffers on the Raspberry Pi

OpenMAX components have input buffers to give data to the component and output buffers to return data either to the application or to the next component in a pipeline. This chapter looks at buffer management boith using OpenMAX and the IL Client library on the Raspberry Pi.

Resources

Files

Files used are here

Buffers

Data is transferred into and out of components using buffers, which are just byte arrays. Each component has ports and the ports each have a number of buffers. The program portinfo of the Components chapter can be used to list how many buffers a port has, how many it must minimally have and what the recommended buffer size is.

For example, runing this on the Broadcom video_decode component's input port gives

	
$./portinfo OMX.broadcom.video_decode 130
Component name: OMX.broadcom.video_decode:46 version 0.0, Spec version 1.1
Port 130
  is input port
  Domain is Video
  Buffer count 20
  Buffer minimum count 1
  Buffer size 81920 bytes
	
      

When a component is created it has an array of pointers to buffer headers (a place holder for buffers and information about them) but doesn't actually have any memory allocated either for the headers or for the buffers. These have to be allocated. In the next section we look at the OpenMAX calls to do this, and then in the following section how the IL Client library manages this.

OpenMAX buffer allocation

There are two OpenMAX calls to get buffers for a port. The first is OMX_AllocateBuffer where the IL client asks the component to do all the work for them (Section 3.2.2.15 of the specification). Typical code is

	
for (i = 0; i < pClient-> nBufferCount; i++) {
    OMX_AllocateBuffer(hComp,
    &pClient->pBufferHdr[i],
    pClient->nPortIndex,
    pClient,
    pClient->nBufferSize);
}
	
      

The component must be in the Loaded state or the port must be disabled. Typically, an application will disable the port and then allocate the buffers.

The other call is OMX_UseBuffer (Section 3.2.2.14). This takes a buffer either allocated by the client or by another component and uses that as its own buffer.

The IL Client library

You don't need to worry about buffer allocation. The library does it for you. The function ilclient_enable_port_buffers does the relevant buffer allocation for that port. A typical call is

	
ilclient_enable_port_buffers(component, <port_index>, 
				 NULL, NULL, NULL);
	
      

(You can give your own malloc function but it is easier to let the library use its own.)

Once buffers have been allocated, the port can be moved to enabled state by

	
ilclient_enable_port(component, <port_index>)
	
      

This is a blocking call and will complete when the port has changed state.

We can modify the state change program of the last chapter to create buffers for its ports. For simplicity we hard code a component in this example, the Broadcom image_encode component, which has one input port 340 and one output port 341. Note that we have to set the component create flags to

	
ILCLIENT_DISABLE_ALL_PORTS
 |
ILCLIENT_ENABLE_INPUT_BUFFERS
 |
ILCLIENT_ENABLE_OUTPUT_BUFFERS
	
      

If you don't you may get obscure errors such as

	
assertion failure:ilclient.c:747:ilclient_change_component_state():error == OMX_ErrorNone

Program received signal SIGABRT, Aborted.
0xb6e41bfc in raise () from /lib/arm-linux-gnueabihf/libc.so.6
	
      

If this occurs, it's best to use a debugger such as gdb to step into the ilclient_change_component_state to find out what the actual error is.

The program is il_buffer.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;
    }
}

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 error_callback(void *userdata, COMPONENT_T *comp, OMX_U32 data) {
    fprintf(stderr, "OMX error %s\n", err2str(data));
}

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

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

    componentName = "image_encode";

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

   ilclient_set_error_callback(handle,
			       error_callback,
			       NULL);

    err = ilclient_create_component(handle,
                                &component,
                                componentName,
                                ILCLIENT_DISABLE_ALL_PORTS
				    |
				    ILCLIENT_ENABLE_INPUT_BUFFERS
				    |
				    ILCLIENT_ENABLE_OUTPUT_BUFFERS
                                );
    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));

    // input port
    ilclient_enable_port_buffers(component, 340, 
				 NULL, NULL, NULL);
    ilclient_enable_port(component, 340);
    // the input port is enabled and has input buffers allocated

    // output port
    ilclient_enable_port_buffers(component, 341, 
				 NULL, NULL, NULL);
    ilclient_enable_port(component, 341);
    // the output port is enabled and has output buffers allocated

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

      

Writing to and reading from buffers

The whole point of using OpenMAX on the RPi is to get data into and out of the Broadcom GPU using the OpenMAX components. You do this by loading up the input buffers of a component with data and asking the component to empty them by a call OMX_EmptyThisBuffer. When the component has processed the data it signals a OMX_FillBufferDone event for an output buffer, at which point your application will take the processed data and do something with it.

When a component has finished processing its input buffer data it signals back to the client with an OMX_EmptyBufferDone event. At this point, the client can put more data into the buffer for processing by the component.

Similarly, when a client has finished doing something with the output data, it returns the buffer back to the component by calling a function OMX_FillThisBuffer.

One input buffer may produce none or more buffers of output data. An output buffer can consume data from one or more input buffers. There is no direct correlation between emptying an input buffer and filling an output buffer. Essentially these processes should be done concurrently by the client:

	      
whenever there is an empty input buffer
    put data into the input buffer
    call EmptyThis Buffer
	      
	    
	      
Whenever an output buffer is full
    process its data
    call FillThisBuffer
	      
	    

Now of course things are not that simple. Unless you want to get into Video Core or Posix threads, a sequential version of this is easier. It looks like

	
while there is more input data
    if there is an empty input buffer
         put data into the input buffer
         call EmptyThis Buffer
    if there is a full output buffer
         process its data
         call FillThisBuffer

// cleanup any remaining output data
while there is another full output buffer
     process its data
     call FillThisBuffer
	
      

Of course, this begs the question of how we know that there are empty input buffers and full output buffers. There are IL Client calls to do this

	
ilclient_get_input_buffer
ilclient_get_output_buffer
	
      

and these look after the background event processing for OMX_EmptyBufferDone and OMX_FillBufferDone. These calls can be blocking or non-blocking. Block if you know that there must be a buffer available before you can continue, don't block otherwise (busy wait).

Sequence of actions

The pseudocode for a simple application would ideally look like

	
create component in loaded state with all ports disabled
move the component to idle state
enable input ports (creating input port buffers)
enable output ports (creating output port buffers)
move the component to executing state

while there is more input data
    if there is an empty input buffer
         put data into the input buffer
         call EmptyThis Buffer
    if there is a full output buffer
         process its data
         call FillThisBuffer

while there is another full output buffer
     process its data
     call FillThisBuffer
	
      

The EOS flag

The client will know when there is no more data - it will have finished reading from a file, a socket connection will have closed, etc. It will need to pass this information into the component so that it in turn can pass it back out to the client to signal that there are no more output buffers.

The client signals this by setting the flag OMX_BUFFERFLAG_EOS in the flags field of the buffer header. Typical code to read data from a file into input buffers, setting EOS on completion, is

	
OMX_ERRORTYPE read_into_buffer_and_empty(FILE *fp,
					 COMPONENT_T *component,
					 OMX_BUFFERHEADERTYPE *buff_header,
					 int *toread) {
    OMX_ERRORTYPE r;

    int buff_size = buff_header->nAllocLen;
    int nread = fread(buff_header->pBuffer, 1, buff_size, fp);

    printf("Read %d\n", nread);

    buff_header->nFilledLen = nread;
    *toread -= nread;
    if (*toread <= 0) {
	printf("Setting EOS on input\n");
	buff_header->nFlags |= OMX_BUFFERFLAG_EOS;
    }
    r = OMX_EmptyThisBuffer(ilclient_get_handle(component),
			buff_header);
    if (r != OMX_ErrorNone) {
	fprintf(stderr, "Empty buffer error %s\n",
		err2str(r));
    }
    return r;
}
		   
      

When getting information from the output buffers, the client will be watching for that flag. Minimal code to just print the size of output data and exit on end-of-stream is

	
OMX_ERRORTYPE save_info_from_filled_buffer(COMPONENT_T *component,
					   OMX_BUFFERHEADERTYPE * buff_header) {
    OMX_ERRORTYPE r;

    printf("Got a filled buffer with %d, allocated %d\n", 
	   buff_header->nFilledLen,
	   buff_header->nAllocLen);

    // do something here, like save the data - do nothing this time

    // quit if no more data coming
    if (buff_header->nFlags & OMX_BUFFERFLAG_EOS) {
	printf("Got EOS on output\n");
	exit(0);
    }

    // otherwise refill it
    r = OMX_FillThisBuffer(ilclient_get_handle(component), 
			   buff_header); 
    if (r != OMX_ErrorNone) {
	fprintf(stderr, "Fill buffer error %s\n",
		err2str(r));
    }
    return r;
}
	
      

A hiccup - port settings changed

Little flies in this ointment are these:

The first point will be examined in detail in later chapters. In the following program just ignore the set_image_decoder_input_format for now.

The second problem is dealt with by OpenMAX generating an OMX_PortSettingsChanged event. When this occurs is problematic: it may occur after (or during) the processing of the first block. This seems to occur with JPEG images for example. But there may need to be a number of blocks read. This seems to occur with H.264 video files. At present I don't have it clear exactly what is happening, so this section is expected to change as I get it sorted out.

The IL Client library has two relevant functions to look for OMX_PortSettingsChanged events:

The blocking ilclient_wait_for_event works on a JPEG image. The second method works on an H.264 movie. I haven't found the middle ground yet.

To handle the case of a blocking wait, the following changes need to be made to the pseudo-code:

Pseudo-code now looks like

	
create component in loaded state with all ports disabled
move the component to idle state
enable input ports (creating input port buffers)
move the component to executing state

put data into an input buffer
call EmptyThis Buffer

wait for port settings changed on the output port
enable output ports (creating output port buffers)

while there is more input data
    if there is an empty input buffer
         put data into the input buffer
         call EmptyThis Buffer
    if there is a full output buffer
         process its data
         call FillThisBuffer

while there is another full output buffer
     process its data
     call FillThisBuffer
	
      

Example image decoding

To make this more concrete, the following program decodes an image using the decode_image component. We aren't (at this point) interested in decoding images, so ignore the function void set_image_decoder_input_format whcih just lets the decoder know the input format (JPEG data).

Almost, the program follows the pseudo-code of the last section. The divergence is caused by what seems to be a bug to me. When the client has finished its data, it adds the OMX_BUFFERFLAG_EOS. However, if the total data is less than the size of the image_decode's input buffers, the component doesn't pass this through. The program hangs. The workaround is to reset the data and send it again.

The program is il_decode_image.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

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

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

#define IMG  "cimg0135.jpg"
//#define IMG "hype.jpg"

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

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 eos_callback(void *userdata, COMPONENT_T *comp, OMX_U32 data) {
    fprintf(stderr, "Got eos event\n");
}

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

int get_file_size(char *fname) {
    struct stat st;

    if (stat(fname, &st) == -1) {
	    perror("Stat'ing img file");
	    return -1;
	}
    return(st.st_size);
}

static void set_image_decoder_input_format(COMPONENT_T *component) {
   // set input image format
    printf("Setting image decoder format\n");
    OMX_IMAGE_PARAM_PORTFORMATTYPE imagePortFormat;
    //setHeader(&imagePortFormat,  sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE));
    memset(&imagePortFormat, 0, sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE));
    imagePortFormat.nSize = sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE);
    imagePortFormat.nVersion.nVersion = OMX_VERSION;

    imagePortFormat.nPortIndex = 320;
    imagePortFormat.eCompressionFormat = OMX_IMAGE_CodingJPEG;
    OMX_SetParameter(ilclient_get_handle(component),
                     OMX_IndexParamImagePortFormat, &imagePortFormat);

}

OMX_ERRORTYPE read_into_buffer_and_empty(FILE *fp,
					 COMPONENT_T *component,
					 OMX_BUFFERHEADERTYPE *buff_header,
					 int *toread) {
    OMX_ERRORTYPE r;

    int buff_size = buff_header->nAllocLen;
    int nread = fread(buff_header->pBuffer, 1, buff_size, fp);

    printf("Read %d\n", nread);

    buff_header->nFilledLen = nread;
    *toread -= nread;
    if (*toread <= 0) {
	printf("Setting EOS on input\n");
	buff_header->nFlags |= OMX_BUFFERFLAG_EOS;
    }
    r = OMX_EmptyThisBuffer(ilclient_get_handle(component),
			buff_header);
    if (r != OMX_ErrorNone) {
	fprintf(stderr, "Empty buffer error %s\n",
		err2str(r));
    }
    return r;
}

OMX_ERRORTYPE save_info_from_filled_buffer(COMPONENT_T *component,
					   OMX_BUFFERHEADERTYPE * buff_header) {
    OMX_ERRORTYPE r;

    printf("Got a filled buffer with %d, allocated %d\n", 
	   buff_header->nFilledLen,
	   buff_header->nAllocLen);
    if (buff_header->nFlags & OMX_BUFFERFLAG_EOS) {
	printf("Got EOS on output\n");
	exit(0);
    }

    // do something here, like save the data - do nothing this time

    // and then refill it
    r = OMX_FillThisBuffer(ilclient_get_handle(component), 
			   buff_header); 
    if (r != OMX_ErrorNone) {
	fprintf(stderr, "Fill buffer error %s\n",
		err2str(r));
    }
    return r;
}

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

    int i;
    char *componentName;
    int err;
    ILCLIENT_T  *handle;
    COMPONENT_T *component;
    FILE *fp = fopen(IMG, "r");
    int toread = get_file_size(IMG);
    OMX_BUFFERHEADERTYPE *buff_header;

    componentName = "image_decode";


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

   ilclient_set_error_callback(handle,
			       error_callback,
			       NULL);
   ilclient_set_eos_callback(handle,
			     eos_callback,
			     NULL);


    err = ilclient_create_component(handle,
                                &component,
                                componentName,
                                ILCLIENT_DISABLE_ALL_PORTS
				    |
				    ILCLIENT_ENABLE_INPUT_BUFFERS
				    |
				    ILCLIENT_ENABLE_OUTPUT_BUFFERS
                                );
    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));

    // must be before we enable buffers
    set_image_decoder_input_format(component);

    // input port
    ilclient_enable_port_buffers(component, 320, 
				 NULL, NULL, NULL);
    ilclient_enable_port(component, 320);




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


    // Read the first block so that the component can get
    // the dimensions of the image and call port settings
    // changed on the output port to configure it
    buff_header = 
	ilclient_get_input_buffer(component,
				  320,
				  1 /* block */);
    if (buff_header != NULL) {
	read_into_buffer_and_empty(fp,
				   component,
				   buff_header,
				   &toread);

	// If all the file has been read in, then
	// we have to re-read this first block.
	// Broadcom bug?
	if (toread <= 0) {
	    printf("Rewinding\n");
	    // wind back to start and repeat
	    fp = freopen(IMG, "r", fp);
	    toread = get_file_size(IMG);
	}
    }

    // wait for first input block to set params for output port
    ilclient_wait_for_event(component, 
			    OMX_EventPortSettingsChanged, 
			    321, 0, 0, 1,
			    ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 
			    10000);

    // now enable output port since port params have been set
    ilclient_enable_port_buffers(component, 321, 
				 NULL, NULL, NULL);
    ilclient_enable_port(component, 321);

    // now work through the file
    while (toread > 0) {
	OMX_ERRORTYPE r;

	// do we have an input buffer we can fill and empty?
	buff_header = 
	    ilclient_get_input_buffer(component,
				      320,
				      1 /* block */);
	if (buff_header != NULL) {
	    read_into_buffer_and_empty(fp,
				   component,
				   buff_header,
				   &toread);
	}

	// do we have an output buffer that has been filled?
	buff_header = 
	    ilclient_get_output_buffer(component,
				      321,
				      0 /* no block */);
	if (buff_header != NULL) {
	    save_info_from_filled_buffer(component,
					 buff_header);
	}
    }

    while (1) {
	printf("Getting last output buffers\n");
	buff_header = 
	    ilclient_get_output_buffer(component,
				       321,
				       1 /* block */);
	if (buff_header != NULL) {
	    save_info_from_filled_buffer(component,
					 buff_header);
	}
    }
    exit(0);
}

      

Conclusion

Buffers are the means of communicating data to and from components. This chapter has looked at how to manage buffers.


      

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