blob: df43b89bc469552a4b7e4e834f0d270c0b9684d4 [file] [log] [blame]
/*
* Copyright (c) 2013-2015, Freescale Semiconductor, Inc. All rights reserved.
* Copyright 2018 NXP
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <string.h>
#include <gst/video/gstvideopool.h>
#include "gstimxv4l2sink.h"
#include "gstimxv4l2allocator.h"
#include "imx_2d_device.h"
#include "gstimxvideooverlay.h"
#include <gst/allocators/gstphymemmeta.h>
#define ALIGNMENT_8 (8)
#define ALIGNMENT_2 (2)
#define ISALIGNED(a, b) (!(a & (b-1)))
#define ALIGNTO(a, b) ((a + (b-1)) & (~(b-1)))
#define MAX_BUFFER (32)
GST_DEBUG_CATEGORY (imxv4l2sink_debug);
#define GST_CAT_DEFAULT imxv4l2sink_debug
#define IMX_V4L2SINK_COMPOMETA_DEFAULT FALSE
enum {
PROP_0,
PROP_DEVICE,
PROP_OVERLAY_TOP,
PROP_OVERLAY_LEFT,
PROP_OVERLAY_WIDTH,
PROP_OVERLAY_HEIGHT,
PROP_CROP_TOP,
PROP_CROP_LEFT,
PROP_CROP_WIDTH,
PROP_CROP_HEIGHT,
PROP_KEEP_VIDEO_RATIO,
PROP_DEINTERLACE_ENABLE,
PROP_DEINTERLACE_MOTION,
PROP_CONFIG,
PROP_COMPOSITION_META_ENABLE,
PROP_VIDEO_DIRECTION,
};
enum {
CONFIG_OVERLAY = 0x1,
CONFIG_CROP = 0x2,
CONFIG_ROTATE = 0x4
};
GST_IMPLEMENT_VIDEO_OVERLAY_METHODS (GstImxV4l2Sink, gst_imx_v4l2sink);
static gboolean v4l2sink_update_video_geo(GstElement * object, GstVideoRectangle win_rect) {
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (object);
if (v4l2sink->overlay.left == win_rect.x && v4l2sink->overlay.top == win_rect.y &&
v4l2sink->overlay.width == win_rect.w && v4l2sink->overlay.height == win_rect.h)
return TRUE;
v4l2sink->overlay.left = win_rect.x;
v4l2sink->overlay.top = win_rect.y;
v4l2sink->overlay.width = win_rect.w;
v4l2sink->overlay.height = win_rect.h;
v4l2sink->config_flag |= CONFIG_OVERLAY;
v4l2sink->config = TRUE;
return TRUE;
}
static void v4l2sink_config_global_alpha(GObject * object, guint alpha)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (object);
if (v4l2sink && v4l2sink->v4l2handle)
gst_imx_v4l2out_config_alpha(v4l2sink->v4l2handle, alpha);
}
static void v4l2sink_config_color_key(GObject * object, gboolean enable, guint color_key)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (object);
if (v4l2sink && v4l2sink->v4l2handle)
gst_imx_v4l2out_config_color_key(v4l2sink->v4l2handle, enable, color_key);
}
static void
gst_imx_v4l2sink_video_direction_interface_init (GstVideoDirectionInterface *
iface)
{
/* We implement the video-direction property */
}
#define gst_imx_v4l2sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstImxV4l2Sink, gst_imx_v4l2sink, GST_TYPE_VIDEO_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
gst_imx_v4l2sink_video_overlay_interface_init);
G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_DIRECTION,
gst_imx_v4l2sink_video_direction_interface_init));
//G_DEFINE_TYPE (GstImxV4l2Sink, gst_imx_v4l2sink, GST_TYPE_VIDEO_SINK);
static void
gst_imx_v4l2sink_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (object);
GST_DEBUG_OBJECT (v4l2sink, "set_property (%d).", prop_id);
switch (prop_id) {
case PROP_DEVICE:
g_free (v4l2sink->device);
v4l2sink->device = g_value_dup_string (value);
break;
case PROP_OVERLAY_TOP:
v4l2sink->overlay.top = g_value_get_int (value);
v4l2sink->config_flag |= CONFIG_OVERLAY;
break;
case PROP_OVERLAY_LEFT:
v4l2sink->overlay.left = g_value_get_int (value);
v4l2sink->config_flag |= CONFIG_OVERLAY;
break;
case PROP_OVERLAY_WIDTH:
v4l2sink->overlay.width = g_value_get_uint (value);
v4l2sink->config_flag |= CONFIG_OVERLAY;
break;
case PROP_OVERLAY_HEIGHT:
v4l2sink->overlay.height = g_value_get_uint (value);
v4l2sink->config_flag |= CONFIG_OVERLAY;
break;
case PROP_CROP_TOP:
v4l2sink->crop.top = g_value_get_uint (value);
v4l2sink->config_flag |= CONFIG_CROP;
break;
case PROP_CROP_LEFT:
v4l2sink->crop.left = g_value_get_uint (value);
v4l2sink->config_flag |= CONFIG_CROP;
break;
case PROP_CROP_WIDTH:
v4l2sink->crop.width = g_value_get_uint (value);
v4l2sink->config_flag |= CONFIG_CROP;
break;
case PROP_CROP_HEIGHT:
v4l2sink->crop.height = g_value_get_uint (value);
v4l2sink->config_flag |= CONFIG_CROP;
break;
case PROP_VIDEO_DIRECTION:
v4l2sink->rotate = g_value_get_enum (value);
v4l2sink->config_flag |= CONFIG_ROTATE;
break;
case PROP_KEEP_VIDEO_RATIO:
v4l2sink->keep_video_ratio = g_value_get_boolean (value);
v4l2sink->config_flag |= CONFIG_OVERLAY;
break;
case PROP_DEINTERLACE_ENABLE:
v4l2sink->do_deinterlace = g_value_get_boolean (value);
break;
case PROP_DEINTERLACE_MOTION:
v4l2sink->deinterlace_motion = g_value_get_uint (value);
break;
case PROP_CONFIG:
v4l2sink->config = g_value_get_boolean (value);
break;
case PROP_COMPOSITION_META_ENABLE:
v4l2sink->composition_meta_enable = g_value_get_boolean(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_imx_v4l2sink_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (object);
switch (prop_id) {
case PROP_DEVICE:
g_value_set_string (value, v4l2sink->device);
break;
case PROP_OVERLAY_TOP:
g_value_set_int (value, v4l2sink->overlay.top);
break;
case PROP_OVERLAY_LEFT:
g_value_set_int (value, v4l2sink->overlay.left);
break;
case PROP_OVERLAY_WIDTH:
g_value_set_uint (value, v4l2sink->overlay.width);
break;
case PROP_OVERLAY_HEIGHT:
g_value_set_uint (value, v4l2sink->overlay.height);
break;
case PROP_CROP_TOP:
g_value_set_uint (value, v4l2sink->crop.top);
break;
case PROP_CROP_LEFT:
g_value_set_uint (value, v4l2sink->crop.left);
break;
case PROP_CROP_WIDTH:
g_value_set_uint (value, v4l2sink->crop.width);
break;
case PROP_CROP_HEIGHT:
g_value_set_uint (value, v4l2sink->crop.height);
break;
case PROP_VIDEO_DIRECTION:
g_value_set_enum (value, v4l2sink->rotate);
break;
case PROP_KEEP_VIDEO_RATIO:
g_value_set_boolean (value, v4l2sink->keep_video_ratio);
break;
case PROP_DEINTERLACE_ENABLE:
g_value_set_boolean (value, v4l2sink->do_deinterlace);
break;
case PROP_DEINTERLACE_MOTION:
g_value_set_uint (value, v4l2sink->deinterlace_motion);
break;
case PROP_CONFIG:
g_value_set_boolean (value, v4l2sink->config);
break;
case PROP_COMPOSITION_META_ENABLE:
g_value_set_boolean(value, v4l2sink->composition_meta_enable);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstStateChangeReturn
gst_imx_v4l2sink_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (element);
GST_DEBUG_OBJECT (v4l2sink, "%d -> %d",
GST_STATE_TRANSITION_CURRENT (transition),
GST_STATE_TRANSITION_NEXT (transition));
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
{
guint w,h;
memset (&v4l2sink->crop, 0, sizeof(IMXV4l2Rect));
memset (&v4l2sink->video_align, 0, sizeof(GstVideoAlignment));
v4l2sink->v4l2handle = gst_imx_v4l2_open_device (v4l2sink->device, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (!v4l2sink->v4l2handle) {
return GST_STATE_CHANGE_FAILURE;
}
gst_imx_v4l2out_get_res (v4l2sink->v4l2handle, &w, &h);
if (v4l2sink->overlay.width == 0) {
v4l2sink->overlay.width = w;
if (v4l2sink->overlay.left > 0)
v4l2sink->overlay.width = w - v4l2sink->overlay.left;
}
if (v4l2sink->overlay.height == 0) {
v4l2sink->overlay.height = h;
if (v4l2sink->overlay.top > 0)
v4l2sink->overlay.height= h - v4l2sink->overlay.top;
}
v4l2sink->config_flag |= CONFIG_OVERLAY;
//need to configure rotate as PXP will store the previous rotate status
v4l2sink->config_flag |= CONFIG_ROTATE;
if ((v4l2sink->config_flag & CONFIG_OVERLAY)
|| (v4l2sink->config_flag & CONFIG_CROP)
|| (v4l2sink->config_flag & CONFIG_ROTATE)) {
v4l2sink->config = TRUE;
}
}
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
if (v4l2sink->do_deinterlace) {
gst_imx_v4l2_config_deinterlace (v4l2sink->v4l2handle,
v4l2sink->do_deinterlace, v4l2sink->deinterlace_motion);
}
v4l2sink->min_buffers =
gst_imx_v4l2_get_min_buffer_num(v4l2sink->v4l2handle,
V4L2_BUF_TYPE_VIDEO_OUTPUT);
gst_imx_video_overlay_start (v4l2sink->imxoverlay);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
v4l2sink->run_time = gst_element_get_start_time (GST_ELEMENT (v4l2sink));
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (v4l2sink->v4l2handle) {
gst_imx_video_overlay_stop (v4l2sink->imxoverlay);
if (gst_imx_v4l2_reset_device (v4l2sink->v4l2handle) < 0) {
return GST_STATE_CHANGE_FAILURE;
}
if (v4l2sink->pool) {
// only deactivate pool if pool activated by own, up stream element
// may still using it
if (v4l2sink->pool_activated) {
gst_buffer_pool_set_active (v4l2sink->pool, FALSE);
v4l2sink->pool_activated = FALSE;
}
gst_object_unref (v4l2sink->pool);
v4l2sink->pool = NULL;
}
if (v4l2sink->allocator) {
gst_object_unref (v4l2sink->allocator);
v4l2sink->allocator = NULL;
}
g_hash_table_remove_all (v4l2sink->v4l2buffer2buffer_table);
}
break;
case GST_STATE_CHANGE_READY_TO_NULL:
if (v4l2sink->v4l2handle) {
if (gst_imx_v4l2_close_device (v4l2sink->v4l2handle) < 0 ) {
return GST_STATE_CHANGE_FAILURE;
}
v4l2sink->v4l2handle = NULL;
}
{
if (v4l2sink->run_time > 0) {
g_print ("Total showed frames (%lld), playing for (%"GST_TIME_FORMAT"), fps (%.3f).\n",
v4l2sink->frame_showed, GST_TIME_ARGS (v4l2sink->run_time),
(gfloat)GST_SECOND * v4l2sink->frame_showed / v4l2sink->run_time);
}
v4l2sink->frame_showed = 0;
v4l2sink->run_time = 0;
}
break;
default:
break;
}
return ret;
}
static guint
gst_imx_v4l2_special_fmt (GstCaps *caps)
{
return 0;
}
static gint
gst_imx_v4l2sink_configure_input (GstImxV4l2Sink *v4l2sink)
{
IMXV4l2Rect crop;
guint w,h;
w = v4l2sink->w + v4l2sink->video_align.padding_left + v4l2sink->video_align.padding_right;
h = v4l2sink->h + v4l2sink->video_align.padding_top + v4l2sink->video_align.padding_bottom;
crop.left = v4l2sink->video_align.padding_left + v4l2sink->crop.left + v4l2sink->cropmeta.x;
crop.top = v4l2sink->video_align.padding_top + v4l2sink->crop.top + v4l2sink->cropmeta.y;
crop.width = MIN (v4l2sink->cropmeta.width, v4l2sink->crop.width);
crop.height = MIN (v4l2sink->cropmeta.height, v4l2sink->crop.height);
GST_DEBUG_OBJECT (v4l2sink, "crop: (%d,%d) -> (%d, %d)",
v4l2sink->crop.left, v4l2sink->crop.top, v4l2sink->crop.width, v4l2sink->crop.height);
GST_DEBUG_OBJECT (v4l2sink, "cropmeta: (%d,%d) -> (%d, %d)",
v4l2sink->cropmeta.x, v4l2sink->cropmeta.y, v4l2sink->cropmeta.width, v4l2sink->cropmeta.height);
GST_DEBUG_OBJECT (v4l2sink, "padding: (%d,%d), (%d, %d)",
v4l2sink->video_align.padding_left, v4l2sink->video_align.padding_top,
v4l2sink->video_align.padding_right, v4l2sink->video_align.padding_bottom);
return gst_imx_v4l2out_config_input (v4l2sink->v4l2handle, v4l2sink->v4l2fmt, w, h, &crop);
}
static gint
gst_imx_v4l2sink_configure_rotate (GstImxV4l2Sink *v4l2sink)
{
gint ret = -1;
if (v4l2sink->rotate < GST_IMX_ROTATION_HFLIP) {
ret = gst_imx_v4l2_config_rotate (v4l2sink->v4l2handle, v4l2sink->rotate * 90);
} else {
guint flip = v4l2sink->rotate > GST_IMX_ROTATION_HFLIP ? V4L2_CID_VFLIP : V4L2_CID_HFLIP;
ret = gst_imx_v4l2_config_flip (v4l2sink->v4l2handle, flip);
}
return ret;
}
static gint
gst_imx_v4l2_allocator_cb (gpointer user_data, gint *count)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (user_data);
guint min, max;
if (v4l2sink->pool) {
GstStructure *config;
config = gst_buffer_pool_get_config (v4l2sink->pool);
// check if has alignment option setted.
// if yes, need to recheck the pool params for reconfigure v4l2 devicec.
if (gst_buffer_pool_config_has_option (config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
memset (&v4l2sink->video_align, 0, sizeof(GstVideoAlignment));
gst_buffer_pool_config_get_video_alignment (config, &v4l2sink->video_align);
GST_DEBUG_OBJECT (v4l2sink, "pool has alignment (%d, %d) , (%d, %d)",
v4l2sink->video_align.padding_left, v4l2sink->video_align.padding_top,
v4l2sink->video_align.padding_right, v4l2sink->video_align.padding_bottom);
}
gst_buffer_pool_config_get_params (config, NULL, NULL, &min, &max);
GST_DEBUG_OBJECT (v4l2sink, "need allocate %d buffers.\n", max);
g_print ("v4l2sink need allocate %d buffers.\n", max);
gst_structure_free(config);
if (gst_imx_v4l2sink_configure_input (v4l2sink) < 0)
return -1;
if (v4l2sink->use_userptr_mode) {
if (gst_imx_v4l2_set_buffer_count (v4l2sink->v4l2handle, MAX_BUFFER, V4L2_MEMORY_USERPTR) < 0)
return -1;
} else {
if (gst_imx_v4l2_set_buffer_count (v4l2sink->v4l2handle, max, V4L2_MEMORY_MMAP) < 0)
return -1;
}
*count = max;
}
else {
GST_ERROR_OBJECT (v4l2sink, "no pool to get buffer count.\n");
return -1;
}
return 0;
}
static gboolean
gst_imx_v4l2sink_buffer_pool_is_ok (GstBufferPool * pool, GstCaps * newcaps,
gint size)
{
GstCaps *oldcaps;
GstStructure *config;
guint bsize;
gboolean ret;
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_get_params (config, &oldcaps, &bsize, NULL, NULL);
ret = (size <= bsize) && gst_caps_is_equal (newcaps, oldcaps);
gst_structure_free (config);
return ret;
}
static gint
gst_imx_v4l2sink_setup_buffer_pool (GstImxV4l2Sink *v4l2sink, GstCaps *caps)
{
GstStructure *structure;
gint size;
GstVideoInfo info;
IMXV4l2AllocatorContext context;
v4l2sink->pool = gst_video_buffer_pool_new ();
if (!v4l2sink->pool) {
GST_ERROR_OBJECT (v4l2sink, "New video buffer pool failed.\n");
return -1;
}
if (!gst_video_info_from_caps (&info, caps)) {
GST_ERROR_OBJECT (v4l2sink, "invalid caps.");
return -1;
}
GST_DEBUG_OBJECT (v4l2sink, "create buffer pool(%p).", v4l2sink->pool);
context.v4l2_handle = v4l2sink->v4l2handle;
context.user_data = (gpointer) v4l2sink;
context.callback = gst_imx_v4l2_allocator_cb;
if (!v4l2sink->allocator)
v4l2sink->allocator = gst_imx_v4l2_allocator_new (&context);
if (!v4l2sink->allocator) {
GST_ERROR_OBJECT (v4l2sink, "New v4l2 allocator failed.\n");
return -1;
}
structure = gst_buffer_pool_get_config (v4l2sink->pool);
gst_buffer_pool_config_add_option (structure, GST_BUFFER_POOL_OPTION_VIDEO_META);
// buffer alignment configuration
gint w = GST_VIDEO_INFO_WIDTH (&info);
gint h = GST_VIDEO_INFO_HEIGHT (&info);
if (!ISALIGNED (w, ALIGNMENT_8) || !ISALIGNED (h, ALIGNMENT_2)) {
GstVideoAlignment alignment;
memset (&alignment, 0, sizeof (GstVideoAlignment));
alignment.padding_right = ALIGNTO (w, ALIGNMENT_8) - w;
alignment.padding_bottom = ALIGNTO (h, ALIGNMENT_2) - h;
GST_DEBUG ("align buffer pool, w(%d) h(%d), padding_right (%d), padding_bottom (%d)",
w, h, alignment.padding_right, alignment.padding_bottom);
gst_buffer_pool_config_add_option (structure, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
gst_buffer_pool_config_set_video_alignment (structure, &alignment);
}
size = (ALIGNTO (w, ALIGNMENT_8)) * (ALIGNTO (h, ALIGNMENT_2)) * gst_imx_v4l2_get_bits_per_pixel (v4l2sink->v4l2fmt) / 8;
gst_buffer_pool_config_set_params (structure, caps, size, v4l2sink->min_buffers, v4l2sink->min_buffers);
gst_buffer_pool_config_set_allocator (structure, v4l2sink->allocator, NULL);
if (!gst_buffer_pool_set_config (v4l2sink->pool, structure)) {
GST_ERROR_OBJECT (v4l2sink, "set buffer pool failed.\n");
return -1;
}
g_hash_table_remove_all (v4l2sink->v4l2buffer2buffer_table);
return 0;
}
static gboolean
gst_imx_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (bsink);
GstVideoInfo info;
guint v4l2fmt;
if (!gst_video_info_from_caps (&info, caps)) {
GST_ERROR_OBJECT (v4l2sink, "invalid caps.");
return FALSE;
}
GST_DEBUG_OBJECT (v4l2sink, "set caps %" GST_PTR_FORMAT, caps);
v4l2fmt = gst_imx_v4l2_fmt_gst2v4l2 (GST_VIDEO_INFO_FORMAT (&info));
if (!v4l2fmt) {
v4l2fmt = gst_imx_v4l2_special_fmt (caps);
}
v4l2sink->v4l2fmt = v4l2fmt;
v4l2sink->w = GST_VIDEO_INFO_WIDTH (&info);
v4l2sink->h = GST_VIDEO_INFO_HEIGHT (&info);
v4l2sink->cropmeta.x = 0;
v4l2sink->cropmeta.y = 0;
v4l2sink->cropmeta.width = v4l2sink->w;
v4l2sink->cropmeta.height = v4l2sink->h;
if (v4l2sink->crop.width <= 0) {
v4l2sink->crop.width = v4l2sink->w;
if (v4l2sink->crop.left > 0)
v4l2sink->crop.width -= v4l2sink->crop.left;
}
if (v4l2sink->crop.height <= 0) {
v4l2sink->crop.height = v4l2sink->h;
if (v4l2sink->crop.top > 0)
v4l2sink->crop.height -= v4l2sink->crop.top;
}
gst_imx_video_overlay_prepare_window_handle (v4l2sink->imxoverlay, TRUE);
return TRUE;
}
static gboolean
gst_imx_v4l2sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (bsink);
guint size = 0;
GstCaps *caps;
gboolean need_pool;
GstVideoInfo info;
gst_query_parse_allocation (query, &caps, &need_pool);
if (caps == NULL) {
GST_ERROR_OBJECT (v4l2sink, "no caps specified.");
return FALSE;
}
if (!gst_video_info_from_caps(&info, caps)) {
GST_ERROR_OBJECT (v4l2sink, "can't get info from caps.");
return FALSE;
}
if (need_pool) {
guint v4l2fmt = gst_imx_v4l2_fmt_gst2v4l2 (GST_VIDEO_INFO_FORMAT (&info));
if (!v4l2fmt) {
v4l2fmt = gst_imx_v4l2_special_fmt (caps);
}
v4l2sink->v4l2fmt = v4l2fmt;
v4l2sink->w = GST_VIDEO_INFO_WIDTH (&info);
v4l2sink->h = GST_VIDEO_INFO_HEIGHT (&info);
v4l2sink->cropmeta.x = 0;
v4l2sink->cropmeta.y = 0;
v4l2sink->cropmeta.width = v4l2sink->w;
v4l2sink->cropmeta.height = v4l2sink->h;
if (v4l2sink->crop.width <= 0) {
v4l2sink->crop.width = v4l2sink->w;
if (v4l2sink->crop.left > 0)
v4l2sink->crop.width -= v4l2sink->crop.left;
}
if (v4l2sink->crop.height <= 0) {
v4l2sink->crop.height = v4l2sink->h;
if (v4l2sink->crop.top > 0)
v4l2sink->crop.height -= v4l2sink->crop.top;
}
if (v4l2sink->pool) {
if (!gst_imx_v4l2sink_buffer_pool_is_ok(v4l2sink->pool, caps, info.size)){
gst_imx_v4l2_reset_device (v4l2sink->v4l2handle);
gst_buffer_pool_set_active (v4l2sink->pool, FALSE);
gst_object_unref (v4l2sink->pool);
v4l2sink->pool = NULL;
}
}
if (!v4l2sink->pool) {
if (gst_imx_v4l2sink_setup_buffer_pool (v4l2sink, caps) < 0)
return FALSE;
}
GST_DEBUG_OBJECT (v4l2sink, "propose_allocation, pool(%p).",v4l2sink->pool);
GstCaps *pcaps;
GstStructure *config;
config = gst_buffer_pool_get_config (v4l2sink->pool);
gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
gst_structure_free (config);
/* we need at least 3 buffers to operate */
gst_query_add_allocation_pool (query, v4l2sink->pool, size, v4l2sink->min_buffers, v4l2sink->min_buffers);
gst_query_add_allocation_param (query, v4l2sink->allocator, NULL);
}
/* we also support various metadata */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
if (v4l2sink->composition_meta_enable && v4l2sink->blend_dev) {
if (imx_video_overlay_composition_is_out_fmt_support(v4l2sink->blend_dev,
info.finfo->format))
imx_video_overlay_composition_add_query_meta (query);
else
g_print("!!!device don't support %s format, can't do in-place "
"blending, will try software blending!!!\n",
gst_video_format_to_string(info.finfo->format));
}
return TRUE;
}
static gboolean
gst_imx_v4l2sink_incrop_changed_and_set (GstVideoCropMeta *src, GstVideoCropMeta *dest)
{
if (src->x != dest->x || src->y != dest->y || src->width != dest->width || src->height != dest->height) {
dest->x = src->x;
dest->y = src->y;
dest->width = src->width;
dest->height = src->height;
return TRUE;
}
return FALSE;
}
static gboolean
remove_list_item (gpointer key, gpointer value, gpointer user_data)
{
return value != user_data;
}
static GstFlowReturn
gst_imx_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buffer)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (bsink);
gboolean not_v4l2buffer = FALSE;
GstVideoCropMeta *cropmeta = NULL;
GstVideoMeta *vmeta = NULL;
GstVideoFrameFlags flags = GST_VIDEO_FRAME_FLAG_NONE;
GstMemory *mem = NULL;
GstBuffer *v4l2_buffer = NULL, *in_buffer = NULL;
cropmeta = gst_buffer_get_video_crop_meta (buffer);
vmeta = gst_buffer_get_video_meta (buffer);
// check if v4l2sink allocated buffer
mem = gst_buffer_get_memory (buffer, 0);
if (mem && mem->allocator != v4l2sink->allocator)
not_v4l2buffer = TRUE;
gst_memory_unref (mem);
GstVideoInfo info;
GstCaps *caps = gst_pad_get_current_caps (GST_VIDEO_SINK_PAD (v4l2sink));
gst_video_info_from_caps (&info, caps);
if (not_v4l2buffer) {
GstVideoFrame frame1, frame2;
GST_DEBUG_OBJECT (v4l2sink, "not v4l2 allocated buffer.");
v4l2sink->use_userptr_mode = FALSE;
if (gst_buffer_is_phymem (buffer) && HAS_IPU()) {
v4l2sink->use_userptr_mode = TRUE;
if (!v4l2sink->pool) {
if (gst_imx_v4l2sink_setup_buffer_pool (v4l2sink, caps) < 0) {
GST_ERROR ("create replace buffer pool failed");
gst_caps_unref (caps);
return GST_FLOW_ERROR;
}
}
} else {
gst_video_frame_map (&frame2, &info, buffer, GST_MAP_READ);
GstCaps *new_caps = gst_video_info_to_caps(&frame2.info);
gst_video_info_from_caps(&info, new_caps); //update the size info
if (v4l2sink->pool) {
if (!gst_imx_v4l2sink_buffer_pool_is_ok(v4l2sink->pool, new_caps,
info.size)) {
gst_imx_v4l2_reset_device (v4l2sink->v4l2handle);
gst_buffer_pool_set_active (v4l2sink->pool, FALSE);
gst_object_unref(v4l2sink->pool);
v4l2sink->pool = NULL;
}
}
if (!v4l2sink->pool) {
if (gst_imx_v4l2sink_setup_buffer_pool (v4l2sink, new_caps) < 0) {
GST_ERROR ("create copy buffer pool failed");
gst_caps_unref (new_caps);
gst_caps_unref (caps);
return GST_FLOW_ERROR;
}
GST_DEBUG_OBJECT(v4l2sink, "creating new input pool");
}
gst_caps_unref (new_caps);
}
GstPhyMemMeta *phymemmeta = NULL;
if (v4l2sink->pool_activated == FALSE) {
phymemmeta = GST_PHY_MEM_META_GET (buffer);
if (phymemmeta) {
v4l2sink->video_align.padding_right = phymemmeta->x_padding;
v4l2sink->video_align.padding_bottom = phymemmeta->y_padding;
GST_DEBUG_OBJECT (v4l2sink,
"physical memory meta x_padding: %d y_padding: %d",
phymemmeta->x_padding, phymemmeta->y_padding);
GstStructure *config = gst_buffer_pool_get_config (v4l2sink->pool);
if (!gst_buffer_pool_config_has_option (config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
}
gint size = (v4l2sink->w + phymemmeta->x_padding) * (v4l2sink->h + phymemmeta->y_padding)
* gst_imx_v4l2_get_bits_per_pixel (v4l2sink->v4l2fmt) / 8;
gst_buffer_pool_config_set_video_alignment (config, &v4l2sink->video_align);
gst_buffer_pool_config_set_params (config, caps, size, v4l2sink->min_buffers, v4l2sink->min_buffers);
gst_buffer_pool_set_config (v4l2sink->pool, config);
} else {
if (vmeta) {
if (vmeta->width > v4l2sink->w)
v4l2sink->video_align.padding_right = vmeta->width - v4l2sink->w;
if (vmeta->height > v4l2sink->h)
v4l2sink->video_align.padding_bottom = vmeta->height - v4l2sink->h;
GST_DEBUG_OBJECT(v4l2sink, "video align right %d, bottom %d",
v4l2sink->video_align.padding_right, v4l2sink->video_align.padding_bottom);
v4l2sink->config = TRUE;
v4l2sink->config_flag |= CONFIG_CROP;
}
}
}
if (gst_buffer_pool_set_active (v4l2sink->pool, TRUE) != TRUE) {
GST_ERROR_OBJECT (v4l2sink, "active pool(%p) failed.", v4l2sink->pool);
gst_caps_unref (caps);
return GST_FLOW_ERROR;
}
gst_buffer_pool_acquire_buffer (v4l2sink->pool, &v4l2_buffer, NULL);
if (!v4l2_buffer) {
GST_ERROR_OBJECT (v4l2sink, "acquire buffer from pool(%p) failed.", v4l2sink->pool);
gst_caps_unref (caps);
return GST_FLOW_ERROR;
}
v4l2sink->pool_activated = TRUE;
if (v4l2sink->use_userptr_mode) {
PhyMemBlock *memblk1, *memblk2;
GST_DEBUG_OBJECT (v4l2sink, "not v4l2 allocated buffer. put physical address");
memblk1 = gst_buffer_query_phymem_block (v4l2_buffer);
memblk2 = gst_buffer_query_phymem_block (buffer);
memblk1->vaddr = memblk2->vaddr;
memblk1->paddr = memblk2->paddr;
memblk1->size = memblk2->size;
in_buffer = buffer;
g_hash_table_replace (v4l2sink->v4l2buffer2buffer_table,
(gpointer)(memblk1->vaddr), (gpointer)(buffer));
} else {
gst_video_frame_map (&frame1, &info, v4l2_buffer, GST_MAP_WRITE);
gst_video_frame_copy (&frame1, &frame2);
gst_video_frame_unmap (&frame1);
gst_video_frame_unmap (&frame2);
}
if (v4l2sink->composition_meta_enable
&& imx_video_overlay_composition_has_meta(buffer)) {
imx_video_overlay_composition_copy_meta(v4l2_buffer, buffer,
info.width, info.height, info.width, info.height);
}
buffer = v4l2_buffer;
}
gst_caps_unref (caps);
if (cropmeta && gst_imx_v4l2sink_incrop_changed_and_set (cropmeta, &v4l2sink->cropmeta)) {
v4l2sink->config = TRUE;
v4l2sink->config_flag |= CONFIG_CROP;
}
if (v4l2sink->config) {
if (v4l2sink->config_flag & CONFIG_CROP) {
if (gst_imx_v4l2sink_configure_input (v4l2sink) < 0) {
GST_ERROR_OBJECT (v4l2sink, "configure input failed.");
return GST_FLOW_ERROR;
}
v4l2sink->config_flag &= ~CONFIG_CROP;
if (v4l2sink->keep_video_ratio) {
//need to recalculate output as input resolution changed
v4l2sink->config_flag |= CONFIG_OVERLAY;
}
}
if (v4l2sink->config_flag & CONFIG_ROTATE) {
if (gst_imx_v4l2sink_configure_rotate (v4l2sink) < 0) {
GST_WARNING_OBJECT (v4l2sink, "configure rotate failed.");
v4l2sink->rotate = v4l2sink->prev_rotate;
} else {
v4l2sink->prev_rotate = v4l2sink->rotate;
if (v4l2sink->keep_video_ratio) {
//need to recalculate output as rotation changed
v4l2sink->config_flag |= CONFIG_OVERLAY;
}
}
v4l2sink->config_flag &= ~CONFIG_ROTATE;
}
if (v4l2sink->config_flag & CONFIG_OVERLAY) {
gint ret = gst_imx_v4l2out_config_output (v4l2sink->v4l2handle,
&v4l2sink->overlay, v4l2sink->keep_video_ratio);
if (ret < 0) {
GST_WARNING_OBJECT (v4l2sink, "configure output failed.");
memcpy(&v4l2sink->overlay, &v4l2sink->prev_overlay, sizeof(IMXV4l2Rect));
} else {
memcpy(&v4l2sink->prev_overlay, &v4l2sink->overlay, sizeof(IMXV4l2Rect));
}
if (ret == 1) {
v4l2sink->invisible = TRUE;
} else {
if (ret == 2) {
g_hash_table_foreach_remove (v4l2sink->v4l2buffer2buffer_table,
remove_list_item, in_buffer);
}
v4l2sink->invisible = FALSE;
}
v4l2sink->config_flag &= ~CONFIG_OVERLAY;
}
v4l2sink->config = FALSE;
}
if (v4l2sink->composition_meta_enable) {
if (imx_video_overlay_composition_has_meta(buffer)) {
VideoCompositionVideoInfo in_v, out_v;
memset (&in_v, 0, sizeof(VideoCompositionVideoInfo));
memset (&out_v, 0, sizeof(VideoCompositionVideoInfo));
in_v.buf = buffer;
in_v.fmt = out_v.fmt = info.finfo->format;
in_v.width = out_v.width = info.width;
in_v.height = out_v.height = info.height;
in_v.stride = out_v.stride = info.stride[0];
in_v.crop_x = out_v.crop_x = v4l2sink->crop.left;
in_v.crop_y = out_v.crop_y = v4l2sink->crop.top;
in_v.crop_w = out_v.crop_w = v4l2sink->crop.width;
in_v.crop_h = out_v.crop_h = v4l2sink->crop.height;
in_v.rotate = out_v.rotate = IMX_2D_ROTATION_0;
out_v.mem = gst_buffer_query_phymem_block(buffer);
memcpy(&out_v.align, &(v4l2sink->video_align), sizeof(GstVideoAlignment));
gint cnt = imx_video_overlay_composition_composite(&v4l2sink->video_comp,
&in_v, &out_v, TRUE);
if (cnt >= 0)
GST_DEBUG ("processed %d video overlay composition buffers", cnt);
else
GST_WARNING ("video overlay composition meta handling failed");
}
}
if (!not_v4l2buffer)
gst_buffer_ref (buffer);
if (vmeta)
flags = vmeta->flags;
if (gst_imx_v4l2_queue_gstbuffer (v4l2sink->v4l2handle, buffer, flags) < 0) {
GST_ERROR_OBJECT (v4l2sink, "Queue buffer %p failed.", buffer);
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
v4l2sink->frame_showed ++;
v4l2_buffer = NULL;
if (gst_imx_v4l2_dequeue_gstbuffer (v4l2sink->v4l2handle, &v4l2_buffer, &flags) < 0) {
GST_ERROR_OBJECT (v4l2sink, "Dequeue buffer failed.");
return GST_FLOW_ERROR;
}
if (v4l2sink->use_userptr_mode) {
if (in_buffer)
gst_buffer_ref (in_buffer);
if (v4l2_buffer) {
PhyMemBlock *memblk;
memblk = gst_buffer_query_phymem_block (v4l2_buffer);
g_hash_table_remove (v4l2sink->v4l2buffer2buffer_table, memblk->vaddr);
GST_DEBUG_OBJECT (v4l2sink, "Reserved count: %d\n.",
g_hash_table_size (v4l2sink->v4l2buffer2buffer_table));
}
if (v4l2sink->invisible)
g_hash_table_remove_all (v4l2sink->v4l2buffer2buffer_table);
}
if (v4l2_buffer)
gst_buffer_unref (v4l2_buffer);
return GST_FLOW_OK;
}
static void
gst_imx_v4l2sink_finalize (GstImxV4l2Sink * v4l2sink)
{
if (v4l2sink->v4l2buffer2buffer_table != NULL) {
g_hash_table_destroy (v4l2sink->v4l2buffer2buffer_table);
v4l2sink->v4l2buffer2buffer_table = NULL;
}
if (v4l2sink->device)
g_free (v4l2sink->device);
if (v4l2sink->imxoverlay) {
gst_imx_video_overlay_finalize (v4l2sink->imxoverlay);
v4l2sink->imxoverlay = NULL;
}
if (v4l2sink->blend_dev) {
imx_video_overlay_composition_deinit(&v4l2sink->video_comp);
v4l2sink->blend_dev->close(v4l2sink->blend_dev);
imx_2d_device_destroy(v4l2sink->blend_dev);
}
G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (v4l2sink));
}
static void
gst_imx_v4l2sink_install_properties (GObjectClass *gobject_class)
{
g_object_class_install_property (gobject_class, PROP_DEVICE,
g_param_spec_string ("device", "Device", "Device location",
gst_imx_v4l2_get_default_device_name(V4L2_BUF_TYPE_VIDEO_OUTPUT), G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_OVERLAY_TOP,
g_param_spec_int ("overlay-top", "Overlay top",
"The topmost (y) coordinate of the video overlay; top left corner of screen is 0,0",
G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_OVERLAY_LEFT,
g_param_spec_int ("overlay-left", "Overlay left",
"The leftmost (x) coordinate of the video overlay; top left corner of screen is 0,0",
G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_OVERLAY_WIDTH,
g_param_spec_uint ("overlay-width", "Overlay width",
"The width of the video overlay; default is equal to screen width",
0, G_MAXUINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_OVERLAY_HEIGHT,
g_param_spec_uint ("overlay-height", "Overlay height",
"The height of the video overlay; default is equal to screen height",
0, G_MAXUINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CROP_TOP,
g_param_spec_uint ("crop-top", "Crop top",
"The topmost (y) coordinate of the video crop; top left corner of image is 0,0",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CROP_LEFT,
g_param_spec_uint ("crop-left", "Crop left",
"The leftmost (x) coordinate of the video crop; top left corner of image is 0,0",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CROP_WIDTH,
g_param_spec_uint ("crop-width", "Crop width",
"The width of the video crop; default is equal to negotiated image width",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CROP_HEIGHT,
g_param_spec_uint ("crop-height", "Crop height",
"The height of the video crop; default is equal to negotiated image height",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_KEEP_VIDEO_RATIO,
g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
FALSE, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CONFIG,
g_param_spec_boolean ("reconfig", "Reconfig",
"Change V4L2 configuration while running; overlay position/size/rotation changed.",
FALSE, G_PARAM_READWRITE));
if (gst_imx_v4l2_support_deinterlace (V4L2_BUF_TYPE_VIDEO_OUTPUT)) {
g_object_class_install_property (gobject_class,PROP_DEINTERLACE_ENABLE,
g_param_spec_boolean ("deinterlace", "deinterlace",
"set deinterlace enabled; can't be configed on fly",
TRUE, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_DEINTERLACE_MOTION,
g_param_spec_uint ("motion",
"set deinterlace motion; can't be configed on fly",
"The interlace motion setting: 0 - low motion, 1 - medium motion, 2 - high motion.",
0, 2, 2, G_PARAM_READWRITE));
}
g_object_class_install_property (gobject_class, PROP_COMPOSITION_META_ENABLE,
g_param_spec_boolean("composition-meta-enable", "Enable composition meta",
"Enable overlay composition meta processing",
TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_override_property (gobject_class, PROP_VIDEO_DIRECTION,
"video-direction");
return;
}
static GstCaps*
gst_imx_v4l2sink_default_caps ()
{
GstCaps *caps;
gint i;
#define CAPS_NUM 8
gchar *caps_str[] = {
(gchar*)GST_VIDEO_CAPS_MAKE("I420"),
(gchar*)GST_VIDEO_CAPS_MAKE("NV12"),
(gchar*)GST_VIDEO_CAPS_MAKE("YV12"),
(gchar*)GST_VIDEO_CAPS_MAKE("Y42B"),
(gchar*)GST_VIDEO_CAPS_MAKE("UYVY"),
(gchar*)GST_VIDEO_CAPS_MAKE("YUY2"),
(gchar*)GST_VIDEO_CAPS_MAKE("RGB16"),
(gchar*)GST_VIDEO_CAPS_MAKE("RGBx")
};
/* make a list of all available caps */
caps = gst_caps_new_empty ();
for(i=0; i<CAPS_NUM; i++) {
GstStructure *structure = gst_structure_from_string(caps_str[i], NULL);
gst_caps_append_structure (caps, structure);
}
return caps;
}
static GstCaps*
gst_imx_v4l2sink_get_all_caps ()
{
GstCaps *caps = NULL;
caps = gst_imx_v4l2_get_device_caps (V4L2_BUF_TYPE_VIDEO_OUTPUT);
if(!caps) {
GST_WARNING ("Can't get caps from default device, use the default setting.");
caps = gst_imx_v4l2sink_default_caps ();
}
caps = gst_caps_simplify(caps);
imx_video_overlay_composition_add_caps(caps);
return caps;
}
static GstCaps *gst_imx_v4l2sink_get_caps (GstBaseSink *sink, GstCaps* filter)
{
GstImxV4l2Sink *v4l2sink = GST_IMX_V4L2SINK (sink);
GstCaps *tmp;
GstCaps *temp_caps = gst_pad_get_pad_template_caps(sink->sinkpad);
GstCaps *caps = gst_caps_copy(temp_caps);
gst_caps_unref(temp_caps);
if (!v4l2sink->composition_meta_enable)
imx_video_overlay_composition_remove_caps(caps);
if (filter) {
tmp = gst_caps_intersect (caps, filter);
gst_caps_unref(caps);
caps = tmp;
}
return caps;
}
//type functions
static void
gst_imx_v4l2sink_class_init (GstImxV4l2SinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
GstBaseSinkClass *basesink_class;
gobject_class = G_OBJECT_CLASS (klass);
element_class = GST_ELEMENT_CLASS (klass);
basesink_class = GST_BASE_SINK_CLASS (klass);
gobject_class->finalize = (GObjectFinalizeFunc) gst_imx_v4l2sink_finalize;
gobject_class->set_property = gst_imx_v4l2sink_set_property;
gobject_class->get_property = gst_imx_v4l2sink_get_property;
element_class->change_state = gst_imx_v4l2sink_change_state;
gst_imx_v4l2sink_install_properties (gobject_class);
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
gst_imx_v4l2sink_get_all_caps ()));
basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_imx_v4l2sink_get_caps);
basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_imx_v4l2sink_set_caps);
basesink_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_imx_v4l2sink_propose_allocation);
basesink_class->render = GST_DEBUG_FUNCPTR (gst_imx_v4l2sink_show_frame);
gst_element_class_set_static_metadata (element_class,
"IMX Video (video4linux2) Sink", "Sink/Video",
"Displays frames on IMX SoC video4linux2 device", IMX_GST_PLUGIN_AUTHOR);
GST_DEBUG_CATEGORY_INIT (imxv4l2sink_debug, "imxv4l2sink", 0, "Freescale IMX V4L2 sink element");
}
static void
gst_imx_v4l2sink_init (GstImxV4l2Sink * v4l2sink)
{
v4l2sink->device = g_strdup (gst_imx_v4l2_get_default_device_name(V4L2_BUF_TYPE_VIDEO_OUTPUT));
v4l2sink->rotate = 0;
v4l2sink->prev_rotate = 0;
v4l2sink->do_deinterlace = TRUE; /* enable deinterlace by default */
v4l2sink->deinterlace_motion = 2; /* high motion by default */
v4l2sink->config = FALSE;
v4l2sink->config_flag = 0;
v4l2sink->v4l2handle = NULL;
v4l2sink->pool = NULL;
v4l2sink->allocator = NULL;
memset (&v4l2sink->overlay, 0, sizeof(IMXV4l2Rect));
memset (&v4l2sink->prev_overlay, 0, sizeof(IMXV4l2Rect));
memset (&v4l2sink->crop, 0, sizeof(IMXV4l2Rect));
v4l2sink->keep_video_ratio = FALSE;
v4l2sink->frame_showed = 0;
v4l2sink->run_time = 0;
v4l2sink->min_buffers = 0;
v4l2sink->pool_activated = FALSE;
v4l2sink->use_userptr_mode = FALSE;
v4l2sink->invisible = FALSE;
v4l2sink->v4l2buffer2buffer_table = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) gst_buffer_unref);
v4l2sink->imxoverlay = gst_imx_video_overlay_init ((GstElement *)v4l2sink,
v4l2sink_update_video_geo,
v4l2sink_config_color_key,
v4l2sink_config_global_alpha);
v4l2sink->composition_meta_enable = IMX_V4L2SINK_COMPOMETA_DEFAULT;
v4l2sink->blend_dev = NULL;
if (HAS_IPU())
v4l2sink->blend_dev = imx_2d_device_create(IMX_2D_DEVICE_IPU);
else if (HAS_PXP())
v4l2sink->blend_dev = imx_2d_device_create(IMX_2D_DEVICE_PXP);
/*
else if (HAS_G2D()) G2D don't support YUV color space
v4l2sink->blend_dev = imx_2d_device_create(IMX_2D_DEVICE_G2D);
*/
if (v4l2sink->blend_dev) {
v4l2sink->blend_dev->open(v4l2sink->blend_dev);
imx_video_overlay_composition_init(&v4l2sink->video_comp,v4l2sink->blend_dev);
}
g_print("====== IMXV4L2SINK: %s build on %s %s. ======\n", (VERSION),__DATE__,__TIME__);
}