static void gst_imx_blitter_compositor_class_init(GstImxBlitterCompositorClass *klass)
{
	GObjectClass *object_class;
	GstImxVideoCompositorClass *parent_class;
	GstElementClass *element_class;

	GST_DEBUG_CATEGORY_INIT(imx_blitter_compositor_debug, "imxblittercompositor", 0, "Freescale i.MX blitter compositor base class");

	object_class = G_OBJECT_CLASS(klass);
	parent_class = GST_IMX_VIDEO_COMPOSITOR_CLASS(klass);
	element_class = GST_ELEMENT_CLASS(klass);

	object_class->dispose = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_dispose);

	element_class->change_state = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_change_state);

	parent_class->get_phys_mem_allocator = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_get_phys_mem_allocator);
	parent_class->set_output_frame       = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_set_output_frame);
	parent_class->set_output_video_info  = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_set_output_video_info);
	parent_class->fill_region            = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_fill_region);
	parent_class->draw_frame             = GST_DEBUG_FUNCPTR(gst_imx_blitter_compositor_draw_frame);

	klass->start          = NULL;
	klass->stop           = NULL;
	klass->create_blitter = NULL;
}
Exemple #2
0
static GstBufferPool* gst_imx_video_compositor_create_bufferpool(GstImxVideoCompositor *compositor, GstCaps *caps, guint size, guint min_buffers, guint max_buffers, GstAllocator *allocator, GstAllocationParams *alloc_params)
{
	GstBufferPool *pool;
	GstStructure *config;
	GstImxVideoCompositorClass *klass;

	g_assert(compositor != NULL);
	klass = GST_IMX_VIDEO_COMPOSITOR_CLASS(G_OBJECT_GET_CLASS(compositor));

	g_assert(klass->get_phys_mem_allocator != NULL);

	if (size == 0)
	{
		GstVideoInfo info;
		if (!gst_video_info_from_caps(&info, caps))
		{
			GST_ERROR_OBJECT(compositor, "could not parse caps for dma bufferpool");
			return NULL;
		}
	}

	pool = gst_imx_phys_mem_buffer_pool_new(FALSE);

	config = gst_buffer_pool_get_config(pool);
	gst_buffer_pool_config_set_params(config, caps, size, min_buffers, max_buffers);

	/* If the allocator value is NULL, get an allocator
	 * it is unref'd by the buffer pool when it is unref'd */
	if (allocator == NULL)
		allocator = klass->get_phys_mem_allocator(compositor);
	if (allocator == NULL)
	{
		GST_ERROR_OBJECT(compositor, "could not create physical memory bufferpool allocator");
		return NULL;
	}

	gst_buffer_pool_config_set_allocator(config, allocator, alloc_params);
	gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_IMX_PHYS_MEM);
	gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META);
	gst_buffer_pool_set_config(pool, config);

	gst_object_unref(allocator);

	return pool;
}
Exemple #3
0
static gboolean gst_imx_video_compositor_negotiated_caps(GstImxBPVideoAggregator *videoaggregator, GstCaps *caps)
{
	GstVideoInfo info;
	GstImxVideoCompositor *compositor = GST_IMX_VIDEO_COMPOSITOR(videoaggregator);

	/* Output caps have been negotiated. Set up a suitable DMA buffer pool
	 * (cleaning up any old buffer pool first) and inform subclass about
	 * the new output caps. */

	if (!gst_video_info_from_caps(&info, caps))
	{
		GST_ERROR_OBJECT(compositor, "could not get video info from negotiated caps");
		return FALSE;
	}

	/* Get the new overall width/height from video info */
	compositor->overall_width = GST_VIDEO_INFO_WIDTH(&info);
	compositor->overall_height = GST_VIDEO_INFO_HEIGHT(&info);

	GST_DEBUG_OBJECT(videoaggregator, "negotiated width/height: %u/%u", compositor->overall_width, compositor->overall_height);

	/* Update the overall region based on the new overall width/height */
	gst_imx_video_compositor_update_overall_region(compositor);

	/* Cleanup old buffer pool */
	if (compositor->dma_bufferpool != NULL)
		gst_object_unref(GST_OBJECT(compositor->dma_bufferpool));

	/* And get the new one */
	compositor->dma_bufferpool = gst_imx_video_compositor_create_bufferpool(compositor, caps, 0, 0, 0, NULL, NULL);

	if (compositor->dma_bufferpool != NULL)
	{
		GstImxVideoCompositorClass *klass = GST_IMX_VIDEO_COMPOSITOR_CLASS(G_OBJECT_GET_CLASS(compositor));

		/* Inform subclass about the new output video info */
		if (klass->set_output_video_info != NULL)
			return klass->set_output_video_info(compositor, &info);
		else
			return TRUE;
	}
	else
		return FALSE;
}
Exemple #4
0
static GstFlowReturn gst_imx_video_compositor_aggregate_frames(GstImxBPVideoAggregator *videoaggregator, GstBuffer *outbuffer)
{
	GstFlowReturn ret = GST_FLOW_OK;
	GList *walk;
	GstImxVideoCompositor *compositor = GST_IMX_VIDEO_COMPOSITOR(videoaggregator);
	GstImxVideoCompositorClass *klass = GST_IMX_VIDEO_COMPOSITOR_CLASS(G_OBJECT_GET_CLASS(videoaggregator));

	g_assert(klass->set_output_frame != NULL);
	g_assert(klass->fill_region != NULL);
	g_assert(klass->draw_frame != NULL);

	/* This function is the heart of the compositor. Here, input frames
	 * are drawn on the output frame, with their specific parameters. */

	/* Set the output buffer */
	if (!(klass->set_output_frame(compositor, outbuffer)))
	{
		GST_ERROR_OBJECT(compositor, "could not set the output frame");
		return GST_FLOW_ERROR;
	}

	/* TODO: are the update_overall_region calls here necessary?
	 * If the video aggregator calls update_caps when a pad is added/removed,
	 * there is no need for these calls */

	/* Update the overall region first if necessary to ensure that it is valid
	 * and that the region_fill_necessary flag is set to the proper value */
	gst_imx_video_compositor_update_overall_region(compositor);

	GST_LOG_OBJECT(compositor, "aggregating frames, region_fill_necessary: %d", (gint)(compositor->region_fill_necessary));

	/* Check if the overall region needs to be filled. This is the case if none
	 * of the input frames completely cover the overall region with 100% alpha
	 * (this is determined by gst_imx_video_compositor_update_overall_region() ) */
	if (!(compositor->region_fill_necessary) || klass->fill_region(compositor, &(compositor->overall_region), compositor->background_color))
	{
		/* Lock object to ensure nothing is changed during composition */
		GST_OBJECT_LOCK(compositor);

		/* First walk: check if there is a new pad. If so, recompute the
		 * overall region, since it might need to be expanded to encompass
		 * the new additional input frames */
		walk = GST_ELEMENT(videoaggregator)->sinkpads;
		while (walk != NULL)
		{
			GstImxVideoCompositorPad *compositor_pad = GST_IMX_VIDEO_COMPOSITOR_PAD_CAST(walk->data);

			if (compositor_pad->pad_is_new)
			{
				GST_DEBUG_OBJECT(compositor, "there is a new pad; invalidate overall region");

				compositor_pad->pad_is_new = FALSE;
				compositor->overall_region_valid = FALSE;

				/* While this call might seem redundant, there is one
				 * benefit in calling this function apparently twice
				 * (once above, and once here): the earlier call
				 * happens outside of the object lock. New pads are less
				 * common than overall region changes, so it is good
				 * if most update calls happen outside of the object
				 * lock (the overall_region_valid flag ensures redundant
				 * calls don't compute anything). */
				gst_imx_video_compositor_update_overall_region(compositor);

				break;
			}

			/* Move to next pad */
			walk = g_list_next(walk);
		}

		/* Second walk: draw the input frames on the output frame */
		walk = GST_ELEMENT(videoaggregator)->sinkpads;
		while (walk != NULL)
		{
			GstImxBPVideoAggregatorPad *videoaggregator_pad = walk->data;
			GstImxVideoCompositorPad *compositor_pad = GST_IMX_VIDEO_COMPOSITOR_PAD_CAST(videoaggregator_pad);

			/* If there actually is a buffer, draw it
			 * Sometimes, pads don't deliver data right from the start;
			 * in these cases, their buffers will be NULL
			 * Just skip to the next pad in that case */
			if (videoaggregator_pad->buffer != NULL)
			{
				GstVideoCropMeta *video_crop_meta;
				if (compositor_pad->input_crop && ((video_crop_meta = gst_buffer_get_video_crop_meta(videoaggregator_pad->buffer)) != NULL))
				{
					/* Crop metadata present. Reconfigure canvas. */

					GstVideoInfo *info = &(videoaggregator_pad->info);

					GstImxRegion source_region;
					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;

					/* 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(GST_VIDEO_INFO_WIDTH(info), source_region.x2);
					source_region.y2 = MIN(GST_VIDEO_INFO_HEIGHT(info), source_region.y2);

					GST_LOG_OBJECT(compositor, "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 (!(compositor_pad->last_frame_with_cropdata) || !gst_imx_region_equal(&source_region, &(compositor_pad->last_source_region)))
					{
						GST_LOG_OBJECT(compositor, "using new crop rectangle %" GST_IMX_REGION_FORMAT, GST_IMX_REGION_ARGS(&source_region));
						compositor_pad->last_source_region = source_region;
						compositor_pad->canvas_needs_update = TRUE;
					}

					compositor_pad->last_frame_with_cropdata = TRUE;

					/* Update canvas and input region if necessary */
					if (compositor_pad->canvas_needs_update)
						gst_imx_video_compositor_pad_update_canvas(compositor_pad, &(compositor_pad->last_source_region));
				}
				else
				{
					/* Force an update if this frame has no crop metadata but the last one did */
					if (compositor_pad->last_frame_with_cropdata)
						compositor_pad->canvas_needs_update = TRUE;
					compositor_pad->last_frame_with_cropdata = FALSE;

					/* Update the pad's canvas if necessary,
					 * to ensure there is a valid canvas to draw to */
					gst_imx_video_compositor_pad_update_canvas(compositor_pad, NULL);
				}

				GST_LOG_OBJECT(
					compositor,
					"pad %p  frame %p  format: %s  width/height: %d/%d  regions: outer %" GST_IMX_REGION_FORMAT "  inner %" GST_IMX_REGION_FORMAT "  source subset %" GST_IMX_REGION_FORMAT,
					(gpointer)(videoaggregator_pad),
					(gpointer)(videoaggregator_pad->buffer),
					gst_video_format_to_string(GST_VIDEO_INFO_FORMAT(&(videoaggregator_pad->info))),
					GST_VIDEO_INFO_WIDTH(&(videoaggregator_pad->info)),
					GST_VIDEO_INFO_HEIGHT(&(videoaggregator_pad->info)),
					GST_IMX_REGION_ARGS(&(compositor_pad->canvas.outer_region)),
					GST_IMX_REGION_ARGS(&(compositor_pad->canvas.inner_region)),
					GST_IMX_REGION_ARGS(&(compositor_pad->source_subset))
				);

				if (!klass->draw_frame(
					compositor,
					&(videoaggregator_pad->info),
					&(compositor_pad->source_subset),
					&(compositor_pad->canvas),
					&videoaggregator_pad->buffer,
					(guint8)(compositor_pad->alpha * 255.0)
				))
				{
					GST_ERROR_OBJECT(compositor, "error while drawing composition frame");
					ret = GST_FLOW_ERROR;
					break;
				}
			}
			else
			{
				GST_LOG_OBJECT(compositor, "pad %p  buffer is NULL, no frame to aggregate - skipping to next pad", (gpointer)(videoaggregator_pad));
			}

			/* Move to next pad */
			walk = g_list_next(walk);
		}

		GST_OBJECT_UNLOCK(compositor);
	}

	/* Release the output buffer, since we don't need it anymore, and
	 * there is no reason to retain it */
	klass->set_output_frame(compositor, NULL);

	return ret;
}