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")