1 /* GStreamer fbdev 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 "gstfbdevsink.h"
49 static void gst_fbdevsink_get_times (GstBaseSink * basesink,
50 GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
53 static GstFlowReturn gst_fbdevsink_show_frame (GstVideoSink * videosink,
56 static gboolean gst_fbdevsink_start (GstBaseSink * bsink);
57 static gboolean gst_fbdevsink_stop (GstBaseSink * bsink);
59 static GstCaps *gst_fbdevsink_getcaps (GstBaseSink * bsink, GstCaps * filter);
60 static gboolean gst_fbdevsink_setcaps (GstBaseSink * bsink, GstCaps * caps);
62 static void gst_fbdevsink_finalize (GObject * object);
63 static void gst_fbdevsink_set_property (GObject * object,
64 guint prop_id, const GValue * value, GParamSpec * pspec);
65 static void gst_fbdevsink_get_property (GObject * object,
66 guint prop_id, GValue * value, GParamSpec * pspec);
67 static GstStateChangeReturn gst_fbdevsink_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_fbdevsink_parent_class
79 G_DEFINE_TYPE (GstFBDEVSink, gst_fbdevsink, GST_TYPE_VIDEO_SINK);
82 gst_fbdevsink_init (GstFBDEVSink * fbdevsink)
84 /* nothing to do here yet */
89 gst_fbdevsink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
90 GstClockTime * start, GstClockTime * end)
92 GstFBDEVSink *fbdevsink;
94 fbdevsink = GST_FBDEVSINK (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 (fbdevsink->fps_n > 0) {
103 gst_util_uint64_scale_int (GST_SECOND, fbdevsink->fps_d,
112 gst_fbdevsink_getcaps (GstBaseSink * bsink, GstCaps * filter)
114 GstFBDEVSink *fbdevsink;
115 GstVideoFormat format;
121 int endianness, depth, bpp;
123 fbdevsink = GST_FBDEVSINK (bsink);
125 caps = gst_static_pad_template_get_caps (&sink_template);
128 if (!fbdevsink->framebuffer)
131 bpp = fbdevsink->varinfo.bits_per_pixel;
133 rmask = ((1 << fbdevsink->varinfo.red.length) - 1)
134 << fbdevsink->varinfo.red.offset;
135 gmask = ((1 << fbdevsink->varinfo.green.length) - 1)
136 << fbdevsink->varinfo.green.offset;
137 bmask = ((1 << fbdevsink->varinfo.blue.length) - 1)
138 << fbdevsink->varinfo.blue.offset;
139 tmask = ((1 << fbdevsink->varinfo.transp.length) - 1)
140 << fbdevsink->varinfo.transp.offset;
142 depth = fbdevsink->varinfo.red.length + fbdevsink->varinfo.green.length
143 + fbdevsink->varinfo.blue.length;
145 switch (fbdevsink->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 += fbdevsink->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 fbdev 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_fbdevsink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist)
214 GstFBDEVSink *fbdevsink;
215 GstStructure *structure;
218 fbdevsink = GST_FBDEVSINK (bsink);
220 structure = gst_caps_get_structure (vscapslist, 0);
222 fps = gst_structure_get_value (structure, "framerate");
223 fbdevsink->fps_n = gst_value_get_fraction_numerator (fps);
224 fbdevsink->fps_d = gst_value_get_fraction_denominator (fps);
226 gst_structure_get_int (structure, "width", &fbdevsink->width);
227 gst_structure_get_int (structure, "height", &fbdevsink->height);
229 /* calculate centering and scanlengths for the video */
230 fbdevsink->bytespp = fbdevsink->fixinfo.line_length / fbdevsink->varinfo.xres;
232 fbdevsink->cx = ((int) fbdevsink->varinfo.xres - fbdevsink->width) / 2;
233 if (fbdevsink->cx < 0)
236 fbdevsink->cy = ((int) fbdevsink->varinfo.yres - fbdevsink->height) / 2;
237 if (fbdevsink->cy < 0)
240 fbdevsink->linelen = fbdevsink->width * fbdevsink->bytespp;
241 if (fbdevsink->linelen > fbdevsink->fixinfo.line_length)
242 fbdevsink->linelen = fbdevsink->fixinfo.line_length;
244 fbdevsink->lines = fbdevsink->height;
245 if (fbdevsink->lines > fbdevsink->varinfo.yres)
246 fbdevsink->lines = fbdevsink->varinfo.yres;
253 gst_fbdevsink_show_frame (GstVideoSink * videosink, GstBuffer * buf)
256 GstFBDEVSink *fbdevsink;
260 fbdevsink = GST_FBDEVSINK (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 < fbdevsink->lines; i++) {
269 memcpy (fbdevsink->framebuffer
270 + (i + fbdevsink->cy) * fbdevsink->fixinfo.line_length
271 + fbdevsink->cx * fbdevsink->bytespp,
272 map.data + i * fbdevsink->width * fbdevsink->bytespp,
276 gst_buffer_unmap (buf, &map);
282 gst_fbdevsink_start (GstBaseSink * bsink)
284 GstFBDEVSink *fbdevsink;
286 fbdevsink = GST_FBDEVSINK (bsink);
288 if (!fbdevsink->device) {
289 fbdevsink->device = g_strdup ("/dev/fb0");
292 fbdevsink->fd = open (fbdevsink->device, O_RDWR);
294 if (fbdevsink->fd == -1)
297 /* get the fixed screen info */
298 if (ioctl (fbdevsink->fd, FBIOGET_FSCREENINFO, &fbdevsink->fixinfo))
301 /* get the variable screen info */
302 if (ioctl (fbdevsink->fd, FBIOGET_VSCREENINFO, &fbdevsink->varinfo))
305 /* map the framebuffer */
306 fbdevsink->framebuffer = mmap (0, fbdevsink->fixinfo.smem_len,
307 PROT_WRITE, MAP_SHARED, fbdevsink->fd, 0);
308 if (fbdevsink->framebuffer == MAP_FAILED)
315 gst_fbdevsink_stop (GstBaseSink * bsink)
317 GstFBDEVSink *fbdevsink;
319 fbdevsink = GST_FBDEVSINK (bsink);
321 if (munmap (fbdevsink->framebuffer, fbdevsink->fixinfo.smem_len))
324 if (close (fbdevsink->fd))
332 gst_fbdevsink_set_property (GObject * object, guint prop_id,
333 const GValue * value, GParamSpec * pspec)
335 GstFBDEVSink *fbdevsink;
337 fbdevsink = GST_FBDEVSINK (object);
341 g_free (fbdevsink->device);
342 fbdevsink->device = g_value_dup_string (value);
346 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
353 gst_fbdevsink_get_property (GObject * object, guint prop_id, GValue * value,
356 GstFBDEVSink *fbdevsink;
358 fbdevsink = GST_FBDEVSINK (object);
362 g_value_set_string (value, fbdevsink->device);
366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371 static GstStateChangeReturn
372 gst_fbdevsink_change_state (GstElement * element, GstStateChange transition)
374 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
376 g_return_val_if_fail (GST_IS_FBDEVSINK (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, "fbdevsink", GST_RANK_NONE,
398 gst_fbdevsink_class_init (GstFBDEVSinkClass * 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_fbdevsink_set_property;
411 gobject_class->get_property = gst_fbdevsink_get_property;
412 gobject_class->finalize = gst_fbdevsink_finalize;
414 gstelement_class->change_state =
415 GST_DEBUG_FUNCPTR (gst_fbdevsink_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_fbdevsink_setcaps);
422 basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_fbdevsink_getcaps);
424 basesink_class->get_times = GST_DEBUG_FUNCPTR (gst_fbdevsink_get_times);
426 basesink_class->start = GST_DEBUG_FUNCPTR (gst_fbdevsink_start);
427 basesink_class->stop = GST_DEBUG_FUNCPTR (gst_fbdevsink_stop);
429 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_fbdevsink_show_frame);
431 gst_element_class_set_static_metadata (gstelement_class, "fbdev 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_fbdevsink_finalize (GObject * object)
442 GstFBDEVSink *fbdevsink = GST_FBDEVSINK (object);
444 g_free (fbdevsink->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-fbdevsink", "http://ao2.it")