Raspberry Pi: Multimedia Programming

Reference: Raspberry Pi GPU Audio Video Programming


Chapter 1 Introduction to the Raspberry Pi


Introduction


ProductSoCSpeedRAMUSB PortsEthernetWireless/Bluetooth GPU
Raspberry Pi Model A+BCM2835700MHz512MB1NoNo
Raspberry Pi Model B+BCM2835700MHz512MB4YesNo
Raspberry Pi 2 Model BBCM2836/7900MHz1GB4YesNo
Raspberry Pi 3 Model BBCM28371200MHz1GB4YesYes
Raspberry Pi ZeroBCM28351000MHz512MB1NoNo
Raspberry Pi Zero WBCM28351000MHz512MB1NoYes
Raspberry Pi Zero WHBCM28351000MHz512MB1NoYes
Raspberry Pi 3 Model B+BCM2837B0, Cortex-A53 (ARMv8)1400MHz1GB4Yes2.4GHz and 5GHz IEEE 802.11.b/g/n/ac wireless LAN, Bluetooth 4.2, BLE Broadcom VideoCore IV @ 250 MHz: H.264, MPEG-4 decode (1080p30); H.264 encode
(1080p30); OpenGL ES 1.1, 2.0 graphics
Raspberry Pi 4 BBCM2711, Quad core Cortex-A72 (ARM v8) 1.5 GHz1 GB , 2 GB, 4 GB2x USB3.0 + 2x USB2.0Gigabit2.4GHz and 5GHz 802.11 b/g/n/ac, Bluetooth 5.0 BLE Broadcom VideoCore VI @ 500 MHz: H.265 (4kp60 decode), H264 (1080p60 decode, 1080p30 encode), OpenGL ES 3.0 graphics

The GPU provides OpenGL ES 2.0, hardware-accelerated OpenVG, and 1080p30 H.264 high-profile encode and decode. While the CPU has changed in the different models, the GPU has remained the same.

RASPBERRY PI CAMERA

RASPBERRY PI CAMERA V1.3RASPBERRY PI CAMERA V2.1
Image Sensor:OmniVision OV5647Sony IMX219
Resolution:2592 × 1944 pixels (5 megapixel)3280 × 2464 (8 megapixel)
Sensor Image Area:3.76 × 2.74 mm3.69 × 2.81 mm
Pixel Size:1.4 µm × 1.4 µm1.12 µm × 1.12 µm
Video:1920 × 1080 (1080p)30p1920 × 1080 (1080p)30p


Resources

Raspberry Pi home page
RPi OpenMAX forum
RPi Graphics forum
RPi OpenGL ES forum
Raspberry Pi VideoCore APIs
Linux Gizmos reports on many SoCs including the RPi
Tutorial: VLC with hardware acceleration on Raspberry Pi
How to draw 2D images using OpenGL, in SDL
Hardware Accelerated Qt Multimedia Backend for Raspberry Pi and OpenGL Shaders on Video
Decoding and Rendering Compressed Images with OpenMAX on Raspberry Pi

Chapter 2 Khronos Group


The Khronos Group is a not-for-profit industry consortium creating open standards including the following:
  • OpenGL
  • OpenGL ES
  • OpenGL ES is a royalty-free, cross-platform API for full-function 2D and 3D graphics on embedded systems.
  • OpenMAX
  • OpenMAX is a royalty-free, cross-platform API that provides comprehensive streaming media codec and application portability by enabling accelerated multimedia components to be developed, integrated, and programmed across multiple operating systems and silicon platforms. The OpenMAX API will be shipped with processors to enable library and codec implementers to rapidly and effectively make use of the full acceleration potential of new silicon, regardless of the underlying hardware architecture.
  • EGL
  • EGL is an interface between Khronos-rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system.
  • OpenVG
  • OpenCL

Resources
Khronos Group

Chapter 3 Compiling Programs for the Raspberry Pi


Generic Information


  • arch
  • Print machine hardware name.
    
    pi@raspberrypi:~ $ arch
    armv7l
    
    
  • /etc/issue
  • /etc/issue is a text file which contains a message or system identification to be printed before the login prompt.
    
    Raspbian GNU/Linux 9 \n \l
    
    
  • /etc/os-release
  • Operating system identification. os-release contains data that is defined by the operating system vendor and should generally not be changed by the administrator.
    
    PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
    NAME="Raspbian GNU/Linux"
    VERSION_ID="9"
    VERSION="9 (stretch)"
    ID=raspbian
    ID_LIKE=debian
    HOME_URL="http://www.raspbian.org/"
    SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
    BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
    
    

GCC


Language Standards Supported by GCC


For each language (C/C++) compiled by GCC, there is a standard GCC tries to follow.
To select a standard,
  • The original ANSI C standard (X3.159-1989) published in 1990
  • Use one of the options: -ansi, -std=c90 or -std=iso9899:1990
  • An amendment to the 1990 standard was published in 1995
  • Use the option -std=iso9899:199409
  • A new edition of the ISO C standard was published in 1999 as ISO/IEC 9899:1999
  • Use -std=c99 or -std=iso9899:1999
  • A fourth version of the C standard, known as C11, was published in 2011 as ISO/IEC 9899:2011.
  • Use the option -std=c11 or -std=iso9899:2011
By default, GCC provides some extensions to the C language ,
  • -std=gnu90
  • for C90 with GNU extensions
  • -std=gnu99
  • for C99 with GNU extensions
  • -std=gnu11
  • for C11 with GNU extensions.
The default, if no C language dialect options are given, is -std=gnu11.

GCC Command Options

Option index.
  • Overall Options
  • The “overall options” allow you to stop this process at an intermediate stage. For example, the -c option says not to run the linker. Then the output consists of object files output by the assembler.
    • --help
    • Print (on the standard output) a description of the command-line options understood by gcc.
    • --target-help
    • Print (on the standard output) a description of target-specific command-line options for each tool(assembler, linker).
  • C Language Options
  • C++ Language Options
  • Objective-C and Objective-C++ Language Options
  • Diagnostic Message Formatting Options
  • Warning Options
  • C and Objective-C-only Warning Options
  • Debugging Options
  • Optimization Options
  • Program Instrumentation Options
  • Preprocessor Options
  • Assembler Options
  • Linker Options
  • Directory Options
  • Code Generation Options
  • Developer Options
    • -Q
    • Makes the compiler print out each function name as it is compiled, and print some statistics about each pass when it finishes.
  • Machine-Dependent Options
    • -march=name
    • Specify the name of the target architecture.
    • -mcpu=name
    • Specify the name of the target processor.
  • Adapteva Epiphany Options
  • ARC Options
  • ARM Options
    • -mabi=name
    • Generate code for the specified ABI(Application Binary Interface). The ABI for the ARM Architecture is a collection of standards. The specifications to which an executable must conform in order to execute in a specific execution environment. Standardizing the inter-operation of binary files requires standardizing certain aspects of code generation itself, so this base standard is aimed principally at the authors and vendors of C and C++ compilers, linkers, and run-time libraries.
    • -mfloat-abi=name
    • Specifies which floating-point ABI to use. Permissible values are: ‘soft’, ‘softfp’ and ‘hard’. 'hard' is the default on Pi.
    • -mtune=name
    • This option specifies the name of the target ARM processor for which GCC should tune the performance of the code.
    • -mfpu=name
    • This specifies what floating-point hardware (or hardware emulation) is available on the target.
    • -mstructure-size-boundary=n
    • The sizes of all structures and unions are rounded up to a multiple of the number of bits set by this option.
    • -mno-sched-prolog
    • Prevent the reordering of instructions in the function prologue, or the merging of those instruction with the instructions in the function’s body. This means that all functions start with a recognizable set of instructions (or in fact one of a choice from a small set of different function prologues), and this information can be used to locate the start of functions inside an executable piece of code. The default is -msched-prolog.
  • AVR Options
  • GNU/Linux Options
  • MIPS Options
  • x86 Options

Chapter 4 Raspberry Pi VideoCore APIs


The Raspberry Pi contains a Broadcom VideoCore IV GPU providing:
  • OpenGL ES 1.1
  • OpenGL ES 2.0
  • hardware-accelerated OpenVG 1.1
  • Open EGL
  • OpenMAX
  • 1080p30 H.264 high-profile decode
This raspberrypi/firmware repository contains pre-compiled binaries of the current Raspberry Pi's:
  • kernel and modules
  • userspace libraries
  • bootloader/GPU firmware
Currently C header files and libraries for many of the Broadcom APIs are installed in /opt/vc/include and /opt/lib respectively. Both are available from GitHub within the same directory structure.
This raspberrypi/userland repository contains the source code for the ARM side libraries used on Raspberry Pi:

interface
├── khronos
│   ├── CMakeLists.txt
│   ├── common
│   ├── egl
│   ├── ext
│   ├── glxx
│   ├── include
│   ├── vg
│   └── wf
├── mmal
│   ├── client
│   ├── CMakeLists.txt
│   ├── components
│   ├── core
│   ├── mmal_buffer.h
│   ├── mmal_clock.h
│   ├── mmal_common.h
│   ├── mmal_component.h
│   ├── mmal_encodings.h
│   ├── mmal_events.h
│   ├── mmal_format.h
│   ├── mmal.h
│   ├── mmal_logging.h
│   ├── mmal_parameters_audio.h
│   ├── mmal_parameters_camera.h
│   ├── mmal_parameters_clock.h
│   ├── mmal_parameters_common.h
│   ├── mmal_parameters.h
│   ├── mmal_parameters_video.h
│   ├── mmal_pool.h
│   ├── mmal_port.h
│   ├── mmal_queue.h
│   ├── mmal_types.h
│   ├── openmaxil
│   ├── test
│   ├── util
│   └── vc
├── peer
│   └── vc_vchi_dispmanx_common.h
├── vchi
│   ├── common
│   ├── connections
│   ├── message_drivers
│   ├── vchi_cfg.h
│   ├── vchi_cfg_internal.h
│   ├── vchi_common.h
│   ├── vchi.h
│   └── vchi_mh.h
├── vchiq_arm
│   ├── CMakeLists.txt
│   ├── vchiq_cfg.h
│   ├── vchiq.h
│   ├── vchiq_if.h
│   ├── vchiq_ioctl.h
│   ├── vchiq_lib.c
│   ├── vchiq_test.c
│   ├── vchiq_test.h
│   ├── vchiq_test_if.h
│   ├── vchiq_util.c
│   └── vchiq_util.h
├── vcos
│   ├── CMakeLists.txt
│   ├── generic
│   ├── glibc
│   ├── pthreads
│   ├── user_nodefs.h
│   ├── vcos_assert.h
│   ├── vcos_atomic_flags.h
│   ├── vcos_attr.h
│   ├── vcos_blockpool.h
│   ├── vcos_build_info.h
│   ├── vcos_cfg.h
│   ├── vcos_cmd.h
│   ├── vcos_ctype.h
│   ├── vcos_dlfcn.h
│   ├── vcos_event_flags.h
│   ├── vcos_event.h
│   ├── vcos.h
│   ├── vcos_init.h
│   ├── vcos_inttypes.h
│   ├── vcos_isr.h
│   ├── vcos_legacy_isr.h
│   ├── vcos_logging_control.h
│   ├── vcos_logging.h
│   ├── vcos_lowlevel_thread.h
│   ├── vcos_mem.h
│   ├── vcos_mempool.h
│   ├── vcos_msgqueue.h
│   ├── vcos_mutex.h
│   ├── vcos_named_semaphore.h
│   ├── vcos_once.h
│   ├── vcos_queue.h
│   ├── vcos_quickslow_mutex.h
│   ├── vcos_reentrant_mutex.h
│   ├── vcos_semaphore.h
│   ├── vcos_stdbool.h
│   ├── vcos_stdint.h
│   ├── vcos_string.h
│   ├── vcos_thread_attr.h
│   ├── vcos_thread.h
│   ├── vcos_timer.h
│   ├── vcos_tls.h
│   └── vcos_types.h
├── vctypes
│   ├── vc_display_types.h
│   ├── vc_image_structs.h
│   └── vc_image_types.h
└── vmcs_host
    ├── CMakeLists.txt
    ├── khronos
    ├── linux
    ├── vc_cec.h
    ├── vc_cecservice_defs.h
    ├── vc_cecservice.h
    ├── vc_cma.h
    ├── vc_dispmanx.h
    ├── vc_dispmanx_types.h
    ├── vc_dispservice_defs.h
    ├── vc_dispservice_x_defs.h
    ├── vc_fileservice_defs.h
    ├── vcfilesys_defs.h
    ├── vcfilesys.h
    ├── vc_gencmd_defs.h
    ├── vcgencmd.h
    ├── vc_hdmi.h
    ├── vc_hdmi_property.h
    ├── vchost.h
    ├── vchost_platform_config.h
    ├── vcilcs.c
    ├── vcilcs_common.c
    ├── vcilcs_common.h
    ├── vc_ilcs_defs.h
    ├── vcilcs.h
    ├── vcilcs_in.c
    ├── vcilcs_out.c
    ├── vc_imageconv_defs.h
    ├── vc_sdtv.h
    ├── vc_service_common.c
    ├── vc_service_common.h
    ├── vc_tvservice_defs.h
    ├── vc_tvservice.h
    ├── vc_vchi_audioserv_defs.h
    ├── vc_vchi_bufman_defs.h
    ├── vc_vchi_bufman.h
    ├── vc_vchi_cecservice.c
    ├── vc_vchi_dispmanx.c
    ├── vc_vchi_dispmanx.h
    ├── vc_vchi_fileservice_defs.h
    ├── vc_vchi_filesys.c
    ├── vc_vchi_filesys.h
    ├── vc_vchi_gencmd.c
    ├── vc_vchi_gencmd.h
    ├── vc_vchi_gpuserv.c
    ├── vc_vchi_gpuserv.h
    └── vc_vchi_tvservice.c



The VideoCore IV GPU is running the ThreadX.
VCOS (VideoCore OS abstractions) is an abstraction layer which is a wrapper around the library of functions over the ThreadX and compiled for the Linux side.
VCOS tasked with performing certain system services, for example running the display and audio outputs.
The VideoCore Host Interface (VCHI) Queue is a message passing system provided to permit communication between these SoC and VC components. The VCHI driver provides an interface to allow Linux drivers to talk to the VideoCore GPU.
The camera module is connected to the SoC via a CSI-2 interface (providing 2 Gbps of bandwidth).

API Guides


raspberrypi/userland

Source code for ARM side libraries for interfacing to Raspberry Pi GPU.
  • The Raspberry Pi requires that the bcm_host_init() function is called first before any GPU calls can be made.
  • raspberrypi/userland/host_applications/linux/libs/bcm_host/bcm_host.c :
    
    
    void bcm_host_init(void)
    {
       VCHIQ_INSTANCE_T vchiq_instance;
       static int initted;
       int success = -1;
       char response[ 128 ];
      
       if (initted)
            return;
       initted = 1;
       vcos_init();
    
       if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS)
       {
          printf("* failed to open vchiq instance\n");
          exit(-1);
       }
    
       vcos_log("vchi_initialise");
       success = vchi_initialise( &global_initialise_instance);
       vcos_assert(success == 0);
       vchiq_instance = (VCHIQ_INSTANCE_T)global_initialise_instance;
    
       global_connection = vchi_create_connection(single_get_func_table(),
                                                  vchi_mphi_message_driver_func_table());
    
       vcos_log("vchi_connect");
       vchi_connect(&global_connection, 1, global_initialise_instance);
    
       vc_vchi_gencmd_init (global_initialise_instance, &global_connection, 1);
       vc_vchi_dispmanx_init (global_initialise_instance, &global_connection, 1);
       vc_vchi_tv_init (global_initialise_instance, &global_connection, 1);
       vc_vchi_cec_init (global_initialise_instance, &global_connection, 1);
       //vc_vchi_bufman_init (global_initialise_instance, &global_connection, 1);
    
       if ( success == 0 )
       {
          success = vc_gencmd( response, sizeof(response), "set_vll_dir /sd/vlls" );
          vcos_assert( success == 0 );
       }
    }
    
    
  • Before calling any of the vc_* functions, you need to initialise vcos and vchi, and make a vchi connection, then, calling the corresponding vc_*_init function for the part of the library you want to use
  • For ex., userland/host_applications/linux/apps/hello_pi/hello_encode/encode.c:
    
    int
    main(int argc, char **argv)
    {
       if (argc < 2) {
          printf("Usage: %s \n", argv[0]);
          exit(1);
       }
       bcm_host_init();
       return video_encode_test(argv[1]);
    }
    
  • Linking against the EGL library (-lEGL) also requires you to link against the GLESv2 library (-lGLESv2).
  • graphics_get_display_size() is a Broadcom-specific function.
  • The raspberrypi/firmware repository has short examples for these and other APIs. They can be found in /opt/vc/src/hello_pi/ on the Raspbian 'wheezy' image.

raspberrypi/firmware


This repository contains pre-compiled binaries of the current Raspberry Pi kernel and modules, userspace libraries, and bootloader/GPU firmware.
  • Download the raspberrypi/firmware
  • Download the source code and unzip it.
    
    wget https://github.com/raspberrypi/firmware/archive/master.zip
    unzip master.zip
    
    
    The user space libraries for the VideCoreIV - EGL/GLES/OpenVG are under firmware-master/opt/vc. To build these examples on Pi,
  • Build the libraries first
  • Provided examples are based on the usage of libs/ilclient/libilclient.a and libs/vgfont/libvgfont.a
    
    cd firmware-master/opt/vc/src/hello_pi
    make -C libs/ilclient
    make -C libs/vgfont
    
    
    If you see the following build error:
    
    fatal error: ft2build.h: No such file or directory
    
    Check the include path:
    
    pkg-config --cflags freetype2
    
    
    If "Package freetype2 was not found in the pkg-config search path.", you need to install it:
    
    sudo apt-get install libfreetype6 libfreetype6-dev
    
    
    Then, you can build all examples by "make" , or, build the example independently. For ex.,
    
    cd hello_world
    make
    
    
    To rebuild all libs and and apps,
    
    ./rebuild.sh
    
    
  • To build these examples on a different PC(cross-compile)
  • Before running make, set:
    
    SDKSTAGE=path/to/firmware-directory
    CC=path/to/cross-compiler
    
    

API examples


hello_world

"hello_word" is a simple program which just prints out a string "hello world".
The purpose is to test if the build environment is setup correctly.

hello_triangle

This sample shows how to get a OpenGL ES context on the Raspberry Pi.

hello_triangle2


hello_video

This sample decodes H.264 video using the OpenMAX APIs and the ilclient library and is distributed with a short scene of Big Buck Bunny

hello_audio

This sample plays a sine wave for ten seconds using the OpenMAX APIs.

hello_font


hello_dispmanx


hello_tiger


hello_encode

This sample is a GPU-assisted H.264 encoder using the OpenMAX APIs, and currently works with raw frames. It outputs a raw H.264 file.

hello_jpeg


hello_videocube


hello_teapot


hello_fft


hello_mmal_encode



DispmanX


There is an API for the Raspberry Pi called DispmanX that allows you to add layers that are displayed over other layers.
In general, people don’t write their graphics applications using Dispmanx APIs directly. Instead, they use Dispmanx to get a window that is then used by a framework such as EGL and from there by OpenGLES.
There is a program called hello_dispmanx which is a very small example of the Dispmanx windowing system.

// A simple demo using dispmanx to display an overlay

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>

#include "bcm_host.h"

#define WIDTH   200
#define HEIGHT  200

#ifndef ALIGN_UP
#define ALIGN_UP(x,y)  ((x + (y)-1) & ~((y)-1))
#endif

typedef struct
{
    DISPMANX_ELEMENT_HANDLE_T   element;
    DISPMANX_DISPLAY_HANDLE_T   display;
    DISPMANX_UPDATE_HANDLE_T    update;
    DISPMANX_MODEINFO_T         info;
    void                       *image;
    DISPMANX_RESOURCE_HANDLE_T  resource;
    uint32_t                    vc_image_ptr;

} RECT_VARS_T;

static RECT_VARS_T  gRectVars;

static void FillRect( VC_IMAGE_TYPE_T type, void *image, int pitch, int aligned_height, int x, int y, int w, int h, int val )
{
    int         row;
    int         col;

    uint16_t *line = (uint16_t *)image + y * (pitch>>1) + x;

    for ( row = 0; row < h; row++ )
    {
        for ( col = 0; col < w; col++ )
        {
            line[col] = val;
        }
        line += (pitch>>1);
    }
}

int main(void)
{
    RECT_VARS_T    *vars;
    uint32_t        screen = 0;
    int             ret;
    VC_RECT_T       src_rect;
    VC_RECT_T       dst_rect;
    VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565;
    int width = WIDTH, height = HEIGHT;
    int pitch = ALIGN_UP(width*2, 32);  // VC_IMAGE_RGB565 means the color space is 2 bytes(16-bits)
    int aligned_height = ALIGN_UP(height, 16);
    VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 
                             120, /*alpha 0->255*/
                             0 };

    vars = &gRectVars;

    bcm_host_init();

    printf("Open display[%i]...\n", screen );
    vars->display = vc_dispmanx_display_open( screen );

    ret = vc_dispmanx_display_get_info( vars->display, &vars->info);
    assert(ret == 0);
    printf( "Display is %d x %d\n", vars->info.width, vars->info.height );

    vars->image = calloc( 1, pitch * height );
    assert(vars->image);

    FillRect( type, vars->image, pitch, aligned_height,  0,  0, width,      height,      0xFFFF );
    FillRect( type, vars->image, pitch, aligned_height,  0,  0, width,      height,      0xF800 );
    FillRect( type, vars->image, pitch, aligned_height, 20, 20, width - 40, height - 40, 0x07E0 );
    FillRect( type, vars->image, pitch, aligned_height, 40, 40, width - 80, height - 80, 0x001F );

    vars->resource = vc_dispmanx_resource_create( type,
                                                  width,
                                                  height,
                                                  &vars->vc_image_ptr );
    assert( vars->resource );
    vc_dispmanx_rect_set( &dst_rect, 0, 0, width, height);
    ret = vc_dispmanx_resource_write_data(  vars->resource,
                                            type,
                                            pitch,
                                            vars->image,
                                            &dst_rect );
    assert( ret == 0 );
    vars->update = vc_dispmanx_update_start( 10 );
    assert( vars->update );

    vc_dispmanx_rect_set( &src_rect, 0, 0, width << 16, height << 16 );

    vc_dispmanx_rect_set( &dst_rect, ( vars->info.width - width ) / 2,
                                     ( vars->info.height - height ) / 2,
                                     width,
                                     height );

    vars->element = vc_dispmanx_element_add(    vars->update,
                                                vars->display,
                                                2000,               // layer
                                                &dst_rect,
                                                vars->resource,
                                                &src_rect,
                                                DISPMANX_PROTECTION_NONE,
                                                &alpha,
                                                NULL,             // clamp
                                                VC_IMAGE_ROT0 );

    ret = vc_dispmanx_update_submit_sync( vars->update );
    assert( ret == 0 );

    printf( "Sleeping for 10 seconds...\n" );
    sleep( 10 );

    vars->update = vc_dispmanx_update_start( 10 );
    assert( vars->update );
    ret = vc_dispmanx_element_remove( vars->update, vars->element );
    assert( ret == 0 );
    ret = vc_dispmanx_update_submit_sync( vars->update );
    assert( ret == 0 );
    ret = vc_dispmanx_resource_delete( vars->resource );
    assert( ret == 0 );
    ret = vc_dispmanx_display_close( vars->display );
    assert( ret == 0 );

    return 0;
}
After the call to vc_dispmanx_element_add(), you have a window stored in vars->element that can be used as a native window object later.
You just use Dispmanx to get some information about the display size and then to create a window that takes up the full screen. Anything else is just a building block on the way to using the Dispmanx window within a higher-level API such as OpenGL ES. For ex.,

    static EGL_DISPMANX_WINDOW_T nativewindow;
    nativewindow.element = vars->element;
    nativewindow.width = width;
    nativewindow.height = height;
    vc_dispmanx_update_submit_sync( vars->display );



raspidmx


raspidmx is a library created by AndrewFromMelbourne that leverages some of the capabilities of the Dispmanx API.
Several programs can be used as a starting point for anyone wanting to make use of DispmanX.
Its a great starting point for anyone wishing to learn the Dispmanx API.
To install raspidmx,
  • Install libpng
  • 
    sudo apt-get install libpng12-dev
    
    
  • Downloadthe source code then build it
  • 
    cd /home/pi/raspidmx
    unzip ~/Downloads/raspidmx-master.zip
    cd raspidmx-master
    sudo make
    export LD_LIBRART_PATH=$PWD/lib
    
  • Test
    • test_pattern
    • The tested pattern is the same four colour square displayed when the Raspberry Pi boots.
      
      test_pattern/test_pattern
      
      
    • pngview
    • Usage: pngview [-b ] [-d ] [-l] [-x ] [-y ] file.png -b – set background colour 16 bit RGBA. For ex., 0x000F is opaque black -d – Raspberry Pi display number -l – DispmanX layer number -x – offset (pixels from the left) -y – offset (pixels from the top)
      
      pngview/pngview -b 0 -l 3 /home/pi/Downloads/image1.png
      

GPU memory


Showing the memory used by the GPU is trickier, and there is a Broadcom tool /opt/vc/bin/vcdbg to show that.
For ex., "gpu_mem=256" is set in /boot/config.txt:

$ sudo vcdbg reloc

Relocatable heap version 4 found at 0x30000000
total space allocated is 236M, with 236M relocatable, 0 legacy and 0 offline
0 legacy blocks of size 2359296

free list at 0x3e401d20
228M free memory in 1 free block(s)
largest free block is 228M bytes

0x30000000: free 228M
[   4] 0x3e401d40: used  576 (refcount 1 lock count 0, size      512, align    4, data 0x3e401d60, d0rual) 'ILCS VC buffer pool'
[   3] 0x3e401f80: used 8.0M (refcount 1 lock count 8, size  8355840, align 4096, data 0x3e402000, d1rual) 'ARM FB'
[   2] 0x3ebfafa0: used  16K (refcount 1 lock count 0, size    16384, align   32, data 0x3ebfafc0, d0ruAl) 'audioplus_tmp_buf'
[   1] 0x3ebfefe0: used 4.0K (refcount 1 lock count 0, size        0, align 4096, data 0x3ebff000, d1rual) 'camera fast alloc arena'
small allocs not requested


Chapter 5 EGL on the Raspberry Pi


Chapter 6: OpenGL ES on the Raspberry Pi


Chapter 7: OpenMAX on the Raspberry Pi Concepts


OpenMAX Overview



  • OpenMAX AL (Application Layer)
  • OpenMAX AL provides a standardized interface between an application and multimedia middleware, where multimedia middleware provides the services needed to perform expected API functionality. OpenMAX AL provides application portability with regards to the multimedia interface.
  • OpenMAX IL (Integration Layer)
  • OpenMAX IL serves as a low-level interface for audio, video, and imaging codecs used in embedded and/or mobile devices. It gives applications and media frameworks the ability to interface with multimedia codecs and supporting components (i.e., sources and sinks) in a unified manner. The codecs themselves may be any combination of hardware or software and are completely transparent to the user. Without a standardized interface of this nature, codec vendors must write to proprietary or closed interfaces to integrate into mobile devices. The principal goal of the IL is to give codecs a degree of system abstraction using a specialized arsenal of features, honed to combat the problem of portability among many vastly different media systems.
  • OpenMAX DL (Development Layer)
  • OpenMAX DL defines an API which contains a comprehensive set of audio, video and imaging functions that can be implemented and optimized on new processors by silicon vendors and then used by codec vendors to code a wide range of codec functionality. It includes audio signal processing functions such as FFTs and filters, imaging processing primitives such as color space conversion and video processing primitives to enable the optimized implementation of codecs such as MPEG-4, H.264, MP3, AAC and JPEG. OpenMAX supports acceleration concurrency via both iDL, which uses OpenMAX IL constructs, and aDL which adds asynchronous interfaces to the OpenMAX DL API.

The RPi does not support OpenMAX AL. What it does support is OpenMAX IL, the integration layer.

Resources


OpenMAX IL Overview


The OpenMAX IL (Integration Layer) API defines a standardized media component interface to enable developers and platform providers to integrate and communicate with multimedia codecs implemented in hardware or software.
The OpenMAX DL defines an API which contains a comprehensive set of audio, video, and imaging primitives that may be used to implement OpenMAX IL components.
The current version of OpenMAX IL is 1.1.2.


The DL primitives and their full relationship to the IL are specified in the OpenMAX Development Layer API specification documents.

The OpenMAX IL API is a component-based media API that consists of two main segments: the core API and the component API.

Consider a system that requires the implementation of four multimedia processing functions denoted as F1, F2, F3, and F4. Each of these functions may be from different vendors.

The OpenMAX IL API provides a means of encapsulating these functions.
The API includes a standard protocol that enables compliant components to exchange data with one another and be used interchangeably.
The OpenMAX IL API interfaces with a higher-level entity denoted as the IL client : OpenMAX AL, or an application.
The IL client uses the OpenMAX IL core for:
  • loading and unloading components
  • setting up direct communication between two OpenMAX IL components
  • accessing the component’s methods
An OpenMAX IL component provides access to a standard set of component functions via its component handle.
Each component has zero or more input and output ports. Each port has a type, such as an audio port, a video port, or a timer port. The type of ports are important: you manipulate different types with different structures. Each port can have one or more buffers that carry data either into or out of a component.
OpenMAX IL components use buffers to carry data. A component will usually process data from an input buffer and place it on an output buffer. This processing is not visible to the API, so it allows vendors to implement components in hardware or software which may be built on top of other A/V components.

OpenMax IL Architectural Overview



The layer of software that invokes the APIs of the core or component is called IL client.
The IL client uses the OpenMAX IL core for loading and unloading components, setting up direct communication between two OpenMAX IL components, and accessing the component’s methods.

Each component can have an arbitrary number of ports for data communication. Components with a single output port are referred to as source components. Components with a single input port are referred to as sink components.

Each OpenMAX IL component can undergo a series of state transition:

  • Every component is first considered to be unloaded. The component shall be loaded through a call to the OpenMAX IL core. All other state transitions may then be achieved by communicating directly with the component.
  • A component can enter an invalid state when a state transition is made with invalid data.
  • The IL client shall stop, de-initialize, unload, and reload the component when the IL client detects an invalid state.
  • The only way to exit the invalid state is to unload and reload the component.

A port shall support callbacks to the IL client and, when part of an interop profile component, shall support communication with ports on other components.
Data communication is specific to a port of the component:
  • Input ports are always called from the IL client with OMX_EmptyThisBuffer
  • The OMX_EmptyThisBuffer macro will send a filled buffer to an input port of a component.
    • When the buffer contains data, the value of the nFilledLen field of the buffer header will not be zero.
    • If the buffer contains no data, the value of nFilledLen is 0x0.
    The OMX_EmptyThisBuffer macro is invoked to pass buffers containing data when the component is in or making a transition to the OMX_StateExecuting or in the OMX_StatePause state.
    
    #define OMX_EmptyThisBuffer (
      hComponent,
      pBuffer     )
      ((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer(
            hComponent,
            pBuffer)
    
  • Output ports are always called from the IL client with OMX_FillThisBuffer
  • The OMX_FillThisBuffer macro will send an empty buffer to an output port of a component. The OMX_FillThisBuffer macro is invoked to pass buffers containing no data when the component is in or making a transition to the OMX_StateExecuting state or is in the OMX_StatePaused state. This call is a non-blocking call since the component will queue the buffer and return immediately. The component should return from this call within five milliseconds.
    
    #define OMX_FillThisBuffer (
      hComponent,
      pBuffer     )
      ((OMX_COMPONENTTYPE*)hComponent)->FillThisBuffer(
            hComponent,
            pBuffer)
    
    
    • When a port is non-tunneled
    • buffers sent to OMX_FillThisBuffer() return to the IL client with the FillBufferDone callback once they have been filled.
    • When a port is tunneled
    • buffers sent to OMX_FillThisBuffer() are sent to the tunneled port once they are filled.
    If the parameter nOutputPortindex in the buffer header does not specify a valid output port, the component returns OMX_ErrorBadPortIndex.

Data communications with components is always directed to a specific component port. Each port has a component-defined minimum number of buffers it shall allocate or use. A port associates a buffer header with each buffer. A buffer header references data in the buffer and provides metadata associated with the contents of the buffer.
A component will usually process data from an input buffer and place it on an output buffer.
The entity that “owns” the buffer passed into a port is called "Buffer Supplier".
The set of buffer requirements for a port includes the number of buffers required and the required size of each buffer.

Any buffer used in a port should be registered on it using the functions OMX_UseBuffer()/OMX_AllocateBuffer() during the setup phase of the component (Loaded state).

A buffer can be marked in its buffer header. The IL client can send a command to mark a buffer. The mark is internally transmitted from an input buffer to an output buffer in a chain of OpenMAX IL components. The mark enables a component to send an event to the IL client when the marked buffer is encountered.


OpenMax IL Core


The OpenMAX IL core is used for dynamically loading and unloading components and for facilitating component communication.
Once loaded, the API allows the IL clients to communicate directly with the component, which eliminates any overhead for high commands. Similarly, the core allows a user to establish a communication tunnel between two components. Once established, the core API is no longer used and communications flow directly between components.

OpenMAX IL Core functions

  • OMX_Init(), OMX_Deinit()
  • Initialize and de-initialize the core environment.
  • OMX_ComponentNameEnum(), OMX_GetComponentsOfRole(), OMX_GetRolesOfComponent()
  • These functions provide information about the components available in the system, the components that support a specific role and the roles supported by a given component.
    • OMX_ComponentNameEnum(cComponentName, nNameLength, nIndex)
    • The OMX_ComponentNameEnum method will enumerate through all the names of recognized components in the system to detect all the components in the system run-time. cComponentName is a pointer to a null-terminated string with the component name.
  • OMX_GetHandle(), OMX_FreeHandle()
  • These functions are used to create and destroy an instance of a given component
  • OMX_SetupTunnel()
  • This function tries to establish a tunnel between two ports of two components.

OpenMax IL Components


Each functional block (HW, SW, or a combination) that is required to implement a particular multimedia function, such as video capture and video encode, is represented as a component.
Components are represented as object oriented classes; where base functionality is inherited from the OMX_BASE class and standard APIs are extended to suit the component.

An OpenMAX IL component is a black box that receives instruction with asynchronous commands and transmit/receive data through ports. Ports represent both the connection for components to the data stream and the buffers needed to maintain the connection. Users may send data to components through input ports or receive data through output ports. Similarly, a communication tunnel between two components can be established by connecting the output port of one component to a similarly formatted input port of another component.

Buffer status, errors, and other time-sensitive data are relayed to the application via a set of callback functions.

The individual parameters of a component can be set or retrieved through a set of associated data structures, enumerations, and interfaces.

A component is base profile compliant if it supports the standard and buffer are exchanged only with an IL client.
A component is interop profile compliant if it supports the base profile and is able to setup the tunneling with other interop components.

Standard Components


OpenMAX IL defines a set of standard components.
OpenMAX IL establishes two constructs for the hierarchical definition of the set of standard components:
  • Standard component class
  • a category of standard components that share the same ports and high level functionality.
  • Standard component
  • an instance of a standard component class that has the same ports and high level functionality as the class but that specifies the supported formats, parameters, and configs on those ports as well as the specific functionality of the component.
For instance, OpenMAX IL defines
  • the audio_decoder class
  • This represents all components that receive encoded audio on a single audio input port and emit decoded audio on single audio output. The audio_decoder class contains a standard component definition for each audio format: audio_decoder.mp3, audio_decoder.aac, audio_decoder.amr, etc.
  • the differences between components in the same audio_decoder class
  • The difference may be the specific format of audio decoding implemented , or, in terms of their specific functionality.
Thus, generally speaking, a component class defines a category of functionality and each component in that class implements one specific type of functionality within that category.

Component Role


A role is defined as the behavior of component acting according to a particular standard component definition.
The name of the standard component defining the behavior identifies the role.
For example, a given component implementation named “OMX.CompanyXYZ.MyAudioDecoder” might support the following roles:
audio_decoder.mp3, audio_decoder.aac, audio_decoder.amr
Two convenient functions are provided for the IL client to query about which roles are supported by which component implementation:
  • OMX_GetRolesOfComponent
  • query all the roles fulfilled by a given a component.
  • OMX_GetComponentsOfRole
  • query the names of all installed components that support a given role.

OpenMAX IL Control API


OpenMAX IL Types


Enumerations

There are 6 enumeration types defined in OMX_Core.h:
  • OMX_COMMANDTYPE
  • used to specify the action in the OMX_SendCommand macro.
    
    typedef enum OMX_COMMANDTYPE
    {
        OMX_CommandStateSet,    /** Change the component state */
        OMX_CommandFlush,       /** Flush the data queue(s) of a component */
        OMX_CommandPortDisable, /** Disable a port on a component. */
        OMX_CommandPortEnable,  /** Enable a port on a component. */
        OMX_CommandMarkBuffer,  /** Mark a component/buffer for observation */
        OMX_CommandKhronosExtensions = 0x6F000000, /** Reserved region for introducing Khronos Standard Extensions */
        OMX_CommandVendorStartUnused = 0x7F000000, /** Reserved region for introducing Vendor Extensions */
        OMX_CommandMax = 0X7FFFFFFF
    } OMX_COMMANDTYPE;
    
  • OMX_STATETYPE (3.1.1.2)
  • used to indicate or change the component state. Figure 3-1 illustrates the transitions among states that occur as a consequence of the IL client calling OMX_SendCommand( OMX_StateSet, state), where the new state for the component is passed as a parameter.
    • OMX_StateLoaded
    • Component has been loaded but has no resources allocated. A component is in the OMX_StateLoaded state after it has been created via an OMX_GetHandle() call and before allocation of its resources.
    • OMX_StateIdle
    • Component has all resources but has not transferred any buffers or begun processing data. The component shall acquire all of its static resources, including buffers for all enabled ports, before completing the transition from OMX_StateLoaded to OMX_StateIdle. Note, the component does not acquire buffers for any disabled ports.
    • OMX_StateExecuting
    • Component is transferring buffers and is processing data (if data is available).
      • Any port that communicates with the IL client
      • shall call the EmptyBufferDone and FillBufferDone callbacks to return an empty or full buffer, respectively, back to the IL client.
      • Any tunneling port
      • shall call OMX_FillThisBuffer() or OMX_EmptyThisBuffer() on its corresponding tunneled port to return an empty or full buffer, respectively, back to its tunneled port.
    • OMX_StateInvalid
    • Component is corrupt or has encountered an error from which it cannot recover.
    • OMX_StatePause
    • Component data processing has been paused but may be resumed from the point it was paused.
    • OMX_StateWaitFor
    • Resources Component is waiting for a resource to become available.
  • OMX_ERRORTYPE
  • defines the standard OMX Errors
  • OMX_EVENTTYPE
  • OMX_BUFFERSUPPLIERTYPE
  • used to dictate port supplier preference when tunneling between two ports.
  • OMX_EXTRADATATYPE
  • used to define the possible extra data payload types.

Data Types

  • OMX_U8
  • An 8 bit unsigned quantity that is byte aligned.
  • OMX_S8
  • An 8 bit signed quantity that is byte aligned.
  • OMX_U16
  • A 16 bit unsigned quantity that is 16 bit word aligned.
  • OMX_S16
  • A 16 bit signed quantity that is 16 bit word aligned.
  • OMX_U32
  • A 32 bit unsigned quantity that is 32 bit word aligned.
  • OMX_S32
  • A 32 bit signed quantity that is 32 bit word aligned.
  • OMX_U64
  • A 64 bit unsigned quantity that is 64 bit word aligned.
  • OMX_S64
  • A 64 bit signed quantity that is 64 bit word aligned.
  • OMX_BU32
  • A bounded 32 bit unsigned quantity.
    
    typedef struct OMX_BU32 {
        OMX_U32 nValue;
        OMX_U32 nMin;
        OMX_U32 nMax;
    } OMX_BU32;
    
    nValue represents the actual value; nMin represents the minimum for the value (i.e. nValue >= nMin) nMax represents the maximum for the value (i.e. nValue <= nMax)
  • OMX_BS32
  • A bounded 32 bit signed quantity.
    
    typedef struct OMX_BS32 {
        OMX_S32 nValue;
        OMX_S32 nMin;
        OMX_S32 nMax;
    } OMX_BS32;
    
    nValue represents the actual value; nMin represents the minimum for the value (i.e. nValue >= nMin) nMax represents the maximum for the value (i.e. nValue <= nMax)
  • OMX_BOOL
  • Represents a true (OMX_TRUE) or false (OMX_FALSE) value.
  • OMX_PTR
  • Used to pass pointers between the OMX applications and the OMX Core and components.
  • OMX_STRING
  • Used to pass "C" type strings between the application and the core and component. This is a pointer to a zero terminated string.
  • OMX_UUIDTYPE
  • A very long unique identifier to uniquely identify at runtime. This identifier should be generated by a component in a way that guarantees that every instance of the identifier running on the system is unique.
  • OMX_ALL
  • Used to as a wildcard to select all entities of the same type when specifying the index, or referring to an object by an index. (I.e. use OMX_ALL to indicate all N channels). When used as a port index for a config, parameter or command OMX_ALL denotes that the config, parameter or command applies to all ports on the component.
  • OMX_TICKS
  • Used to represent time or duration in microseconds. Refer to 6.2.1 for more information.
  • OMX_HANDLETYPE
  • Defines the component handle as seen by the IL client. The component handle is used by the IL core to access all of the public methods of the component. The component handle also contains pointers to the private data area of the component. The OpenMAX IL core allocates and initializes the component handle with help from the component during the process of loading the component. After the component is successfully loaded, the IL client can safely access any of the public functions of the component via the IL core macros, although some may return an error because the state of the component is inappropriate for the access. The component handle shall not be used directly by the IL client or other IL components to access the public methods of an implementation. The IL core macros shall be used instead.

Structures


The first two fields of each OpenMAX IL defined data structure are:

 OMX_U32 nSize;             /* size of the structure in bytes */
 OMX_VERSIONTYPE nVersion;  /* OMX specification version information */

The entity that allocates an OpenMAX IL defined structure is responsible for filling in these two values.

The OMX_INDEXTYPE enumeration is used to select a structure when either getting or setting parameters and/or configuration data.
Each entry in this enumeration maps to an OMX specified structure. OMX_Index.h defines it:

typedef enum OMX_INDEXTYPE {
    OMX_IndexComponentStartUnused = 0x01000000,
    OMX_IndexParamPriorityMgmt,                 /**< reference: OMX_PRIORITYMGMTTYPE */
    OMX_IndexParamAudioInit,                /**< reference: OMX_PORT_PARAM_TYPE */
    OMX_IndexParamImageInit,                /**< reference: OMX_PORT_PARAM_TYPE */
    OMX_IndexParamVideoInit,                /**< reference: OMX_PORT_PARAM_TYPE */
    OMX_IndexParamOtherInit,                /**< reference: OMX_PORT_PARAM_TYPE */
.
    OMX_IndexPortStartUnused = 0x02000000,
    OMX_IndexParamPortDefinition,           /**< reference: OMX_PARAM_PORTDEFINITIONTYPE */
    OMX_IndexParamCompBufferSupplier,       /**< reference: OMX_PARAM_BUFFERSUPPLIERTYPE */ 
    OMX_IndexReservedStartUnused = 0x03000000,
    /* Audio parameters and configurations */
    OMX_IndexAudioStartUnused = 0x04000000,
    OMX_IndexParamAudioPortFormat,          /**< reference: OMX_AUDIO_PARAM_PORTFORMATTYPE */
.
    /* Image specific parameters and configurations */
    OMX_IndexImageStartUnused = 0x05000000,
    OMX_IndexParamImagePortFormat,          /**< reference: OMX_IMAGE_PARAM_PORTFORMATTYPE */
.
    /* Video specific parameters and configurations */
    OMX_IndexVideoStartUnused = 0x06000000,
    OMX_IndexParamVideoPortFormat,          /**< reference: OMX_VIDEO_PARAM_PORTFORMATTYPE */
.
    /* Image & Video common Configurations */
    OMX_IndexCommonStartUnused = 0x07000000,
    OMX_IndexParamCommonDeblocking,         /**< reference: OMX_PARAM_DEBLOCKINGTYPE */
.
    /* Reserved Time range */
    OMX_IndexTimeStartUnused = 0x09000000,
    OMX_IndexConfigTimeScale,               /**< reference: OMX_TIME_CONFIG_SCALETYPE */
.
    OMX_IndexMax = 0x7FFFFFFF
} OMX_INDEXTYPE;


OpenMAX IL Core Methods/Macros


The OpenMAX IL core implements the main interface for an IL client that wants to use OpenMAX IL components.

Functions

  • OMX_Init()
  • The OMX_Init() method initializes the OpenMAX IL core. Each OpenMAX IL client shall use OMX_Init() as their first call into OpenMAX IL.
  • OMX_Deinit()
  • An OpenMAX IL client shall use OMX_Deinit() as their last call into OpenMAX IL, after all OpenMAX IL-related resources have been released.
  • OMX_GetHandle()
  • If the component is valid, OMX_GetHandle() will invoke the component's methods to fill the component handle and set up the callbacks.
    
    OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle(
      OMX_OUT OMX_HANDLETYPE *pHandle,
      OMX_IN OMX_STRING cComponentName,
      OMX_IN OMX_PTR pAppData,
      OMX_CALLBACKTYPE *pCallBacks
    )
    
    
    Parameters:
    • in
    • cComponentName, pAppData, pCallBacks.
    • out
    • pHandle
  • OMX_FreeHandle()
  • The IL client should call OMX_FreeHandle() only when the component is in the OMX_StateLoaded and when all the ports are not connected via any tunnels.
    
    do {
          OMX_GetState(hComp, &eState);
    } while (OMX_StateLoaded != eState);
    
    OMX_FreeHandle(hComp);
    

OpenMAX IL Component Methods and Structures


OpenMAX IL components are defined in the OMX_Component.h header file. The structure OMX_COMPONENTTYPE holds the data fields and function entry points for a component:
  • pComponentPrivate
  • The component allocates and initializes this member when the component is first loaded. The application should not access this data area.
  • pApplicationPrivate
  • The component initializes this field during the call to SetCallbacks, as this field is provided back to the IL client when the component issues callbacks.
  • GetComponentVersion()
  • The IL client calls the GetComponentVersion component method via the OMX_GetComponentVersion() core macro.
    
    OMX_ERRORTYPE (*GetComponentVersion)(
                OMX_IN  OMX_HANDLETYPE hComponent,
                OMX_OUT OMX_STRING pComponentName,
                OMX_OUT OMX_VERSIONTYPE* pComponentVersion,
                OMX_OUT OMX_VERSIONTYPE* pSpecVersion,
                OMX_OUT OMX_UUIDTYPE* pComponentUUID);
    
  • SendCommand()
  • The IL client calls the SendCommand component method via the OMX_SendCommand() core macro.
    
    OMX_ERRORTYPE (*SendCommand)(
                OMX_IN  OMX_HANDLETYPE hComponent,
                OMX_IN  OMX_COMMANDTYPE Cmd,
                OMX_IN  OMX_U32 nParam1,
                OMX_IN  OMX_PTR pCmdData);
    
  • GetParameter()
  • The IL client or a tunneled component calls the GetParameter component method via the OMX_GetParameter() core macro.
    
    OMX_ERRORTYPE (*GetParameter)(
                OMX_IN  OMX_HANDLETYPE hComponent,
                OMX_IN  OMX_INDEXTYPE nParamIndex,
                OMX_INOUT OMX_PTR pComponentParameterStructure);
    
    
  • SetParameter()
  • The IL client or a tunneled component calls the SetParameter component method via the OMX_SetParameter() core macro.
    
    OMX_ERRORTYPE (*SetParameter)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_IN  OMX_INDEXTYPE nIndex,
            OMX_IN  OMX_PTR pComponentParameterStructure);
    
  • GetConfig()
  • The IL client calls the GetConfig component method via the OMX_GetConfig() core macro.
    
    OMX_ERRORTYPE (*GetConfig)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_IN  OMX_INDEXTYPE nIndex,
            OMX_INOUT OMX_PTR pComponentConfigStructure);
    SetConfig
  • SetConfig()
  • 
    OMX_ERRORTYPE (*SetConfig)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_IN  OMX_INDEXTYPE nIndex,
            OMX_IN  OMX_PTR pComponentConfigStructure);
    
  • GetExtensionIndex()
  • 
    OMX_ERRORTYPE (*GetExtensionIndex)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_IN  OMX_STRING cParameterName,
            OMX_OUT OMX_INDEXTYPE* pIndexType);
    
  • GetState()
  • 
    OMX_ERRORTYPE (*GetState)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_OUT OMX_STATETYPE* pState);
    
    
  • ComponentTunnelRequest()
  • 
    OMX_ERRORTYPE (*ComponentTunnelRequest)(
        OMX_IN  OMX_HANDLETYPE hComp,
        OMX_IN  OMX_U32 nPort,
        OMX_IN  OMX_HANDLETYPE hTunneledComp,
        OMX_IN  OMX_U32 nTunneledPort,
        OMX_INOUT  OMX_TUNNELSETUPTYPE* pTunnelSetup);
    
  • UseBuffer()
  • 
    OMX_ERRORTYPE (*UseBuffer)(
            OMX_IN OMX_HANDLETYPE hComponent,
            OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
            OMX_IN OMX_U32 nPortIndex,
            OMX_IN OMX_PTR pAppPrivate,
            OMX_IN OMX_U32 nSizeBytes,
            OMX_IN OMX_U8* pBuffer);
    
  • AllocateBuffer()
  • 
    OMX_ERRORTYPE (*AllocateBuffer)(
            OMX_IN OMX_HANDLETYPE hComponent,
            OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer,
            OMX_IN OMX_U32 nPortIndex,
            OMX_IN OMX_PTR pAppPrivate,
            OMX_IN OMX_U32 nSizeBytes);
    
  • FreeBuffer()
  • 
    OMX_ERRORTYPE (*FreeBuffer)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_IN  OMX_U32 nPortIndex,
            OMX_IN  OMX_BUFFERHEADERTYPE* pBuffer);
    
  • EmptyThisBuffer()
  • 
    OMX_ERRORTYPE (*EmptyThisBuffer)(
            OMX_IN  OMX_HANDLETYPE hComponent,
            OMX_IN  OMX_BUFFERHEADERTYPE* pBuffer);
    
  • FillThisBuffer()
  • 
    OMX_ERRORTYPE (*FillThisBuffer)(
             OMX_IN  OMX_HANDLETYPE hComponent,
             OMX_IN  OMX_BUFFERHEADERTYPE* pBuffer);
    
  • SetCallbacks()
    OMX_ERRORTYPE (*SetCallbacks)(
                OMX_IN  OMX_HANDLETYPE hComponent,
                OMX_IN  OMX_CALLBACKTYPE* pCallbacks,
                OMX_IN  OMX_PTR pAppData);
    
  • ComponentDeInit()
  • 
    OMX_ERRORTYPE (*ComponentDeInit)(
                OMX_IN  OMX_HANDLETYPE hComponent);
    
  • UseEGLImage()
  • 
    OMX_ERRORTYPE (*UseEGLImage)(
                OMX_IN OMX_HANDLETYPE hComponent,
                OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
                OMX_IN OMX_U32 nPortIndex,
                OMX_IN OMX_PTR pAppPrivate,
                OMX_IN void* eglImage);
    
  • ComponentRoleEnum()
  • 
    OMX_ERRORTYPE (*ComponentRoleEnum)(
            OMX_IN OMX_HANDLETYPE hComponent,
                OMX_OUT OMX_U8 *cRole,
                OMX_IN OMX_U32 nIndex);
    

OMX_BUFFERHEADERTYPE, 3.1.2.7


In the context of a single port, each data buffer has a header associated with it that contains meta-information about the buffer.
The buffer headers are shared between IL client and ports.


typedef struct OMX_BUFFERHEADERTYPE
{
  OMX_U32 nSize;
  OMX_VERSIONTYPE nVersion;
  OMX_U8* pBuffer;
  OMX_U32 nAllocLen;
  OMX_U32 nFilledLen;
  OMX_U32 nOffset;
  OMX_PTR pAppPrivate;
  OMX_PTR pPlatformPrivate;
  OMX_PTR pInputPortPrivate;
  OMX_PTR pOutputPortPrivate;
  OMX_HANDLETYPE hMarkTargetComponent;
  OMX_PTR pMarkData;
  OMX_U32 nTickCount;
  OMX_TICKS nTimeStamp;
  OMX_U32 nFlags;
  OMX_U32 nOutputPortIndex;
  OMX_U32 nInputPortIndex;
} OMX_BUFFERHEADERTYPE;

  • pBuffer
  • a pointer to the actual buffer where data is stored but not necessarily the start of valid data; for more information, see the description of nOffset below.
  • nAllocLen
  • the total size of the allocated buffer in bytes, including valid and unused byte.
  • nFilledLen
  • the total size of valid bytes currently in the buffer starting from the location specified by pBuffer and nOffset.
  • nOffset
  • the start offset of valid data in bytes from the start of the buffer.
  • pAppPrivate
  • a pointer to an IL client private structure.
  • pPlatformPrivate
  • a pointer to a private platform-specific structure.
  • pOutputPortPrivate
  • a private pointer of the output port that uses the buffer.
  • pInputPortPrivate
  • a private pointer of the input port that uses the buffer.
  • hMarkTargetComponent
  • the handle of the component that should emit an OMX_EventMark event upon processing this buffer.
  • pMarkData
  • pointer refers to IL client-specific data associated with the mark that is sent on OMX_EventMark when emitted.
  • nTickCount
  • an optional entry
  • nTimeStamp
  • a timestamp corresponding to the sample starting at the first logical sample boundary in the buffer.
  • nFlags
    • OMX_BUFFERFLAG_EOS
    • A source component (e.g. a demuxer) sets OMX_BUFFERFLAG_EOS when it has reached the end of the stream content on a particular output port.
    • OMX_BUFFERFLAG_STARTTIME
    • The source of a stream (e.g., a de-multiplexing component) sets the OMX_BUFFERFLAG_STARTTIME flag on the buffer that contains the starting timestamp for the stream. The starting timestamp corresponds to the first data that should be displayed at startup or after a seek operation.
    • OMX_BUFFERFLAG_DECODEONLY
    • The source of a stream (e.g., a de-multiplexing component) sets the OMX_BUFFERFLAG_DECODEONLY flag on any buffer that should be decoded but not rendered.
    • OMX_BUFFERFLAG_DECODEONLY
    • The source of a stream (e.g., a de-multiplexing component) sets the OMX_BUFFERFLAG_DECODEONLY flag on any buffer that should be decoded but not rendered.
    • OMX_BUFFERFLAG_DATACORRUPT
    • set when the IL client identifies the data in the associated buffer as corrupt.
    • OMX_BUFFERFLAG_ENDOFFRAME
    • set by an output port when the last byte that a buffer payload contains is an end-of-frame.
    • OMX_BUFFERFLAG_SYNCFRAME
    • set by an output port to indicate that the buffer content contains a coded synchronization frame.
    • OMX_BUFFERFLAG_EXTRADATA
    • set by an output port when the buffer payload contains additional information appended to the end of the buffer payload.
    • OMX_BUFFERFLAG_CODECCONFIG
    • set by an output port when all bytes in the buffer form part or all of a set of codec specific configuration data.
  • nOutputPortIndex
  • the port index of the output port that uses the buffer. The value of nOutputPortIndex is undefined for the input port's buffer.
  • nInputPortIndex
  • the port index of the input port that uses the buffer. The value of nInputPortIndex is undefined for the output port's buffer.

Calling Sequences


The IL client, the OpenMAX IL core, and the components dynamically interact in a few meaningful use cases:
  • initialization
  • de-initialization
  • data flow
  • data tunneling setup
  • data flow in the case of data tunneling
  • dynamic port reconfiguration

OpenMAX IL Data API


Chapter 8: OpenMAX Components on the Raspberry Pi


Broadcom's Implementation of OpenMAX IL


This raspberrypi/userland repository contains the source code for the ARM side libraries used on Raspberry Pi. These typically are installed in /opt/vc/lib and includes source for the ARM side code to interface to: EGL, mmal, GLESv2, vcos, vchiq_arm, bcm_host, WFC, OpenVG.
It appears to be a thin wrapper(userland/interface) around its GPU API.
The implementation of OpenMAX IL Core is based on VCOS APIs.
Therefore, to build the OpenMax IL application, the include and library path of VCOS need to be set:

-I /opt/vc/include/IL -I /opt/vc/include -I /opt/vc/include/interface/vcos/pthreads -L /opt/vc/lib -l openmaxil -l bcm_host

Component Initialization


All IL clients must initialize OpenMAX IL by calling OMX_Init().

  err = OMX_Init();
  if(err != OMX_ErrorNone) {
      fprintf(stderr, "OMX_Init() failed\n", 0);
      exit(1); 
  }
OMX_Init() initiatizes the OMX framework. This MUST be called once before calling any OMX APIs
Before you can do anything with a component , an instance of the component must be created.
OMX_GetHandle() must be invoked to create an instance of a component,

OMX_HANDLETYPE handle;
OMX_CALLBACKTYPE callbacks;
OMX_ERRORTYPE err;

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

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


An IL client gets a component by calling OMX_GetHandle(), passing in the name of the component. The components do not have a standard name .No standardization among component names is dictated across different vendors.
A naming convention of a component is defined. OpenMAX IL component names are zero-terminated strings with the following format:

  “OMX.<vendor_name>.<vendor_specified_convention>"
For example: OMX.CompanyABC.MP3Decoder.productXYZ.
There is a simple way to get the list of the supported components by writing a simple IL application program.

#include <stdio.h>
#include <stdlib.h>
#include <OMX_Core.h>
#ifdef RASPBERRY_PI
#include <bcm_host.h>
#endif
OMX_ERRORTYPE err;

void listroles(char *name) {
    int n;
    OMX_U32 numRoles;
    OMX_U8 *roles[32];
    /* get the number of roles by passing in a NULL roles param */
    err = OMX_GetRolesOfComponent(name, &numRoles, NULL);
    if (err != OMX_ErrorNone) {
        fprintf(stderr, "Getting roles failed\n", 0);
        exit(1); 
    }
    printf("  Num roles is %d\n", numRoles);
    if (numRoles > 32) {
        printf("Too many roles to list\n");
        return; 
    }
    /* now get the roles */
    for (n = 0; n < numRoles; n++) {
        roles[n] = malloc(OMX_MAX_STRINGNAME_SIZE);
    }
    err = OMX_GetRolesOfComponent(name, &numRoles, roles);
    if (err != OMX_ErrorNone) {
        fprintf(stderr, "Getting roles failed\n", 0);
        exit(1); 
    }
    for (n = 0; n < numRoles; n++) {
        printf("    role: %s\n", roles[n]);
        free(roles[n]);
    }
    /* This is in version 1.2
    for (i = 0; OMX_ErrorNoMore != err; i++) {
        err = OMX_RoleOfComponentEnum(role, name, i);
        if (OMX_ErrorNone == err) {
            printf("   Role of omponent is %s\n", role);
        }
    }
    */ 
}

int main(int argc, char** argv) {
    int i;
    unsigned char name[OMX_MAX_STRINGNAME_SIZE];

# ifdef RASPBERRY_PI
    bcm_host_init();
# endif
    err = OMX_Init();
    if (err != OMX_ErrorNone) {
        fprintf(stderr, "OMX_Init() failed\n", 0);
        exit(1);
    }
    err = OMX_ErrorNone;
    for (i = 0; OMX_ErrorNoMore != err; i++) {
        err = OMX_ComponentNameEnum(name, OMX_MAX_STRINGNAME_SIZE, i);
        if (OMX_ErrorNone == err) {
            printf("Component is %s\n", name);
            listroles(name);
        }
    }
    printf("No more components\n");
    /*
    i= 0 ;
    while (1) {
        printf("Component %s\n", OMX_ComponentRegistered[i++]);
    }
    */

    exit(0); 
}

Build the code on the Pi,

cc -g -DRASPBERRY_PI -I /opt/vc/include/IL -I /opt/vc/include -I /opt/vc/include/interface/vcos/pthreads -L /opt/vc/lib -l openmaxil -l bcm_host -o listcomponents listcomponents.c

Each component may support a number of roles. The Broadcom IL has zero roles for each component so that there is no queried.

$ ./listcomponents

Raspi supported the following OpenMAX IL components:
  • OMX.broadcom.audio_capture
  • OMX.broadcom.audio_decode
  • OMX.broadcom.audio_encode
  • OMX.broadcom.audio_render
  • OMX.broadcom.audio_mixer
  • OMX.broadcom.audio_splitter
  • OMX.broadcom.audio_processor
  • OMX.broadcom.camera
  • OMX.broadcom.clock
  • OMX.broadcom.coverage
  • OMX.broadcom.egl_render
  • OMX.broadcom.image_fx
  • OMX.broadcom.image_decode
  • OMX.broadcom.image_encode
  • OMX.broadcom.image_read
  • OMX.broadcom.image_write
  • OMX.broadcom.read_media
  • OMX.broadcom.resize
  • OMX.broadcom.source
  • OMX.broadcom.text_scheduler
  • OMX.broadcom.transition
  • OMX.broadcom.video_decode
  • OMX.broadcom.video_encode
  • OMX.broadcom.video_render
  • OMX.broadcom.video_scheduler
  • OMX.broadcom.video_splitter
  • OMX.broadcom.visualisation
  • OMX.broadcom.write_media
  • OMX.broadcom.write_still

The Broadcom OpenMAX IL components used on the Raspberry Pi are documented at the VMCS-X OpenMAX IL Components page.

As the visualization component as an example, it has
  • two audio ports (green)
  • an input port #140 and an output port #141;
  • a timer port (yellow)
  • an input port #143
  • a video output port (red)
  • an output port #142
the visualization component's name is OMX.broadcom.visualisation.


The notification of various completion events occur asynchronous. So the use of callback functions is implementated as part of the OpenMax framework to facilitate the asynchronous flow between the application and components.

Port Information ( Data Structure)


Ports are the means by which a component communicates, where clients pass data into a port’s input buffers and receive processed data back from a port’s output buffers.
The OMX_PARAM_PORTDEFINITIONTYPE(3.1.2.12) structure contains a set of generic fields that characterize each port of the component.
The IL client uses this structure to retrieve general information from each port.

typedef struct OMX_PARAM_PORTDEFINITIONTYPE {
  OMX_U32 nSize;
  OMX_VERSIONTYPE nVersion;
  OMX_U32 nPortIndex;
  OMX_DIRTYPE eDir;
  OMX_U32 nBufferCountActual;
  OMX_U32 nBufferCountMin;
  OMX_U32 nBufferSize;
  OMX_BOOL bEnabled;
  OMX_BOOL bPopulated;
  OMX_PORTDOMAINTYPE eDomain;
  union {
    OMX_AUDIO_PORTDEFINITIONTYPE audio;
    OMX_VIDEO_PORTDEFINITIONTYPE video;
    OMX_IMAGE_PORTDEFINITIONTYPE image;
    OMX_OTHER_PORTDEFINITIONTYPE other;
  } format;
  OMX_BOOL bBuffersContiguous;
  OMX_U32 nBufferAlignment;
} OMX_PARAM_PORTDEFINITIONTYPE;
The 1st field is the size of the structure , the 2nd field is the version of OpenMAX that is being used.
All of the OpenMAX data structures have the following 2 fields and must be initialized before using it.

struct OMX_PARAM_PORTDEFINITIONTYPE portdef;
memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
portdef.nVersion.nVersion = OMX_VERSION;
The 3rd filed field nPortIndex is a read-only field that identifies the port. One way of finding this is from the documentation.

Each port has buffers. Information about these buffers is stored in the next set of fields:

OMX_U32 nBufferCountActual; // the number of buffers that are required on this port before it is populated. >= nBufferCountMin
OMX_U32 nBufferCountMin;    // read-only, the minimum number of buffers that the port requires
OMX_U32 nBufferSize;        // read-only, the minimum size in bytes for buffers that are allocated for this port
OMX_BOOL bPopulated;        // read-only, indicate if a port is populated( all of the buffers have been allocated on the port).

The filed eDomain can be used to identify the type (3.1.3) of the port and imply the specific data structure used in OMX_PARAM_PORTDEFINITIONTYPE.format:
  • OMX_PortDomainAudio
  • Specifies that the field format is a structure of the OMX_AUDIO_PORTDEFINITIONTYPE type.
  • OMX_PortDomainVideo
  • Specifies that the field format is a structure of the OMX_VIDEO_PORTDEFINITIONTYPE type.
  • OMX_PortDomainImage
  • Specifies that the field format is a structure of the OMX_IMAGE_PORTDEFINITIONTYPE type.

Getting and Setting Ports


OpenMAX has a standard way of getting and setting parameters. A handle to a component is required for getting/setting.
For each parameter data structure, there is a corresponding parameter index.

For OMX_PARAM_PORTDEFINITIONTYPE as an ex., the index is OMX_IndexParamPortDefinition.
To get the information of a generic port:


OMX_PARAM_PORTDEFINITIONTYPE portdef;

err = OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &portdef)
}

This similar pattern can be used for other parameter data structures.

Getting and setting information about port is done by the functions
  • OMX_GetParameter(), 3.2.2.8
  • The OMX_GetParameter macro will get a parameter setting from a component.
    
    #define OMX_ GetParameter ( hComponent, nParamIndex, pComponentParameterStructure)
     ((OMX_COMPONENTTYPE*)hComponent)->GetParameter(
                                      hComponent,
                                      nParamIndex,
                                      pComponentParameterStructure)
    
    • The caller shall provide memory for the structure and populate the nSize and nVersion fields before invoking this macro.
    • If the parameter settings are for a port, the caller shall also provide a valid port number in the nPortIndex field before invoking this macro.
    • This call is a blocking call. The component should return from this call within 20
      milliseconds.
    Here is an example:
    
    /* disable every audio port of a component*/
    OMX_GetParameter(hComp, OMX_IndexParamAudioInit, &oParam);
    for (i=0; i< oParam.nPorts; i++) {
      OMX_SendCommand(
        hComp,
        OMX_CommandPortDisable,
        oParam.nStartPortNumber + i,
        0);
    }
    
  • OMX_SetParameter(), 3.2.2.9
  • The OMX_SetParameter macro will send a parameter structure to a component. The nParamIndex parameter indicates which structure is passed to the component.
    
    #define OMX_SetParameter (
      hComponent,
      nParamIndex,
      pComponentParameterStructure)
      ((OMX_COMPONENTTYPE*)hComponent)->SetParameter(
        hComponent,
        nParamIndex,
        pComponentParameterStructure)
    
    • The caller shall provide the memory for the correct structure and shall fill in the structure nSize and nVersion fields in addition to all other fields before invoking this macro.
    • The caller is free to dispose of this structure after the call, as the component is required to copy any data it shall retain.
    • This call is a blocking call. The component should return from this call within 20 milliseconds.
    Here is an example:
    
    /* force a port to be the supplier */
    OMX_GetParameter(hComp, OMX_IndexParamPortDefinition , &oPortDef);
    if (oPortDef.eDir == OMX_DirInput){
      oSupplier.eBufferSupplier = OMX_BufferSupplyInput;
    } else {
      oSupplier.eBufferSupplier = OMX_BufferSupplyOutput;
    }
    oSupplier.nPortIndex = nPortIndex;
    OMX_SetParameter(hComp, OMX_IndexParamCompBufferSupplier, &oSupplier);
    
  • OMX_GetConfig()
  • The OMX_GetConfig macro will get a configuration structure from a component.
    
    #define OMX_GetConfig (
      hComponent,
      nConfigIndex,
      pComponentConfigStructure)
      ((OMX_COMPONENTTYPE*)hComponent)->GetConfig(
        hComponent,
        nConfigIndex,
        pComponentConfigStructure)
    
    • This macro can be invoked at any time after the component has been loaded.
    • The caller shall provide the memory for the structure and populate the
      nSize and nVersion fields before invoking this macro.
    • The nConfigIndex parameter indicates which structure is being requested from the component.
      • Audio
      • Table 4-2: [ OMX_IndexConfigAudioMidiImmediateEvent, ... , OMX_IndexConfigAudioNoiseReduction ]
      • Video
      • Table 4-32: [ OMX_IndexConfigCommonColorFormatConversion, ..., OMX_IndexConfigCommonFocusStatus ] Table 4-45: [ OMX_IndexConfigVideoBitrate, ..., OMX_IndexConfigVideoNalSize ]
      • Image
      • Table 4-65: [ OMX_IndexConfigFlashControl, ..., OMX_IndexConfigFocusControl ]
      • Other
      • Table 4-71: [ OMX_IndexConfigTimeScale, ..., OMX_IndexConfigTimeSeekMode]
    • If the configuration settings are for a port, the caller shall also provide a valid port number in the nPortIndex field before invoking this macro.
    Here is an example:
    
    /* Wait until a certain playback position */
    do {
      OMX_GetConfig(hClockComp, OMX_IndexConfigTimeCurrentMediaTime, oMediaTime);
    } while (oMediaStamp.nTimeStamp < nTargetTimeStamp);
    
  • OMX_SetConfig()
  • The OMX_SetConfig macro will set a component configuration value.
    
    #define OMX_SetConfig (
      hComponent,
      nConfigIndex,
      pComponentConfigStructure
      )
      ((OMX_COMPONENTTYPE*)hComponent)->SetConfig(
        hComponent,
        nConfigIndex,
        pComponentConfigStructure)
    
    • Some configuration structures contain read-only fields.
    • The OMX_SetConfig method will preserve read-only fields in configuration structures that contain them, and shall not generate an error when the caller attempts to change the value of a read-only field.
    • This call is a blocking call.
    Here is an example:
    
    /* Change the time scale of the clock component*/
    oScale.xScale = 0x00020000; /*2x*/
    OMX_SetConfig(hClockComp, OMX_IndexConfigTimeScale, (OMX_PTR)&oScale);
    

Getting Information About a Port


Try to write a program portinfo.c to get the information about a particular port on a component and print it out.
This program takes a component name and a port index as parameters.

#include <stdio.h>
#include <stdlib.h>
#include <OMX_Core.h>
#include <OMX_Component.h>
#include <bcm_host.h>

OMX_ERRORTYPE get_port_info(OMX_HANDLETYPE handle, OMX_PARAM_PORTDEFINITIONTYPE *portdef) {
 //                                          assign the index to use the structure passed
 return OMX_GetParameter(handle, OMX_IndexParamPortDefinition, portdef);
}

void print_port_info(OMX_PARAM_PORTDEFINITIONTYPE *portdef) {
 char *domain;

 printf("Port %d\n", portdef->nPortIndex);
 if (portdef->eDir == OMX_DirInput) {
  printf(" is input port\n");
 } else {
  printf(" is output port\n");
 }
 switch (portdef->eDomain) {
  case OMX_PortDomainAudio: 
    domain = "Audio";
    break;
  case OMX_PortDomainVideo: 
    domain = "Video";
    break;
  case OMX_PortDomainImage: 
    domain = "Image";
    break;
  case OMX_PortDomainOther: 
    domain = "Other";
    break;
 }
 printf("Domain is %s\n", domain);
 printf("Buffer count %d\n", portdef->nBufferCountActual);
 printf("Buffer minimum count %d\n", portdef->nBufferCountMin);
 printf("Buffer size %d bytes\n", portdef->nBufferSize);
};

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

int main(int argc, char** argv) {
 int i;
 char componentName[128]; // min space required see /opt/vc/include/IL/OMX_Core.h
 OMX_ERRORTYPE err;
 OMX_HANDLETYPE handle;
 OMX_PARAM_PORTDEFINITIONTYPE portdef;
 OMX_VERSIONTYPE specVersion, compVersion;
 OMX_UUIDTYPE uid;
 int portindex;

 if (argc < 3) {
 fprintf(stderr, "Usage: %s component-name port-index\n", argv[0]);
 exit(1);
 }
 
 strncpy(componentName, argv[1], 128);
 portindex = atoi(argv[2]);
 bcm_host_init();
 err = OMX_Init();
 if (err != OMX_ErrorNone) {
  fprintf(stderr, "OMX_Init() failed\n", 0);
 exit(1);
 }

 /** Ask the core for a handle to the component
 */
 err = OMX_GetHandle(&handle, componentName, NULL, &callbacks);
 if (err != OMX_ErrorNone) {
  fprintf(stderr, "OMX_GetHandle failed\n", 0);
  exit(1);
 }
 // Get some version info
 err = OMX_GetComponentVersion(handle, componentName, &compVersion, &specVersion, &uid);
 if (err != OMX_ErrorNone) {
  fprintf(stderr, "OMX_GetComponentVersion failed\n", 0);
  exit(1);
 }
 printf("Component name: %s version %d.%d, Spec version %d.%d\n",
   componentName, compVersion.s.nVersionMajor,
   compVersion.s.nVersionMinor,
   specVersion.s.nVersionMajor,
   specVersion.s.nVersionMinor);

 memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
 portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
 portdef.nVersion.nVersion = OMX_VERSION;
 portdef.nPortIndex = portindex;

 if (get_port_info(handle, &portdef) == OMX_ErrorNone) {
  print_port_info(&portdef);
 }
 exit(0);
}


Build the code:

$ cc -I /opt/vc/include/IL -I /opt/vc/include -I /opt/vc/include/interface/vcos/pthreads -L /opt/vc/lib -l openmaxil -l bcm_host portinfo.c -o portinfo

Then, run it:

$ ./portinfo OMX.broadcom.image_decode 320
Component name: OMX.broadcom.image_decode:0 version 0.0, Spec version 1.1
Port 320
 is input port
Domain is Image
Buffer count 3
Buffer minimum count 2
Buffer size 81920 bytes

Getting Information About Specific Type of Ports


A component uses the OMX_PORT_PARAM_TYPE structure to identify the number and starting index of ports of a particular domain.

typedef struct OMX_PORT_PARAM_TYPE {
  OMX_U32 nSize;
  OMX_VERSIONTYPE nVersion;
  OMX_U32 nPorts;
  OMX_U32 nStartPortNumber;
} OMX_PORT_PARAM_TYPE;
  • nPorts is the number of ports of a given port domain (audio, video, image, or other) for the component.
  • nStartPortNumber is the index of the first port of a given port domain (audio, video, image, or other) for the component .
  • Subsequent ports of the given domain are numbered sequentially from nStartPortNumber.
The following indexes are corresponded to the structure OMX_PORT_PARAM_TYPE:
  • OMX_IndexParamAudioInit
  • OMX_IndexParamImageInit
  • OMX_IndexParamVideoInit
  • OMX_IndexParamOtherInit
An example to get the information of the port base and number for a video port:

OMX_PORT_PARAM_TYPE param;

err = OMX_GetParameter(handle, OMX_IndexParamVideoInit, &param);


Detailed Information for the Audio/Video/Image/Other Ports


To get the port's information:

  OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &portdef)
Each type of port hase its own specific information, to get the specific information for each type of port:
  • the audio port
  • portdef.format.audio is the specific information with the type OMX_AUDIO_PORTDEFINITIONTYPE (4.1.5). This tructure is used to define all of the parameters necessary for the compliant component to set up an input or an output audio path:
    
    typedef struct OMX_AUDIO_PORTDEFINITIONTYPE {
      OMX_STRING cMIMEType;
      OMX_NATIVE_DEVICETYPE pNativeRender;
      OMX_BOOL bFlagErrorConcealment;
      OMX_AUDIO_CODINGTYPE eEncoding;
    } OMX_AUDIO_PORTDEFINITIONTYPE;
    
    • cMIMEType
    • is the MIME type of data for the port. If a MIME type string buffer is not supplied this parameter shall be set to NULL.
    • pNativeRender
    • is the platform-specific reference for an output device; otherwise this field is 0.
    • bFlagErrorConcealment
    • turns on error concealment if it is supported by the OpenMAX IL component.
    • eEncoding
    • is the type of data expected for this port (e.g., PCM, AMR, MP3, and so forth). These types are also defined in the header file /opt/vc/include/IL/OMX_Audio.h .
    To look at the PCM information, the index OMX_IndexParamAudioPcm for the structure OMX_AUDIO_PARAM_PCMMODETYPE is used:
    
    typedef struct OMX_AUDIO_PARAM_PCMMODETYPE {
      OMX_U32 nSize;
      OMX_VERSIONTYPE nVersion;
      OMX_U32 nPortIndex;
      OMX_U32 nChannels;
      OMX_NUMERICALDATATYPE eNumData;
      OMX_ENDIANTYPE eEndian;
      OMX_BOOL bInterleaved;
      OMX_U32 nBitPerSample;
      OMX_U32 nSamplingRate;
      OMX_AUDIO_PCMMODETYPE ePCMMode;
      OMX_AUDIO_CHANNELTYPE eChannelMapping[OMX_AUDIO_MAXCHANNELS];
    } OMX_AUDIO_PARAM_PCMMODETYPE;
    
     OMX_AUDIO_PARAM_PCMMODETYPE sPCMMode;
    
     err = OMX_GetParameter(handle, OMX_IndexParamAudioPcm, &sPCMMode);
    
    
  • the video port
  • These parameter and configuration details are specified in the omx_video.h header. The OMX_VIDEO_PORTDEFINITIONTYPE structure can query the current or default definition of a video port or set the definition of a video port for a component.
    
    typedef struct OMX_VIDEO_PORTDEFINITIONTYPE {
      OMX_STRING cMIMEType;
      OMX_NATIVE_DEVICETYPE pNativeRender;
      OMX_U32 nFrameWidth;
      OMX_U32 nFrameHeight;
      OMX_S32 nStride;      // buffer parameter
      OMX_U32 nSliceHeight; // buffer parameter
      OMX_U32 nBitrate;
      OMX_U32 xFramerate;
      OMX_BOOL bFlagErrorConcealment;
      OMX_VIDEO_CODINGTYPE eCompressionFormat;
      OMX_COLOR_FORMATTYPE eColorFormat;
      OMX_NATIVE_WINDOWTYPE pNativeWindow;
    } OMX_VIDEO_PORTDEFINITIONTYPE;
    
    • nStride
    • is a field containing the number of bytes per span of an image, which indicates the number of bytes to get from span N to span N+1.
    • nSliceHeight
    • is a read-only field containing the slice height parameter used when processing uncompressed image data. This describes the plane height of a multi-planar (YUV) video bytebuffer layout. "4.2.3 Buffer Payload Requirements for Uncompressed Data":
      
      For each of these uncompressed formats(OMX_COLOR_FormatYUVxxx), 
      each buffer payload contains a slice of the Y, U, and V planes.
      The nSliceHeight refers to the slice height of the Y plane.
      The slice height of the U and V planes are derived from the slice height for the Y plane based upon for the format.
      
      "4.2.2 Minimum Buffer Payload Size for Uncompressed Data":
      
      Uncompressed image and video data have a minimum buffer payload size. 
      The minimum buffer payload size is determined by the nSliceHeight and nStride fields. 
      nStride indicates the width of a span in bytes; when negative, it indicates the data is bottom-up instead of the top-down). nSliceHeight indicates the number of spans in a slice.
      The minimum buffer payload size can be easily calculated as the absolute value of
      
          ( nSliceHeight * nStride )
      
      
      For example, for OMX_COLOR_FormatYUV420PackedPlanar with a nSliceHeight of 16, a buffer payload shall contain 16 spans of Y followed by 8 spans of U (half the stride) and 8 spans of V (half the stride). This enables ports that process planar data in slices to operate on all three planes simultaneously, instead of forcing the ports to buffer the entire image before processing can begin.
    • xFramerate
    • frames per second. This value is represented in Q16 format.
    • eCompressionFormat
    • OMX_IMAGE_CodingJPEG, OMX_IMAGE_CodingJPEG2K, ... When OMX_IMAGE_CodingUnused is specified, the eColorFormat field is valid.
    • eColorFormat
    • OMX_COLOR_Formatxxx defined in "Table 4-31: Uncompressed Data Formats". For ex., OMX_COLOR_FormatYUV420PackedPlanar
  • the image port
  • 
    
    
  • other ports
  • 
    
    
OMX_Types.h:

/** The OMX_PTR type is intended to be used to pass pointers between the OMX
    applications and the OMX Core and components.  This is a 32 bit pointer and
    is aligned on a 32 bit boundary.
 */
typedef void* OMX_PTR;

You can query the port information in more detail:


static void setStructureSizeVer(OMX_PTR header, OMX_U32 size) {
 /* header->nVersion */
 OMX_VERSIONTYPE* ver = (OMX_VERSIONTYPE*)(header + sizeof(OMX_U32));
 /* header->nSize */
 *((OMX_U32*)header) = size;

 ver->s.nVersionMajor = specVersion.s.nVersionMajor;
 ver->s.nVersionMinor = specVersion.s.nVersionMinor;
 ver->s.nRevision = specVersion.s.nRevision;
 ver->s.nStep = specVersion.s.nStep;
}

OMX_ERRORTYPE setAudioPortNumEncoding(int portNumber, OMX_AUDIO_CODINGTYPE encoding) {
 OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
 setStructureSizeVer(&sPortDef, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
 sPortDef.nPortIndex = portNumber;
 sPortDef.nPortIndex = portNumber;
 err = OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &sPortDef);
 if(err != OMX_ErrorNone){
  fprintf(stderr, "Error in getting OMX_PORT_DEFINITION_TYPE parameter\n",0);
  exit(1);
 }
 sPortDef.format.audio.eEncoding = encoding;
 sPortDef.nBufferCountActual = sPortDef.nBufferCountMin;
 err = OMX_SetParameter(handle, OMX_IndexParamPortDefinition, &sPortDef);
 return err;
}


void getPCMInformation(int portNumber) {
 /* assert: PCM is a supported mode */
 OMX_AUDIO_PARAM_PCMMODETYPE sPCMMode;
 /* set it into PCM format before asking for PCM info */
 if (setAudioPortNumEncoding(portNumber, OMX_AUDIO_CodingPCM) != OMX_ErrorNone) {
  fprintf(stderr, "Error in setting coding to PCM\n");
  return;
 }
 setStructureSizeVer(&sPCMMode, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE));

 /* read back the port's setting */
 sPCMMode.nPortIndex = portNumber;
 err = OMX_GetParameter(handle, OMX_IndexParamAudioPcm, &sPCMMode);
 if(err != OMX_ErrorNone){
  printf("PCM mode unsupported\n");
 } else {
  printf(" PCM default sampling rate %d\n", sPCMMode.nSamplingRate);
  printf(" PCM default bits per sample %d\n", sPCMMode.nBitPerSample);
  printf(" PCM default number of channels %d\n", sPCMMode.nChannels);
 }
}
void getSupportedAudioFormats(int indentLevel, int portNumber) {

 OMX_AUDIO_PARAM_PORTFORMATTYPE sAudioPortFormat;
 setStructureSizeVer(&sAudioPortFormat, sizeof(OMX_AUDIO_PARAM_PORTFORMATTYPE));
 sAudioPortFormat.nIndex = 0;
 sAudioPortFormat.nPortIndex = portNumber;
 printf("Supported audio formats are:\n");

 for(;;) {
  err = OMX_GetParameter(handle, OMX_IndexParamAudioPortFormat, &sAudioPortFormat);
  if (err == OMX_ErrorNoMore) {
   printf("No more formats supported\n");
   return;
  }
  /* This shouldn't occur, but does with Broadcom library */
  if (sAudioPortFormat.eEncoding == OMX_AUDIO_CodingUnused) {
   printf("No coding format returned\n");
   return;
  }

  switch (sAudioPortFormat.eEncoding) {
   case OMX_AUDIO_CodingPCM:
    printf("Supported encoding is PCM\n");
    getPCMInformation(portNumber);
    break;
   case OMX_AUDIO_CodingVORBIS:
    printf("Supported encoding is Ogg Vorbis\n");
    break;
   case OMX_AUDIO_CodingMP3:
    printf("Supported encoding is MP3\n");
    getMP3Information(portNumber);
    break;
   .
   .
   .
   default:
    printf("Supported encoding is not PCM or MP3 or Vorbis, is 0x%X\n", sAudioPortFormat.eEncoding);
   }
  sAudioPortFormat.nIndex++;
 }
}

void getPortMediaInformation(int nPort, OMX_PARAM_PORTDEFINITIONTYPE sPortDef) {

 printf("Port %d requires %d buffers\n", nPort, sPortDef.nBufferCountMin);
 printf("Port %d has min buffer size %d bytes\n", nPort, sPortDef.nBufferSize);
 if (sPortDef.eDir == OMX_DirInput) {
  printf("Port %d is an input port\n", nPort);
 } else {
  printf("Port %d is an output port\n", nPort);
 }

 switch (sPortDef.eDomain) {
  case OMX_PortDomainAudio:
   printf("Port %d is an audio port\n", nPort);
   printf("Port mimetype %s\n",
   sPortDef.format.audio.cMIMEType);
   switch (sPortDef.format.audio.eEncoding) {
    case OMX_AUDIO_CodingPCM:
     printf("Port encoding is PCM\n");
     break;
    case OMX_AUDIO_CodingVORBIS:
     printf("Port encoding is Ogg Vorbis\n");
     break;
    case OMX_AUDIO_CodingMP3:
     printf("Port encoding is MP3\n");
     break;
    default:
     printf("Port encoding is not PCM or MP3 or Vorbis, is %d\n",
     sPortDef.format.audio.eEncoding);
   }
   getSupportedAudioFormats(nPort);
   break;
  /* could put other port types here */
  case OMX_PortDomainVideo:
   .
   .
   break
  case OMX_PortDomainImage:
   .
   .
   break
  default:
   printf("Port %d is not an audio port\n", nPort);
 }
}


void getAllPortsInformation() {
 OMX_PORT_PARAM_TYPE param;
 OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
 int startPortNumber;
 int nPorts;
 int n;

 int index_param_media =0;
 for ( index_param_media= OMX_IndexParamAudioInit; i<= OMX_IndexParamOtherInit; i ++) {
  if ( index_param_media == OMX_IndexParamAudioInit)
   printf("Audio ports:\n");
  else if ( index_param_media == OMX_IndexParamVideoInit)
   printf("Video ports:\n");
  else if ( index_param_media == OMX_IndexParamImageInit)
   printf("Image ports:\n");
  else if ( index_param_media == OMX_IndexParamOtherInit)
   printf("Other ports:\n");
  setStructureSizeVer(&param, sizeof(OMX_PORT_PARAM_TYPE));
  err = OMX_GetParameter(handle, index_param_media, &param);
  if(err != OMX_ErrorNone){
   fprintf(stderr, "Error in getting audio OMX_PORT_PARAM_TYPE parameter\n", 0);
   return;
  }
  startPortNumber = param.nStartPortNumber;
  nPorts = param.nPorts;
  if (nPorts == 0) {
   printf("No ports of this type\n");
   return;
  }
  printf("Ports start on %d\n", startPortNumber);
  printf("There are %d open ports\n", nPorts);
  for (n = 0; n < nPorts; n++) {
   setStructureSizeVer(&sPortDef, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
   sPortDef.nPortIndex = startPortNumber + n;
   err = OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &sPortDef);
   if(err != OMX_ErrorNone){
    fprintf(stderr, "Error in getting OMX_PORT_DEFINITION_TYPE parameter\n", 0);
    exit(1);
   }
   printf("Port %d has %d buffers of size %d\n",
   sPortDef.nPortIndex,
   sPortDef.nBufferCountActual,
   sPortDef.nBufferSize);
   printf("Direction is %s\n", (sPortDef.eDir == OMX_DirInput ? "input" : "output"));
   getPortMediaInformation(startPortNumber + n, sPortDef);
  }
 }
}



Chapter 9: OpenMAX on the Raspberry Pi State


The OpenMAX IL includes a callback mechanism that allows a component to communicate with the IL client
Whenever a component changes state, it will invoke the event callback handler established for that component.
To accomplish a callback, the OpenMAX IL has 3 callback functions defined: 1 generic event handler(EventHandler) and 2 callbacks related to the dataflow (EmptyBufferDone and FillBufferDone).
The IL client is responsible for filling in an OMX_CALLBACKTYPE structure with its callback entry points and passing the structure to the OpenMAX IL core at initialization (init) time.

typedef struct OMX_CALLBACKTYPE {
  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);
  OMX_ERRORTYPE (*EmptyBufferDone)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
  OMX_ERRORTYPE (*FillBufferDone)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
} OMX_CALLBACKTYPE;
  • EventHandler
  • A component uses the EventHandler method to notify the IL client when an event of interest occurs within the component. The parameters are as follows.
    • hComponent
    • The handle of the component that calls this function.
    • eEvent
    • The event that the component is communicating to the IL client.
    • nData1
    • The first integer event-specific parameter.
    • nData2
    • The second integer event-specific parameter.
    • pEventData
    • A pointer to additional event-specific data.
    The meaning of data parameters in the context of each event is defined in "Table 3-7: Event Parameter Usage".
  • EmptyBufferDone
  • A component uses the EmptyBufferDone callback to pass a buffer from an input port back to the IL client. The EmptyBufferDone call is a blocking call that should return from within five milliseconds. The parameters are as follows.
    • hComponent
    • The handle of the component that is calling this function.
    • pAppData
    • A pointer to IL client-defined data.
    • pBuffer
    • A pointer to an OMX_BUFFERHEADERTYPE structure that was consumed or returned.
  • FillBufferDone
  • A component uses the FillBufferDone callback to pass a buffer from an output port back to the IL client.


Chapter 10: OpenMAX IL Client Library on the Raspberry Pi


The IL Client library is designed to make it easier to use OpenMAX on the RPi.
Many of the OpenMAX IL client function calls in the Broadcom examples ( userland/host_applications/linux/apps/hello_pi ) are embedded in a Broadcom convenience functions: userland/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c ( installed as /opt/vc/src/hello_pi/libs/ilclient/ilclient.c ).

The Il Client functions are documented in the file hello_pi/libs/ilclient/ilclient.h and defined in hello_pi/libs/ilclient/ilclient.c

The 1st Step


bcm_host_init();

The 2nd Step


Use the IL client library to to the initialzation of a component:

   COMPONENT_T *list[5];
   ILCLIENT_T *client;
   OMX_ERRORTYPE r;
   COMPONENT_T *video_encode = NULL;

   memset(list, 0, sizeof(list));

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

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

   // create video_encode
   r = ilclient_create_component(client, &video_encode, "video_encode",
                                 ILCLIENT_DISABLE_ALL_PORTS |
                                 ILCLIENT_ENABLE_INPUT_BUFFERS |
                                 ILCLIENT_ENABLE_OUTPUT_BUFFERS);
   if (r != 0) {
      printf("ilclient_create_component() for video_encode failed with %x!\n",r);
      exit(1);
   }

   list[0] = video_encode;

  • ILCLIENT_T
  • The ILCLIENT_T structure encapsulates the state needed for the IL Client API. It contains a set of callback functions used to communicate with the user. It also includes a linked list of free event structures.
    
    typedef struct _ILCLIENT_T ILCLIENT_T;
    
    struct _ILCLIENT_T {
       ILEVENT_T *event_list;
       VCOS_SEMAPHORE_T event_sema;
       ILEVENT_T event_rep[NUM_EVENTS];
    
       ILCLIENT_CALLBACK_T port_settings_callback;
       void *port_settings_callback_data;
       ILCLIENT_CALLBACK_T eos_callback;
       void *eos_callback_data;
       ILCLIENT_CALLBACK_T error_callback;
       void *error_callback_data;
       ILCLIENT_BUFFER_CALLBACK_T fill_buffer_done_callback;
       void *fill_buffer_done_callback_data;
       ILCLIENT_BUFFER_CALLBACK_T empty_buffer_done_callback;
       void *empty_buffer_done_callback_data;
       ILCLIENT_CALLBACK_T configchanged_callback;
       void *configchanged_callback_data;
    };
    
  • COMPONENT_T
  • The COMPONENT_T structure represents an IL component, together with the necessary extra information required by the IL Client API. This structure stores the handle to the OMX component, as well as the event list containing all events sent by this component. The component state structure also holds a pair of buffer queues, for input and output buffers returned to the client by the FillBufferDone and EmptyBufferDone callbacks. As some operations result in error callbacks that can be ignored, an error mask is maintained to allow some errors to be ignored. A pointer to the client state structure is also added.
    
    typedef struct _COMPONENT_T COMPONENT_T;
    struct _COMPONENT_T {
       OMX_HANDLETYPE comp;
       ILCLIENT_CREATE_FLAGS_T flags;
       VCOS_SEMAPHORE_T sema;
       VCOS_EVENT_FLAGS_T event;
       struct _COMPONENT_T *related;
       OMX_BUFFERHEADERTYPE *out_list;
       OMX_BUFFERHEADERTYPE *in_list;
       char name[32];
       char bufname[32];
       unsigned int error_mask;
       unsigned int private;
       ILEVENT_T *list;
       ILCLIENT_T *client;
    };
    
  • ILCLIENT_T *ilclient_init()
  • An IL Client structure is created by the ilclient_init() method. This structure is used when creating components and not needed in other API functions as a pointer to this structure is maintained in the COMPONENT_T structure.
    
       i = vcos_semaphore_create(&st->event_sema, "il:event", 1);
    
       ilclient_lock_events(st);
       st->event_list = NULL;
       for (i=0; i < NUM_EVENTS; i++)
       {
          st->event_rep[i].eEvent = -1; // mark as unused
          st->event_rep[i].next = st->event_list;
          st->event_list = st->event_rep+i;
       }
       ilclient_unlock_events(st);
    
  • ilclient_create_component()
  • Components are created using the ilclient_create_component() function. It creates a handle to a component and does something according to the flag.
    
    int ilclient_create_component(ILCLIENT_T *client, COMPONENT_T **comp, char *name, ILCLIENT_CREATE_FLAGS_T flags)
    
    
    • handle
    • The client handle
    • comp
    • On successful creation, the component structure pointer will be written back into comp.
    • name
    • The name of the component to be created. Component names as provided are automatically prefixed with "OMX.broadcom." before passing to the IL core.
    • flags
    • The client can specify some creation behaviour by using the flags field. The meaning of each flag is defined by the ILCLIENT_CREATE_FLAGS_T type.
      • ILCLIENT_DISABLE_ALL_PORTS
      • disable all ports on creation
      • ILCLIENT_ENABLE_INPUT_BUFFERS
      • allow the client to communicate with input ports via buffer communication, rather than tunneling with another component.
      • ILCLIENT_ENABLE_OUTPUT_BUFFERS
      • allow the client to communicate with output ports via buffer communication, rather than tunneling with another component.
    • return 0 on success, -1 on failure
    
       *comp = vcos_malloc(sizeof(COMPONENT_T), "il:comp");
       status = vcos_event_flags_create(&(*comp)->event,"il:comp");
       (*comp)->flags = flags;
    
       callbacks.EventHandler = ilclient_event_handler;
       callbacks.EmptyBufferDone = flags & ILCLIENT_ENABLE_INPUT_BUFFERS ? ilclient_empty_buffer_done : ilclient_empty_buffer_done_error;
       callbacks.FillBufferDone = flags & ILCLIENT_ENABLE_OUTPUT_BUFFERS ? ilclient_fill_buffer_done : ilclient_fill_buffer_done_error;
    
       error = OMX_GetHandle(&(*comp)->comp, component_name, *comp, &callbacks);
       if (error == OMX_ErrorNone) {
          if(flags & (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_OUTPUT_ZERO_BUFFERS)) {
             // disable ports
    
          }
       } else
       {
          vcos_event_flags_delete(&(*comp)->event);
          vcos_semaphore_delete(&(*comp)->sema);
          vcos_free(*comp);
          *comp = NULL;
          return -1;
       }
    
    ilclient_create_component() will install the buffer handler callbacks according to the fags.

The 3rd Step


Use OpenMAX IL API to do the detail configuration on input/output ports:


   OMX_PARAM_PORTDEFINITIONTYPE def;

   // get current settings of video_encode component from port 200
   memset(&def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
   def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
   def.nVersion.nVersion = OMX_VERSION;
   def.nPortIndex = 200;

   if ( OMX_GetParameter( ILC_GET_HANDLE(video_encode), OMX_IndexParamPortDefinition, &def) != OMX_ErrorNone) {
      printf("%s:%d: OMX_GetParameter() for video_encode port 200 failed!\n",__FUNCTION__, __LINE__);
      exit(1);
   }

   // Port 200: in 1/1 115200 16 enabled,not pop.,not cont. 320x240 320x240 @1966080 20
   def.format.video.nFrameWidth = 640;
   def.format.video.nFrameHeight = ((640)*9/16);
   def.format.video.xFramerate = 30 << 16;
   def.format.video.nSliceHeight = ALIGN_UP(def.format.video.nFrameHeight, 16);
   def.format.video.nStride = def.format.video.nFrameWidth;
   def.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;

   printf("Port %u: %s %u/%u %u %u %s,%s,%s %ux%u %ux%u @%u %u\n",
          def.nPortIndex,
          def.eDir == OMX_DirInput ? "in" : "out",
          def.nBufferCountActual,
          def.nBufferCountMin,
          def.nBufferSize,
          def.nBufferAlignment,
          def.bEnabled ? "enabled" : "disabled",
          def.bPopulated ? "populated" : "not pop.",
          def.bBuffersContiguous ? "contig." : "not cont.",
          def.format.video.nFrameWidth,
          def.format.video.nFrameHeight,
          def.format.video.nStride,
          def.format.video.nSliceHeight,
          def.format.video.xFramerate, def.format.video.eColorFormat);

   r = OMX_SetParameter( ILC_GET_HANDLE(video_encode), OMX_IndexParamPortDefinition, &def);
   if (r != OMX_ErrorNone) {
      printf("%s:%d: OMX_SetParameter() for video_encode port 200 failed with %x!\n", __FUNCTION__, __LINE__, r);
      exit(1);
   }

   OMX_VIDEO_PARAM_PORTFORMATTYPE format;

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

   printf("OMX_SetParameter for video_encode:201...\n");
   r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoPortFormat, &format);
   if (r != OMX_ErrorNone) {
      printf
         ("%s:%d: OMX_SetParameter() for video_encode port 201 failed with %x!\n",
          __FUNCTION__, __LINE__, r);
      exit(1);
   }

   OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
   // set current bitrate to 1Mbit
   memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE));
   bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE);
   bitrateType.nVersion.nVersion = OMX_VERSION;
   bitrateType.eControlRate = OMX_Video_ControlRateVariable;
   bitrateType.nTargetBitrate = 1000000;
   bitrateType.nPortIndex = 201;
   r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, &bitrateType);
   if (r != OMX_ErrorNone) {
      printf("%s:%d: OMX_SetParameter() for bitrate for video_encode port 201 failed with %x!\n",__FUNCTION__, __LINE__, r);
      exit(1);
   }

   // get current bitrate
   memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE));
   bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE);
   bitrateType.nVersion.nVersion = OMX_VERSION;
   bitrateType.nPortIndex = 201;

   if (OMX_GetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, &bitrateType) != OMX_ErrorNone) {
      printf("%s:%d: OMX_GetParameter() for video_encode for bitrate port 201 failed!\n",__FUNCTION__, __LINE__);
      exit(1);
   }
   printf("Current Bitrate=%u\n",bitrateType.nTargetBitrate);


The 4-th Step


Use the IL client library to bring the component in the idle state and enable its ports and the buffering :

   printf("encode to idle...\n");
   if (ilclient_change_component_state(video_encode,  OMX_StateIdle) == -1) {
      printf("%s:%d: ilclient_change_component_state(video_encode, OMX_StateIdle) failed",__FUNCTION__, __LINE__);
   }

   printf("enabling port buffers for 200...\n");
   if (ilclient_enable_port_buffers(video_encode, 200, NULL, NULL, NULL) != 0) {
      printf("enabling port buffers for 200 failed!\n");
      exit(1);
   }

   printf("enabling port buffers for 201...\n");
   if (ilclient_enable_port_buffers(video_encode, 201, NULL, NULL, NULL) != 0) {
      printf("enabling port buffers for 201 failed!\n");
      exit(1);
   }

  • ilclient_change_component_state(COMPONENT_T *comp, OMX_STATETYPE state)
  • The ilclient_change_component_state() function changes the state of an individual component. This will trigger the state change, and also wait for that state change to be completed. It should not be called if this state change has dependencies on other components (if the component is tunnelled, requires connected components to also change state.) also changing states.
    
       error = OMX_SendCommand(comp->comp, OMX_CommandStateSet, state, NULL);
       ilclient_wait_for_command_complete(comp, OMX_CommandStateSet, state)
    
    
  • ilclient_enable_port_buffers( COMPONENT_T *comp, int portIndex, ILCLIENT_MALLOC_T ilclient_malloc, ILCLIENT_FREE_T ilclient_free, void *userdata)
  • The ilclient_enable_port_buffers() function enables a port in base profile mode on a given component and allocates buffers. The port is not tunneled, so requires buffers to be allocated. userdata is the first argument to calls to ilclient_malloc and ilclient_free, if these arguments are not NULL.
    
    OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL);
    
    for (i=0; i != portdef.nBufferCountActual; i++)
       {
          unsigned char *buf;
          if(ilclient_malloc)
             buf = ilclient_malloc(private, portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname);
          else
             buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname);
    
          if(!buf)
             break;
    
          error = OMX_UseBuffer(comp->comp, end, portIndex, NULL, portdef.nBufferSize, buf);
          if(error != OMX_ErrorNone)
          {
             if(ilclient_free)
                ilclient_free(private, buf);
             else
                vcos_free(buf);
    
             break;
          }
          end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate);
       }
    
    OMX_UseBuffer() ( 3.2.2.14) requests the component to use a buffer already allocated by the IL client or a buffer already supplied by a tunneled component.
    
    #define OMX_UseBuffer (\
        hComponent,\
        ppBufferHdr,\
        nPortIndex,\
        pAppPrivate,\
        nSizeBytes,\
        pBuffer)\
    ((OMX_COMPONENTTYPE*)hComponent->UseBuffer(\
        hComponent,\
        ppBufferHdr,\
        nPortIndex,\
        pAppPrivate,\
        nSizeBytes,\
        pBuffer)
    
    • hComponent [in]
    • The handle of that component that executes the call.
    • ppBufferHdr [out]
    • A pointer to a pointer of an OMX_BUFFERHEADERTYPE structure that receives the pointer to the buffer header.
    • nPortIndex [in]
    • The index of the port that will use the specified buffer. This index is relative to the component that owns the port
    • pAppPrivate [in]
    • A pointer that refers to an implementation-specific memory area that is under responsibility of the supplier of the buffer.
    • nSizeBytes [in]
    • The buffer size in bytes.
    • pBuffer [in]
    • A pointer to the memory buffer area to be used.
For ex., IL client/supplier port allocates buffers and pass them to the non-supplier port

for (i=0;inBufferCount;i++) {
    pPort->pBuffer[i] = malloc(pPort->nBufferSize);
    OMX_UseBuffer( pPort->hTunnelComponent,
        &pPort->pBufferHdr[i],
        pPort->nTunnelPort,
        pPort,
        pPort->nBufferSize,
        pPort->pBuffer[j]);
}

The 5-th Step


Start to work:

   printf("encode to executing...\n");
   ilclient_change_component_state(video_encode, OMX_StateExecuting);

   outf = fopen(outputfilename, "w");
   if (outf == NULL) {
      printf("Failed to open '%s' for writing video\n", outputfilename);
      exit(1);
   }

   printf("looping for buffers...\n");
   do {
      buf = ilclient_get_input_buffer(video_encode, 200, 1);
      if (buf == NULL) {
         printf("Doh, no buffers for me!\n");
      } else {
         /* A pseudo card to fill the raw frame according to def.format.video.eColorFormat */
         generate_test_card(buf->pBuffer, &buf->nFilledLen, framenumber++, &def);

         if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_encode), buf) != OMX_ErrorNone) {
            printf("Error emptying buffer!\n");
         }

         out = ilclient_get_output_buffer(video_encode, 201, 1);

         if (out != NULL) {
            if (out->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
               int i;
               for (i = 0; i < out->nFilledLen; i++)
                  printf("%x ", out->pBuffer[i]);
               printf("\n");
            }

            r = fwrite(out->pBuffer, 1, out->nFilledLen, outf);
            if (r != out->nFilledLen) {
               printf("fwrite: Error emptying buffer: %d!\n", r);
            } else {
               printf("Writing frame %d/%d, len %u\n", framenumber, NUMFRAMES, out->nFilledLen);
            }
            out->nFilledLen = 0;
         } else {
            printf("Not getting it :(\n");
         }

         r = OMX_FillThisBuffer(ILC_GET_HANDLE(video_encode), out);
         if (r != OMX_ErrorNone) {
            printf("Error sending buffer for filling: %x\n", r);
         }
      }
   }
   while (framenumber < NUMFRAMES);

   fclose(outf);

  • OMX_BUFFERHEADERTYPE ilclient_get_input_buffer(COMPONENT_T *comp, int portIndex, int block)
  • The ilclient_get_input_buffer() function returns a buffer that was sent to an input port and that has been returned from a component using the OMX_EmptyBufferDone callback. If block non-zero, the function will block until a buffer is available. A pointer to buffer will be returned if available, otherwise NULL.
  • ilclient_get_output_buffer(COMPONENT_T *comp, int portIndex, int block)
  • The ilclient_get_output_buffer() function returns a buffer that was sent to an output port and that has been returned from a component using the OMX_FillBufferDone callback.

Chapter 11: OpenMAX Buffers on the Raspberry Pi


Each component has ports, and each port has a number of buffers.
When a component is created, it has an array of pointers to buffer headers but doesn’t actually have any memory allocated either for the headers or for the buffers. There are 2 eays to allocate buffers:
  • OMX_AllocateBuffer()
  • 
    #define OMX_AllocateBuffer
        ( hComponent, pBuffer, nPortIndex, pAppPrivate, nSizeBytes ) \
    ((OMX_COMPONENTTYPE*) hComponent)->AllocateBuffer
        ( hComponent, ppBuffer, nPortIndex, pAppPrivate, nSizeBytes) pAppPrivate,nSizeBytes, pBuffer)
    
    The OMX_AllocateBuffer macro will request that the component allocate a new buffer and buffer header. The component will allocate the buffer and the buffer header and return a pointer to the buffer header.
    
    for (i = 0; i < portDef->nBufferCount; i++) {
      OMX_AllocateBuffer(hComp, &portDef->pBufferHdr[i], portDef->nPortIndex, portDef, portDef->nBufferSize);
    }
    
    For ex., IL client asks component to allocate buffers:
    
    for (i=0;i<pClient->nBufferCount;i++) {
        OMX_AllocateBuffer(hComp,
            &pClient->pBufferHdr[i],
            pClient->nPortIndex,
            pClient,
            pClient->nBufferSize);
    }
    
  • The IL Client Library
  • The ilclient_enable_port_buffers() function enables a port in base profile mode on a given component and allocates buffers for the port ( by OMX_UseBuffer() ).

OpenMAX IL defines two means of data communication:
  • Non-tunneled communication
  • a port exchanges data only with the IL client
  • Tunneled communication
  • a port exchanges data directly with a port on another component

Non-tunneled Data Flow, 3.4.2.1


The IL client is entirely responsible for moving data buffers among components if data tunneling is not used.

An IL client that has a data buffer to deliver to a component input port shall issue an OMX_EmptyThisBuffer() call.

For the component output port, the IL client shall initially provide one or more empty buffers into which the component can write output data; the OMX_FillThisBuffer() call accomplishes this task. As soon as one buffer is available from the component output port, the component shall send an FillBufferDone() callback. At this point, your application will take the processed data and do something with it.


Tunneled Initialization, 3.4.1.2


To avoid moving data buffers back and forth among the IL client and OpenMAX IL components, data tunnels can be set up so that the output buffer of one component is passed directly to the input port of the next component in the chain.
The tunneling setup and initialization based on the following steps:
  • The components are constructed with the calls to OMX_GetHandle.
  • The components are tunneled, linking an output port of the first component to an input port of the second component. The port that shall supply the buffer is decided in this phase.
  • The IL client may override the input ports’ choice of buffer supplier after OMX_SetupTunnel has completed by setting the buffer supplier into the input port, which in turn will reprogram the supplier to the output port.


Tunneled Data Flow, 3.4.2.2


In data tunneling, OpenMAX IL components directly pass data buffers among themselves without returning them to the IL client.

Writing to and Reading from Buffers


The following 2 processes should be done concurrently by the client:
  • whenever there is an empty input buffer:
  • put data into the input buffer ( get by ilclient_get_input_buffer() ) then call OMX_EmptyThisBuffer() to ask the component to process it
  • Whenever an output buffer is full:
  • process the buffered output ( get by ilclient_get_output_buffer(), data is received by the FillBufferDone callback ) data then call OMX_FillThisBuffer() to inform the component

The pseudo code:

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


EOS Flag


The component needs to know when there is no input data so that it sends a signal to the client 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,

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,

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

Dynamic Port Reconfiguration,3.4.5


You may not know some information before you can configure ports correctly. These information can be got after the component processed it. For ex., the size of the buffer for the output port.

When the OpenMAX IL component detects the setting needed to be changed, it shall notify the IL client by generating the OMX_PortSettingsChanged event.
As soon as the IL client receives this callback, it shall disable the related port then read the new port settings with OMX_GetConfig().

After the setting is corrected and necessary resource is allocated according to the setting, related ports shall also be set up with OMX_SetConfig().

The IL Client library has two relevant functions to receive OMX_PortSettingsChanged events:
  • ilclient_remove_event(COMPONENT_T *st, OMX_EVENTTYPE eEvent, OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2)
  • The ilclient_remove_event() function queries the event list for the given component, matching against the given criteria. If a matching event is found, the event is removed and added to the free event list.
  • 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)
  • The ilclient_wait_for_event() function is similar to ilclient_remove_event(), but allows the caller to block until that event arrives.

Chapter 12: Image Processing on the Raspberry Pi


OMX_IMAGE_PORTDEFINITIONTYPE (4.4.3) is the data structure that is used to define an image path.
The OMX_IMAGE_PORTDEFINITIONTYPE structure is included as part of the OMX_PARAM_PORTDEFINITIONTYPE structure

typedef struct OMX_PARAM_PORTDEFINITIONTYPE {
  ...
  union {
    OMX_AUDIO_PORTDEFINITIONTYPE audio;
    OMX_VIDEO_PORTDEFINITIONTYPE video;
    OMX_IMAGE_PORTDEFINITIONTYPE image;
    OMX_OTHER_PORTDEFINITIONTYPE other;
  } format;
  ...
} OMX_PARAM_PORTDEFINITIONTYPE;

typedef struct OMX_IMAGE_PORTDEFINITIONTYPE {
    OMX_STRING cMIMEType;
    OMX_NATIVE_DEVICETYPE pNativeRender;
    OMX_U32 nFrameWidth;
    OMX_U32 nFrameHeight;
    OMX_S32 nStride;
    OMX_U32 nSliceHeight;
    OMX_BOOL bFlagErrorConcealment;
    OMX_IMAGE_CODINGTYPE eCompressionFormat;
    OMX_COLOR_FORMATTYPE eColorFormat;
    OMX_NATIVE_WINDOWSTYPE pNativeWindow;
} OMX_IMAGE_PORTDEFINITIONTYPE;

OMX_IMAGE_PARAM_PORTFORMATTYPE (4.4.4) is used to enumerate the various data input/output format supported by the port.

typedef struct OMX_IMAGE_PARAM_PORTFORMATTYPE {
    OMX_U32 nSize;
    OMX_VERSIONTYPE nVersion;
    OMX_U32 nPortIndex;
    OMX_U32 nIndex;
    OMX_IMAGE_CODINGTYPE eCompressionFormat;
    OMX_COLOR_FORMATTYPE eColorFormat;
} OMX_IMAGE_PARAM_PORTFORMATTYPE;
  • eCompressionFormat
  • This is the enumeration describing the compression format used on the port. Table 4-66 shows the supported image compression formats. When OMX_IMAGE_CodingUnused is specified, the eColorFormat field is valid. On the RPi, the only value given is OMX_IMAGE_CodingUnused .
  • eColorFormat
  • This is the decompressed color format used for the port. This field is valid only when the eCompressionFormat field is set to OMX_IMAGE_CodingUnused. For enumerations on OMX_COLOR_FORMATTYPE, see section 4.2. /opt/vc/include/IL/OMX_IVCommon.h defines it.
The input port supports several compressed formats, the output port supports several color formats.
The color format will be set to match that emitted by the input image format.

JPEG Encoding


image_encode takes raw images in various formats ranging form 16/24/32-bit RGB with Alpha formats to variants of YUV and CrCbY. The output can be any of the supported compressed image formats
You basically only have to configure the input and output port and you are ready to go and throw data on it.

The basic steps:
  • Get handle with callbacks for EmptyBufferDone and FillBufferDone.
  • Disable ports 340 and 341.
  • Switch handle to idle.
  • Configure, enable and allocate bufferX for port 340.
  • Configure, enable and allocate bufferY for port 341.
  • Switch handle to executing.
  • Loop over the data:
    • Copy compressed data from bufferY.
    • Fill bufferY.
    • Copy your image data to bufferX.
    • Empty bufferX.
  • Switch handle to idle.
  • Disable and deallocate bufferX for port 340.
  • Disable and deallocate bufferY for port 341.
  • Switch handle to loaded.
  • Free handle.
You can set the compression ratio or quantization factor (Q-Factor) for the JPEG data format.


Chapter 13: OpenMAX Video Processing on the Raspberry Pi


Video Basics


About YUV Video


YUV refers to a family of color spaces, all of which encode brightness information separately from color information.
In fact, YUV almost always refers to one particular color space named Y'CbCr. However, YUV is currently often used as a general term for any color space that works along the same principles as Y'CbCr.
The Y' component, also called luma, represents the brightness value of the color. luma is different from luminance, which is designated Y.
Luminance is derived from linear RGB values, whereas luma is derived from non-linear (gamma-corrected) RGB values.
Luma is derived from an RGB color:
  • SDTV(ITU-R BT.601)
  • 
    Y' = 0.299R + 0.587G + 0.114B
    
  • HDTV(ITU-R BT.709)
  • 
    Y' = 0.2125R + 0.7154G + 0.0721B
    
The U and V components, also called chroma values or color difference values, are derived by subtracting the Y value from the red and blue components of the original RGB color:

U = B - Y'
V = R - Y'
YUV contains enough information to recover the original RGB value.
Black-and-white televisions ignore the chroma and display the combined signal as a grayscale image.

Y'CbCr: YUV in Computer Video



Pb = (0.5 / (1 - 0.114)) × (B - Y')
Pr = (0.5 / (1 - 0.299)) × (R - Y')
Y' = 16 + 219 × Y'
Cb = 128 + 224 × Pb
Cr = 128 + 224 × Pr
These scaling and offset factors produce the range of values listed in the following table:
ComponentRange
Y'16–235
Cb/Cr16–240, with 128 representing zero

YUV Formats

  • 緊縮格式(packed formats):將Y、U、V值儲存成Macro Pixels,和RGB的存放方式類似。
  • In packed formats, you usually have only one plane, with all the luma and chroma data interleaved. This is similar to RGB pixel formats. Packet formats cannot normally deal with vertical sub-sampling. Otherwise scan lines would have different sizes. So generally, packed formats are horizontally subsampled, especially by a factor of 2 (i.e., YUV 4:2:2).
  • 平面格式(planar formats):將Y、U、V的三個分量分別存放在不同的矩陣中。
  • Planar formats use separate matrices for each of the 3 color components. In other words, there is one table of luminance pixel values, and two separate tables for the chrominance components. This segregated representation in memory of pixels is more convenient for video coding. Y份量,U份量和V份量都是以獨立的平面組織的,也就是說所有的U份量必須在Y分量後面,而V份量在所有的U份量後面

YUV Sub-sampling

為節省頻寬起見,大多數YUV格式平均使用的每像素位數都少於24位元。
主要的抽樣(subsample)格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和YCbCr 4:4:4。
  • 4:4:4表示完全取樣。
  • 4:2:2表示2:1的水平取樣,垂直完全採樣。
  • 4:2:0表示2:1的水平取樣,垂直2:1採樣。
  • DVD-Video是以YUV 4:2:0的方式採樣記錄,也就是我們俗稱的I420
  • 4:1:1表示4:1的水平取樣,垂直完全採樣。

I420 planar formats

Most video decoders output raw pictures in I420 format. They comprise an [n , m] Y plane followed by [(n/2), (m/2)] U and V planes.

Image Stride

When a video image is stored in memory, the memory buffer might contain extra padding bytes after each row of pixels. The padding bytes affect how the image is stored in memory, but do not affect how the image is displayed. The stride is the number of bytes from one row of pixels in memory to the next row of pixels in memory. Stride is also called pitch.

Generate a Video image

Generate an video image changing with frame number in OMX_COLOR_FormatYUV420PackedPlanar format:

            /*
                A video frame is divided into multiple slices which are to be saved in buffers.
                
                        nStride
                  +------------------+
                  |      slice#1     | nSliceHeight
                  +------------------+
                  |                  |
                  
                  |                  |
                  +------------------+
                  
                Each buffer contains a slice of the Y, U, and V planes.
             
               buf_start
                  +-----------------------------+
                  |              Y              |
                  |                             |
                  +--------------+--------------+
                  |       U      |     V        |
                  +--------------+--------------+
                  
                Fetch the output buffer until a complete frame is read                 
             */

   def->format.video.nFrameWidth = WIDTH;
   def->format.video.nFrameHeight = HEIGHT;
   def->format.video.xFramerate = 30 << 16;
   def->format.video.nSliceHeight = ALIGN_UP(def.format.video.nFrameHeight, 16);
   def->format.video.nStride = def.format.video.nFrameWidth;
   def->format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;

   int frame=0;
   OMX_PARAM_PORTDEFINITIONTYPE *def;
   OMX_U32 * filledLen;
   int i, j;
   OMX_VIDEO_PORTDEFINITIONTYPE *vid = &def->format.video;
   // locate the starting position of each plane in Y'CrCb
   char *y = buf;
   char *u = y + vid->nStride * vid->nSliceHeight;
   char *v = u + (vid->nStride >> 1) * (vid->nSliceHeight >> 1);

   for (j = 0; j < vid->nFrameHeight / 2; j++) {
      // move the starting position for each scan
      char *py = y + 2 * j * vid->nStride;
      char *pu = u + j * (vid->nStride >> 1);
      char *pv = v + j * (vid->nStride >> 1);
      for (i = 0; i < vid->nFrameWidth / 2; i++) {
         int z = (((i + frame) >> 4) ^ ((j + frame) >> 4)) & 15;
         py[0] = py[1] = py[vid->nStride] = py[vid->nStride + 1] = 0x80 + z * 0x8;
         pu[0] = 0x00 + z * 0x10;
         pv[0] = 0x80 + z * 0x30;
         py += 2;
         pu++;
         pv++;
      }
   }
   *filledLen = (vid->nStride * vid->nSliceHeight * 3) >> 1;
   frame++;

Video Use Case Examples, 4.3.3


Figure 4-4 depicts one possible set of components as well as the tunneling of ports for these components to implement a H.263 video encoding scheme. This use case encodes raw video into H.263 format and writes it to a file while previewing the captured video on a display.



Chapter 14: OpenMAX Audio on the Raspberry Pi

Chapter 15: Rendering OpenMAX to OpenGL on the Raspberry Pi

Chapter 16: Playing Multimedia Files on the Raspberry Pi

Chapter 17: Basic OpenVG on the Raspberry Pi

Chapter 18: Text Processing in OpenVG on the Raspberry Pi

Chapter 19: Overlays on the Raspberry Pi

VMCS-X OpenMAX IL Components

Video Domain Components

OMX.broadcom.camera

73
camera
70
71
72
This is an interface to a platform-specific camera hardware, and produces raw YUV images.
  • port#73
  • This port supports only the clock format type. This is a clock input port for correctly assigning timestamps to frames.
  • port#70
  • The preview port, it will emit images whenever the component is in the executing state and the port is enabled.
  • port#71
  • The video capture port, it will emit images only when the capturing configuration parameter OMX_IndexConfigPortCapturing is set for that port, and either the clock port is disabled or the connected clock is in the Running state. When a connected clock is transitioned to stop the component will stop recording data at the end of the current image, and then transmit a packet with no data but with the EOS flag set.
  • port#72
  • The still image capture port, it will only capture an image when the port is executing and OMX_IndexConfigPortCapturing is set. If the bOneShot flag is set, an EOS will be emited from the port after each complete capture.
Both the preview and video capture ports must be at the same resolution and of the same image format. Basically, the video ports will emit images in one of the following formats:
  • OMX_COLOR_FormatYUV420PackedPlanar
  • This format differs from OMX_COLOR_FormatYUV420Planar in that each slice of data shall contain a plane of Y, U, and V data in this order, whereas the OMX_COLOR_FormatYUV420Planarr format transfers each plane in its entirety. (planar的YUV格式,先連續儲存所有畫素點的Y,緊接著儲存所有畫素點的U,隨後是所有畫素點的V。Y,U,V分別用三個獨立的陣列表示。)
  • OMX_COLOR_FormatYUV420PackedSemiPlanar
  • OMX_COLOR_FormatYUV422PackedPlanar
Output images can be provided in Broadcom-specific format when configured for proprietary tunnelling to a Broadcom component supporting the OMX_IndexParamImagePoolDisplayFunction callback, or OMX_IndexParamCameraPoolToEncoderFunction callback The recommended initialisation code sequence is:
  1. Create component.
  2. Use OMX_IndexConfigRequestCallback to request callbacks on OMX_IndexParamCameraDeviceNumber.
  3. Set OMX_IndexParamISPTunerName.
  4. Set OMX_IndexParamCameraFlashType.
  5. Set OMX_IndexParamCameraDeviceNumber.
  6. Wait for the callback that OMX_IndexParamCameraDeviceNumber has changed. At this point, all the drivers have been loaded. Other settings can be applied whilst waiting for this event.
  7. Query for OMX_IndexConfigCameraSensorModes as required.
  8. Change state to IDLE, and proceed as required.
Parameter and Data structure used for each port are defined in userland/interface/vmcs_host/khronos/IL/OMX_Index.h, Broadcom Custom parameters are described in Broadcom Custom Index List.
  • component
    • OMX_IndexParamCameraDeviceNumber
    • This index uses the standard IL structure OMX_PARAM_U32TYPE . When this is set, the value will be checked to ensure that the camera number specified is defined for the platform. Setting this parameter also triggers loading of the relevant drivers to speed up queries for OMX_IndexConfigCameraSensorModes, which require the drivers to be loaded. This parameter is supported by OMX_IndexConfigRequestCallback, so the client can be notified once that driver is loaded, rather than having to poll the settingss.
    • OMX_IndexConfigRequestCallback
    • This config implements IL416c to allow clients to request notification of when a config or parameter is changed. Enable config change notifications. When the parameter specified in nIndex for port nPortIndex changes, an OMX_EventParamOrConfigChanged event is generated for the client. OMX_Broadcom.h:
      
      typedef struct OMX_CONFIG_REQUESTCALLBACKTYPE
      {
         OMX_U32 nSize;
         OMX_VERSIONTYPE nVersion;
         OMX_U32 nPortIndex;
         OMX_INDEXTYPE nIndex;
         OMX_BOOL bEnable;
      } OMX_CONFIG_REQUESTCALLBACKTYPE;
      
    • OMX_IndexParamISPTunerName
    • ISP tuner name. Allow the client to query / set the name of the camera tuner module to be used by Camplus. If tuner_name is a null string, the default tuner for the camera module is loaded. It is recommended that this parameter is set before OMX_IndexParamCameraDeviceNumber to avoid reloading the CDI and default tuner.
      
      typedef struct OMX_PARAM_CAMERAISPTUNERTYPE {
         OMX_U32 nSize;
         OMX_VERSIONTYPE nVersion;
         OMX_U8 tuner_name[64];
      } OMX_PARAM_CAMERAISPTUNERTYPE;
      
  • port#73
  • OMX_IndexParamOtherPortFormat -> OMX_OTHER_PARAM_PORTFORMATTYPE
    
    typedef struct OMX_OTHER_PARAM_PORTFORMATTYPE {
        OMX_U32 nSize; 
        OMX_VERSIONTYPE nVersion;
        OMX_U32 nPortIndex; 
        OMX_U32 nIndex; /** Indicates the enumeration index for the format from 0x0 to N-1 */
        OMX_OTHER_FORMATTYPE eFormat; /** Type of data expected for this channel */
    } OMX_OTHER_PARAM_PORTFORMATTYPE;
    
    
  • port#70
  • port#71
    • OMX_IndexConfigPortCapturing
    • Query / set whether images are to be emitted on the specified port or not. Changing to enabling capture when the component is not executing will reset the internal counter used for time stamping frames when not connected to a clock component.
      
      typedef struct OMX_CONFIG_PORTBOOLEANTYPE{
          OMX_U32 nSize;
          OMX_VERSIONTYPE nVersion;
          OMX_U32 nPortIndex;
          OMX_BOOL bEnabled;
      } OMX_CONFIG_PORTBOOLEANTYPE;
      
  • port#72

Image Domain Components

OMX.broadcom.image_encode


340
image_encode
341
A conformant image encode component, which takes raw pixels on its input port, and encodes the image into various compressed formats on the output port.


OMX.broadcom.image_write



330
image_write
A conformant image writer component, which accepts a compressed image on its input port and writes a file to the filesystem.

Raspberry Pi Camera Module

There are four applications provided: raspistill, raspivid, raspiyuv and raspividyuv. raspistill and raspiyuv are very similar and are intended for capturing images; raspivid and raspvidyuv are for capturing video. All the applications are driven from the command line, and written to take advantage of the MMAL API which runs over OpenMAX. The MMAL API provides an easier to use system than that presented by OpenMAX. Note that MMAL is a Broadcom-specific API used only on Videocore 4 systems. The applications use up to four OpenMAX (MMAL) components: camera, preview, encoder, and null_sink.
Why to use MMAL: Good luck with OMX. I've been working on and off with it for 5 years, and still have trouble. It really isn't very good. The spec document is awful to learn from (It's a spec not a manual) and the code is a nightmare to debug. Fair point on MMAL not being docuemnted per se - but note it does have full Doxygen comments in the ARM side source code, which is published on the web somewhere.

Cobalt

Set up Cobalt environment

The Cobalt Authors originally maintained a port of Chromium called H5VCC, the HTML5 Video Container for Consoles, ported to each of the major game consoles was a dangerous task. Cobalt is a lightweight application container (i.e. an application runtime, like a JVM or the Flash Player) that is compatible with a subset of the W3C HTML5 specifications. If you author a single-page web application (SPA) that complies with the Cobalt Subset of W3C standards, it will run as well as possible on all the devices that Cobalt supports. These instructions explain how to set up Cobalt for your workstation and your Raspberry Pi device.

Set up your environment - Linux

These instructions explain how Linux users set up their Cobalt development environment,
  • clone the tools used by Cobalt
  • 
     cd ~/
     install -d cobalt
     cd cobalt
     git clone https://cobalt.googlesource.com/depot_tools
    
    
    The above put Cobalt tools in ~/cobalt/depot_tools . Then, setup the tool's path:
    
    export PATH=${PATH}:~/cobalt/depot_tools
    
    
  • Run the following command to install packages needed to build and run Cobalt on your Linux/Pi
  • 
    sudo apt-get install build-essential coreutils git gperf \
           libasound2-dev libavformat-dev libavresample-dev \
           libdirectfb-dev libdirectfb-extra libpulse-dev \
           libgl1-mesa-dev libgles2-mesa-dev libx11-dev \
           libxcomposite-dev libxcomposite1 libxrender-dev libxrender1 \
           libxpm-dev m4 ruby tar xserver-xephyr xz-utils yasm
    
    For now, ruby is required:
    
    sudo apt-get install ruby
    
    
  • Install the latest version of the standard C++ header files (libstdc++)
  • 
    sudo apt-get install libstdc++-4.8-dev
    
    
  • Install bison
    • For old Cobalt, use bison-2.7.1
    • For new Cobalt, use bison-3.0.5
    Remove installed bison and install bison-x.y.z
    
     sudo apt-get remove bison
     cd /tmp
     wget http://ftp.gnu.org/gnu/bison/bison-x.y.z.tar.gz
     tar zxf bison-x.y.z.tar.gz
     cd bison-x.y.z
     sh configure && make && sudo make install
    
    
  • clone the Cobalt source code repository
  • 
     cd ~/cobalt
     git clone https://cobalt.googlesource.com/cobalt
    
    
  • Modify your path to include the version of Clang that will be downloaded later
  • 
    export PATH="~/cobalt/cobalt/src/third_party/llvm-build/Release+Asserts/bin:${PATH}"
    
    
  • generate build files
  • 
    cd ~/cobalt/cobalt/src
    cobalt/build/gyp_cobalt [-C <build_type>] <platform>
    
    
    • To generate for Linux x64
    • 
      cobalt/build/gyp_cobalt linux-x64x11
      
      
    • To generate for Raspberry Pi
    • 
      cobalt/build/gyp_cobalt raspi-1
      
      
  • build the code
  • 
    cd ~/cobalt/cobalt/src
    ninja -C out/<platform>_<build_type> <target_name>
    
    
    build_type:
    TypeOptimizationsLoggingAssertsDebug InfoConsole
    debugNoneFullFullFullEnabled
    develFullFullFullFullEnabled
    qaFullLimitedNoneNoneEnabled
    goldFullNoneNoneNoneDisabled
    • To build for Linux 64
    • 
      ninja -C out/linux-x64x11_debug cobalt
      
      
    • To build for Raspberry Pi
    • 
      ninja -C out/raspi-1_debug cobalt
      
      
  • launch the built Cobalt client
  • 
    out/<platform>_<build_type>/<target_name> --allow_http --ignore_certificate_errors [--url=<url>]
    
    
    If you want to connect to an https host that doesn't have a certificate validatable by our set of root CAs, you must pass the --ignore_certificate_errors flag to the Cobalt command-line. See cobalt/browser/switches.cc for more command-line options.
    
    out/linux-x64x11_debug/cobalt --allow_http --ignore_certificate_errors 
    
    
    For the usage of cobalt,
    
    ./cobalt --help 
    Options: 
      --video_playback_rate_multiplier
      Specifies the multiplier of video playback rate.  Set to a value greater than 1.0 to play video faster.  Set to a value less than 1.0 to play video slower.
    
      --viewport
      Specifies the viewport size: width ['x' height]
    
      --version
      Prints the current version of Cobalt
    
      --fallback_splash_screen_url
      Setting this switch defines the splash screen URL that Cobalt will use in absence of a web cache. The referenced url should be a content file (for example file:///foobar.html) or an embedded file (for example h5vcc-embedded://foobar.html) and all files referenced must be content or embedded files as well. If none is passed (case-insensitive), no splash screen will be constructed. If no value is set, the URL in gyp_configuration.gypi or base.gypi will be used.
    
      --software_surface_cache_size_in_bytes
      Only relevant if you are using the Blitter API. Determines the capacity of the software surface cache, which is used to cache all surfaces that are rendered via a software rasterizer to avoid re-rendering them.
    
      --skia_atlas_texture_dimensions
      Specifies the dimensions of the Skia caching texture atlases (e.g. 2048x2048).
    
      --skia_cache_size_in_bytes
      Determines the capacity of the skia cache.  The Skia cache is maintained within Skia and is used to cache the results of complicated effects such as shadows, so that Skia draw calls that are used repeatedly across frames can be cached into surfaces.  This setting is only relevant when using the hardware-accelerated Skia rasterizer.  While it depends on the platform, this setting may affect GPU memory usage.
    
      --scratch_surface_cache_size_in_bytes
      Determines the capacity of the scratch surface cache.  The scratch surface cache facilitates the reuse of temporary offscreen surfaces within a single frame.  This setting is only relevant when using the hardware-accelerated Skia rasterizer.  While it depends on the platform, this setting may affect GPU memory usage.
    
      --retain_remote_typeface_cache_during_suspend
      Causes the remote typeface cache to be retained when Cobalt is suspended, so that they don't need to be re-downloaded when Cobalt is resumed.
    
      --remote_typeface_cache_size_in_bytes
      Determines the capacity of the remote typefaces cache which manages all typefaces downloaded from a web page.
    
      --reduce_gpu_memory_by
      Reduces the gpu-memory of the system by this amount. This causes AutoMem to reduce the runtime size of the GPU-Memory caches.
    
      --reduce_cpu_memory_by
      Reduces the cpu-memory of the system by this amount. This causes AutoMem to reduce the runtime size of the CPU-Memory caches.
    
      --qr_code_overlay
      Display QrCode based overlay information. These information can be used for performance tuning or playback quality check.
    
      --offscreen_target_cache_size_in_bytes
      Determines the amount of GPU memory the offscreen target atlases will use. This is specific to the direct-GLES rasterizer and caches any render tree nodes which require skia for rendering. Two atlases will be allocated from this memory or multiple atlases of the frame size if the limit allows. It is recommended that enough memory be reserved for two RGBA atlases about a quarter of the frame size.
    
      --max_cobalt_gpu_usage
      Specifies the maximum GPU usage of the cobalt.
    
      --max_cobalt_cpu_usage
      Specifies the maximum CPU usage of the cobalt.
    
      --local_storage_partition_url
      Overrides the default storage partition with a custom partition URL to use for local storage. The provided URL is canonicalized.
    
      --javascript_gc_threshold_in_bytes
      Specifies the javascript gc threshold. When this amount of garbage has collected then the garbage collector will begin running.
    
      --url
      Setting this switch defines the startup URL that Cobalt will use.  If no value is set, a default URL will be used.
    
      --image_cache_size_in_bytes
      Determines the capacity of the image cache which manages image surfaces300 downloaded from a web page.  While it depends on the platform, often (and ideally) these images are cached within GPU memory.
    
      --help
      Prints help information of cobalt command
    
      --fps_overlay
      If toggled, framerate statistics will be displayed in an on-screen overlay and updated after each animation completes, or after a maximum number of frames has been collected.
    
      --fps_stdout
      If toggled, framerate statistics will be printed to stdout after each animation completes, or after a maximum number of frames has been collected.
    
      --force_migration_for_storage_partitioning
      Overrides the default storage migration policy when upgrading to partitioned storage and forces data migration regardless of theinitial app url. The default policy is to migrate data only forhttps://www.youtube.com/tv.
    
      --enable_map_to_mesh_rectangular
      If toggled and map-to-mesh is supported on this platform, this allows it to accept the 'rectangular' keyword. Useful to get rectangular stereo video on platforms that do not support stereoscopy natively, letting the client apply a stereo mesh projection (one that differs for each eye).
    
      --disable_timer_resolution_limit
      By default, window.performance.now() will return values at a clamped minimum resolution of 20us.  By specifying this flag, the limit will be removed and the resolution will be 1us (or larger depending on the platform.
    
      --disable_javascript_jit
      Specifies that javascript jit should be disabled.
    
    

Set up your environment - Raspberry Pi

  • Set up your Pi
  • Update the package configuration on your Raspberry Pi so that Cobalt can run properly
    
    sudo apt-get remove -y --purge --auto-remove libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa libgles2-mesa-dev
    sudo apt-get install -y libpulse-dev libasound2-dev libavformat-dev libavresample-dev
    
    
    Note: Raspberry Pi cannot have MesaGL installed and will return an error, like `DRI2 not supported` or "DRI2 failed to authenticate" if MesaGL is installed.
  • Setup the build host for Pi
  • Setup the path for the Pi's cross-compiling toolchain:
    
    export PATH=${PATH}:~/cobalt/depot_tools
    export PATH="~/cobalt/cobalt/src/third_party/llvm-build/Release+Asserts/bin:${PATH}"
    cd ~/cobalt/cobalt/src
    
    export RASPI_HOME=~/pi
    install -d $RASPI_HOME/sysroot
    
    
  • Sync remote Pi's system files to your host
  • 
    cd ~
    install -d pi/sysroot
    rsync -avzh --safe-links \
          --delete-after pi@$RASPI_ADDR:/{opt,lib,usr} \
          --exclude="lib/firmware" --exclude="lib/modules" \
          --include="usr/lib" --include="usr/include" \
          --include="usr/local/include" --include="usr/local/lib" \
          --exclude="usr/*" --include="opt/vc" --exclude="opt/*" \
          ~/pi/sysroot
    
    
    password: raspberry. Note for the "delete" option, the "rsync [options] SOURCE TARGET" command wont delete any file in the SOURCE while you use some of its options delete in that command. The delete options are just for TARGET:
    • --delete-before
    • receiver deletes before transfer (sync) files
    • --delete-after
    • receiver deletes after transfer (sync) files
  • Build
  • 
    cobalt/build/gyp_cobalt raspi-2
    ninja -C out/raspi-2_debug cobalt
    
    
    You may see the following build error:
    1. 
      ...
      In file included from ../../starboard/types.h:33:0,
                       from ../../starboard/system.h:25,
                       from ../../starboard/log.h:33,
                       from ../../starboard/client_porting/poem/assert_poem.h:22,
                       from ../../third_party/libwebp/src/utils/bit_reader_utils.c:19:
      /home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/lib/gcc/arm-linux-gnueabihf/4.8.3/include/stdint.h:9:26: fatal error: stdint.h: No such file or directory
       # include_next <stdint.h>
      
      
      The above may be the toolchain's problem with ARM cross-compilers provided with mainstream Debian/Ubuntu. Most people said this in the Internet discussion group and suggest you to build a ARM toolchain by yourself. But, it is not the root cause for the building of Cobalt. Something are hard-coded in the Cobalt's building mechanism ( starboard/raspi/shared/gyp_configuration.py ) which causes the cross-compiling issue for Raspberry Pi. Therefore, you need to do "Sync your sysroot" as mentioned.
    2. 
      ninja: Entering directory `out/raspi-2_debug'
      [1/1] LINK cobalt
      FAILED: /home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++ @cobalt.rsp
      collect2: error: ld terminated with signal 9 [Killed]
      ninja: build stopped: subcommand failed.
      
      
      Your virtual machine does not have enough memory to perform the linking phase. Linking is typical the most memory intensive part of a build since it's where all the object code comes together and is operated on as a whole. You can consider to allocate more RAM to the VM or increase the amount of swap space. With luck, building it in the non-debug mode may save enough memory to pass the linking stage.
    3. 
                                                                  ^
      [4817/5744] CXX obj/cobalt/renderer/ra...dware_rasterizer.hardware_rasterizer.o
      ../../cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc: In constructor ‘cobalt::renderer::rasterizer::skia::HardwareRasterizer::Impl::Impl(cobalt::renderer::backend::GraphicsContext*, int, int, int, int, bool, bool)’:
      ../../cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc:589:65: warning: passing NULL to non-pointer argument 2 of ‘static GrContext* GrContext::Create(GrBackend, GrBackendContext, const GrContextOptions&)’ [-Wconversion-null]
             GrContext::Create(kOpenGL_GrBackend, NULL, context_options));
                                                                       ^
      [5744/5744] LINK cobalt
      FAILED: /home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++ @cobalt.rsp
      /home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/../lib/gcc/arm-linux-gnueabihf/4.8.3/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lEGL
      /home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/../lib/gcc/arm-linux-gnueabihf/4.8.3/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lGLESv2
      collect2: error: ld returned 1 exit status
      ninja: build stopped: subcommand failed.
      
      
      The device may have the Mesa version of libEGL and libGLESv2 in /usr/lib/arm-linux-gnueabihf, resulting linking picking these instead of the real thing from /opt/vc/lib. Mesa's version may be fine for X11 desktop apps not caring about OpenGL performance. But, is totally useless for windowing system-less, fullscreen embedded apps. These libraries were renamed to libbrcmEGL.so and libbrcmGLESv2.so , in order to reduce the risk of conflicting with their libgles2-mesa-dev counterparts. You can change the makefile to use the link option:
      
      -lbrcmEGL -lbrcmGLESv2
      
      
      Or create symbolick links,
      
      cd ~/pi/sysroot/usr/lib/arm-linux-gnueabihf
      sudo mv libEGL.so.1.0.0 libEGL.so.1.0.0_backup
      sudo mv libGLESv2.so.2.0.0 libGLESv2.so.2.0.0_backup
      sudo ln -s ../../../opt/vc/lib/libEGL.so libEGL.so.1.0.0
      sudo ln -s ../../../opt/vc/lib/libGLESv2.so libGLESv2.so.2.0.0
      
      cd ~/pi/sysroot/opt/vc/lib
      sudo ln -s libbrcmEGL.so libEGL.so
      sudo ln -s libEGL.so libEGL.so.1
      sudo ln -s libbrcmGLESv2.so libGLESv2.so
      sudo ln -s libGLESv2.so libGLESv2.so.2
      
      
    4. 
      ...
      /home/jerry/pi/sysroot/usr/lib/arm-linux-gnueabihf/libopenmpt.so.0: undefined reference to `VTT for std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >@GLIBCXX_3.4.21'
      /home/jerry/pi/sysroot/usr/lib/arm-linux-gnueabihf/libopenmpt.so.0: undefined reference to `std::range_error::range_error(char const*)@GLIBCXX_3.4.21'
      collect2: error: ld returned 1 exit status
      ninja: build stopped: subcommand failed.
      
      
      If you get linker errors about undefined references to symbols that involve types in the std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are trying to link together object files that were compiled with different values for the _GLIBCXX_USE_CXX11_ABI macro. This commonly happens when linking to a third-party library that was compiled with an older version of GCC. If the third-party library cannot be rebuilt with the new ABI then you will need to recompile your code with the old ABI. The error msg refers to an undefined function that the compiler is unable to find.
      
      `std::range_error::range_error(char const*)@GLIBCXX_3.4.21'
      
      Check the toolchain's version:
      • Build host
      • 
        $ /home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++ -v
        Using built-in specs.
        COLLECT_GCC=/home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++
        COLLECT_LTO_WRAPPER=/home/jerry/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/../libexec/gcc/arm-linux-gnueabihf/4.8.3/lto-wrapper
        Target: arm-linux-gnueabihf
        ...
        gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03) 
        
        
      • Pi
      • 
        pi@raspberrypi:~ $ gcc --version
        gcc (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516
        Copyright (C) 2016 Free Software Foundation, Inc.
        This is free software; see the source for copying conditions.  There is NO
        warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
        
        pi@raspberrypi:~ $ g++ --version
        g++ (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516
        Copyright (C) 2016 Free Software Foundation, Inc.
        This is free software; see the source for copying conditions.  There is NO
        warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
        
        pi@raspberrypi:~ $ ldd --version
        ldd (Debian GLIBC 2.24-11+deb9u3) 2.24
        
        
      Check GLIBCXX_ in the built library:
      • ~/pi/sysroot/usr/lib/arm-linux-gnueabihf
      • All were built with GLIBCXX_3.4.21
      • ~/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/arm-linux-gnueabihf/lib/libstdc++.so
      • Support until GLIBCXX_3.4.19
      The problem is attempting to compile the apps using a newer GCC or rather GLIBCXX than what the sdk is compiled with (gcc v4.8). Download the newer cross-compiler gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz:
      
      cd ~/pi/tools/arm-bcm2708
      wget https://releases.linaro.org/components/toolchain/binaries/6.3-2017.05/arm-linux-gnueabihf/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz 
      tar -xJf gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz
      
      
      Then, change the configuration of the toolchain( starboard/raspi/shared/gyp_configuration.py )
      
          toolchain = os.path.realpath(
              os.path.join(
                  raspi_home,
                  'tools/arm-bcm2708/gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf'))
              #    'tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64'))
      
      
      Run the following again:
      
      cobalt/build/gyp_cobalt raspi-2
      ninja -C out/raspi-2_devel cobalt
      
      
      References: 1, 2 If you are trying to build Glibc 2.24 with a more modern GCC , like 7.x or 8.x, you are going to get a lot of errors. Easiest approach is to build Glibc with GCC 6.3.0 and later use it with the latest and greatest GCC , which at this time is 8.1. To build a cross-compiler for Pi on the build host:
      
      #0 install required packages
      
      sudo apt-get install g++ make gawk
      
      #1 create a working folder in your home:
      
      cd ~
      mkdir gcc_all && cd gcc_all
      
      #2 download the source code, 
      #  we do need the kernel header files in order to build the target system’s standard C library.
      
      wget https://ftpmirror.gnu.org/binutils/binutils-2.28.tar.bz2
      wget https://ftpmirror.gnu.org/gcc/gcc-6.3.0/gcc-6.3.0.tar.gz
      wget https://ftpmirror.gnu.org/glibc/glibc-2.24.tar.bz2
      wget https://ftpmirror.gnu.org/gcc/gcc-8.1.0/gcc-8.1.0.tar.gz
      git clone --depth=1 https://github.com/raspberrypi/linux
      
      #3 extract the archives and erase them:
      
      tar xf binutils-2.28.tar.bz2
      tar xf glibc-2.24.tar.bz2
      tar xf gcc-6.3.0.tar.gz
      tar xf gcc-8.1.0.tar.gz
      rm *.tar.*
      
      #4 some prerequisites needed by GCC
      
      cd gcc-6.3.0
      contrib/download_prerequisites
      rm *.tar.*
      cd ..
      cd gcc-8.1.0
      contrib/download_prerequisites
      rm *.tar.*
      
      #5 create a folder in which we’ll put the cross compiler and add it to the path
      
      cd ..
      sudo mkdir -p /opt/cross-pi-gcc
      sudo chown $USER /opt/cross-pi-gcc
      export PATH=/opt/cross-pi-gcc/bin:$PATH
      
      #6 Copy the kernel headers in the above folder
      
      cd linux
      KERNEL=kernel7
      make ARCH=arm INSTALL_HDR_PATH=/opt/cross-pi-gcc/arm-linux-gnueabihf headers_install
      
      #7 build Binutils
      
      cd ..
      mkdir build-binutils && cd build-binutils
      ../binutils-2.28/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
      make -j 8
      make install
      
      #8 change a line in ubsan.c from gcc-6.3.0/gcc/ , find line 1474:
      
             || xloc.file == '\0' || xloc.file[0] == '\xff'
      with
             || xloc.file[0] == '\0' || xloc.file[0] == '\xff'
      
      #8 a partial build of Glibc
      
      cd ..
      mkdir build-gcc && cd build-gcc
      ../gcc-6.3.0/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
      make -j8 all-gcc
      make install-gcc
      
      #9 partially build Glibc , install Glibc’s standard C library headers to:
      
      cd ..
      mkdir build-glibc && cd build-glibc
      ../glibc-2.24/configure --prefix=/opt/cross-pi-gcc/arm-linux-gnueabihf --build=$MACHTYPE --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --with-arch=armv6 --with-fpu=vfp --with-float=hard --with-headers=/opt/cross-pi-gcc/arm-linux-gnueabihf/include --disable-multilib libc_cv_forced_unwind=yes
      make install-bootstrap-headers=yes install-headers
      make -j8 csu/subdir_lib
      install csu/crt1.o csu/crti.o csu/crtn.o /opt/cross-pi-gcc/arm-linux-gnueabihf/lib
      arm-linux-gnueabihf-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/libc.so
      touch /opt/cross-pi-gcc/arm-linux-gnueabihf/include/gnu/stubs.h
      
      #10 Back to GCC with 2nd build
      
      cd ..
      cd build-gcc
      make -j8 all-target-libgcc
      make install-target-libgcc
      
      #11 Finish building Glibc, the standard C library is installed to /opt/cross-pi-gcc/arm-linux-gnueabihf/lib:
      
      cd ..
      cd build-glibc
      make -j8
      make install
      
      #12 Finish building GCC 6.3.0,  the standard C++ library is installed to /opt/cross/arm-linux-gnueabihf/lib/ :
      
      cd ..
      cd build-gcc
      make -j8
      make install
      cd ..
      
      #13 Test
      # test.cpp:
      
      #include <iostream>
      
      int main()
      {
          auto lambda = [](auto x){ return x; };
          std::cout << lambda("Hello generic lambda!\n");
          return 0;
      }
      
      
      arm-linux-gnueabihf-g++ test.cpp -o test -v
      file test
      
      #14 Make a backup to the working space:
      
      sudo cp -r /opt/cross-pi-gcc /opt/arm-linux-gnueabihf-pi-6.3.0
      
      ###################################################################################################################
      # Next, we are going to use the above built Glibc to build a more modern cross compiler that will overwrite gcc-6.3
      ###################################################################################################################
      cd ..
      mkdir build-gcc8 && cd build-gcc8
      ../gcc-8.1.0/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
      make -j8
      make install
      
      
      

      Or, to build a new GCC on the Pi device:
      
      sudo apt-get install libgmp-dev libmpfr-dev libmpc-dev
      
      wget https://ftp.gnu.org/gnu/gcc/gcc-6.3.0/gcc-6.3.0.tar.gz
      tar xzf gcc-6.3.0.tar.gz
      cd gcc-6.3.0/
      contrib/download_prerequisites
      mkdir ../gcc-build
      cd ../gcc-build
      
      ../gcc-6.3.0/configure -v \
      # for Pi3 and new Pi2
      ../configure -v --enable-languages=c,c++ --with-cpu=cortex-a53 \
        --with-fpu=neon-fp-armv8 --with-float=hard --build=arm-linux-gnueabihf \
        --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --enable-checking=release
      
      # ARM 64-bit 
      #../configure --enable-languages=c,c++ --with-cpu=cortex-a53 --enable-checking=release
      
      # x86_64 
      #../configure --disable-multilib --enable-languages=c,c++ --enable-checking=release
      
      # Pi Zero (best use -j2 or less)
      #../configure -v --enable-languages=c,c++ --with-cpu=arm1176jzf-s \
      #  --with-fpu=vfp --with-float=hard --build=arm-linux-gnueabihf \
      #  --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --enable-checking=release
      
      # Old Pi2, the Pi3 config should work for the new Pi2  
      #../configure -v --enable-languages=c,c++ --with-cpu=cortex-a7 \
      #  --with-fpu=neon-vfpv4 --with-float=hard --build=arm-linux-gnueabihf \
      #  --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --enable-checking=release
      
      sudo dd if=/dev/zero of=/swapfile1GB bs=1M count=1024
      sudo chmod 0600 /swapfile1GB
      sudo mkswap /swapfile1GB
      sudo swapon /swapfile1GB
      
      make -j5
      
      # sudo make install
      
      
      
    5. You may see the following build error:
      
      ../../starboard/shared/ffmpeg/ffmpeg_common.h:20:32: fatal error: libavcodec/avcodec.h: No such file or directory
      
      ../../starboard/shared/alsa/alsa_util.h:18:28: fatal error: alsa/asoundlib.h: No such file or directory
      
      
      Modify the file "starboard/raspi/shared/gyp_configuration.gypi":
      
            '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
            # and for alsa, ffmpeg
            '-I<(sysroot)/usr/include',
            '-I<(sysroot)/usr/include/arm-linux-gnueabihf',
          ],
          'linker_flags': [
      
      
      
    6. Comment out the functions causing warning:
      
      ../../starboard/shared/libevent/socket_waiter_internal.cc:73:6: error: ‘void {anonymous}::GetSocketPipe(SbSocketPrivate**, SbSocketPrivate**)’ defined but not used [-Werror=unused-function]
       void GetSocketPipe(SbSocket* client_socket, SbSocket* server_socket) {
            ^~~~~~~~~~~~~
      cc1plus: all warnings being treated as errors
      
      ../../starboard/shared/libevent/socket_waiter_internal.cc:41:17: error: ‘SbSocketAddress {anonymous}::GetIpv4Localhost()’ defined but not used [-Werror=unused-function]
       SbSocketAddress GetIpv4Localhost() {
                       ^~~~~~~~~~~~~~~~
      cc1plus: all warnings being treated as errors
      
      ../../starboard/shared/libevent/socket_waiter_internal.cc:50:10: error: ‘SbSocketPrivate* {anonymous}::AcceptBySpinning(SbSocket, SbTime)’ defined but not used [-Werror=unused-function]
       SbSocket AcceptBySpinning(SbSocket server_socket, SbTime timeout) {
                ^~~~~~~~~~~~~~~~
      cc1plus: all warnings being treated as errors
      
      
      ../../starboard/shared/starboard/application.cc:41:6: error: ‘void starboard::shared::starboard::{anonymous}::Dispatch(SbEventType, void*, SbEventDataDestructor)’ defined but not used [-Werror=unused-function]
       void Dispatch(SbEventType type, void* data, SbEventDataDestructor destructor) {
            ^~~~~~~~
      cc1plus: all warnings being treated as errors
      
      ../../starboard/shared/signal/suspend_signals.cc:73:6: error: ‘void starboard::shared::signal::{anonymous}::Ignore(int)’ defined but not used [-Werror=unused-function]
       void Ignore(int signal_id) {
            ^~~~~~
      cc1plus: all warnings being treated as errors
      
      
    7. You may see the following error:
      
      ../../base/logging.h:598:26: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
      
      
      Use the type conversion in the parameters of "DCHECK_EQ((unsigned int) , (unsigned int) )" for the following files:
      
      cobalt/css_parser/position_parse_structures.cc
      cobalt/debug/debug_dispatcher.cc
      cobalt/layout/used_style.cc
      cobalt/media_capture/encoders/linear16_audio_encoder.cc
      cobalt/media_capture/media_recorder.cc
      cobalt/speech/audio_encoder_flac.cc
      cobalt/websocket/web_socket_impl.cc
      cobalt/xhr/xml_http_request.cc
      
    8. 
      [1/1] LINK cobalt
      FAILED: /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/arm-linux-gnueabihf-g++ @cobalt.rsp
      /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/../lib/gcc/arm-linux-gnueabihf/6.3.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lasound
      /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/../lib/gcc/arm-linux-gnueabihf/6.3.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lavcodec
      /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/../lib/gcc/arm-linux-gnueabihf/6.3.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lavformat
      /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/../lib/gcc/arm-linux-gnueabihf/6.3.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lavutil
      /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/../lib/gcc/arm-linux-gnueabihf/6.3.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/libpthread.so.0
      /home/jerry/pi/tools/arm-bcm2708/cross-pi-gcc/bin/../lib/gcc/arm-linux-gnueabihf/6.3.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/libpthread_nonshared.a
      collect2: error: ld returned 1 exit status
      
      
      Modified src/starboard/raspi/shared/gyp_configuration.gypi:
      
      --- a/src/starboard/raspi/shared/gyp_configuration.gypi
      +++ b/src/starboard/raspi/shared/gyp_configuration.gypi
      @@ -55,6 +55,9 @@
             '-I<(sysroot)/opt/vc/include',
             '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
             '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
      +      # and for flsa, fmpeg
      +      '-I<(sysroot)/usr/include',
      +      '-I<(sysroot)/usr/include/arm-linux-gnueabihf',
           ],
           'linker_flags': [
             '--sysroot=<(sysroot)',
      @@ -62,6 +65,11 @@
             # libraries.
             '-L<(sysroot)/opt/vc/lib',
             '-Wl,-rpath=<(sysroot)/opt/vc/lib',
      +      '-L<(sysroot)/usr/lib/arm-linux-gnueabihf',
      +      '-Wl,-rpath-link=<(sysroot)/usr/lib/arm-linux-gnueabihf',
      +      '-L<(sysroot)/lib/arm-linux-gnueabihf',
      +      '-Wl,-rpath-link=<(sysroot)/lib/arm-linux-gnueabihf',
      +      '-v',
      
             # We don't wrap these symbols, but this ensures that they aren't
             # linked in.
      @@ -103,6 +111,7 @@
             '-fno-rtti',
           ],
           'platform_libraries': [
      +      '-lm',
             '-lasound',
             '-lavcodec',
             '-lavformat',
      @@ -115,6 +124,7 @@
             '-lbcm_host',
             '-lvcos',
             '-lvchiq_arm',
      +      '-lmmal_core -lmmal_util -lcontainers -lmmal_components -lmmal_vc_client -lvcsm',
           ],
           'conditions': [
             ['cobalt_fastbuild==0', {
      
      
      
  • Install your Cobalt binary (and content) on the remote Pi
  • 
    rsync -avzh --exclude="obj*" out/raspi-2_devel pi@10.0.0.45:~/
    
    
  • run Colbat
  • Using SSH will make it easy for you to quit or restart Cobalt.
    
    ssh pi@$RASPI_ADDR
    cd raspi-2_debug
    ./cobalt
    
    
    With this approach, you can just hit [CTRL-C] to close Cobalt.

Cobalt for Raspberry Pi

Source


src/starboard/raspi/1/gyp_configuration.gypi
src/starboard/raspi/1/starboard_platform.gyp
src/starboard/raspi/shared/configuration_public.h
src/starboard/raspi/shared/open_max/decode_target_create.cc
src/starboard/raspi/shared/open_max/decode_target_destroy.cc
src/starboard/raspi/shared/open_max/decode_target_get_format.cc
src/starboard/raspi/shared/open_max/decode_target_get_plane.cc
src/starboard/raspi/shared/open_max/decode_target_get_size.cc
src/starboard/raspi/shared/open_max/decode_target_internal.h
src/starboard/raspi/shared/open_max/decode_target_is_opaque.cc
src/starboard/raspi/shared/open_max/dispmanx_resource_pool.cc
src/starboard/raspi/shared/open_max/dispmanx_resource_pool.h
src/starboard/raspi/shared/open_max/image_decode.cc
src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
src/starboard/raspi/shared/open_max/open_max_component.cc
src/starboard/raspi/shared/open_max/open_max_component.h
src/starboard/raspi/shared/open_max/open_max_component_base.cc
src/starboard/raspi/shared/open_max/open_max_component_base.h
src/starboard/raspi/shared/open_max/open_max_egl_render_component.cc
src/starboard/raspi/shared/open_max/open_max_egl_render_component.h
src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
src/starboard/raspi/shared/open_max/open_max_video_decode_component.cc
src/starboard/raspi/shared/open_max/open_max_video_decode_component.h
src/starboard/raspi/shared/open_max/video_decoder.cc
src/starboard/raspi/shared/open_max/video_decoder.h



留言

熱門文章