2021-08-26

Annotation of my minimal mmal programming experience

 To make further supplementary notes of my previous post Jottings on Raspberry Pi mmal programming

  1. my original idea is to have real time manipulation of the video data, I have abandoned to usage of encoder component
  2. in my example of a frame size of 320x240 pixels, I can achieve a 30 frame-per-second on yuv format even with codes to dump the yuv data onto disk.
  3. I find the the boc_host_init(), which documentation says it is called first before any GPU (vc_*) calls can be made, is not necessary in my mmal program
  4. most of the mmal API is defined by a header/value structure, which has the header labelled as hdr and defined by designated enumeration values to define the the subsequent functionality of the API

2021-08-25

Jottings on Raspberry Pi mmal programming

Reference: Multi-Media Abstraction Layer (MMAL). Draft Version 0.1

MMAL entities

  • components
  • ports
  • buffer

component

  • use mmal_component_create API to create, return MMAL_COMPONENT_T

port

  • created automatically by component (and does not need separate create API)
  • but the format of the input port must be set by client (using mmal_port_format_commit API)
  • output port format will be automatically set (provided there is sufficient information)
  • checked by whether output format is MMAL_ENCODING_UNKNOWN 

buffer

  • used for exchange data (but not contain data directly but instead contain a pointer
  • data can be allocated outside MMAL 
  • buffer header are allocated from pool and are "reference counted" - call  mmal_buffer_header_release to drop refcount
  • "after" commiting the format of port, a pool of buffer header should be created
  • queue (MMAL_QUEUE_T) is a facility to process buffer header.  Callback triggered when three is available data in queue

The following is my minimal mmal program with reference to tasanakorn's rpi-mmal-demo project


/* minimal API to make pi camera work */
#include <stdio.h>
#include <unistd.h>
#include <assert.h>

#include "bcm_host.h"
#include "interface/mmal/mmal.h"
#include "interface/mmal/util/mmal_default_components.h"
#include "interface/mmal/util/mmal_connection.h"

#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 240
#define DEFAULT_VIDEO_FPS 30

typedef struct {
    MMAL_COMPONENT_T *camera;
    MMAL_COMPONENT_T *preview;
    MMAL_PORT_T *camera_preview_port;
    MMAL_PORT_T *camera_video_port;
    MMAL_PORT_T *camera_still_port;
    MMAL_POOL_T *camera_video_port_pool;
} PORT_USERDATA;

static void camera_video_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
static int frame_count = 0;
static struct timespec t1;
struct timespec t2;

if (frame_count == 0)
   clock_gettime(CLOCK_MONOTONIC, &t1);

frame_count++;

PORT_USERDATA *userdata = (PORT_USERDATA *) port->userdata;
MMAL_POOL_T *pool = userdata->camera_video_port_pool;

mmal_buffer_header_mem_lock(buffer);

// put codes to manipulate buffer here

mmal_buffer_header_mem_unlock(buffer);

// calculate frame rate
if (frame_count % 10 == 0) {
   clock_gettime(CLOCK_MONOTONIC, &t2);
   float d = (t2.tv_sec + t2.tv_nsec / 1000000000.0) - (t1.tv_sec + t1.tv_nsec / 1000000000.0);
   float fps = 0.0;

   if (d > 0)
     fps = frame_count / d;
   else
     fps = frame_count;

   printf("Frame = %d,  Framerate = %.1f fps \n", frame_count, fps);
}

mmal_buffer_header_release(buffer);

// and send one back to the port (if still open)
if (port->is_enabled) {
  MMAL_STATUS_T status;
  MMAL_BUFFER_HEADER_T *new_buffer;
  new_buffer = mmal_queue_get(pool->queue);

  if (new_buffer)
         status = mmal_port_send_buffer(port, new_buffer);

  if (!new_buffer || status != MMAL_SUCCESS)
    printf("Error: Unable to return a buffer to the video port\n");

  }

} // camera_video_buffer_callback()

int fill_port_buffer(MMAL_PORT_T *port, MMAL_POOL_T *pool)
{
int q;
int num = mmal_queue_length(pool->queue);

for (q = 0; q < num; q++)
  {
  MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(pool->queue);
  if (!buffer)
    printf("Unable to get a required buffer %d from pool queue\n", q);

  if (mmal_port_send_buffer(port, buffer) != MMAL_SUCCESS)
    printf("Unable to send a buffer to port (%d)\n", q);
  } // for
} // fill_port_buffer


int main (void)
{
// I find bcm_host_int is not necessary although documentation says it is called first before any GPU (vc_*) calls can be made
// bcm_host_init();

MMAL_STATUS_T status;
PORT_USERDATA userdata;
MMAL_PARAMETER_CAMERA_CONFIG_T cam_config;

status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &(userdata.camera));
if (status != MMAL_SUCCESS) {
   printf("Error: create camera %x\n", status);
   goto closing;
   }
else
  printf ("Calling mmal_component_create() to create camera component is successful\n");

assert (userdata.camera != NULL);

userdata.camera_preview_port = userdata.camera->output[0];
userdata.camera_video_port   = userdata.camera->output[1];
userdata.camera_still_port   = userdata.camera->output[2];

cam_config.hdr.id = MMAL_PARAMETER_CAMERA_CONFIG;
cam_config.hdr.size = sizeof (cam_config);
cam_config.max_stills_w = DEFAULT_WIDTH,
cam_config.max_stills_h = DEFAULT_HEIGHT,
cam_config.stills_yuv422 = 0,
cam_config.one_shot_stills = 1,
cam_config.max_preview_video_w = DEFAULT_WIDTH,
cam_config.max_preview_video_h = DEFAULT_HEIGHT,
cam_config.num_preview_video_frames = 3,
cam_config.stills_capture_circular_buffer_height = 0,
cam_config.fast_preview_resume = 0,
cam_config.use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC;

printf ("Calling  mmal_port_parameter_set() at line %d\n", __LINE__);
status = mmal_port_parameter_set(userdata.camera->control, &cam_config.hdr);
if (status != MMAL_SUCCESS) {
  printf("Could not select camera : error %d", status);
  goto closing;  
  }
else
  printf ("Calling mmal_port_parameter_set() for Camera config is successful\n");

// Setup camera preview port format
userdata.camera_preview_port->format->encoding = MMAL_ENCODING_OPAQUE;
userdata.camera_preview_port->format->encoding_variant = MMAL_ENCODING_I420;
userdata.camera_preview_port->format->es->video.width = DEFAULT_WIDTH;
userdata.camera_preview_port->format->es->video.height = DEFAULT_HEIGHT;
userdata.camera_preview_port->format->es->video.crop.x = 0;
userdata.camera_preview_port->format->es->video.crop.y = 0;
userdata.camera_preview_port->format->es->video.crop.width = DEFAULT_WIDTH;
userdata.camera_preview_port->format->es->video.crop.height = DEFAULT_HEIGHT;

status = mmal_port_format_commit(userdata.camera_preview_port);
if (status != MMAL_SUCCESS) {
  printf("Error: camera viewfinder format couldn't be set\n");
  goto closing;  
  }
else
  printf ("Calling mmal_port_format_commit() for preview port is successful\n");

userdata.camera_video_port->format->encoding = MMAL_ENCODING_I420;
userdata.camera_video_port->format->encoding_variant = MMAL_ENCODING_I420;
userdata.camera_video_port->format->es->video.width = DEFAULT_WIDTH;
userdata.camera_video_port->format->es->video.height = DEFAULT_HEIGHT;
userdata.camera_video_port->format->es->video.crop.x = 0;
userdata.camera_video_port->format->es->video.crop.y = 0;
userdata.camera_video_port->format->es->video.crop.width = DEFAULT_WIDTH;
userdata.camera_video_port->format->es->video.crop.height = DEFAULT_HEIGHT;
userdata.camera_video_port->format->es->video.frame_rate.num = DEFAULT_VIDEO_FPS;
userdata.camera_video_port->format->es->video.frame_rate.den = 1;

userdata.camera_video_port->buffer_size = userdata.camera_video_port->format->es->video.width * userdata.camera_video_port->format->es->video.height * 12 / 8;
userdata.camera_video_port->buffer_num = 2;

status = mmal_port_format_commit(userdata.camera_video_port);
if (status != MMAL_SUCCESS) {
  printf("Error: unable to commit camera video port format (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_port_format_commit for video port is successful\n");

// now create buffer pool

userdata.camera_video_port_pool = (MMAL_POOL_T *) mmal_port_pool_create(userdata.camera_video_port, userdata.camera_video_port->buffer_num, userdata.camera_video_port->buffer_size);

userdata.camera_video_port->userdata = (struct MMAL_PORT_USERDATA_T *) &userdata;

status = mmal_port_enable(userdata.camera_video_port, camera_video_buffer_callback);
if (status != MMAL_SUCCESS) {
  printf("Error: unable to enable camera video port (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_port_enable() for video port is successful\n");

status = mmal_component_enable(userdata.camera);
if (status != MMAL_SUCCESS) {
  printf("Error: unable to enable camera (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_component_enable() for camera component is successful\n");

fill_port_buffer(userdata.camera_video_port, userdata.camera_video_port_pool);

if (mmal_port_parameter_set_boolean(userdata.camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
  printf("Failed to start capture\n");
else
  printf ("Successful to call mmal_port_parameter_set_boolean() to start capture\n");

// do not set up encoder

// now set up preview

MMAL_COMPONENT_T *preview = 0;
MMAL_CONNECTION_T *camera_preview_connection = 0;
MMAL_PORT_T *preview_input_port;

status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &preview);
if (status != MMAL_SUCCESS) {
  printf("Error: unable to create preview (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_component_create() to create preview component is successful\n");


if (!preview->input_num)
  {
  printf("No input ports found on component");
  goto closing;  
  }

preview_input_port = preview->input[0];

MMAL_RECT_T previewWindow;   // Destination rectangle for the preview window
previewWindow.x = 100;
previewWindow.y = 100;
previewWindow.width = 320;
previewWindow.height = 240;

MMAL_DISPLAYREGION_T param;
param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
param.hdr.size = sizeof (MMAL_DISPLAYREGION_T);
param.set = MMAL_DISPLAY_SET_LAYER;
param.layer = 2;
param.set |= MMAL_DISPLAY_SET_ALPHA;
param.alpha = 255;

param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN);
param.fullscreen = 0;
param.dest_rect = previewWindow;

status = mmal_port_parameter_set(preview_input_port, &param.hdr);
if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) {
  printf("Error: unable to set preview port parameters (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_port_parameter_set() on preview input port is successful\n");

status = mmal_connection_create(&camera_preview_connection,
   userdata.camera_preview_port,
   preview_input_port,
   MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
if (status != MMAL_SUCCESS) {
  printf("Error: unable to create connection (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_connection_create() preview connection is successful\n");

status = mmal_connection_enable(camera_preview_connection);
if (status != MMAL_SUCCESS) {
  printf("Error: unable to enable connection (%u)\n", status);
  goto closing;  
  }
else
  printf ("Calling mmal_connection_enable() on preview connection is successful\n");

printf ("Press Control-C to abort\n");

while (1)
  sleep (1);

closing:

if (userdata.camera_preview_port && userdata.camera_preview_port->is_enabled)
  mmal_port_disable (userdata.camera_preview_port);

if (userdata.camera)
  mmal_component_destroy (userdata.camera);


} // main

2021-08-24

Initializing a structure in C language

#include <stdio.h>
typedef struct sub_struct {
int field1;
int field2;
int field3;
} SUB_STRUCT;

typedef struct my_struct {
SUB_STRUCT field1;
int field2;
int field3;
int field4;
} MY_STRUCT;

main() {

MY_STRUCT var =
{
.field1 = {
   .field1=1 ,
   .field3=3 },
.field2 = 2,
.field4 = 4
};
printf ("var.field1.field1 = %d\n", var.field1.field1);
printf ("var.field1.field2 = %d\n", var.field1.field2);
printf ("var.field1.field3 = %d\n", var.field1.field3);
printf ("var.field2 = %d\n", var.field2);
printf ("var.field3 = %d\n", var.field3);
printf ("var.field4 = %d\n", var.field4);
}

var.field1.field1 = 1
var.field1.field2 = 0
var.field1.field3 = 3
var.field2 = 2
var.field3 = 0
var.field4 = 4