static gboolean gst_imx_blitter_video_transform_set_caps(GstBaseTransform *transform, GstCaps *in, GstCaps *out) { gboolean inout_info_equal; GstVideoInfo in_info, out_info; GstImxBlitterVideoTransform *blitter_video_transform = GST_IMX_BLITTER_VIDEO_TRANSFORM(transform); GstImxBlitterVideoTransformClass *klass = GST_IMX_BLITTER_VIDEO_TRANSFORM_CLASS(G_OBJECT_GET_CLASS(transform)); GstImxCanvas *canvas = &(blitter_video_transform->canvas); GstImxRegion source_subset; g_assert(klass->are_video_infos_equal != NULL); g_assert(blitter_video_transform->blitter != NULL); if (!gst_video_info_from_caps(&in_info, in) || !gst_video_info_from_caps(&out_info, out)) { GST_ERROR_OBJECT(transform, "caps are invalid"); blitter_video_transform->inout_info_set = FALSE; return FALSE; } inout_info_equal = klass->are_video_infos_equal(blitter_video_transform, &in_info, &out_info); if (inout_info_equal) GST_DEBUG_OBJECT(transform, "input and output caps are equal"); else GST_DEBUG_OBJECT(transform, "input and output caps are not equal: input: %" GST_PTR_FORMAT " output: %" GST_PTR_FORMAT, (gpointer)in, (gpointer)out); gst_imx_blitter_set_input_video_info(blitter_video_transform->blitter, &in_info); gst_imx_blitter_set_output_video_info(blitter_video_transform->blitter, &out_info); /* setting new caps changes the canvas, so recalculate it * the recalculation here is done without any input cropping, so set * last_frame_with_cropdata to FALSE, in case subsequent frames do * contain crop metadata */ blitter_video_transform->last_frame_with_cropdata = FALSE; /* the canvas always encompasses the entire output frame */ canvas->outer_region.x1 = 0; canvas->outer_region.y1 = 0; canvas->outer_region.x2 = GST_VIDEO_INFO_WIDTH(&out_info); canvas->outer_region.y2 = GST_VIDEO_INFO_HEIGHT(&out_info); gst_imx_canvas_calculate_inner_region(canvas, &in_info); gst_imx_canvas_clip(canvas, &(canvas->outer_region), &in_info, 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); blitter_video_transform->input_video_info = in_info; blitter_video_transform->output_video_info = out_info; blitter_video_transform->inout_info_equal = inout_info_equal; blitter_video_transform->inout_info_set = TRUE; return TRUE; }
static gboolean gst_imx_blitter_video_transform_acquire_blitter(GstImxBlitterVideoTransform *blitter_video_transform) { /* must be called with lock held */ GstImxBlitterVideoTransformClass *klass = GST_IMX_BLITTER_VIDEO_TRANSFORM_CLASS(G_OBJECT_GET_CLASS(blitter_video_transform)); g_assert(blitter_video_transform != NULL); g_assert(klass->create_blitter != NULL); /* Do nothing if the blitter is already acquired */ if (blitter_video_transform->blitter != NULL) return TRUE; if ((blitter_video_transform->blitter = klass->create_blitter(blitter_video_transform)) == NULL) { GST_ERROR_OBJECT(blitter_video_transform, "could not acquire blitter"); return FALSE; } return TRUE; }
static gboolean gst_imx_blitter_video_transform_set_caps(GstBaseTransform *transform, GstCaps *in, GstCaps *out) { gboolean inout_info_equal; GstVideoInfo in_info, out_info; 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_video_infos_equal != NULL); g_assert(blitter_video_transform->blitter != NULL); if (!gst_video_info_from_caps(&in_info, in) || !gst_video_info_from_caps(&out_info, out)) { GST_ERROR_OBJECT(transform, "caps are invalid"); blitter_video_transform->inout_info_set = FALSE; return FALSE; } inout_info_equal = klass->are_video_infos_equal(blitter_video_transform, &in_info, &out_info); if (inout_info_equal) GST_DEBUG_OBJECT(transform, "input and output caps are equal"); else GST_DEBUG_OBJECT(transform, "input and output caps are not equal: input: %" GST_PTR_FORMAT " output: %" GST_PTR_FORMAT, (gpointer)in, (gpointer)out); if (!gst_imx_base_blitter_set_input_video_info(blitter_video_transform->blitter, &in_info)) { GST_ERROR_OBJECT(transform, "could not use input caps: %" GST_PTR_FORMAT, (gpointer)in); blitter_video_transform->inout_info_set = FALSE; return FALSE; } blitter_video_transform->input_video_info = in_info; blitter_video_transform->output_video_info = out_info; blitter_video_transform->inout_info_equal = inout_info_equal; blitter_video_transform->inout_info_set = TRUE; return TRUE; }
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); }