static GstFlowReturn gst_imx_blitter_video_transform_transform_frame(GstBaseTransform *transform, GstBuffer *in, GstBuffer *out)
{
	gboolean ret;
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(transform);

	g_assert(blitter_video_transform->blitter != NULL);

	if (!blitter_video_transform->inout_info_set)
	{
		GST_ELEMENT_ERROR(transform, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
		return GST_FLOW_NOT_NEGOTIATED;
	}

	if (in == out)
	{
		GST_LOG_OBJECT(transform, "passing buffer through");
		return GST_FLOW_OK;
	}

	GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

	ret = TRUE;
	ret = ret && gst_imx_base_blitter_set_input_buffer(blitter_video_transform->blitter, in);
	ret = ret && gst_imx_base_blitter_set_output_buffer(blitter_video_transform->blitter, out);
	ret = ret && gst_imx_base_blitter_blit(blitter_video_transform->blitter);

	GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

	return ret ? GST_FLOW_OK : GST_FLOW_ERROR;
}
static gboolean gst_imx_blitter_video_transform_sink_event(GstBaseTransform *transform, GstEvent *event)
{
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(transform);

	switch (GST_EVENT_TYPE(event))
	{
		case GST_EVENT_FLUSH_STOP:
		{
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);
			if (blitter_video_transform->blitter != NULL)
			{
				if (!gst_imx_base_blitter_flush(blitter_video_transform->blitter))
					GST_WARNING_OBJECT(transform, "could not flush blitter");
			}
			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

			break;
		}

		default:
			break;
	}

	return GST_BASE_TRANSFORM_CLASS(gst_imx_blitter_video_transform_parent_class)->sink_event(transform, event);
}
Beispiel #3
0
static void gst_imx_pxp_video_transform_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
	GstImxPxPVideoTransform *pxp_video_transform = GST_IMX_PXP_VIDEO_TRANSFORM(object);

	switch (prop_id)
	{
		case PROP_OUTPUT_ROTATION:
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(pxp_video_transform);
			g_value_set_enum(value, pxp_video_transform->output_rotation);
			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(pxp_video_transform);
			break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
			break;
	}
}
static void gst_imx_blitter_video_transform_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(object);

	switch (prop_id)
	{
		case PROP_INPUT_CROP:
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);
			g_value_set_boolean(value, blitter_video_transform->input_crop);
			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);
			break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
			break;
	}
}
static GstFlowReturn gst_imx_blitter_video_transform_prepare_output_buffer(GstBaseTransform *transform, GstBuffer *input, GstBuffer **outbuf)
{
	gboolean passthrough = FALSE;
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(transform);
	GstImxBlitterVideoTransformClass *klass = GST_IMX_BLITTER_VIDEO_TRANSFORM_CLASS(G_OBJECT_GET_CLASS(transform));

	g_assert(klass->are_transforms_necessary != NULL);

	GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

	/* Test if passthrough should be enabled */
	if ((input != NULL) && blitter_video_transform->inout_info_equal)
	{
		/* if there is an input buffer and the input/output caps are equal,
		 * assume passthrough should be used, and test for exceptions where
		 * passthrough must not be enabled; such exceptions are transforms
		 * like rotation, deinterlacing ... these are defined by the
		 * derived video transform class */
		passthrough = !(klass->are_transforms_necessary(blitter_video_transform, input));
	}
	else if (!blitter_video_transform->inout_info_equal)
		GST_LOG_OBJECT(transform, "input and output caps are not equal");
	else if (input == NULL)
		GST_LOG_OBJECT(transform, "input buffer is NULL");

	GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

	GST_LOG_OBJECT(transform, "passthrough: %s", passthrough ? "yes" : "no");

	if (passthrough)
	{
		*outbuf = input;
		return GST_FLOW_OK;
	}
	else
		return GST_BASE_TRANSFORM_CLASS(gst_imx_blitter_video_transform_parent_class)->prepare_output_buffer(transform, input, outbuf);
}
static GstStateChangeReturn gst_imx_blitter_video_transform_change_state(GstElement *element, GstStateChange transition)
{
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(element);
	GstImxBlitterVideoTransformClass *klass = GST_IMX_BLITTER_VIDEO_TRANSFORM_CLASS(G_OBJECT_GET_CLASS(element));
	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;

	g_assert(blitter_video_transform != NULL);
	g_assert(klass->start != NULL);

	switch (transition)
	{
		case GST_STATE_CHANGE_NULL_TO_READY:
		{
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

			blitter_video_transform->initialized = TRUE;

			if (!(klass->start(blitter_video_transform)))
			{
				GST_ERROR_OBJECT(blitter_video_transform, "start() failed");
				blitter_video_transform->initialized = FALSE;
				GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);
				return GST_STATE_CHANGE_FAILURE;
			}

			/* start() must call gst_imx_blitter_video_transform_set_blitter(),
			 * otherwise the video transform element cannot function properly */
			g_assert(blitter_video_transform->blitter != NULL);

			gst_imx_base_blitter_enable_crop(blitter_video_transform->blitter, blitter_video_transform->input_crop);

			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

			break;
		}

		default:
			break;
	}

	ret = GST_ELEMENT_CLASS(gst_imx_blitter_video_transform_parent_class)->change_state(element, transition);
	if (ret == GST_STATE_CHANGE_FAILURE)
		return ret;

	switch (transition)
	{
		case GST_STATE_CHANGE_READY_TO_NULL:
		{
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

			blitter_video_transform->initialized = FALSE;

			if ((klass->stop != NULL) && !(klass->stop(blitter_video_transform)))
				GST_ERROR_OBJECT(blitter_video_transform, "stop() failed");

			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

			if (blitter_video_transform->blitter != NULL)
			{
				gst_object_unref(GST_OBJECT(blitter_video_transform->blitter));
				blitter_video_transform->blitter = NULL;
			}

			break;
		}

		default:
			break;
	}

	return ret;
}
static GstStateChangeReturn gst_imx_blitter_video_transform_change_state(GstElement *element, GstStateChange transition)
{
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(element);
	GstImxBlitterVideoTransformClass *klass = GST_IMX_BLITTER_VIDEO_TRANSFORM_CLASS(G_OBJECT_GET_CLASS(element));
	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;

	g_assert(blitter_video_transform != NULL);

	switch (transition)
	{
		case GST_STATE_CHANGE_NULL_TO_READY:
		{
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

			blitter_video_transform->initialized = TRUE;

			if ((klass->start != NULL) && !(klass->start(blitter_video_transform)))
			{
				GST_ERROR_OBJECT(blitter_video_transform, "start() failed");
				blitter_video_transform->initialized = FALSE;
				GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);
				return GST_STATE_CHANGE_FAILURE;
			}

			if (!gst_imx_blitter_video_transform_acquire_blitter(blitter_video_transform))
			{
				GST_ERROR_OBJECT(blitter_video_transform, "acquiring blitter failed");
				GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);
				return GST_STATE_CHANGE_FAILURE;
			}

			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

			break;
		}

		default:
			break;
	}

	ret = GST_ELEMENT_CLASS(gst_imx_blitter_video_transform_parent_class)->change_state(element, transition);
	if (ret == GST_STATE_CHANGE_FAILURE)
		return ret;

	switch (transition)
	{
		case GST_STATE_CHANGE_PAUSED_TO_READY:
		{
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);
			blitter_video_transform->last_frame_with_cropdata = FALSE;
			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);
			break;
		}

		case GST_STATE_CHANGE_READY_TO_NULL:
		{
			GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

			blitter_video_transform->initialized = FALSE;

			if ((klass->stop != NULL) && !(klass->stop(blitter_video_transform)))
				GST_ERROR_OBJECT(blitter_video_transform, "stop() failed");

			if (blitter_video_transform->blitter != NULL)
			{
				gst_object_unref(GST_OBJECT(blitter_video_transform->blitter));
				blitter_video_transform->blitter = NULL;
			}

			GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

			break;
		}

		default:
			break;
	}

	return ret;
}
static GstFlowReturn gst_imx_blitter_video_transform_prepare_output_buffer(GstBaseTransform *transform, GstBuffer *input, GstBuffer **outbuf)
{
	gboolean passthrough;
	GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(transform);
	GstImxBlitterVideoTransformClass *klass = GST_IMX_BLITTER_VIDEO_TRANSFORM_CLASS(G_OBJECT_GET_CLASS(transform));
	GstVideoCropMeta *video_crop_meta;
	gboolean update_canvas = FALSE;

	/* If either there is no input buffer or in- and output info are not equal,
	 * it is clear there can be no passthrough mode */
	passthrough = (input != NULL) && blitter_video_transform->inout_info_equal;

	GST_IMX_BLITTER_VIDEO_TRANSFORM_LOCK(blitter_video_transform);

	/* Check if cropping needs to be done */
	if ((input != NULL) && blitter_video_transform->input_crop && ((video_crop_meta = gst_buffer_get_video_crop_meta(input)) != NULL))
	{
		GstImxRegion source_region;
		gint in_width, in_height;

		source_region.x1 = video_crop_meta->x;
		source_region.y1 = video_crop_meta->y;
		source_region.x2 = video_crop_meta->x + video_crop_meta->width;
		source_region.y2 = video_crop_meta->y + video_crop_meta->height;

		in_width = GST_VIDEO_INFO_WIDTH(&(blitter_video_transform->input_video_info));
		in_height = GST_VIDEO_INFO_HEIGHT(&(blitter_video_transform->input_video_info));

		/* Make sure the source region does not exceed valid bounds */
		source_region.x1 = MAX(0, source_region.x1);
		source_region.y1 = MAX(0, source_region.y1);
		source_region.x2 = MIN(in_width, source_region.x2);
		source_region.y2 = MIN(in_height, source_region.y2);

		/* If the crop rectangle encompasses the entire frame, cropping is
		 * effectively a no-op, so make it passthrough in that case,
		 * unless passthrough is already FALSE */
		passthrough = passthrough && (source_region.x1 == 0) && (source_region.y1 == 0) && (source_region.x2 == in_width) && (source_region.y2 == in_height);

		GST_LOG_OBJECT(blitter_video_transform, "retrieved crop rectangle %" GST_IMX_REGION_FORMAT, GST_IMX_REGION_ARGS(&source_region));

		/* Canvas needs to be updated if either one of these applies:
		 * - the current frame has crop metadata, the last one didn't
		 * - the new crop rectangle and the last are different */
		if (!(blitter_video_transform->last_frame_with_cropdata) || !gst_imx_region_equal(&source_region, &(blitter_video_transform->last_source_region)))
		{
			GST_LOG_OBJECT(blitter_video_transform, "using new crop rectangle %" GST_IMX_REGION_FORMAT, GST_IMX_REGION_ARGS(&source_region));
			blitter_video_transform->last_source_region = source_region;
			update_canvas = TRUE;
		}

		blitter_video_transform->last_frame_with_cropdata = TRUE;
	}
	else
	{
		/* Force a canvas update if this frame has no crop metadata but the last one did */
		if (blitter_video_transform->last_frame_with_cropdata)
			update_canvas = TRUE;
		blitter_video_transform->last_frame_with_cropdata = FALSE;
	}

	if (update_canvas)
	{
		GstImxRegion source_subset;
		GstImxCanvas *canvas = &(blitter_video_transform->canvas);

		gst_imx_canvas_clip(
			canvas,
			&(canvas->outer_region),
			&(blitter_video_transform->input_video_info),
			blitter_video_transform->last_frame_with_cropdata ? &(blitter_video_transform->last_source_region) : NULL,
			&source_subset
		);

		gst_imx_blitter_set_input_region(blitter_video_transform->blitter, &source_subset);
		gst_imx_blitter_set_output_canvas(blitter_video_transform->blitter, canvas);
	}

	if ((input != NULL) && passthrough)
	{
		/* test for additional special cases for passthrough must not be enabled
		 * such case are transforms like rotation, deinterlacing ... */
		passthrough = passthrough && (blitter_video_transform->canvas.inner_rotation == GST_IMX_CANVAS_INNER_ROTATION_NONE) &&
		              (klass->are_transforms_necessary != NULL) &&
		              !(klass->are_transforms_necessary(blitter_video_transform, input));
	}
	else if (!blitter_video_transform->inout_info_equal)
		GST_LOG_OBJECT(transform, "input and output caps are not equal");
	else if (blitter_video_transform->last_frame_with_cropdata && !passthrough)
		GST_LOG_OBJECT(transform, "cropping is performed");
	else if (input == NULL)
		GST_LOG_OBJECT(transform, "input buffer is NULL");

	GST_IMX_BLITTER_VIDEO_TRANSFORM_UNLOCK(blitter_video_transform);

	GST_LOG_OBJECT(transform, "passthrough: %s", passthrough ? "yes" : "no");

	if (passthrough)
	{
		/* This instructs the base class to not allocate a new buffer for
		 * the output, and instead pass the input buffer as the output
		 * (this is used in the fransform_frame function below) */
		*outbuf = input;
		return GST_FLOW_OK;
	}
	else
		return GST_BASE_TRANSFORM_CLASS(gst_imx_blitter_video_transform_parent_class)->prepare_output_buffer(transform, input, outbuf);
}