1 /* GStreamer am7xxx plugin
 
   2  * Copyright (C) 2007 Sean D'Epagnier <sean@depagnier.com>
 
   4  * This library is free software; you can redistribute it and/or
 
   5  * modify it under the terms of the GNU Library General Public
 
   6  * License as published by the Free Software Foundation; either
 
   7  * version 2 of the License, or (at your option) any later version.
 
   9  * This library is distributed in the hope that it will be useful,
 
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
  12  * Library General Public License for more details.
 
  14  * You should have received a copy of the GNU Library General Public
 
  15  * License along with this library; if not, write to the
 
  16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 
  17  * Boston, MA 02110-1301, USA.
 
  20 /* currently the driver does not switch modes, instead uses current mode.
 
  21    the video is centered and cropped if needed to fit onscreen.
 
  22    Whatever bitdepth is set is used, and tested to work for 16, 24, 32 bits
 
  35 #include <sys/ioctl.h>
 
  40 #include "gstam7xxxsink.h"
 
  49 static void gst_am7xxxsink_get_times (GstBaseSink * basesink,
 
  50     GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
 
  53 static GstFlowReturn gst_am7xxxsink_show_frame (GstVideoSink * videosink,
 
  56 static gboolean gst_am7xxxsink_start (GstBaseSink * bsink);
 
  57 static gboolean gst_am7xxxsink_stop (GstBaseSink * bsink);
 
  59 static GstCaps *gst_am7xxxsink_getcaps (GstBaseSink * bsink, GstCaps * filter);
 
  60 static gboolean gst_am7xxxsink_setcaps (GstBaseSink * bsink, GstCaps * caps);
 
  62 static void gst_am7xxxsink_finalize (GObject * object);
 
  63 static void gst_am7xxxsink_set_property (GObject * object,
 
  64     guint prop_id, const GValue * value, GParamSpec * pspec);
 
  65 static void gst_am7xxxsink_get_property (GObject * object,
 
  66     guint prop_id, GValue * value, GParamSpec * pspec);
 
  67 static GstStateChangeReturn gst_am7xxxsink_change_state (GstElement * element,
 
  68     GstStateChange transition);
 
  70 #define VIDEO_CAPS "{ RGB, BGR, BGRx, xBGR, RGB, RGBx, xRGB, RGB15, RGB16 }"
 
  72 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
 
  75     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_CAPS))
 
  78 #define parent_class gst_am7xxxsink_parent_class
 
  79 G_DEFINE_TYPE (GstAM7XXXSink, gst_am7xxxsink, GST_TYPE_VIDEO_SINK);
 
  82 gst_am7xxxsink_init (GstAM7XXXSink * am7xxxsink)
 
  84   /* nothing to do here yet */
 
  89 gst_am7xxxsink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
 
  90     GstClockTime * start, GstClockTime * end)
 
  92   GstAM7XXXSink *am7xxxsink;
 
  94   am7xxxsink = GST_AM7XXXSINK (basesink);
 
  96   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
 
  97     *start = GST_BUFFER_TIMESTAMP (buffer);
 
  98     if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
 
  99       *end = *start + GST_BUFFER_DURATION (buffer);
 
 101       if (am7xxxsink->fps_n > 0) {
 
 103             gst_util_uint64_scale_int (GST_SECOND, am7xxxsink->fps_d,
 
 112 gst_am7xxxsink_getcaps (GstBaseSink * bsink, GstCaps * filter)
 
 114   GstAM7XXXSink *am7xxxsink;
 
 115   GstVideoFormat format;
 
 121   int endianness, depth, bpp;
 
 123   am7xxxsink = GST_AM7XXXSINK (bsink);
 
 125   caps = gst_static_pad_template_get_caps (&sink_template);
 
 128   if (!am7xxxsink->framebuffer)
 
 131   bpp = am7xxxsink->varinfo.bits_per_pixel;
 
 133   rmask = ((1 << am7xxxsink->varinfo.red.length) - 1)
 
 134       << am7xxxsink->varinfo.red.offset;
 
 135   gmask = ((1 << am7xxxsink->varinfo.green.length) - 1)
 
 136       << am7xxxsink->varinfo.green.offset;
 
 137   bmask = ((1 << am7xxxsink->varinfo.blue.length) - 1)
 
 138       << am7xxxsink->varinfo.blue.offset;
 
 139   tmask = ((1 << am7xxxsink->varinfo.transp.length) - 1)
 
 140       << am7xxxsink->varinfo.transp.offset;
 
 142   depth = am7xxxsink->varinfo.red.length + am7xxxsink->varinfo.green.length
 
 143       + am7xxxsink->varinfo.blue.length;
 
 145   switch (am7xxxsink->varinfo.bits_per_pixel) {
 
 147       /* swap endianness of masks */
 
 148       rmask = GUINT32_SWAP_LE_BE (rmask);
 
 149       gmask = GUINT32_SWAP_LE_BE (gmask);
 
 150       bmask = GUINT32_SWAP_LE_BE (bmask);
 
 151       tmask = GUINT32_SWAP_LE_BE (tmask);
 
 152       depth += am7xxxsink->varinfo.transp.length;
 
 153       endianness = G_BIG_ENDIAN;
 
 156       /* swap red and blue masks */
 
 161       endianness = G_BIG_ENDIAN;
 
 167       endianness = G_LITTLE_ENDIAN;
 
 170       goto unsupported_bpp;
 
 173   format = gst_video_format_from_masks (depth, bpp, endianness, rmask, gmask,
 
 176   if (format == GST_VIDEO_FORMAT_UNKNOWN)
 
 179   caps = gst_caps_make_writable (caps);
 
 180   gst_caps_set_simple (caps, "format", G_TYPE_STRING,
 
 181       gst_video_format_to_string (format), NULL);
 
 185   if (filter != NULL) {
 
 188     icaps = gst_caps_intersect (caps, filter);
 
 189     gst_caps_unref (caps);
 
 198     GST_WARNING_OBJECT (bsink, "unsupported bit depth: %d", bpp);
 
 203     GST_WARNING_OBJECT (bsink, "could not map am7xxx format to GstVideoFormat: "
 
 204         "depth=%u, bpp=%u, endianness=%u, rmask=0x%08x, gmask=0x%08x, "
 
 205         "bmask=0x%08x, tmask=0x%08x", depth, bpp, endianness, rmask, gmask,
 
 212 gst_am7xxxsink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist)
 
 214   GstAM7XXXSink *am7xxxsink;
 
 215   GstStructure *structure;
 
 218   am7xxxsink = GST_AM7XXXSINK (bsink);
 
 220   structure = gst_caps_get_structure (vscapslist, 0);
 
 222   fps = gst_structure_get_value (structure, "framerate");
 
 223   am7xxxsink->fps_n = gst_value_get_fraction_numerator (fps);
 
 224   am7xxxsink->fps_d = gst_value_get_fraction_denominator (fps);
 
 226   gst_structure_get_int (structure, "width", &am7xxxsink->width);
 
 227   gst_structure_get_int (structure, "height", &am7xxxsink->height);
 
 229   /* calculate centering and scanlengths for the video */
 
 230   am7xxxsink->bytespp = am7xxxsink->fixinfo.line_length / am7xxxsink->varinfo.xres;
 
 232   am7xxxsink->cx = ((int) am7xxxsink->varinfo.xres - am7xxxsink->width) / 2;
 
 233   if (am7xxxsink->cx < 0)
 
 236   am7xxxsink->cy = ((int) am7xxxsink->varinfo.yres - am7xxxsink->height) / 2;
 
 237   if (am7xxxsink->cy < 0)
 
 240   am7xxxsink->linelen = am7xxxsink->width * am7xxxsink->bytespp;
 
 241   if (am7xxxsink->linelen > am7xxxsink->fixinfo.line_length)
 
 242     am7xxxsink->linelen = am7xxxsink->fixinfo.line_length;
 
 244   am7xxxsink->lines = am7xxxsink->height;
 
 245   if (am7xxxsink->lines > am7xxxsink->varinfo.yres)
 
 246     am7xxxsink->lines = am7xxxsink->varinfo.yres;
 
 253 gst_am7xxxsink_show_frame (GstVideoSink * videosink, GstBuffer * buf)
 
 256   GstAM7XXXSink *am7xxxsink;
 
 260   am7xxxsink = GST_AM7XXXSINK (videosink);
 
 262   /* optimization could remove this memcpy by allocating the buffer
 
 263      in framebuffer memory, but would only work when xres matches
 
 265   if (!gst_buffer_map (buf, &map, GST_MAP_READ))
 
 266     return GST_FLOW_ERROR;
 
 268   for (i = 0; i < am7xxxsink->lines; i++) {
 
 269     memcpy (am7xxxsink->framebuffer
 
 270         + (i + am7xxxsink->cy) * am7xxxsink->fixinfo.line_length
 
 271         + am7xxxsink->cx * am7xxxsink->bytespp,
 
 272         map.data + i * am7xxxsink->width * am7xxxsink->bytespp,
 
 273         am7xxxsink->linelen);
 
 276   gst_buffer_unmap (buf, &map);
 
 282 gst_am7xxxsink_start (GstBaseSink * bsink)
 
 284   GstAM7XXXSink *am7xxxsink;
 
 286   am7xxxsink = GST_AM7XXXSINK (bsink);
 
 288   if (!am7xxxsink->device) {
 
 289     am7xxxsink->device = g_strdup ("/dev/fb0");
 
 292   am7xxxsink->fd = open (am7xxxsink->device, O_RDWR);
 
 294   if (am7xxxsink->fd == -1)
 
 297   /* get the fixed screen info */
 
 298   if (ioctl (am7xxxsink->fd, FBIOGET_FSCREENINFO, &am7xxxsink->fixinfo))
 
 301   /* get the variable screen info */
 
 302   if (ioctl (am7xxxsink->fd, FBIOGET_VSCREENINFO, &am7xxxsink->varinfo))
 
 305   /* map the framebuffer */
 
 306   am7xxxsink->framebuffer = mmap (0, am7xxxsink->fixinfo.smem_len,
 
 307       PROT_WRITE, MAP_SHARED, am7xxxsink->fd, 0);
 
 308   if (am7xxxsink->framebuffer == MAP_FAILED)
 
 315 gst_am7xxxsink_stop (GstBaseSink * bsink)
 
 317   GstAM7XXXSink *am7xxxsink;
 
 319   am7xxxsink = GST_AM7XXXSINK (bsink);
 
 321   if (munmap (am7xxxsink->framebuffer, am7xxxsink->fixinfo.smem_len))
 
 324   if (close (am7xxxsink->fd))
 
 332 gst_am7xxxsink_set_property (GObject * object, guint prop_id,
 
 333     const GValue * value, GParamSpec * pspec)
 
 335   GstAM7XXXSink *am7xxxsink;
 
 337   am7xxxsink = GST_AM7XXXSINK (object);
 
 341       g_free (am7xxxsink->device);
 
 342       am7xxxsink->device = g_value_dup_string (value);
 
 346       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
 353 gst_am7xxxsink_get_property (GObject * object, guint prop_id, GValue * value,
 
 356   GstAM7XXXSink *am7xxxsink;
 
 358   am7xxxsink = GST_AM7XXXSINK (object);
 
 362       g_value_set_string (value, am7xxxsink->device);
 
 366       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
 371 static GstStateChangeReturn
 
 372 gst_am7xxxsink_change_state (GstElement * element, GstStateChange transition)
 
 374   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
 
 376   g_return_val_if_fail (GST_IS_AM7XXXSINK (element), GST_STATE_CHANGE_FAILURE);
 
 378   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 
 380   switch (transition) {
 
 388 plugin_init (GstPlugin * plugin)
 
 390   if (!gst_element_register (plugin, "am7xxxsink", GST_RANK_NONE,
 
 391           GST_TYPE_AM7XXXSINK))
 
 398 gst_am7xxxsink_class_init (GstAM7XXXSinkClass * klass)
 
 400   GObjectClass *gobject_class;
 
 401   GstElementClass *gstelement_class;
 
 402   GstBaseSinkClass *basesink_class;
 
 403   GstVideoSinkClass *videosink_class;
 
 405   gobject_class = (GObjectClass *) klass;
 
 406   gstelement_class = (GstElementClass *) klass;
 
 407   basesink_class = (GstBaseSinkClass *) klass;
 
 408   videosink_class = (GstVideoSinkClass *) klass;
 
 410   gobject_class->set_property = gst_am7xxxsink_set_property;
 
 411   gobject_class->get_property = gst_am7xxxsink_get_property;
 
 412   gobject_class->finalize = gst_am7xxxsink_finalize;
 
 414   gstelement_class->change_state =
 
 415       GST_DEBUG_FUNCPTR (gst_am7xxxsink_change_state);
 
 417   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
 
 418       g_param_spec_string ("device", "device",
 
 419           "The framebuffer device eg: /dev/fb0", NULL, G_PARAM_READWRITE));
 
 421   basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_am7xxxsink_setcaps);
 
 422   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_am7xxxsink_getcaps);
 
 424   basesink_class->get_times = GST_DEBUG_FUNCPTR (gst_am7xxxsink_get_times);
 
 426   basesink_class->start = GST_DEBUG_FUNCPTR (gst_am7xxxsink_start);
 
 427   basesink_class->stop = GST_DEBUG_FUNCPTR (gst_am7xxxsink_stop);
 
 429   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_am7xxxsink_show_frame);
 
 431   gst_element_class_set_static_metadata (gstelement_class, "am7xxx video sink",
 
 432       "Sink/Video", "Linux framebuffer videosink",
 
 433       "Sean D'Epagnier <sean@depagnier.com>");
 
 435   gst_element_class_add_pad_template (gstelement_class,
 
 436       gst_static_pad_template_get (&sink_template));
 
 440 gst_am7xxxsink_finalize (GObject * object)
 
 442   GstAM7XXXSink *am7xxxsink = GST_AM7XXXSINK (object);
 
 444   g_free (am7xxxsink->device);
 
 446   G_OBJECT_CLASS (parent_class)->finalize (object);
 
 449 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
 
 452     "Linux framebuffer video sink",
 
 453     plugin_init, VERSION, "LGPL", "gst-am7xxxsink", "http://ao2.it")