gstv4l2.c 7.22 KB
Newer Older
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
1 2 3
/* GStreamer
 *
 * Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
Edgard Lima's avatar
Edgard Lima committed
4
 *               2006 Edgard Lima <edgard.lima@gmail.com>
5 6
 *
 * gstv4l2.c: plugin for v4l2 elements
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
20 21
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
22 23 24 25 26 27
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

28 29 30 31
#ifndef _GNU_SOURCE
# define _GNU_SOURCE            /* O_CLOEXEC */
#endif

Benjamin Otte's avatar
Benjamin Otte committed
32 33
#include "gst/gst-i18n-plugin.h"

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
34 35
#include <gst/gst.h>

36 37 38 39 40 41 42 43 44
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "ext/videodev2.h"
#include "v4l2-utils.h"

45
#include "gstv4l2object.h"
46
#include "gstv4l2src.h"
Rob Clark's avatar
Rob Clark committed
47
#include "gstv4l2sink.h"
48
#include "gstv4l2radio.h"
49
#include "gstv4l2videodec.h"
50
#include "gstv4l2deviceprovider.h"
51
#include "gstv4l2transform.h"
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
52

53 54
/* used in v4l2_calls.c and v4l2src_calls.c */
GST_DEBUG_CATEGORY (v4l2_debug);
55 56
#define GST_CAT_DEFAULT v4l2_debug

57
#ifdef GST_V4L2_ENABLE_PROBE
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/* This is a minimalist probe, for speed, we only enumerate formats */
static GstCaps *
gst_v4l2_probe_template_caps (const gchar * device, gint video_fd,
    enum v4l2_buf_type type)
{
  gint n;
  struct v4l2_fmtdesc format;
  GstCaps *caps;

  GST_DEBUG ("Getting %s format enumerations", device);
  caps = gst_caps_new_empty ();

  for (n = 0;; n++) {
    GstStructure *template;

    memset (&format, 0, sizeof (format));

    format.index = n;
    format.type = type;

    if (ioctl (video_fd, VIDIOC_ENUM_FMT, &format) < 0)
      break;                    /* end of enumeration */

    GST_LOG ("index:       %u", format.index);
    GST_LOG ("type:        %d", format.type);
    GST_LOG ("flags:       %08x", format.flags);
    GST_LOG ("description: '%s'", format.description);
    GST_LOG ("pixelformat: %" GST_FOURCC_FORMAT,
        GST_FOURCC_ARGS (format.pixelformat));

    template = gst_v4l2_object_v4l2fourcc_to_structure (format.pixelformat);

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    if (template) {
      GstStructure *alt_t = NULL;

      switch (format.pixelformat) {
        case V4L2_PIX_FMT_RGB32:
          alt_t = gst_structure_copy (template);
          gst_structure_set (alt_t, "format", G_TYPE_STRING, "ARGB", NULL);
          break;
        case V4L2_PIX_FMT_BGR32:
          alt_t = gst_structure_copy (template);
          gst_structure_set (alt_t, "format", G_TYPE_STRING, "BGRA", NULL);
        default:
          break;
      }

105
      gst_caps_append_structure (caps, template);
106 107 108 109

      if (alt_t)
        gst_caps_append_structure (caps, alt_t);
    }
110 111 112 113 114 115 116 117 118 119 120 121
  }

  return gst_caps_simplify (caps);
}

static gboolean
gst_v4l2_probe_and_register (GstPlugin * plugin)
{
  GstV4l2Iterator *it;
  gint video_fd = -1;
  struct v4l2_capability vcap;
  gboolean ret = TRUE;
122
  guint32 device_caps;
123 124 125 126 127 128 129

  it = gst_v4l2_iterator_new ();

  while (gst_v4l2_iterator_next (it)) {
    GstCaps *src_caps, *sink_caps;
    gchar *basename;

130
    if (video_fd >= 0)
131 132
      close (video_fd);

133 134
    video_fd = open (it->device_path, O_RDWR | O_CLOEXEC);

135 136 137 138 139 140 141 142 143 144 145 146
    if (video_fd == -1) {
      GST_DEBUG ("Failed to open %s: %s", it->device_path, g_strerror (errno));
      continue;
    }

    memset (&vcap, 0, sizeof (vcap));

    if (ioctl (video_fd, VIDIOC_QUERYCAP, &vcap) < 0) {
      GST_DEBUG ("Failed to get device capabilities: %s", g_strerror (errno));
      continue;
    }

147 148 149
    if (vcap.capabilities & V4L2_CAP_DEVICE_CAPS)
      device_caps = vcap.device_caps;
    else
150
      device_caps = vcap.capabilities;
151 152

    if (!((device_caps & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE)) ||
153
            /* But legacy driver may expose both CAPTURE and OUTPUT */
154
            ((device_caps &
155
                    (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) &&
156
                (device_caps &
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
                    (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE)))))
      continue;

    GST_DEBUG ("Probing '%s' located at '%s'",
        it->device_name ? it->device_name : (const gchar *) vcap.driver,
        it->device_path);

    /* get sink supported format (no MPLANE for codec) */
    sink_caps = gst_caps_merge (gst_v4l2_probe_template_caps (it->device_path,
            video_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT),
        gst_v4l2_probe_template_caps (it->device_path, video_fd,
            V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE));

    /* get src supported format */
    src_caps = gst_caps_merge (gst_v4l2_probe_template_caps (it->device_path,
            video_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE),
        gst_v4l2_probe_template_caps (it->device_path, video_fd,
            V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE));

176 177 178 179 180 181 182
    /* Skip devices without any supported formats */
    if (gst_caps_is_empty (sink_caps) || gst_caps_is_empty (src_caps)) {
      gst_caps_unref (sink_caps);
      gst_caps_unref (src_caps);
      continue;
    }

183 184 185 186 187
    basename = g_path_get_basename (it->device_path);

    if (gst_v4l2_is_video_dec (sink_caps, src_caps))
      ret = gst_v4l2_video_dec_register (plugin, basename, it->device_path,
          sink_caps, src_caps);
188 189 190
    else if (gst_v4l2_is_transform (sink_caps, src_caps))
      ret = gst_v4l2_transform_register (plugin, basename, it->device_path,
          sink_caps, src_caps);
191 192 193 194 195 196 197 198 199 200
    /* else if ( ... etc. */

    gst_caps_unref (sink_caps);
    gst_caps_unref (src_caps);
    g_free (basename);

    if (!ret)
      break;
  }

201
  if (video_fd >= 0)
202 203 204 205 206 207
    close (video_fd);

  gst_v4l2_iterator_free (it);

  return ret;
}
208
#endif
209

210
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
211
plugin_init (GstPlugin * plugin)
212
{
213 214 215
  const gchar *paths[] = { "/dev", "/dev/v4l2", NULL };
  const gchar *names[] = { "video", NULL };

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
216 217
  GST_DEBUG_CATEGORY_INIT (v4l2_debug, "v4l2", 0, "V4L2 API calls");

218 219 220 221 222
  /* Add some depedency, so the dynamic features get updated upon changes in
   * /dev/video* */
  gst_plugin_add_dependency (plugin,
      NULL, paths, names, GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_PREFIX);

223
  if (!gst_element_register (plugin, "v4l2src", GST_RANK_PRIMARY,
Rob Clark's avatar
Rob Clark committed
224
          GST_TYPE_V4L2SRC) ||
225
      !gst_element_register (plugin, "v4l2sink", GST_RANK_NONE,
Rob Clark's avatar
Rob Clark committed
226
          GST_TYPE_V4L2SINK) ||
227 228
      !gst_element_register (plugin, "v4l2radio", GST_RANK_NONE,
          GST_TYPE_V4L2RADIO) ||
229
      !gst_device_provider_register (plugin, "v4l2deviceprovider",
230
          GST_RANK_PRIMARY, GST_TYPE_V4L2_DEVICE_PROVIDER)
231
      /* etc. */
232 233 234 235
#ifdef GST_V4L2_ENABLE_PROBE
      || !gst_v4l2_probe_and_register (plugin)
#endif
      )
236 237
    return FALSE;

238 239
#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
240
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
241 242
#endif /* ENABLE_NLS */

243 244 245
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
246 247
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
248
    video4linux2,
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
249
    "elements for Video 4 Linux",
250
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)