Created
March 24, 2014 20:04
-
-
Save dennis-hamester/9747915 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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