Skip to content

Instantly share code, notes, and snippets.

@dennis-hamester
Created March 24, 2014 20:04
Show Gist options
  • Save dennis-hamester/9747915 to your computer and use it in GitHub Desktop.
Save dennis-hamester/9747915 to your computer and use it in GitHub Desktop.
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/util/mmal_default_components.h>
struct data_t {
MMAL_COMPONENT_T *dec;
MMAL_PORT_T *dec_input;
MMAL_PORT_T *dec_output;
MMAL_POOL_T *dec_input_pool;
MMAL_COMPONENT_T *vout;
MMAL_PORT_T *vout_input;
MMAL_POOL_T *vout_input_pool;
MMAL_ES_FORMAT_T *format;
MMAL_QUEUE_T *decoded;
pthread_mutex_t mutex;
unsigned buffers_in_transit;
int error;
};
static volatile sig_atomic_t aborted = 0;
static void on_signal(int sig);
static int change_output_format(struct data_t *data);
static void dec_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
static void dec_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
static void dec_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
static void vout_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
static void vout_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
static void* pool_allocator_alloc(void *context, uint32_t size);
static void pool_allocator_free(void *context, void *mem);
int main(int argc, char *argv[]) {
struct data_t data;
FILE *src;
MMAL_BUFFER_HEADER_T *buffer;
MMAL_STATUS_T status;
int ret = EXIT_SUCCESS;
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
memset(&data, 0, sizeof(struct data_t));
pthread_mutex_init(&data.mutex, NULL);
if (argc == 1) {
printf("Playback should be fine\n");
data.error = 0;
}
else {
printf("Playback should produce EIO and refcount errors\n");
data.error = 1;
}
src = fopen("test.h264", "r");
if (!src) {
printf("Failed to open %s\n", argv[1]);
ret = EXIT_FAILURE;
goto out;
}
status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &data.dec);
if (status != MMAL_SUCCESS) {
printf("Failed to create MMAL decoder component %s (status=%"PRIx32" %s)\n", MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
data.dec->control->userdata = (struct MMAL_PORT_USERDATA_T *)&data;
status = mmal_port_enable(data.dec->control, dec_control_port_cb);
if (status != MMAL_SUCCESS) {
printf("Failed to enable decoder control port %s (status=%"PRIx32" %s)\n", data.dec->control->name, status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
data.dec_input = data.dec->input[0];
data.dec_input->userdata = (struct MMAL_PORT_USERDATA_T *)&data;
data.dec_input->format->encoding = MMAL_ENCODING_H264;
status = mmal_port_format_commit(data.dec_input);
if (status != MMAL_SUCCESS) {
printf("Failed to commit format for decoder input port %s (status=%"PRIx32" %s)\n", data.dec_input->name, status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
data.dec_input->buffer_size = data.dec_input->buffer_size_recommended;
data.dec_input->buffer_num = data.dec_input->buffer_num_recommended;
status = mmal_port_enable(data.dec_input, dec_input_port_cb);
if (status != MMAL_SUCCESS) {
printf("Failed to enable decoder input port %s (status=%"PRIx32" %s)\n", data.dec_input->name, status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
data.dec_output = data.dec->output[0];
data.dec_output->userdata = (struct MMAL_PORT_USERDATA_T *)&data;
status = mmal_port_enable(data.dec_output, dec_output_port_cb);
if (status != MMAL_SUCCESS) {
printf("Failed to enable decoder output port %s (status=%"PRIx32" %s)\n", data.dec_output->name, status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
status = mmal_component_enable(data.dec);
if (status != MMAL_SUCCESS) {
printf("Failed to enable decoder component %s (status=%"PRIx32" %s)\n", data.dec->name, status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
data.dec_input_pool = mmal_pool_create_with_allocator(data.dec_input->buffer_num, data.dec_input->buffer_size, data.dec_input, pool_allocator_alloc, pool_allocator_free);
if (!data.dec_input_pool) {
printf("Failed to create pool for decoder input port (%d, %s)\n", status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
while (!aborted) {
buffer = mmal_queue_get(data.dec_input_pool->queue);
if (buffer) {
mmal_buffer_header_reset(buffer);
buffer->cmd = 0;
buffer->pts = 1;
buffer->length = fread(buffer->data, 1, buffer->alloc_size, src);
if (buffer->length != buffer->alloc_size) {
aborted = 1;
}
status = mmal_port_send_buffer(data.dec_input, buffer);
if (status != MMAL_SUCCESS) {
printf("Failed send buffer to decoder input port (%d, %s)\n", status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
}
if (data.format && !data.vout_input_pool) {
if (change_output_format(&data) < 0)
goto out;
}
if (data.vout_input_pool) {
pthread_mutex_lock(&data.mutex);
if (data.buffers_in_transit < data.dec_output->buffer_num) {
buffer = mmal_queue_get(data.vout_input_pool->queue);
if (buffer) {
mmal_buffer_header_reset(buffer);
buffer->cmd = 0;
status = mmal_port_send_buffer(data.dec_output, buffer);
if (status != MMAL_SUCCESS) {
printf("Failed send buffer to decoder output port (%d, %s)\n", status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
}
++data.buffers_in_transit;
}
pthread_mutex_unlock(&data.mutex);
}
buffer = mmal_queue_get(data.decoded);
if (buffer) {
if (data.error)
usleep(20000);
mmal_port_send_buffer(data.vout_input, buffer);
}
}
out:
pthread_mutex_destroy(&data.mutex);
if (data.dec) {
mmal_component_disable(data.dec);
mmal_port_disable(data.dec->control);
}
if (data.dec_input)
mmal_port_disable(data.dec_input);
if (data.dec_output)
mmal_port_disable(data.dec_output);
if (data.vout) {
mmal_component_disable(data.vout);
mmal_port_disable(data.vout->control);
}
if (data.vout_input)
mmal_port_disable(data.vout_input);
if (data.decoded)
mmal_queue_destroy(data.decoded);
if (data.dec_input_pool)
mmal_pool_destroy(data.dec_input_pool);
if (data.vout_input_pool)
mmal_pool_destroy(data.vout_input_pool);
if (data.vout)
mmal_component_release(data.vout);
if (data.dec)
mmal_component_release(data.dec);
if (data.format)
mmal_format_free(data.format);
return ret;
}
static void on_signal(int sig)
{
if (aborted) {
abort();
}
aborted = 1;
}
static int change_output_format(struct data_t *data)
{
MMAL_STATUS_T status;
int ret = 0;
status = mmal_port_disable(data->dec_output);
if (status != MMAL_SUCCESS) {
printf("Failed to disable decoder output port (status=%"PRIx32" %s)\n", status, mmal_status_to_string(status));
ret = -1;
goto out;
}
mmal_format_full_copy(data->dec_output->format, data->format);
status = mmal_port_format_commit(data->dec_output);
if (status != MMAL_SUCCESS) {
printf("Failed to commit output format (status=%"PRIx32" %s)\n", status, mmal_status_to_string(status));
ret = -1;
goto out;
}
data->dec_output->buffer_num = data->dec_output->buffer_num_recommended;
data->dec_output->buffer_size = data->dec_output->buffer_size_min;
status = mmal_port_enable(data->dec_output, dec_output_port_cb);
if (status != MMAL_SUCCESS) {
printf("Failed to enable output port (status=%"PRIx32" %s)\n", status, mmal_status_to_string(status));
ret = -1;
goto out;
}
status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &data->vout);
if (status != MMAL_SUCCESS) {
printf("Failed to create vout component %s (%x, %s)\n", MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
ret = -1;
goto out;
}
data->vout->control->userdata = (struct MMAL_PORT_USERDATA_T *)data;
status = mmal_port_enable(data->vout->control, vout_control_port_cb);
if (status != MMAL_SUCCESS) {
printf("Failed to enable vout control port %s (%x, %s)\n", data->vout->control->name, status, mmal_status_to_string(status));
ret = -1;
goto out;
}
data->vout_input = data->vout->input[0];
data->vout_input->userdata = (struct MMAL_PORT_USERDATA_T *)data;
mmal_format_full_copy(data->vout_input->format, data->format);
status = mmal_port_format_commit(data->vout_input);
if (status != MMAL_SUCCESS) {
printf("Failed to commit vout intput format (status=%"PRIx32" %s)\n", status, mmal_status_to_string(status));
ret = -1;
goto out;
}
data->vout_input->buffer_num = data->vout_input->buffer_num_recommended;
if (data->error) {
data->vout_input->buffer_num *= 2;
}
status = mmal_port_enable(data->vout_input, vout_input_port_cb);
if (status != MMAL_SUCCESS) {
printf("Failed to vout enable input port %s (%d, %s)\n", data->vout_input->name, status, mmal_status_to_string(status));
ret = -1;
goto out;
}
status = mmal_component_enable(data->vout);
if (status != MMAL_SUCCESS) {
printf("Failed to enable vout component %s (%d, %s)\n", data->vout->name, status, mmal_status_to_string(status));
ret = -1;
goto out;
}
data->vout_input_pool = mmal_pool_create_with_allocator(data->vout_input->buffer_num, data->vout_input->buffer_size, data->vout_input, pool_allocator_alloc, pool_allocator_free);
if (!data->dec_input_pool) {
printf("Failed to create pool for decoder input port (%d, %s)\n", status, mmal_status_to_string(status));
ret = EXIT_FAILURE;
goto out;
}
data->decoded = mmal_queue_create();
out:
return ret;
}
static void dec_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) {
MMAL_STATUS_T status;
if (buffer->cmd == MMAL_EVENT_ERROR) {
status = *(uint32_t *)buffer->data;
printf("Decoder MMAL error %"PRIx32" \"%s\"\n", status, mmal_status_to_string(status));
}
mmal_buffer_header_release(buffer);
}
static void dec_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) {
mmal_buffer_header_release(buffer);
}
static void dec_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) {
struct data_t *data = (struct data_t *)port->userdata;
MMAL_EVENT_FORMAT_CHANGED_T *fmt;
MMAL_ES_FORMAT_T *format;
if (buffer->cmd == 0) {
pthread_mutex_lock(&data->mutex);
if (buffer->length > 0) {
mmal_queue_put(data->decoded, buffer);
}
else {
mmal_buffer_header_release(buffer);
}
--data->buffers_in_transit;
pthread_mutex_unlock(&data->mutex);
}
else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
fmt = mmal_event_format_changed_get(buffer);
format = mmal_format_alloc();
mmal_format_full_copy(format, fmt->format);
format->encoding = MMAL_ENCODING_OPAQUE;
data->format = format;
mmal_buffer_header_release(buffer);
}
else {
mmal_buffer_header_release(buffer);
}
}
static void vout_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) {
MMAL_STATUS_T status;
if (buffer->cmd == MMAL_EVENT_ERROR) {
status = *(uint32_t *)buffer->data;
printf("Vout MMAL error %"PRIx32" \"%s\"\n", status, mmal_status_to_string(status));
}
mmal_buffer_header_release(buffer);
}
static void vout_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) {
mmal_buffer_header_release(buffer);
}
static void* pool_allocator_alloc(void *context, uint32_t size)
{
return mmal_port_payload_alloc((MMAL_PORT_T *)context, size);
}
static void pool_allocator_free(void *context, void *mem)
{
mmal_port_payload_free((MMAL_PORT_T *)context, (uint8_t *)mem);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment