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; }
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; }
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; }
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; }