static void gst_v4l2sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (object); if (!gst_v4l2_object_get_property_helper (v4l2sink->v4l2object, prop_id, value, pspec)) { switch (prop_id) { case PROP_QUEUE_SIZE: g_value_set_uint (value, v4l2sink->num_buffers); 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_PHYS_BASE: g_value_set_uint (value, v4l2sink->phys_base); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } }
static GstCaps * gst_v4l2sink_get_caps (GstBaseSink * bsink) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); GstCaps *ret; GSList *walk; GSList *formats; if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) { /* FIXME: copy? */ GST_DEBUG_OBJECT (v4l2sink, "device is not open"); return gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (v4l2sink))); } if (v4l2sink->probed_caps) { LOG_CAPS (v4l2sink, v4l2sink->probed_caps); return gst_caps_ref (v4l2sink->probed_caps); } formats = gst_v4l2_object_get_format_list (v4l2sink->v4l2object); ret = gst_caps_new_empty (); for (walk = v4l2sink->v4l2object->formats; walk; walk = walk->next) { struct v4l2_fmtdesc *format; GstStructure *template;
static gboolean gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type) { GstV4l2Object *v4l2object = GST_V4L2SINK (iface)->v4l2object; #ifdef HAVE_XVIDEO g_assert (iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_NAVIGATION || iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION || iface_type == GST_TYPE_TUNER); #else g_assert (iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION || iface_type == GST_TYPE_TUNER); #endif if (v4l2object->video_fd == -1) return FALSE; #ifdef HAVE_XVIDEO if (!GST_V4L2_IS_OVERLAY (v4l2object)) { if (iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_NAVIGATION) return FALSE; } #endif return TRUE; }
static gboolean gst_v4l2sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); GstV4l2Object *obj = v4l2sink->v4l2object; GstBufferPool *pool; guint size = 0; GstCaps *caps; gboolean need_pool; gst_query_parse_allocation (query, &caps, &need_pool); if (caps == NULL) goto no_caps; if ((pool = obj->pool)) gst_object_ref (pool); if (pool != NULL) { GstCaps *pcaps; GstStructure *config; /* we had a pool, check caps */ config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); GST_DEBUG_OBJECT (v4l2sink, "we had a pool with caps %" GST_PTR_FORMAT, pcaps); if (!gst_caps_is_equal (caps, pcaps)) { gst_structure_free (config); gst_object_unref (pool); goto different_caps; } gst_structure_free (config); } /* we need at least 2 buffers to operate */ gst_query_add_allocation_pool (query, pool, size, 2, 0); /* 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 (pool) gst_object_unref (pool); return TRUE; /* ERRORS */ no_caps: { GST_DEBUG_OBJECT (v4l2sink, "no caps specified"); return FALSE; } different_caps: { /* different caps, we can't use this pool */ GST_DEBUG_OBJECT (v4l2sink, "pool has different caps"); return FALSE; } }
static gboolean gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); GstV4l2Object *obj = v4l2sink->v4l2object; LOG_CAPS (v4l2sink, caps); if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) { GST_DEBUG_OBJECT (v4l2sink, "device is not open"); return FALSE; } /* make sure the caps changed before doing anything */ if (gst_v4l2_object_caps_equal (obj, caps)) return TRUE; if (!gst_v4l2_object_stop (obj)) goto stop_failed; if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, caps)) goto invalid_format; gst_v4l2sink_sync_overlay_fields (v4l2sink); gst_v4l2sink_sync_crop_fields (v4l2sink); #ifdef HAVE_XVIDEO gst_v4l2_video_overlay_prepare_window_handle (v4l2sink->v4l2object, TRUE); #endif GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()"); v4l2sink->video_width = GST_V4L2_WIDTH (v4l2sink->v4l2object); v4l2sink->video_height = GST_V4L2_HEIGHT (v4l2sink->v4l2object); /* TODO: videosink width/height should be scaled according to * pixel-aspect-ratio */ GST_VIDEO_SINK_WIDTH (v4l2sink) = v4l2sink->video_width; GST_VIDEO_SINK_HEIGHT (v4l2sink) = v4l2sink->video_height; return TRUE; /* ERRORS */ stop_failed: { GST_DEBUG_OBJECT (v4l2sink, "failed to stop streaming"); return FALSE; } invalid_format: { /* error already posted */ GST_DEBUG_OBJECT (v4l2sink, "can't set format"); return FALSE; } }
static void gst_v4l2sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (object); if (!gst_v4l2_object_set_property_helper (v4l2sink->v4l2object, prop_id, value, pspec)) { switch (prop_id) { case PROP_OVERLAY_TOP: v4l2sink->overlay.top = g_value_get_int (value); v4l2sink->overlay_fields_set |= RECT_TOP_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_OVERLAY_LEFT: v4l2sink->overlay.left = g_value_get_int (value); v4l2sink->overlay_fields_set |= RECT_LEFT_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_OVERLAY_WIDTH: v4l2sink->overlay.width = g_value_get_uint (value); v4l2sink->overlay_fields_set |= RECT_WIDTH_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_OVERLAY_HEIGHT: v4l2sink->overlay.height = g_value_get_uint (value); v4l2sink->overlay_fields_set |= RECT_HEIGHT_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_CROP_TOP: v4l2sink->crop.top = g_value_get_int (value); v4l2sink->crop_fields_set |= RECT_TOP_SET; gst_v4l2sink_sync_crop_fields (v4l2sink); break; case PROP_CROP_LEFT: v4l2sink->crop.left = g_value_get_int (value); v4l2sink->crop_fields_set |= RECT_LEFT_SET; gst_v4l2sink_sync_crop_fields (v4l2sink); break; case PROP_CROP_WIDTH: v4l2sink->crop.width = g_value_get_uint (value); v4l2sink->crop_fields_set |= RECT_WIDTH_SET; gst_v4l2sink_sync_crop_fields (v4l2sink); break; case PROP_CROP_HEIGHT: v4l2sink->crop.height = g_value_get_uint (value); v4l2sink->crop_fields_set |= RECT_HEIGHT_SET; gst_v4l2sink_sync_crop_fields (v4l2sink); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } }
static void gst_v4l2sink_dispose (GObject * object) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (object); if (v4l2sink->probed_caps) { gst_caps_unref (v4l2sink->probed_caps); } G_OBJECT_CLASS (parent_class)->dispose (object); }
/* this is somewhat of a hack.. but better to keep the hack in * one place than copy/pasting it around.. */ static GstV4l2Object * get_v4l2_object (GstElement * v4l2elem) { GstV4l2Object *v4l2object = NULL; if (GST_IS_V4L2SRC (v4l2elem)) { v4l2object = (GST_V4L2SRC (v4l2elem))->v4l2object; } else if (GST_IS_V4L2SINK (v4l2elem)) { v4l2object = (GST_V4L2SINK (v4l2elem))->v4l2object; } else { GST_ERROR_OBJECT (v4l2elem, "unknown v4l2 element"); } return v4l2object; }
static GstCaps * gst_v4l2sink_get_caps (GstBaseSink * bsink, GstCaps * filter) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) { /* FIXME: copy? */ GST_DEBUG_OBJECT (v4l2sink, "device is not open"); return gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (v4l2sink)); } return gst_v4l2_object_get_caps (v4l2sink->v4l2object, filter); }
static GstStateChangeReturn gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstV4l2Sink *v4l2sink = GST_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: /* open the device */ if (!gst_v4l2_object_start (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: if (v4l2sink->state == STATE_STREAMING) { if (!gst_v4l2_object_stop_streaming (v4l2sink->v4l2object)) { return GST_STATE_CHANGE_FAILURE; } v4l2sink->state = STATE_PENDING_STREAMON; } break; case GST_STATE_CHANGE_READY_TO_NULL: if (NULL != v4l2sink->pool) gst_v4l2_buffer_pool_destroy (v4l2sink->pool); v4l2sink->pool = NULL; /* close the device */ if (!gst_v4l2_object_stop (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; v4l2sink->state = STATE_OFF; break; default: break; } return ret; }
static void gst_v4l2sink_navigation_send_event (GstNavigation * navigation, GstStructure * structure) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (navigation); GstV4l2Xv *xv = v4l2sink->v4l2object->xv; GstPad *peer; if (!xv) return; if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (v4l2sink)))) { GstVideoRectangle rect; gdouble x, y, xscale = 1.0, yscale = 1.0; gst_v4l2_video_overlay_get_render_rect (v4l2sink->v4l2object, &rect); /* We calculate scaling using the original video frames geometry to * include pixel aspect ratio scaling. */ xscale = (gdouble) v4l2sink->video_width / rect.w; yscale = (gdouble) v4l2sink->video_height / rect.h; /* Converting pointer coordinates to the non scaled geometry */ if (gst_structure_get_double (structure, "pointer_x", &x)) { x = MIN (x, rect.x + rect.w); x = MAX (x - rect.x, 0); gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, (gdouble) x * xscale, NULL); } if (gst_structure_get_double (structure, "pointer_y", &y)) { y = MIN (y, rect.y + rect.h); y = MAX (y - rect.y, 0); gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, (gdouble) y * yscale, NULL); } gst_pad_send_event (peer, gst_event_new_navigation (structure)); gst_object_unref (peer); } }
static GstStateChangeReturn gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstV4l2Sink *v4l2sink = GST_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: /* open the device */ if (!gst_v4l2_object_open (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: if (!gst_v4l2_object_stop (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; break; case GST_STATE_CHANGE_READY_TO_NULL: /* we need to call stop here too */ if (!gst_v4l2_object_stop (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; /* close the device */ if (!gst_v4l2_object_close (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; break; default: break; } return ret; }
static gboolean gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type) { GstV4l2Object *v4l2object = GST_V4L2SINK (iface)->v4l2object; #if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ g_assert (iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION); #else g_assert (iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION); #endif if (v4l2object->video_fd == -1) return FALSE; #if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2object)) return FALSE; #endif return TRUE; }
/* called after A/V sync to render frame */ static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) { GstFlowReturn ret; GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); GstV4l2Object *obj = v4l2sink->v4l2object; GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf); if (G_UNLIKELY (obj->pool == NULL)) goto not_negotiated; ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL_CAST (obj->pool), buf); return ret; /* ERRORS */ not_negotiated: { GST_ERROR_OBJECT (bsink, "not negotiated"); return GST_FLOW_NOT_NEGOTIATED; } }