/* GStreamer am7xxx sink plugin * Copyright (C) 2013 Antonio Ospite * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstam7xxxsink.h" /* Debugging category */ GST_DEBUG_CATEGORY_STATIC (am7xxxsink_debug); #define GST_CAT_DEFAULT am7xxxsink_debug enum { ARG_0, ARG_DEVICE_INDEX }; #if 0 static void gst_am7xxxsink_get_times (GstBaseSink * basesink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end); #endif static GstFlowReturn gst_am7xxxsink_show_frame (GstVideoSink * videosink, GstBuffer * buff); static gboolean gst_am7xxxsink_start (GstBaseSink * bsink); static gboolean gst_am7xxxsink_stop (GstBaseSink * bsink); static GstCaps *gst_am7xxxsink_getcaps (GstBaseSink * bsink, GstCaps * filter); static gboolean gst_am7xxxsink_setcaps (GstBaseSink * bsink, GstCaps * caps); static void gst_am7xxxsink_finalize (GObject * object); static void gst_am7xxxsink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_am7xxxsink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstStateChangeReturn gst_am7xxxsink_change_state (GstElement * element, GstStateChange transition); #define RAW_VIDEO_CAPS "{ NV12 }" static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("image/jpeg;" GST_VIDEO_CAPS_MAKE (RAW_VIDEO_CAPS)) ); #define parent_class gst_am7xxxsink_parent_class G_DEFINE_TYPE (GstAM7XXXSink, gst_am7xxxsink, GST_TYPE_VIDEO_SINK); static void gst_am7xxxsink_init (GstAM7XXXSink * am7xxxsink) { /* nothing to do here yet */ } #if 0 static void gst_am7xxxsink_get_times (GstBaseSink * basesink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end) { GstAM7XXXSink *am7xxxsink; am7xxxsink = GST_AM7XXXSINK (basesink); if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { *start = GST_BUFFER_TIMESTAMP (buffer); if (GST_BUFFER_DURATION_IS_VALID (buffer)) { *end = *start + GST_BUFFER_DURATION (buffer); } else { if (am7xxxsink->fps_n > 0) { *end = *start + gst_util_uint64_scale_int (GST_SECOND, am7xxxsink->fps_d, am7xxxsink->fps_n); } } } } #endif static GstCaps * gst_am7xxxsink_getcaps (GstBaseSink * bsink, GstCaps * filter) { GstAM7XXXSink *am7xxxsink; GstCaps *caps; am7xxxsink = GST_AM7XXXSINK (bsink); caps = gst_static_pad_template_get_caps (&sink_template); if (!am7xxxsink->dev) goto done; /* display size negotiation */ caps = gst_caps_make_writable (caps); gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, GST_VIDEO_SINK_WIDTH(am7xxxsink), "height", GST_TYPE_INT_RANGE, 1, GST_VIDEO_SINK_HEIGHT(am7xxxsink), "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL); done: if (filter != NULL) { GstCaps *icaps; icaps = gst_caps_intersect (caps, filter); gst_caps_unref (caps); caps = icaps; } return caps; } static gboolean gst_am7xxxsink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist) { GstAM7XXXSink *am7xxxsink; GstStructure *structure; const gchar *mimetype; GstVideoInfo vinfo; int ret; am7xxxsink = GST_AM7XXXSINK (bsink); ret = gst_video_info_from_caps (&vinfo, vscapslist); if (!ret) goto unknown_format; structure = gst_caps_get_structure (vscapslist, 0); mimetype = gst_structure_get_name (structure); if (g_str_equal (mimetype, "image/jpeg")) { am7xxxsink->format = AM7XXX_IMAGE_FORMAT_JPEG; } else if (g_str_equal (mimetype, "video/x-raw")) { if (GST_VIDEO_INFO_FORMAT (&vinfo) == GST_VIDEO_FORMAT_NV12) { am7xxxsink->format = AM7XXX_IMAGE_FORMAT_NV12; } else { goto unknown_format; } } else { goto unknown_format; } am7xxxsink->width = GST_VIDEO_INFO_WIDTH(&vinfo); am7xxxsink->height = GST_VIDEO_INFO_HEIGHT(&vinfo); return TRUE; unknown_format: { GST_WARNING_OBJECT (am7xxxsink, "Could not figure format of input caps"); return FALSE; } } static GstFlowReturn gst_am7xxxsink_show_frame (GstVideoSink * videosink, GstBuffer * buf) { GstAM7XXXSink *am7xxxsink; GstMapInfo map; int ret; am7xxxsink = GST_AM7XXXSINK (videosink); ret = gst_buffer_map (buf, &map, GST_MAP_READ); if (!ret) return GST_FLOW_ERROR; ret = am7xxx_send_image_async(am7xxxsink->dev, am7xxxsink->format, am7xxxsink->width, am7xxxsink->height, map.data, map.size); if (ret < 0) { ret = GST_FLOW_ERROR; goto out; } ret = GST_FLOW_OK; out: gst_buffer_unmap (buf, &map); return ret; } static gboolean gst_am7xxxsink_start (GstBaseSink * bsink) { GstAM7XXXSink *am7xxxsink; int ret; am7xxxsink = GST_AM7XXXSINK (bsink); ret = am7xxx_init(&am7xxxsink->ctx); if (ret < 0) return FALSE; ret = am7xxx_open_device(am7xxxsink->ctx, &am7xxxsink->dev, am7xxxsink->device_index); if (ret < 0) return FALSE; ret = am7xxx_get_device_info(am7xxxsink->dev, &am7xxxsink->device_info); if (ret < 0) return FALSE; GST_VIDEO_SINK_WIDTH (am7xxxsink) = (am7xxxsink->device_info).native_width; GST_VIDEO_SINK_HEIGHT (am7xxxsink) = (am7xxxsink->device_info).native_height; return TRUE; } static gboolean gst_am7xxxsink_stop (GstBaseSink * bsink) { GstAM7XXXSink *am7xxxsink; am7xxxsink = GST_AM7XXXSINK (bsink); /* open devices will be closed by am7xxx_shutdown() */ am7xxx_shutdown(am7xxxsink->ctx); return TRUE; } static void gst_am7xxxsink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstAM7XXXSink *am7xxxsink; am7xxxsink = GST_AM7XXXSINK (object); switch (prop_id) { case ARG_DEVICE_INDEX:{ am7xxxsink->device_index = g_value_get_uint (value); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_am7xxxsink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstAM7XXXSink *am7xxxsink; am7xxxsink = GST_AM7XXXSINK (object); switch (prop_id) { case ARG_DEVICE_INDEX:{ g_value_set_uint (value, am7xxxsink->device_index); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GstStateChangeReturn gst_am7xxxsink_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; g_return_val_if_fail (GST_IS_AM7XXXSINK (element), GST_STATE_CHANGE_FAILURE); ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { default: break; } return ret; } static gboolean plugin_init (GstPlugin * plugin) { if (!gst_element_register (plugin, "am7xxxsink", GST_RANK_NONE, GST_TYPE_AM7XXXSINK)) return FALSE; GST_DEBUG_CATEGORY_INIT (am7xxxsink_debug, "am7xxxsink", 0, "am7xxx video sink element"); return TRUE; } static void gst_am7xxxsink_class_init (GstAM7XXXSinkClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseSinkClass *basesink_class; GstVideoSinkClass *videosink_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; basesink_class = (GstBaseSinkClass *) klass; videosink_class = (GstVideoSinkClass *) klass; gobject_class->set_property = gst_am7xxxsink_set_property; gobject_class->get_property = gst_am7xxxsink_get_property; gobject_class->finalize = gst_am7xxxsink_finalize; gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_am7xxxsink_change_state); g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE_INDEX, g_param_spec_uint ("device-index", "device index", "The am7xxx device index eg: 0", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_am7xxxsink_setcaps); basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_am7xxxsink_getcaps); #if 0 basesink_class->get_times = GST_DEBUG_FUNCPTR (gst_am7xxxsink_get_times); #endif basesink_class->start = GST_DEBUG_FUNCPTR (gst_am7xxxsink_start); basesink_class->stop = GST_DEBUG_FUNCPTR (gst_am7xxxsink_stop); videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_am7xxxsink_show_frame); gst_element_class_set_static_metadata (gstelement_class, "am7xxx video sink", "Sink/Video", "A videosink for projectors supported by libam7xxx", "Antonio Ospite "); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sink_template)); } static void gst_am7xxxsink_finalize (GObject * object) { G_OBJECT_CLASS (parent_class)->finalize (object); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, am7xxxsink, "libam7xxx video sink", plugin_init, VERSION, "LGPL", "gst-am7xxxsink", "http://ao2.it")