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, ¶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