summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPetri Hintukainen <phintuka@users.sourceforge.net>2014-05-12 13:34:07 +0300
committerPetri Hintukainen <phintuka@users.sourceforge.net>2014-05-12 13:34:07 +0300
commit0d205ba48e9fa4e1a741de95cc9abea1c4f15a6b (patch)
tree7df8e5e83485e4e7e27fcbe69734c41705e3f348 /src
parent92759df506622b9680eb94de79d9482ba1e162d3 (diff)
downloadxine-lib-0d205ba48e9fa4e1a741de95cc9abea1c4f15a6b.tar.gz
xine-lib-0d205ba48e9fa4e1a741de95cc9abea1c4f15a6b.tar.bz2
add libmmal-based HW video decoder plugin for Raspberry Pi
Diffstat (limited to 'src')
-rw-r--r--src/video_dec/Makefile.am10
-rw-r--r--src/video_dec/mmal.c868
2 files changed, 878 insertions, 0 deletions
diff --git a/src/video_dec/Makefile.am b/src/video_dec/Makefile.am
index c68c9192a..b5765d11b 100644
--- a/src/video_dec/Makefile.am
+++ b/src/video_dec/Makefile.am
@@ -28,11 +28,16 @@ if ENABLE_VPX
libvpx_module = xineplug_decode_libvpx.la
endif
+if ENABLE_MMAL
+libmmal_module = xineplug_decode_libmmal.la
+endif
+
xineplug_LTLIBRARIES = $(image_module) \
$(gdkpixbuf_module) \
$(libjpeg_module) \
$(theora_module) \
$(libvpx_module) \
+ $(libmmal_module) \
xineplug_decode_bitplane.la \
xineplug_decode_rgb.la \
xineplug_decode_yuv.la
@@ -61,3 +66,8 @@ xineplug_decode_libjpeg_la_CFLAGS = $(AM_CFLAGS) $(JPEG_CFLAGS)
xineplug_decode_libvpx_la_SOURCES = libvpx.c
xineplug_decode_libvpx_la_LIBADD = $(XINE_LIB) $(VPX_LIBS)
xineplug_decode_libvpx_la_CFLAGS = $(AM_CFLAGS) $(VPX_CFLAGS)
+
+xineplug_decode_libmmal_la_SOURCES = mmal.c
+xineplug_decode_libmmal_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) $(MMAL_LIBS)
+xineplug_decode_libmmal_la_CFLAGS = $(AM_CFLAGS) $(MMAL_CFLAGS)
+xineplug_decode_libmmal_la_LDFLAGS = $(AM_LDFLAGS) $(MMAL_LDFLAGS)
diff --git a/src/video_dec/mmal.c b/src/video_dec/mmal.c
new file mode 100644
index 000000000..fa5632db6
--- /dev/null
+++ b/src/video_dec/mmal.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) 2014 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * libmmal decoder wrapped by Petri Hintukainen <phintuka@users.sourceforge.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include <bcm_host.h>
+#include <interface/mmal/mmal.h>
+#include <interface/mmal/util/mmal_util.h>
+#include <interface/mmal/util/mmal_default_components.h>
+
+#define LOG_MODULE "mmal_video_decoder"
+
+#define XINE_ENGINE_INTERNAL /* access to stream->video_decoder_streamtype */
+
+#include <xine/xine_internal.h>
+#include <xine/video_out.h>
+#include <xine/buffer.h>
+#include <xine/xineutils.h>
+
+
+typedef struct mmal_decoder_s {
+ video_decoder_t video_decoder;
+
+ pthread_mutex_t mutex;
+
+ /* xine */
+ xine_stream_t *stream;
+
+ MMAL_BUFFER_HEADER_T *input_buffer;
+
+ /* mmal decoder */
+ MMAL_COMPONENT_T *decoder;
+ MMAL_POOL_T *input_pool;
+ MMAL_POOL_T *output_pool;
+ MMAL_QUEUE_T *decoded_frames;
+ MMAL_ES_FORMAT_T *output_format;
+
+ /* decoder output format */
+ double ratio;
+ int width;
+ int height;
+ int frame_flags;
+ int crop_x, crop_y, crop_w, crop_h;
+
+ uint8_t decoder_ok;
+ uint8_t discontinuity;
+
+} mmal_decoder_t;
+
+/*
+ * decoder output buffers
+ */
+
+static void free_output_buffer(MMAL_BUFFER_HEADER_T *buffer)
+{
+ vo_frame_t *frame = (vo_frame_t *)buffer->user_data;
+
+ if (frame) {
+ if (buffer->data != frame->base[0]) {
+ /* free indirect rendering buffer */
+ free(buffer->data);
+ }
+ frame->free(frame);
+ }
+
+ buffer->user_data = NULL;
+ buffer->alloc_size = 0;
+ buffer->data = NULL;
+
+ mmal_buffer_header_release(buffer);
+}
+
+static int send_output_buffer(mmal_decoder_t *this)
+{
+ MMAL_BUFFER_HEADER_T *buffer;
+ vo_frame_t *frame;
+ MMAL_STATUS_T status;
+
+ buffer = mmal_queue_get(this->output_pool->queue);
+ if (!buffer) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to get new output buffer\n");
+ return -1;
+ }
+
+ frame = this->stream->video_out->get_frame (this->stream->video_out,
+ this->width, this->height,
+ this->ratio, XINE_IMGFMT_YV12,
+ this->frame_flags | VO_BOTH_FIELDS);
+
+ if (!frame) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to get new xine frame\n");
+ mmal_buffer_header_release(buffer);
+ return -1;
+ }
+
+ mmal_buffer_header_reset(buffer);
+ buffer->user_data = frame;
+ buffer->cmd = 0;
+ buffer->alloc_size = this->decoder->output[0]->buffer_size;
+ buffer->data = frame->base[0];
+
+ /* check if we can render directly to frame */
+ if (frame->pitches[0] != this->width || frame->pitches[1] != this->width/2 || frame->pitches[2] != this->width/2 ||
+ frame->base[1] - frame->base[0] != this->width * this->height ||
+ frame->base[2] - frame->base[1] != this->width * this->height / 4 ) {
+
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "xine frame not suitable for direct rendering\n");
+
+ buffer->data = malloc(buffer->alloc_size);
+ }
+
+ status = mmal_port_send_buffer(this->decoder->output[0], buffer);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to send buffer to output port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ free_output_buffer(buffer);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void fill_output_port(mmal_decoder_t *this)
+{
+ if (this->output_pool) {
+
+ unsigned buffers_available = mmal_queue_length(this->output_pool->queue);
+ unsigned buffers_to_send = this->decoder->output[0]->buffer_num_recommended -
+ ( this->output_pool->headers_num - buffers_available -
+ mmal_queue_length(this->decoded_frames));
+ unsigned i;
+
+ if (buffers_to_send > buffers_available) {
+ buffers_to_send = buffers_available;
+ }
+
+ for (i = 0; i < buffers_to_send; ++i) {
+ if (send_output_buffer(this) < 0) {
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * MMAL callbacks
+ */
+
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *)port->userdata;
+
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ MMAL_STATUS_T status = *(uint32_t *)buffer->data;
+
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "MMAL error: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ }
+
+ mmal_buffer_header_release(buffer);
+}
+
+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ mmal_buffer_header_release(buffer);
+}
+
+static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *)port->userdata;
+
+ if (buffer->cmd == 0) {
+ if (buffer->length > 0) {
+ mmal_queue_put(this->decoded_frames, buffer);
+ pthread_mutex_lock(&this->mutex);
+ fill_output_port(this);
+ pthread_mutex_unlock(&this->mutex);
+ } else {
+ free_output_buffer(buffer);
+ }
+
+ } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
+ MMAL_EVENT_FORMAT_CHANGED_T *fmt = mmal_event_format_changed_get(buffer);
+ MMAL_ES_FORMAT_T *format = mmal_format_alloc();
+ mmal_format_full_copy(format, fmt->format);
+
+ pthread_mutex_lock(&this->mutex);
+ if (this->output_format) {
+ mmal_format_free(this->output_format);
+ this->output_format = NULL;
+ }
+ this->output_format = format;
+ pthread_mutex_unlock(&this->mutex);
+
+ mmal_buffer_header_release(buffer);
+
+ } else {
+ mmal_buffer_header_release(buffer);
+ }
+}
+
+/*
+ * mmal codec
+ */
+
+static void stop_codec(mmal_decoder_t *this)
+{
+ if (this->decoder) {
+ if (this->decoder->control->is_enabled) {
+ mmal_port_disable(this->decoder->control);
+ }
+
+ if (this->decoder->input[0]->is_enabled) {
+ mmal_port_disable(this->decoder->input[0]);
+ }
+
+ if (this->decoder->output[0]->is_enabled) {
+ mmal_port_disable(this->decoder->output[0]);
+ }
+
+ if (this->decoder->is_enabled) {
+ mmal_component_disable(this->decoder);
+ }
+ }
+}
+
+static int start_codec(mmal_decoder_t *this)
+{
+ MMAL_PORT_T *input = this->decoder->input[0];
+ MMAL_STATUS_T status;
+
+ if (!this->decoder->output[0]->is_enabled) {
+ status = mmal_port_enable(this->decoder->output[0], output_port_cb);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to enable output port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ return -1;
+ }
+ }
+
+ if (!this->decoder->is_enabled) {
+ status = mmal_component_enable(this->decoder);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to enable decoder: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ return -1;
+ }
+ }
+
+ if (!this->input_pool) {
+ this->input_pool = mmal_pool_create_with_allocator(input->buffer_num,
+ input->buffer_size, input,
+ (mmal_pool_allocator_alloc_t)mmal_port_payload_alloc,
+ (mmal_pool_allocator_free_t)mmal_port_payload_free);
+ }
+
+ if (!this->decoded_frames) {
+ this->decoded_frames = mmal_queue_create();
+ }
+
+ return 0;
+}
+
+/*
+ * decoder output
+ */
+
+static void send_frames(mmal_decoder_t *this)
+{
+ MMAL_BUFFER_HEADER_T *buffer;
+ vo_frame_t *frame;
+
+ if (!this->decoded_frames) {
+ return;
+ }
+
+ /* get ready frames */
+ while (NULL != (buffer = mmal_queue_get(this->decoded_frames))) {
+ frame = (vo_frame_t *)buffer->user_data;
+ if (frame) {
+ frame->pts = buffer->pts;
+ frame->crop_left = this->crop_x;
+ frame->crop_top = this->crop_y;
+ frame->crop_right = this->width - this->crop_x - this->crop_w;
+ frame->crop_bottom = this->height - this->crop_y - this->crop_h;
+
+ /* indirect rendering ? */
+ if (buffer->data != frame->base[0]) {
+ int sz = this->width * this->height;
+ yv12_to_yv12(
+ /* Y */
+ buffer->data, this->width,
+ frame->base[0], frame->pitches[0],
+ /* U */
+ buffer->data + sz, this->width / 2,
+ frame->base[1], frame->pitches[1],
+ /* V */
+ buffer->data + sz*5/4, this->width / 2,
+ frame->base[2], frame->pitches[2],
+ /* width x height */
+ this->width, this->height);
+ }
+
+ frame->draw(frame, this->stream);
+ }
+
+ free_output_buffer(buffer);
+ }
+
+ if (pthread_mutex_trylock(&this->mutex) == 0) {
+ fill_output_port(this);
+ pthread_mutex_unlock(&this->mutex);
+ }
+}
+
+static int change_output_format(mmal_decoder_t *this)
+{
+ MMAL_PORT_T *output = this->decoder->output[0];
+ MMAL_STATUS_T status;
+ double rate;
+ int ret = 0;
+
+ status = mmal_port_disable(output);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to disable output port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ ret = -1;
+ goto out;
+ }
+
+ mmal_format_full_copy(output->format, this->output_format);
+
+ status = mmal_port_format_commit(output);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to commit output format: %s (%d)",
+ mmal_status_to_string(status), status);
+ ret = -1;
+ goto out;
+ }
+
+ output->buffer_num = output->buffer_num_recommended;
+ output->buffer_size = output->buffer_size_recommended;
+
+ status = mmal_port_enable(output, output_port_cb);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to enable output port: %s (%d)",
+ mmal_status_to_string(status), status);
+ ret = -1;
+ goto out;
+ }
+
+ if (!this->output_pool) {
+ this->output_pool = mmal_pool_create(output->buffer_num_recommended + 10, 0);
+ }
+
+ this->width = output->format->es->video.width;
+ this->height = output->format->es->video.height;
+ this->crop_x = output->format->es->video.crop.x;
+ this->crop_y = output->format->es->video.crop.y;
+ this->crop_w = output->format->es->video.crop.width;
+ this->crop_h = output->format->es->video.crop.height;
+ this->ratio = output->format->es->video.par.num;
+ this->ratio /= output->format->es->video.par.den;
+ this->ratio *= this->width;
+ this->ratio /= this->height;
+ switch (output->format->es->video.color_space) {
+ case MMAL_COLOR_SPACE_ITUR_BT601:
+ case MMAL_COLOR_SPACE_BT470_2_BG:
+ case MMAL_COLOR_SPACE_JFIF_Y16_255:
+ VO_SET_FLAGS_CM (10, this->frame_flags);
+ break;
+ case MMAL_COLOR_SPACE_ITUR_BT709:
+ VO_SET_FLAGS_CM (2, this->frame_flags);
+ break;
+ case MMAL_COLOR_SPACE_JPEG_JFIF:
+ VO_SET_FLAGS_CM (11, this->frame_flags);
+ break;
+ case MMAL_COLOR_SPACE_FCC:
+ VO_SET_FLAGS_CM (8, this->frame_flags);
+ break;
+ case MMAL_COLOR_SPACE_SMPTE240M:
+ VO_SET_FLAGS_CM (14, this->frame_flags);
+ break;
+ case MMAL_COLOR_SPACE_UNKNOWN:
+ default:
+ //VO_SET_FLAGS_CM (4, this->frame_flags); /* undefined, mpeg range */
+ // might have beed set by demux
+ break;
+ }
+ rate = output->format->es->video.frame_rate.num;
+ rate /= output->format->es->video.frame_rate.den;
+
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_RATIO, this->ratio*10000);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, 90000/rate);
+
+ out:
+ mmal_format_free(this->output_format);
+ this->output_format = NULL;
+ return ret;
+}
+
+static void handle_output(mmal_decoder_t *this)
+{
+ /* handle output re-config request */
+ if (this->output_format) {
+ pthread_mutex_lock(&this->mutex);
+ change_output_format(this);
+ pthread_mutex_unlock(&this->mutex);
+ }
+
+ /* handle decoder output */
+ send_frames(this);
+}
+
+/*
+ * decoder input
+ */
+
+static void set_extradata(mmal_decoder_t *this, void *extradata, size_t extradata_size)
+{
+ MMAL_PORT_T *input = this->decoder->input[0];
+ MMAL_STATUS_T status;
+
+ status = mmal_port_disable(input);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to disable input port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ }
+
+ status = mmal_format_extradata_alloc(input->format, extradata_size);
+ if (status == MMAL_SUCCESS) {
+ memcpy(input->format->extradata, extradata, extradata_size);
+ input->format->extradata_size = extradata_size;
+ } else {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to allocate extradata: %s (%d)",
+ mmal_status_to_string(status), status);
+ }
+
+ status = mmal_port_format_commit(input);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to commit input format: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ }
+
+ status = mmal_port_enable(input, input_port_cb);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to enable input port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ }
+}
+
+static void free_input_buffer(mmal_decoder_t *this)
+{
+ if (this->input_buffer) {
+ mmal_buffer_header_release(this->input_buffer);
+ this->input_buffer = NULL;
+ }
+}
+
+static MMAL_BUFFER_HEADER_T *get_input_buffer(mmal_decoder_t *this)
+{
+ if (!this->input_buffer) {
+ int retries = 40;
+
+ this->input_buffer = mmal_queue_timedwait(this->input_pool->queue, 200);
+ while (!this->input_buffer) {
+ handle_output(this);
+ if (--retries < 1 || this->stream->emergency_brake) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to retrieve buffer header for input data\n");
+ this->discontinuity = 1;
+ return NULL;
+ }
+ this->input_buffer = mmal_queue_timedwait(this->input_pool->queue, 200);
+ }
+
+ mmal_buffer_header_reset(this->input_buffer);
+ this->input_buffer->cmd = 0;
+ this->input_buffer->length = 0;
+ this->input_buffer->flags = 0;
+ }
+
+ return this->input_buffer;
+}
+
+static int send_input_buffer(mmal_decoder_t *this)
+{
+ MMAL_STATUS_T status;
+
+ if (this->input_buffer) {
+ if (this->discontinuity) {
+ this->input_buffer->flags |= MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY;
+ this->discontinuity = 0;
+ }
+
+ status = mmal_port_send_buffer(this->decoder->input[0], this->input_buffer);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to send buffer to input port: %s (%d)",
+ mmal_status_to_string(status), status);
+ free_input_buffer(this);
+ return -1;
+ }
+ this->input_buffer = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * xine video decoder plugin functions
+ */
+
+static void handle_header(mmal_decoder_t *this, buf_element_t *buf)
+{
+ xine_bmiheader *bih;
+ size_t extradata_size = 0;
+ uint8_t *extradata = NULL;
+
+ if (buf->decoder_flags & BUF_FLAG_STDHEADER) {
+ bih = (xine_bmiheader *) buf->content;
+ this->width = (bih->biWidth + 1) & ~1;
+ this->height = (bih->biHeight + 1) & ~1;
+
+ if (buf->decoder_flags & BUF_FLAG_ASPECT)
+ this->ratio = (double)buf->decoder_info[1] / (double)buf->decoder_info[2];
+ else
+ this->ratio = (double)this->width / (double)this->height;
+
+ if (bih->biSize > sizeof(xine_bmiheader)) {
+ extradata_size = bih->biSize - sizeof(xine_bmiheader);
+ extradata = buf->content + sizeof(xine_bmiheader);
+ }
+ }
+
+ if (buf->type == BUF_VIDEO_H264) {
+ if (extradata && extradata_size > 0) {
+ set_extradata(this, extradata, extradata_size);
+ }
+ }
+}
+
+static void mmal_decode_data (video_decoder_t *this_gen, buf_element_t *buf)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *) this_gen;
+
+ if (buf->decoder_flags & (BUF_FLAG_PREVIEW | BUF_FLAG_SPECIAL)) {
+ return;
+ }
+
+ if (buf->decoder_flags & BUF_FLAG_COLOR_MATRIX) {
+ VO_SET_FLAGS_CM (buf->decoder_info[4], this->frame_flags);
+ }
+
+ if (buf->decoder_flags & BUF_FLAG_STDHEADER) {
+ handle_header(this, buf);
+ return;
+ }
+
+ if (!this->decoder_ok) {
+ start_codec(this);
+
+ (this->stream->video_out->open) (this->stream->video_out, this->stream);
+ this->decoder_ok = 1;
+ }
+
+ /* handle decoder output and config */
+ handle_output(this);
+
+ /* feed decoder */
+ while (buf->size > 0) {
+
+ MMAL_BUFFER_HEADER_T *buffer = get_input_buffer(this);
+ if (!buffer)
+ return;
+
+ if (buf->pts > 0)
+ buffer->pts = buf->pts;
+
+ uint32_t len = buf->size;
+ if (len > buffer->alloc_size - buffer->length)
+ len = buffer->alloc_size - buffer->length;
+
+ memcpy(buffer->data + buffer->length, buf->content, len);
+ buf->content += len;
+ buf->size -= len;
+
+ buffer->length += len;
+
+ if (buf->size > 0 || (buf->decoder_flags & BUF_FLAG_FRAME_END)) {
+ send_input_buffer(this);
+ }
+ }
+}
+
+static void mmal_flush (video_decoder_t *this_gen)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *) this_gen;
+
+ send_frames(this);
+}
+
+static void mmal_reset (video_decoder_t *this_gen)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *) this_gen;
+
+ free_input_buffer(this);
+
+ if (this->decoder && this->decoder->is_enabled) {
+
+ stop_codec(this);
+
+ /* free frames */
+ MMAL_BUFFER_HEADER_T *buffer;
+ while ((buffer = mmal_queue_get(this->decoded_frames))) {
+ free_output_buffer(buffer);
+ }
+
+ mmal_port_enable(this->decoder->control, control_port_cb);
+ mmal_port_enable(this->decoder->input[0], input_port_cb);
+
+ this->stream->video_out->close(this->stream->video_out, this->stream);
+ this->decoder_ok = 0;
+ }
+}
+
+static void mmal_discontinuity (video_decoder_t *this_gen)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *) this_gen;
+ send_input_buffer(this);
+ this->discontinuity = 1;
+}
+
+static void mmal_dispose (video_decoder_t *this_gen)
+{
+ mmal_decoder_t *this = (mmal_decoder_t *) this_gen;
+
+ free_input_buffer(this);
+
+ stop_codec(this);
+
+ if (this->input_pool) {
+ mmal_pool_destroy(this->input_pool);
+ }
+
+ if (this->output_format) {
+ mmal_format_free(this->output_format);
+ }
+
+ /* free frames */
+ if (this->decoded_frames) {
+ MMAL_BUFFER_HEADER_T *buffer;
+ while ((buffer = mmal_queue_get(this->decoded_frames))) {
+ free_output_buffer(buffer);
+ }
+
+ mmal_queue_destroy(this->decoded_frames);
+ }
+
+ if (this->output_pool) {
+ mmal_pool_destroy(this->output_pool);
+ }
+
+ if (this->decoder) {
+ mmal_component_release(this->decoder);
+ }
+
+
+ if (this->decoder_ok) {
+ this->decoder_ok = 0;
+ this->stream->video_out->close(this->stream->video_out, this->stream);
+ }
+
+ pthread_mutex_destroy (&this->mutex);
+
+ free (this_gen);
+
+ bcm_host_deinit();
+}
+
+static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream)
+{
+ mmal_decoder_t *this;
+ MMAL_STATUS_T status;
+
+ bcm_host_init();
+
+ this = (mmal_decoder_t *) calloc(1, sizeof(mmal_decoder_t));
+
+ pthread_mutex_init (&this->mutex, NULL);
+
+ this->video_decoder.decode_data = mmal_decode_data;
+ this->video_decoder.flush = mmal_flush;
+ this->video_decoder.reset = mmal_reset;
+ this->video_decoder.discontinuity = mmal_discontinuity;
+ this->video_decoder.dispose = mmal_dispose;
+
+ this->stream = stream;
+
+ VO_SET_FLAGS_CM (4, this->frame_flags); /* undefined, mpeg range */
+
+ /* create decoder component */
+
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &this->decoder);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to create "MMAL_COMPONENT_DEFAULT_VIDEO_DECODER": %s (%d)\n",
+ mmal_status_to_string(status), status);
+ mmal_dispose(&this->video_decoder);
+ return NULL;
+ }
+
+ this->decoder->control->userdata = (void *)this;
+ this->decoder->input[0]->userdata = (void *)this;
+ this->decoder->output[0]->userdata = (void *)this;
+
+ status = mmal_port_enable(this->decoder->control, control_port_cb);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to enable control port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ mmal_dispose(&this->video_decoder);
+ return NULL;
+ }
+
+ /* test if decoder supports requested codec */
+
+ uint32_t video_type = BUF_VIDEO_BASE | (stream->video_decoder_streamtype << 16);
+ MMAL_PORT_T *input = this->decoder->input[0];
+
+ switch (video_type) {
+ case BUF_VIDEO_H264:
+ _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "H.264");
+ input->format->encoding = MMAL_ENCODING_H264;
+ break;
+ case BUF_VIDEO_VC1:
+ _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "VC-1");
+ input->format->encoding = MMAL_FOURCC('V','C','-','1');
+ break;
+ case BUF_VIDEO_MPEG:
+ _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "MPEG");
+ input->format->encoding = MMAL_ENCODING_MP2V;
+ break;
+ case BUF_VIDEO_JPEG:
+ _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "JPEG");
+ input->format->encoding = MMAL_ENCODING_JPEG;
+ break;
+ default:
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "unsupported video codec: 0x%x\n",
+ stream->video_decoder_streamtype);
+ mmal_dispose(&this->video_decoder);
+ return (video_decoder_t *)1;
+ }
+
+ if (video_type == BUF_VIDEO_H264) {
+ MMAL_PARAMETER_BOOLEAN_T param;
+ param.hdr.id = MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT;
+ param.hdr.size = sizeof(MMAL_PARAMETER_BOOLEAN_T);
+ param.enable = MMAL_FALSE;
+ status = mmal_port_parameter_set(input, &param.hdr);
+ if (status != MMAL_SUCCESS)
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to disable error concealment: %s (%d)",
+ mmal_status_to_string(status), status);
+ }
+
+ status = mmal_port_format_commit(input);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to commit input format: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ mmal_dispose(&this->video_decoder);
+ return NULL;
+ }
+
+ input->buffer_size = input->buffer_size_recommended;
+ input->buffer_num = input->buffer_num_recommended * 4;
+
+ status = mmal_port_enable(input, input_port_cb);
+ if (status != MMAL_SUCCESS) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE": "
+ "failed to enable input port: %s (%d)\n",
+ mmal_status_to_string(status), status);
+ mmal_dispose(&this->video_decoder);
+ return NULL;
+ }
+
+ return &this->video_decoder;
+}
+
+static void *init_plugin (xine_t *xine, void *data)
+{
+ video_decoder_class_t *this;
+
+ this = (video_decoder_class_t *) calloc(1, sizeof(video_decoder_class_t));
+
+ this->open_plugin = open_plugin;
+ this->identifier = "libmmal";
+ this->description = N_("mmal-based HW video decoder plugin");
+ this->dispose = default_video_decoder_class_dispose;
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+static const uint32_t video_types[] = {
+ BUF_VIDEO_H264,
+ BUF_VIDEO_VC1,
+ BUF_VIDEO_MPEG,
+ BUF_VIDEO_JPEG,
+ 0
+};
+
+static const decoder_info_t dec_info = {
+ video_types, /* supported types */
+ 10 /* priority */
+};
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_VIDEO_DECODER, 19, "libmmal", XINE_VERSION_CODE, &dec_info, init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};