Home > Linux, Programming > Raspberry Pi OpenGL and OpenMAX IL “Hello World!” Applications

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

February 18th, 2012 Leave a comment Go to comments

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

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter

  1. jvc
    February 13th, 2013 at 18:26 | #1

    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?

  2. February 13th, 2013 at 19:07 | #2

    @jvc
    Now you’d better try another program called omxplayer, it will support many more formats and codecs.
    I have the instructions to build it http://www.cnx-software.com/2013/01/26/raspberry-pi-now-has-experimental-support-for-vp6-vp8-mjpeg-and-ogg-theora-video-codecs/, but you may not need it since it’s already installed, just run omxplayer with the name of the file.

  3. jvc
    February 14th, 2013 at 01:14 | #3

    @cnxsoft 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.

  4. Eugene
    November 16th, 2014 at 19:58 | #4

    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

  1. No trackbacks yet.