Reference: Multi-Media Abstraction Layer (MMAL). Draft Version 0.1
MMAL entities
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
/* 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, ¶m.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