diff options
author | Petri Hintukainen <phintuka@users.sourceforge.net> | 2014-05-15 21:35:26 +0300 |
---|---|---|
committer | Petri Hintukainen <phintuka@users.sourceforge.net> | 2014-05-15 21:35:26 +0300 |
commit | 2685fd1821cdcb1ddab46792b8dfb43e88b044df (patch) | |
tree | 89b4e7fcc879803c59e31cf5e39595f374304049 /src | |
parent | 7aec1541305c1a07b40e47a0a6834bd1e6fc599f (diff) | |
download | xine-lib-2685fd1821cdcb1ddab46792b8dfb43e88b044df.tar.gz xine-lib-2685fd1821cdcb1ddab46792b8dfb43e88b044df.tar.bz2 |
video_out_mmal: libmmal-based video output plugin for Raspberry Pi
Diffstat (limited to 'src')
-rw-r--r-- | src/video_out/Makefile.am | 10 | ||||
-rw-r--r-- | src/video_out/video_out_mmal.c | 585 |
2 files changed, 595 insertions, 0 deletions
diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am index 9bd08cd31..9f1d984ff 100644 --- a/src/video_out/Makefile.am +++ b/src/video_out/Makefile.am @@ -89,6 +89,10 @@ if ENABLE_SDL sdl_module = xineplug_vo_out_sdl.la endif +if ENABLE_MMAL +mmal_module = xineplug_vo_out_mmal.la +endif + if ENABLE_STK stk_module = xineplug_vo_out_stk.la endif @@ -126,6 +130,7 @@ xineplug_LTLIBRARIES = $(xshm_module) $(xv_module) $(xvmc_module) \ $(xcbxv_module) \ $(vdpau_module) \ $(vaapi_module) \ + $(mmal_module) \ xineplug_vo_out_raw.la \ xineplug_vo_out_none.la @@ -213,6 +218,11 @@ xineplug_vo_out_sdl_la_SOURCES = video_out_sdl.c xineplug_vo_out_sdl_la_LIBADD = $(XINE_LIB) $(SDL_LIBS) $(X_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) xineplug_vo_out_sdl_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) $(SDL_CFLAGS) +xineplug_vo_out_mmal_la_SOURCES = video_out_mmal.c +xineplug_vo_out_mmal_la_LIBADD = $(XINE_LIB) $(MMAL_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) +xineplug_vo_out_mmal_la_CFLAGS = $(AM_CFLAGS) $(MMAL_CFLAGS) +xineplug_vo_out_mmal_la_LDFLAGS = $(AM_LDFLAGS) $(MMAL_LDFLAGS) + xineplug_vo_out_stk_la_SOURCES = video_out_stk.c xineplug_vo_out_stk_la_LIBADD = $(XINE_LIB) $(LIBSTK_LIBS) $(PTHREAD_LIBS) xineplug_vo_out_stk_la_CFLAGS = $(AM_CFLAGS) $(LIBSTK_CFLAGS) diff --git a/src/video_out/video_out_mmal.c b/src/video_out/video_out_mmal.c new file mode 100644 index 000000000..4ac343f57 --- /dev/null +++ b/src/video_out/video_out_mmal.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2000-2014 the xine project + * Copyright (C) 2014 Petri Hintukainen <phintuka@users.sourceforge.net> + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.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 "video_out_mmal" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include "xine.h" +#include <xine/xine_internal.h> +#include <xine/video_out.h> +#include <xine/xineutils.h> + +#define MAX_VIDEO_WIDTH 1920 +#define MAX_VIDEO_HEIGHT 1088 +#define MAX_VIDEO_FRAMES 20 + + +typedef struct { + vo_frame_t vo_frame; + + MMAL_BUFFER_HEADER_T *buffer; + int width, height, format; + double ratio; + + int displayed; +} mmal_frame_t; + +typedef struct { + + vo_driver_t vo_driver; + + /* xine */ + xine_t *xine; + alphablend_t alphablend_extra_data; + uint32_t capabilities; + int gui_width, gui_height; + + /* mmal */ + MMAL_COMPONENT_T *renderer; + MMAL_POOL_T *pool; + int frames_in_renderer; + double renderer_ratio; + + pthread_mutex_t mutex; + pthread_cond_t cond; +} mmal_driver_t; + +typedef struct { + video_driver_class_t driver_class; + xine_t *xine; +} mmal_class_t; + +#define LOG_STATUS(msg) \ + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " msg ": %s (%d)\n", \ + mmal_status_to_string(status), status) + +/* + * display config + */ + +static int update_tv_resolution(mmal_driver_t *this) { + + TV_DISPLAY_STATE_T display_state; + + if (vc_tv_get_display_state(&display_state) != 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "failed to query display resolution\n"); + return -1; + } + + if (display_state.state & 0xFF) { + this->gui_width = display_state.display.hdmi.width; + this->gui_height = display_state.display.hdmi.height; + } else if (display_state.state & 0xFF00) { + this->gui_width = display_state.display.sdtv.width; + this->gui_height = display_state.display.sdtv.height; + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "invalid display state %x", (unsigned)display_state.state); + return -1; + } + + xprintf(this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE": " + "display size %dx%d\n", this->gui_width, this->gui_height); + return 0; +} + +static int config_display(mmal_driver_t *this, + int src_x, int src_y, int src_w, int src_h) { + + MMAL_DISPLAYREGION_T display_region; + MMAL_STATUS_T status; + + display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; + display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); + display_region.fullscreen = MMAL_FALSE; + display_region.src_rect.x = src_x; + display_region.src_rect.y = src_y; + display_region.src_rect.width = src_w; + display_region.src_rect.height = src_h; + display_region.dest_rect.x = 0; + display_region.dest_rect.y = 0; + display_region.dest_rect.width = this->gui_width; + display_region.dest_rect.height = this->gui_height; + display_region.layer = 1; + display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | + MMAL_DISPLAY_SET_SRC_RECT | + MMAL_DISPLAY_SET_DEST_RECT | + MMAL_DISPLAY_SET_LAYER; + + status = mmal_port_parameter_set(this->renderer->input[0], &display_region.hdr); + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to set display region"); + return -1; + } + return 0; +} + +/* + * MMAL callbacks + */ + +static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { + + mmal_driver_t *this = (mmal_driver_t *)port->userdata; + MMAL_STATUS_T status; + + if (buffer->cmd == MMAL_EVENT_ERROR) { + status = *(uint32_t *)buffer->data; + LOG_STATUS("MMAL error"); + } + + mmal_buffer_header_release(buffer); +} + +static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { + + mmal_driver_t *this = (mmal_driver_t *)port->userdata; + vo_frame_t *frame = (vo_frame_t *)buffer->user_data; + + pthread_mutex_lock(&this->mutex); + --this->frames_in_renderer; + pthread_cond_signal(&this->cond); + pthread_mutex_unlock(&this->mutex); + + if (frame) { + frame->free(frame); + } +} + +/* + * renderer configuration + */ + +static void disable_renderer(mmal_driver_t *this) { + + if (this->renderer) { + + if (this->renderer->control->is_enabled) { + mmal_port_disable(this->renderer->control); + } + + if (this->renderer->input[0]->is_enabled) { + mmal_port_disable(this->renderer->input[0]); + } + + if (this->renderer->is_enabled) { + mmal_component_disable(this->renderer); + } + } +} + +static int configure_renderer(mmal_driver_t *this, int format, int width, int height, + int crop_x, int crop_y, int crop_w, int crop_h, double ratio) { + + MMAL_PORT_T *input = this->renderer->input[0]; + MMAL_STATUS_T status; + + disable_renderer(this); + + this->renderer_ratio = ratio; + + input->userdata = (struct MMAL_PORT_USERDATA_T *)this; + input->format->encoding = (format == XINE_IMGFMT_YV12 ? MMAL_ENCODING_I420 : MMAL_ENCODING_YUYV); + input->format->es->video.width = width; + input->format->es->video.height = height; + input->format->es->video.crop.x = crop_x; + input->format->es->video.crop.y = crop_y; + input->format->es->video.crop.width = crop_w; + input->format->es->video.crop.height = crop_h; + input->format->es->video.par.num = height * ratio; + input->format->es->video.par.den = width; + + status = mmal_port_format_commit(input); + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to commit input format"); + } + + input->buffer_size = input->buffer_size_recommended; + + status = mmal_port_enable(this->renderer->control, control_port_cb); + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to enable control port"); + return -1; + } + + status = mmal_port_enable(input, input_port_cb); + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to enable input port"); + return -1; + } + + status = mmal_component_enable(this->renderer); + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to enable renderer component"); + return -1; + } + + if (!this->pool) { + int buffer_size = MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT * 2; + this->pool = mmal_pool_create_with_allocator(MAX_VIDEO_FRAMES, buffer_size, + input, + (mmal_pool_allocator_alloc_t)mmal_port_payload_alloc, + (mmal_pool_allocator_free_t)mmal_port_payload_free); + if (!this->pool) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "failed to create MMAL pool for %u buffers of size %d\n", + MAX_VIDEO_FRAMES, buffer_size); + return -1; + } + } + + return 0; +} + +/* + * xine interface + */ + +static uint32_t mmal_get_capabilities (vo_driver_t *this_gen) { + + mmal_driver_t *this = (mmal_driver_t *) this_gen; + + return this->capabilities; +} + +static void mmal_frame_field (vo_frame_t *vo_img, int which_field) { +} + +static void mmal_frame_dispose (vo_frame_t *vo_img) { + + mmal_frame_t *frame = (mmal_frame_t *) vo_img ; + + if (frame->buffer) { + frame->buffer->user_data = NULL; + mmal_buffer_header_release(frame->buffer); + frame->buffer = NULL; + } + + free(frame); +} + +static vo_frame_t *mmal_alloc_frame (vo_driver_t *this_gen) { + + mmal_frame_t *frame; + + frame = (mmal_frame_t *) calloc(1, sizeof(mmal_frame_t)); + + if (!frame) + return NULL; + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + frame->vo_frame.proc_slice = NULL; + frame->vo_frame.proc_frame = NULL; + frame->vo_frame.field = mmal_frame_field; + frame->vo_frame.dispose = mmal_frame_dispose; + + return (vo_frame_t *) frame; +} + +static void mmal_update_frame_format (vo_driver_t *this_gen, + vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + double ratio, int format, int flags) { + + mmal_driver_t *this = (mmal_driver_t *)this_gen; + mmal_frame_t *frame = (mmal_frame_t *)frame_gen; + + /* limit frame size */ + if (width > MAX_VIDEO_WIDTH) { + width = MAX_VIDEO_WIDTH; + frame->vo_frame.width = width; + } + if (height > MAX_VIDEO_HEIGHT) { + height = MAX_VIDEO_HEIGHT; + frame->vo_frame.height = height; + } + + /* alignment */ + width = (width + 31) & ~31; + height = (height + 1) & ~1; + + if (!frame->buffer) { + frame->buffer = mmal_queue_wait(this->pool->queue); + if (!frame->buffer) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "failed to get mmal buffer for frame\n"); + frame->vo_frame.width = frame->vo_frame.height = 0; + return; + } + frame->buffer->user_data = frame; + } + + frame->width = width; + frame->height = height; + frame->format = format; + frame->ratio = ratio; + + if (format == XINE_IMGFMT_YV12) { + frame->vo_frame.pitches[0] = width; + frame->vo_frame.pitches[1] = width/2; + frame->vo_frame.pitches[2] = width/2; + frame->vo_frame.base[0] = frame->buffer->data; + frame->vo_frame.base[1] = frame->vo_frame.base[0] + width * height; + frame->vo_frame.base[2] = frame->vo_frame.base[1] + width/2 * height/2; + } else if (format == XINE_IMGFMT_YUY2) { + frame->vo_frame.pitches[0] = width; + frame->vo_frame.base[0] = frame->buffer->data; + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "unsupported frame format %x\n", format); + frame->vo_frame.width = frame->vo_frame.height = 0; + } + + frame->displayed = 0; +} + +static void mmal_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame, vo_overlay_t *overlay) { + + mmal_driver_t *this = (mmal_driver_t *) this_gen; + + if (overlay->width <= 0 || overlay->height <= 0 || !overlay->rle) + return; + + this->alphablend_extra_data.offset_x = frame->overlay_offset_x; + this->alphablend_extra_data.offset_y = frame->overlay_offset_y; + + if (overlay->rle) { + if( frame->format == XINE_IMGFMT_YV12 ) + _x_blend_yuv( frame->base, overlay, frame->width, frame->height, frame->pitches, &this->alphablend_extra_data); + else + _x_blend_yuy2( frame->base[0], overlay, frame->width, frame->height, frame->pitches[0], &this->alphablend_extra_data); + } +} + +static int mmal_redraw_needed (vo_driver_t *this_gen) { + + return 0; +} + + +static void mmal_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + + mmal_driver_t *this = (mmal_driver_t *) this_gen; + mmal_frame_t *frame = (mmal_frame_t *) frame_gen; + MMAL_PORT_T *input = this->renderer->input[0]; + MMAL_STATUS_T status; + + int visible_width = frame_gen->width - frame_gen->crop_left - frame_gen->crop_right; + int visible_height = frame_gen->height - frame_gen->crop_top - frame_gen->crop_bottom; + + if (input->format->es->video.width != frame->width || + input->format->es->video.height != frame->height || + this->renderer_ratio != frame_gen->ratio || + input->format->es->video.crop.x != frame_gen->crop_left || + input->format->es->video.crop.y != frame_gen->crop_top || + input->format->es->video.crop.width != visible_width || + input->format->es->video.crop.height != visible_height) { + + configure_renderer(this, frame->format, frame->width, frame->height, + frame_gen->crop_left, frame_gen->crop_top, visible_width, visible_height, frame_gen->ratio); + + config_display(this, 0, 0, frame_gen->width, frame_gen->height); + } + + frame->buffer->cmd = 0; + frame->buffer->length = this->renderer->input[0]->buffer_size; + + pthread_mutex_lock(&this->mutex); + + while (this->frames_in_renderer > 1) { + pthread_cond_wait(&this->cond, &this->mutex); + } + + status = mmal_port_send_buffer(this->renderer->input[0], frame->buffer); + if (status == MMAL_SUCCESS) { + this->frames_in_renderer++; + } + + pthread_mutex_unlock(&this->mutex); + + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to send frame to renderer input port"); + frame_gen->free(frame_gen); + return; + } + + if (frame->displayed) { + frame_gen->free(frame_gen); + return; + } + + frame->displayed = 1; +} + +static int mmal_get_property (vo_driver_t *this_gen, int property) { + + mmal_driver_t *this = (mmal_driver_t *) this_gen; + + switch (property) { + case VO_PROP_WINDOW_WIDTH: + return this->gui_width; + case VO_PROP_WINDOW_HEIGHT: + return this->gui_height; + case VO_PROP_MAX_VIDEO_WIDTH: + return MAX_VIDEO_WIDTH; + case VO_PROP_MAX_VIDEO_HEIGHT: + return MAX_VIDEO_HEIGHT; + case VO_PROP_MAX_NUM_FRAMES: + return MAX_VIDEO_FRAMES; + } + return 0; +} + +static int mmal_set_property (vo_driver_t *this_gen, int property, int value) { + + return value; +} + +static void mmal_get_property_min_max (vo_driver_t *this_gen, int property, int *min, int *max) { + + *min = *max = 0; +} + +static int mmal_gui_data_exchange (vo_driver_t *this_gen, int data_type, void *data) { + + return -1; +} + +static void mmal_dispose (vo_driver_t * this_gen) { + + mmal_driver_t *this = (mmal_driver_t*) this_gen; + + if (this->renderer) { + disable_renderer(this); + mmal_component_release(this->renderer); + } + + if (this->pool) { + mmal_pool_destroy(this->pool); + } + + _x_alphablend_free(&this->alphablend_extra_data); + + pthread_cond_destroy(&this->cond); + pthread_mutex_destroy(&this->mutex); + + free(this); + + bcm_host_deinit(); +} + +static vo_driver_t *open_plugin (video_driver_class_t *class_gen, const void *visual_gen) { + + mmal_class_t *class = (mmal_class_t*) class_gen; + mmal_driver_t *this; + MMAL_STATUS_T status; + + this = (mmal_driver_t *) calloc(1, sizeof(mmal_driver_t)); + if (!this) + return NULL; + + this->xine = class->xine; + this->capabilities = VO_CAP_YUY2 | VO_CAP_YV12 | VO_CAP_CROP; + + pthread_mutex_init (&this->mutex, NULL); + pthread_cond_init (&this->cond, NULL); + + bcm_host_init(); + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &this->renderer); + if (status != MMAL_SUCCESS) { + LOG_STATUS("failed to create MMAL component " MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER); + mmal_dispose(&this->vo_driver); + return NULL; + } + + this->renderer->control->userdata = (struct MMAL_PORT_USERDATA_T *)this; + this->renderer->input[0]->userdata = (struct MMAL_PORT_USERDATA_T *)this; + + configure_renderer(this, XINE_IMGFMT_YV12, 720, 576, 0, 0, 720, 576, 4.0/3.0); + update_tv_resolution(this); + config_display(this, 0, 0, 720, 576); + + this->vo_driver.get_capabilities = mmal_get_capabilities; + this->vo_driver.alloc_frame = mmal_alloc_frame; + this->vo_driver.update_frame_format = mmal_update_frame_format; + this->vo_driver.overlay_begin = NULL; + this->vo_driver.overlay_blend = mmal_overlay_blend; + this->vo_driver.overlay_end = NULL; + this->vo_driver.display_frame = mmal_display_frame; + this->vo_driver.get_property = mmal_get_property; + this->vo_driver.set_property = mmal_set_property; + this->vo_driver.get_property_min_max = mmal_get_property_min_max; + this->vo_driver.gui_data_exchange = mmal_gui_data_exchange; + this->vo_driver.dispose = mmal_dispose; + this->vo_driver.redraw_needed = mmal_redraw_needed; + + return &this->vo_driver; +} + +/** + * Class Functions + */ +static void *init_class (xine_t *xine, void *visual_gen) { + mmal_class_t *this; + + this = (mmal_class_t*) calloc(1, sizeof(mmal_class_t)); + + this->driver_class.open_plugin = open_plugin; + this->driver_class.identifier = "MMAL"; + this->driver_class.description = N_("xine video output plugin using MMAL"); + this->driver_class.dispose = default_video_driver_class_dispose; + + this->xine = xine; + + return this; +} + +static const vo_info_t vo_info_mmal = { + 10, /* priority */ + XINE_VISUAL_TYPE_FB, /* visual type supported by this plugin */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_OUT, 22, "mmal", XINE_VERSION_CODE, &vo_info_mmal, init_class }, + { PLUGIN_NONE, 0, "" , 0 , NULL, NULL} +}; |