diff options
-rw-r--r-- | src/video_out/video_out_mmal.c | 285 |
1 files changed, 282 insertions, 3 deletions
diff --git a/src/video_out/video_out_mmal.c b/src/video_out/video_out_mmal.c index 62e37fcb4..b5df7cb9e 100644 --- a/src/video_out/video_out_mmal.c +++ b/src/video_out/video_out_mmal.c @@ -33,6 +33,7 @@ #include <interface/mmal/mmal.h> #include <interface/mmal/util/mmal_util.h> #include <interface/mmal/util/mmal_default_components.h> +#include <interface/vmcs_host/vc_dispmanx.h> #define LOG_MODULE "video_out_mmal" #define LOG_VERBOSE @@ -40,6 +41,7 @@ #define LOG */ #define FRAME_ALLOC /* allocate buffer based on frame size. if not defined, all buffers are suitable for 1920x1088 YUY2. */ +#define HW_OVERLAY /* draw overlay using HW. if undefined, draw in software. */ #include "xine.h" #include <xine/xine_internal.h> @@ -70,13 +72,28 @@ typedef struct { int displayed; } mmal_frame_t; +typedef struct mmal_overlay_s mmal_overlay_t; +struct mmal_overlay_s { + mmal_overlay_t *next; + + void *mem; /* temp storage for rle -> argb */ + int src_width, src_height, src_pitch; + VC_RECT_T src_rect; + VC_RECT_T dst_rect; + + DISPMANX_ELEMENT_HANDLE_T element; + DISPMANX_RESOURCE_HANDLE_T resource; +}; + typedef struct { vo_driver_t vo_driver; /* xine */ xine_t *xine; +#ifndef HW_OVERLAY alphablend_t alphablend_extra_data; +#endif int gui_width, gui_height; /* mmal */ @@ -85,6 +102,14 @@ typedef struct { int frames_in_renderer; double renderer_ratio; + /* dispmanx */ + DISPMANX_DISPLAY_HANDLE_T dispmanx_handle; + DISPMANX_UPDATE_HANDLE_T overlay_update; + + /* overlays */ + mmal_overlay_t *overlays; + mmal_overlay_t *old_overlays; + pthread_mutex_t mutex; pthread_cond_t cond; } mmal_driver_t; @@ -129,6 +154,10 @@ static int update_tv_resolution(mmal_driver_t *this) { return 0; } +/* + * + */ + static int config_display(mmal_driver_t *this, int src_x, int src_y, int src_w, int src_h) { @@ -294,7 +323,9 @@ static uint32_t mmal_get_capabilities (vo_driver_t *this_gen) { return VO_CAP_YUY2 | VO_CAP_YV12 | - VO_CAP_CROP; + VO_CAP_CROP | + VO_CAP_UNSCALED_OVERLAY | + VO_CAP_CUSTOM_EXTENT_OVERLAY; } static void mmal_frame_field (vo_frame_t *vo_img, int which_field) { @@ -426,6 +457,110 @@ static void mmal_update_frame_format (vo_driver_t *this_gen, frame->displayed = 0; } + +/* + * overlay + */ + +static void overlay_free(mmal_overlay_t *ovl, DISPMANX_UPDATE_HANDLE_T update) { + + if (ovl->resource != DISPMANX_NO_HANDLE) { + vc_dispmanx_element_remove(update, ovl->element); + vc_dispmanx_resource_delete(ovl->resource); + } + free(ovl->mem); + free(ovl); +} + +static void overlay_update(mmal_overlay_t *ovl, DISPMANX_UPDATE_HANDLE_T update, uint32_t *argb) { + + vc_dispmanx_resource_write_data(ovl->resource, VC_IMAGE_RGBA32, + ovl->src_pitch, argb, &ovl->src_rect); + vc_dispmanx_element_change_source(update, ovl->element, ovl->resource); +} + +static mmal_overlay_t *overlay_new(mmal_driver_t *this, + DISPMANX_UPDATE_HANDLE_T update, + int src_width, int src_height, int src_pitch, + int x, int y, int width, int height, int layer, + uint32_t *argb) { + + mmal_overlay_t *ovl = calloc(1, sizeof(mmal_overlay_t)); + uint32_t image_handle; + static VC_DISPMANX_ALPHA_T alpha; + VC_RECT_T src_rect; + //src_width &= 31; + //dst_width &= 15; + ovl->src_pitch = src_pitch; + ovl->src_width = src_width; + ovl->src_height = src_height; + + vc_dispmanx_rect_set(&src_rect, 0, 0, src_width << 16, src_height << 16); + vc_dispmanx_rect_set(&ovl->src_rect, 0, 0, src_width, src_height); + vc_dispmanx_rect_set(&ovl->dst_rect, x, y, width, height); + + ovl->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, + src_pitch | (src_pitch<<16), src_height | (src_height<<16), + &image_handle); + if (ovl->resource == DISPMANX_NO_HANDLE) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "failed to create dispmanx resource for overlay\n"); + overlay_free(ovl, update); + return NULL; + } + if (vc_dispmanx_resource_write_data(ovl->resource, VC_IMAGE_RGBA32, + src_pitch, argb, &ovl->src_rect)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "failed to write overlay data to dispmanx resource\n"); + overlay_free(ovl, update); + return NULL; + } + + alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE /*| DISPMANX_FLAGS_ALPHA_MIX*/; + alpha.mask = DISPMANX_NO_HANDLE; + ovl->element = vc_dispmanx_element_add(update, this->dispmanx_handle, + layer, &ovl->dst_rect, ovl->resource, + &src_rect, DISPMANX_PROTECTION_NONE, + &alpha, NULL, VC_IMAGE_ROT0); + + if (ovl->element == DISPMANX_NO_HANDLE) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "vc_dispmanx_element_add() failed\n"); + overlay_free(ovl, update); + return NULL; + } + + return ovl; +} + +static void close_overlays(mmal_driver_t *this, mmal_overlay_t *ovls) { + + while (ovls) { + mmal_overlay_t *tmp = ovls; + ovls = ovls->next; + overlay_free(tmp, this->overlay_update); + } +} + +/* + * + */ + +static void mmal_overlay_begin (vo_driver_t *this_gen, + vo_frame_t *frame_gen, int changed) { + +#ifdef HW_OVERLAY + mmal_driver_t *this = (mmal_driver_t *)this_gen; + + if (changed) { + this->overlay_update = vc_dispmanx_update_start(10); + /* re-create active overlay list to maintain blending order */ + this->old_overlays = this->overlays; + this->overlays = NULL; + } +#endif +} + 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; @@ -433,6 +568,7 @@ static void mmal_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame, vo_ove if (overlay->width <= 0 || overlay->height <= 0 || !overlay->rle) return; +#ifndef HW_OVERLAY this->alphablend_extra_data.offset_x = frame->overlay_offset_x; this->alphablend_extra_data.offset_y = frame->overlay_offset_y; @@ -442,8 +578,125 @@ static void mmal_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame, vo_ove else _x_blend_yuy2( frame->base[0], overlay, frame->width, frame->height, frame->pitches[0], &this->alphablend_extra_data); } +#else + + if (!this->overlay_update) { + return; + } + + int dst_x = overlay->x, dst_y = overlay->y, dst_w = overlay->width, dst_h = overlay->height; + + /* coordinate system */ + int extent_width = overlay->extent_width; + int extent_height = overlay->extent_height; + if (extent_width < 1 || extent_height < 1) { + if (overlay->unscaled) { + extent_width = this->gui_width; + extent_height = this->gui_height; + } else { + extent_width = frame->width; + extent_height = frame->height; + } + } + + /* scale dst region if needed */ + if (extent_width != this->gui_width) { + dst_x = dst_x * this->gui_width / extent_width; + dst_w = dst_w * this->gui_width / extent_width; + } + if (extent_height != this->gui_height) { + dst_y = dst_y * this->gui_height / extent_height; + dst_h = dst_h * this->gui_height / extent_height; + } + +#ifdef LOG + fprintf(stderr, "overlay: %d,%d %dx%d unscaled:%d extent: %dx%d argb: %d-> surface %dx%d\n", + overlay->x, overlay->y, overlay->width, overlay->height, + overlay->unscaled, overlay->extent_width, overlay->extent_height, + (overlay->argb_layer && overlay->argb_layer->buffer), + extent_width, extent_height); +#endif + + /* find suitable region, maintain overlay blending order */ + mmal_overlay_t *ovl = this->old_overlays, *prev = NULL; + while (ovl) { + /* source, dst coordinates must be same (= size + location + scaling) */ + if (ovl->src_width == overlay->width && ovl->src_height == overlay->height && + dst_x == ovl->dst_rect.x && dst_y == ovl->dst_rect.y && dst_w == ovl->dst_rect.width && dst_h == ovl->dst_rect.height) { + if (prev) { + prev->next = ovl->next; + } else { + this->old_overlays = ovl->next; + } + ovl->next = NULL; + break; + } + prev = ovl; + ovl = ovl->next; + } + + /* new overlay */ + if (!ovl) { + if (overlay->rle) { + _x_overlay_clut_yuv2rgb(overlay, 0); + + void *mem = NULL; + int src_pitch = (sizeof(uint32_t) * overlay->width + 31) & ~31; + mem = malloc(src_pitch * overlay->height); + _x_overlay_to_argb32(overlay, mem, src_pitch/4, "RGBA"); + ovl = overlay_new(this, this->overlay_update, overlay->width, overlay->height, src_pitch, + dst_x, dst_y, dst_w, dst_h, 2, mem); + if (!ovl) + return; + ovl->mem = mem; + } + } + + /* update overlay */ + else if (overlay->rle) { + _x_overlay_clut_yuv2rgb(overlay, 0); + + if (!ovl->mem) { + int src_pitch = (sizeof(uint32_t) * overlay->width + 31) & ~31; + ovl->mem = malloc(src_pitch * overlay->height); + ovl->src_pitch = src_pitch; + } + _x_overlay_to_argb32(overlay, ovl->mem, ovl->src_pitch/4, "RGBA"); + overlay_update(ovl, this->overlay_update, ovl->mem); + } + + /* add to list */ + mmal_overlay_t **tail = &this->overlays; + while (*tail) { + tail = &(*tail)->next; + } + *tail = ovl; +#endif /* HW_OVERLAY */ } +static void mmal_overlay_end (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + +#ifdef HW_OVERLAY + mmal_driver_t *this = (mmal_driver_t *)this_gen; + + if (!this->overlay_update) { + return; + } + + /* remove handles not in use */ + close_overlays(this, this->old_overlays); + this->old_overlays = NULL; + + /* commit updates */ + vc_dispmanx_update_submit_sync(this->overlay_update); + this->overlay_update = 0; +#endif +} + +/* + * + */ + static int mmal_redraw_needed (vo_driver_t *this_gen) { return 0; @@ -542,6 +795,20 @@ static void mmal_dispose (vo_driver_t * this_gen) { mmal_driver_t *this = (mmal_driver_t*) this_gen; + if (this->dispmanx_handle) { + + if (this->overlays) { + this->overlay_update = vc_dispmanx_update_start(10); + close_overlays(this, this->overlays); + this->overlays = NULL; + vc_dispmanx_update_submit_sync(this->overlay_update); + this->overlay_update = 0; + } + + vc_dispmanx_display_close(this->dispmanx_handle); + this->dispmanx_handle = DISPMANX_NO_HANDLE; + } + if (this->renderer) { disable_renderer(this); mmal_component_release(this->renderer); @@ -551,7 +818,9 @@ static void mmal_dispose (vo_driver_t * this_gen) { mmal_pool_destroy(this->pool); } +#ifndef HW_OVERLAY _x_alphablend_free(&this->alphablend_extra_data); +#endif pthread_cond_destroy(&this->cond); pthread_mutex_destroy(&this->mutex); @@ -573,7 +842,9 @@ static vo_driver_t *open_plugin (video_driver_class_t *class_gen, const void *vi this->xine = class->xine; +#ifndef HW_OVERLAY _x_alphablend_init(&this->alphablend_extra_data, class->xine); +#endif pthread_mutex_init (&this->mutex, NULL); pthread_cond_init (&this->cond, NULL); @@ -594,12 +865,20 @@ static vo_driver_t *open_plugin (video_driver_class_t *class_gen, const void *vi update_tv_resolution(this); config_display(this, 0, 0, 720, 576); + this->dispmanx_handle = vc_dispmanx_display_open(0); + if (this->dispmanx_handle == DISPMANX_NO_HANDLE) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE": " + "failed to open dispmanx display\n"); + mmal_dispose(&this->vo_driver); + return NULL; + } + 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_begin = mmal_overlay_begin; this->vo_driver.overlay_blend = mmal_overlay_blend; - this->vo_driver.overlay_end = NULL; + this->vo_driver.overlay_end = mmal_overlay_end; this->vo_driver.display_frame = mmal_display_frame; this->vo_driver.get_property = mmal_get_property; this->vo_driver.set_property = mmal_set_property; |