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>
46 #include "gstfbdevsink.h"
55 static void gst_fbdevsink_get_times (GstBaseSink * basesink,
56 GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
59 static GstFlowReturn gst_fbdevsink_show_frame (GstVideoSink * videosink,
62 static gboolean gst_fbdevsink_start (GstBaseSink * bsink);
63 static gboolean gst_fbdevsink_stop (GstBaseSink * bsink);
65 static GstCaps *gst_fbdevsink_getcaps (GstBaseSink * bsink, GstCaps * filter);
66 static gboolean gst_fbdevsink_setcaps (GstBaseSink * bsink, GstCaps * caps);
68 static void gst_fbdevsink_finalize (GObject * object);
69 static void gst_fbdevsink_set_property (GObject * object,
70 guint prop_id, const GValue * value, GParamSpec * pspec);
71 static void gst_fbdevsink_get_property (GObject * object,
72 guint prop_id, GValue * value, GParamSpec * pspec);
73 static GstStateChangeReturn gst_fbdevsink_change_state (GstElement * element,
74 GstStateChange transition);
76 #define VIDEO_CAPS "{ RGB, BGR, BGRx, xBGR, RGB, RGBx, xRGB, RGB15, RGB16 }"
78 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
81 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_CAPS))
84 #define parent_class gst_fbdevsink_parent_class
85 G_DEFINE_TYPE (GstFBDEVSink, gst_fbdevsink, GST_TYPE_VIDEO_SINK);
86 GST_ELEMENT_REGISTER_DEFINE (fbdevsink, "fbdevsink", GST_RANK_NONE,
90 gst_fbdevsink_init (GstFBDEVSink * fbdevsink)
92 /* nothing to do here yet */
97 gst_fbdevsink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
98 GstClockTime * start, GstClockTime * end)
100 GstFBDEVSink *fbdevsink;
102 fbdevsink = GST_FBDEVSINK (basesink);
104 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
105 *start = GST_BUFFER_TIMESTAMP (buffer);
106 if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
107 *end = *start + GST_BUFFER_DURATION (buffer);
109 if (fbdevsink->fps_n > 0) {
111 gst_util_uint64_scale_int (GST_SECOND, fbdevsink->fps_d,
120 gst_fbdevsink_getcaps (GstBaseSink * bsink, GstCaps * filter)
122 GstFBDEVSink *fbdevsink;
123 GstVideoFormat format;
129 int endianness, depth, bpp;
131 fbdevsink = GST_FBDEVSINK (bsink);
133 caps = gst_static_pad_template_get_caps (&sink_template);
136 if (!fbdevsink->framebuffer)
139 bpp = fbdevsink->varinfo.bits_per_pixel;
141 rmask = ((1 << fbdevsink->varinfo.red.length) - 1)
142 << fbdevsink->varinfo.red.offset;
143 gmask = ((1 << fbdevsink->varinfo.green.length) - 1)
144 << fbdevsink->varinfo.green.offset;
145 bmask = ((1 << fbdevsink->varinfo.blue.length) - 1)
146 << fbdevsink->varinfo.blue.offset;
147 tmask = ((1 << fbdevsink->varinfo.transp.length) - 1)
148 << fbdevsink->varinfo.transp.offset;
150 depth = fbdevsink->varinfo.red.length + fbdevsink->varinfo.green.length
151 + fbdevsink->varinfo.blue.length;
153 switch (fbdevsink->varinfo.bits_per_pixel) {
155 /* swap endianness of masks */
156 rmask = GUINT32_SWAP_LE_BE (rmask);
157 gmask = GUINT32_SWAP_LE_BE (gmask);
158 bmask = GUINT32_SWAP_LE_BE (bmask);
159 tmask = GUINT32_SWAP_LE_BE (tmask);
160 depth += fbdevsink->varinfo.transp.length;
161 endianness = G_BIG_ENDIAN;
164 /* swap red and blue masks */
169 endianness = G_BIG_ENDIAN;
175 endianness = G_LITTLE_ENDIAN;
178 goto unsupported_bpp;
181 format = gst_video_format_from_masks (depth, bpp, endianness, rmask, gmask,
184 if (format == GST_VIDEO_FORMAT_UNKNOWN)
187 caps = gst_caps_make_writable (caps);
188 gst_caps_set_simple (caps, "format", G_TYPE_STRING,
189 gst_video_format_to_string (format), NULL);
193 if (filter != NULL) {
196 icaps = gst_caps_intersect (caps, filter);
197 gst_caps_unref (caps);
206 GST_WARNING_OBJECT (bsink, "unsupported bit depth: %d", bpp);
211 GST_WARNING_OBJECT (bsink, "could not map fbdev format to GstVideoFormat: "
212 "depth=%u, bpp=%u, endianness=%u, rmask=0x%08x, gmask=0x%08x, "
213 "bmask=0x%08x, tmask=0x%08x", depth, bpp, endianness, rmask, gmask,
220 gst_fbdevsink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist)
222 GstFBDEVSink *fbdevsink;
223 GstStructure *structure;
226 fbdevsink = GST_FBDEVSINK (bsink);
228 structure = gst_caps_get_structure (vscapslist, 0);
230 fps = gst_structure_get_value (structure, "framerate");
231 fbdevsink->fps_n = gst_value_get_fraction_numerator (fps);
232 fbdevsink->fps_d = gst_value_get_fraction_denominator (fps);
234 gst_structure_get_int (structure, "width", &fbdevsink->width);
235 gst_structure_get_int (structure, "height", &fbdevsink->height);
237 /* calculate centering and scanlengths for the video */
239 fbdevsink->fixinfo.line_length / fbdevsink->varinfo.xres_virtual;
241 fbdevsink->cx = ((int) fbdevsink->varinfo.xres - fbdevsink->width) / 2;
242 if (fbdevsink->cx < 0)
245 fbdevsink->cy = ((int) fbdevsink->varinfo.yres - fbdevsink->height) / 2;
246 if (fbdevsink->cy < 0)
249 fbdevsink->linelen = fbdevsink->width * fbdevsink->bytespp;
250 if (fbdevsink->linelen > fbdevsink->fixinfo.line_length)
251 fbdevsink->linelen = fbdevsink->fixinfo.line_length;
253 fbdevsink->lines = fbdevsink->height;
254 if (fbdevsink->lines > fbdevsink->varinfo.yres)
255 fbdevsink->lines = fbdevsink->varinfo.yres;
262 gst_fbdevsink_show_frame (GstVideoSink * videosink, GstBuffer * buf)
265 GstFBDEVSink *fbdevsink;
269 fbdevsink = GST_FBDEVSINK (videosink);
271 /* optimization could remove this memcpy by allocating the buffer
272 in framebuffer memory, but would only work when xres matches
274 if (!gst_buffer_map (buf, &map, GST_MAP_READ))
275 return GST_FLOW_ERROR;
277 for (i = 0; i < fbdevsink->lines; i++) {
278 memcpy (fbdevsink->framebuffer
279 + (i + fbdevsink->cy) * fbdevsink->fixinfo.line_length
280 + fbdevsink->cx * fbdevsink->bytespp,
281 map.data + i * fbdevsink->width * fbdevsink->bytespp,
285 gst_buffer_unmap (buf, &map);
291 gst_fbdevsink_start (GstBaseSink * bsink)
293 GstFBDEVSink *fbdevsink;
295 fbdevsink = GST_FBDEVSINK (bsink);
297 if (!fbdevsink->device) {
298 fbdevsink->device = g_strdup ("/dev/fb0");
301 fbdevsink->fd = open (fbdevsink->device, O_RDWR);
303 if (fbdevsink->fd == -1)
306 /* get the fixed screen info */
307 if (ioctl (fbdevsink->fd, FBIOGET_FSCREENINFO, &fbdevsink->fixinfo))
310 /* get the variable screen info */
311 if (ioctl (fbdevsink->fd, FBIOGET_VSCREENINFO, &fbdevsink->varinfo))
314 /* map the framebuffer */
315 fbdevsink->framebuffer = mmap (0, fbdevsink->fixinfo.smem_len,
316 PROT_WRITE, MAP_SHARED, fbdevsink->fd, 0);
317 if (fbdevsink->framebuffer == MAP_FAILED)
324 gst_fbdevsink_stop (GstBaseSink * bsink)
326 GstFBDEVSink *fbdevsink;
328 fbdevsink = GST_FBDEVSINK (bsink);
330 if (munmap (fbdevsink->framebuffer, fbdevsink->fixinfo.smem_len))
333 if (close (fbdevsink->fd))
341 gst_fbdevsink_set_property (GObject * object, guint prop_id,
342 const GValue * value, GParamSpec * pspec)
344 GstFBDEVSink *fbdevsink;
346 fbdevsink = GST_FBDEVSINK (object);
350 g_free (fbdevsink->device);
351 fbdevsink->device = g_value_dup_string (value);
355 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362 gst_fbdevsink_get_property (GObject * object, guint prop_id, GValue * value,
365 GstFBDEVSink *fbdevsink;
367 fbdevsink = GST_FBDEVSINK (object);
371 g_value_set_string (value, fbdevsink->device);
375 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380 static GstStateChangeReturn
381 gst_fbdevsink_change_state (GstElement * element, GstStateChange transition)
383 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
385 g_return_val_if_fail (GST_IS_FBDEVSINK (element), GST_STATE_CHANGE_FAILURE);
387 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
389 switch (transition) {
397 plugin_init (GstPlugin * plugin)
399 return GST_ELEMENT_REGISTER (fbdevsink, plugin);
403 gst_fbdevsink_class_init (GstFBDEVSinkClass * klass)
405 GObjectClass *gobject_class;
406 GstElementClass *gstelement_class;
407 GstBaseSinkClass *basesink_class;
408 GstVideoSinkClass *videosink_class;
410 gobject_class = (GObjectClass *) klass;
411 gstelement_class = (GstElementClass *) klass;
412 basesink_class = (GstBaseSinkClass *) klass;
413 videosink_class = (GstVideoSinkClass *) klass;
415 gobject_class->set_property = gst_fbdevsink_set_property;
416 gobject_class->get_property = gst_fbdevsink_get_property;
417 gobject_class->finalize = gst_fbdevsink_finalize;
419 gstelement_class->change_state =
420 GST_DEBUG_FUNCPTR (gst_fbdevsink_change_state);
422 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
423 g_param_spec_string ("device", "device",
424 "The framebuffer device eg: /dev/fb0", NULL, G_PARAM_READWRITE));
426 basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_fbdevsink_setcaps);
427 basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_fbdevsink_getcaps);
429 basesink_class->get_times = GST_DEBUG_FUNCPTR (gst_fbdevsink_get_times);
431 basesink_class->start = GST_DEBUG_FUNCPTR (gst_fbdevsink_start);
432 basesink_class->stop = GST_DEBUG_FUNCPTR (gst_fbdevsink_stop);
434 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_fbdevsink_show_frame);
436 gst_element_class_set_static_metadata (gstelement_class, "fbdev video sink",
437 "Sink/Video", "Linux framebuffer videosink",
438 "Sean D'Epagnier <sean@depagnier.com>");
440 gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
444 gst_fbdevsink_finalize (GObject * object)
446 GstFBDEVSink *fbdevsink = GST_FBDEVSINK (object);
448 g_free (fbdevsink->device);
450 G_OBJECT_CLASS (parent_class)->finalize (object);
453 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
456 "Linux framebuffer video sink",
457 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)