Raspberry Pi OpenGL and OpenMAX IL “Hello World!” Applications

As you may already know, Raspberry Pi has released their first SD card image with Debian. This morning, I explained how to use that image in qemu.

I’ve been waiting for samples to take advantage of the power Videocore GPU inside Broadcom BCM2835 SoC used in the Raspberry Pi board and the goods news is that they added Hello World code samples in C to make use of those capabilities.

The sample are located in /opt/vc/src/hello_pi directory:

  • hello_audio – Audio output demo using OpenMAX IL through the ilcient helper library
  • hello_triangle – A rotating cube rendered with OpenGL ES with 3 images used as textures on the cube faces.
  • hello_video – Video decode demo using OpenMAX IL through the ilcient helper library

You can either compile those samples in the board or cross-compile them in your host machine. Since you need the GPU, you will obviously not be able to test those in the emulator but it’s still good to be able to study the code before getting the hardware.

If you don’t have time to check the source code inside the SD card image, here’s video.c OpenMAX IL sample code to playback a h.264 video:


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

#include "bcm_host.h"
#include "ilclient.h"

static int video_decode_test(char *filename)
{
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL, *clock = NULL;
COMPONENT_T *list[5];
TUNNEL_T tunnel[4];
ILCLIENT_T *client;
FILE *in;
int status = 0;
unsigned char *data = NULL;
unsigned int data_len = 0;
int find_start_codes = 0;
int packet_size = 16<<10;
memset(list, 0, sizeof(list));
memset(tunnel, 0, sizeof(tunnel));

if((in = fopen(filename, "rb")) == NULL)
return -2;

if((client = ilclient_init()) == NULL)
{
fclose(in);
return -3;
}

if(OMX_Init() != OMX_ErrorNone)
{
ilclient_destroy(client);
fclose(in);
return -4;
}

if(find_start_codes && (data = malloc(packet_size+4)) == NULL)
{
status = -16;
if(OMX_Deinit() != OMX_ErrorNone)
status = -17;
ilclient_destroy(client);
fclose(in);
return status;
}
// create video_decode
if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0)
status = -14;
list[0] = video_decode;

// create video_render
if(status == 0 && ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0)
status = -14;
list[1] = video_render;

// create clock
if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0)
status = -14;
list[2] = clock;
memset(&cstate, 0, sizeof(cstate));
cstate.nSize = sizeof(cstate);
cstate.nVersion.nVersion = OMX_VERSION;
cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
cstate.nWaitMask = 1;
if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
status = -13;

// create video_scheduler
if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0)
status = -14;
list[3] = video_scheduler;

set_tunnel(tunnel, video_decode, 131, video_scheduler, 10);
set_tunnel(tunnel+1, video_scheduler, 11, video_render, 90);
set_tunnel(tunnel+2, clock, 80, video_scheduler, 12);

// setup clock tunnel first
if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0)
status = -15;
else
ilclient_change_component_state(clock, OMX_StateExecuting);

if(status == 0)
ilclient_change_component_state(video_decode, OMX_StateIdle);

memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE));
format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE);
format.nVersion.nVersion = OMX_VERSION;
format.nPortIndex = 130;
format.eCompressionFormat = OMX_VIDEO_CodingAVC;

if(status == 0 &&
OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone &&
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0)
{
OMX_BUFFERHEADERTYPE *buf;
int port_settings_changed = 0;
int first_packet = 1;

ilclient_change_component_state(video_decode, OMX_StateExecuting);
while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL)
{
// feed data and wait until we get port settings changed
unsigned char *dest = find_start_codes ? data + data_len : buf->pBuffer;

data_len += fread(dest, 1, packet_size+(find_start_codes*4)-data_len, in);

if(port_settings_changed == 0 &&
((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) ||
(data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1,
ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0)))
{
port_settings_changed = 1;

if(ilclient_setup_tunnel(tunnel, 0, 0) != 0)
{
status = -7;
break;
}

ilclient_change_component_state(video_scheduler, OMX_StateExecuting);

// now setup tunnel to video_render
if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0)
{
status = -12;
break;
}

ilclient_change_component_state(video_render, OMX_StateExecuting);
}
if(!data_len)
break;

if(find_start_codes)
{
int i, start = -1, len = 0;
int max_len = data_len > packet_size ? packet_size : data_len;
for(i=2; i<max_len; i++)
{
if(data[i-2] == 0 && data[i-1] == 0 && data[i] == 1)
{
len = 3;
start = i-2;

// check for 4 byte start code
if(i > 2 && data[i-3] == 0)
{
len++;
start--;
}

break;
}
}

if(start == 0)
{
// start code is next, so just send that
buf->nFilledLen = len;
}
else if(start == -1)
{
// no start codes seen, send the first block
buf->nFilledLen = max_len;
}
else
{
// start code in the middle of the buffer, send up to the code
buf->nFilledLen = start;
}

memcpy(buf->pBuffer, data, buf->nFilledLen);
memmove(data, data + buf->nFilledLen, data_len - buf->nFilledLen);
data_len -= buf->nFilledLen;
}
else
{
buf->nFilledLen = data_len;
data_len = 0;
}
buf->nOffset = 0;
if(first_packet)
{
buf->nFlags = OMX_BUFFERFLAG_STARTTIME;
first_packet = 0;
}
else
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;

if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone)
{
status = -6;
break;
}

}

buf->nFilledLen = 0;
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone)
status = -20;

// wait for EOS from render
ilclient_wait_for_event(video_render, OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0,
ILCLIENT_BUFFER_FLAG_EOS, 10000);

// need to flush the renderer to allow video_decode to disable its input port
ilclient_flush_tunnels(tunnel, 0);

ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL);
}

fclose(in);

ilclient_disable_tunnel(tunnel);
ilclient_disable_tunnel(tunnel+1);
ilclient_disable_tunnel(tunnel+2);
ilclient_teardown_tunnels(tunnel);
ilclient_state_transition(list, OMX_StateIdle);
ilclient_state_transition(list, OMX_StateLoaded);

ilclient_cleanup_components(list);

OMX_Deinit();

ilclient_destroy(client);
return status;
}

int main (int argc, char **argv)
{
if (argc < 2) {
printf("Usage: %s <filename>\n", argv[0]);
exit(1);
}
bcm_host_init();
return video_decode_test(argv[1]);
}

That’s it. It make take some time for me to understand as I’m not quite familiar with OpenMAX although parts of it seems similar to what Sigma Designs does in their media processors (loop to send to video decode and wait for End Of Stream).

Share this:

Support CNX Software! Donate via cryptocurrencies, become a Patron on Patreon, or purchase goods on Amazon or Aliexpress

ROCK 5 ITX Rockchip RK3588 mini-ITX motherboard
Subscribe
Notify of
guest
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.
5 Comments
oldest
newest
jvc
jvc
11 years ago

can you explain how find_start_codes would be used? i am guessing something like this is needed to play other videos than the test.h264 file?

jvc
jvc
11 years ago

thanks for the response! I have that version of omxplayer and am looking at the code quite a bit.

I am building an OMXPlayer for openFrameworks and where the primary difference is that I am using EGLImages to get pixel access where omxplayer just draws to the screen. Most of my working code is based off of this example/ilclient and I am able to get the pixels and apply shaders (short demo here: https://vimeo.com/59279581)

Even if I had a clue to just make other h.264/mp4 files work with your code would be a big help.

Eugene
Eugene
10 years ago

You work is amazing… I try to play other video files, and it does not play. How should I convert video to the format that’s can play with your code?

Thanks

Boardcon Rockchip RK3588S SBC with 8K, WiFI 6, 4G LTE, NVME SSD, HDMI 2.1...