mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 09:10:36 +00:00
27310365d5
This implements H264 encoding support using generic V4L2 interface. It is reported to work with Samsung MFC driver, IXM.6 CODA driver and Qualcomm mainline Venus driver. Other platform should be supported as none of this work is platform specific. The implementation consist of a GstV4l2VideoEnc base class, which implements the core streaming functionality. This base class is implemented by GstV4l2H264Enc class that implements the caps negotiation specific to H264 profiles and level. This implementation supports hardware with multiple H264 encoder. Though, to make it simplier to use, the first discovered H264 encoder will be named v4l2h264enc. Other encoder found during discovery will have a unique name like v4l2video0h264enc. This work is the combined work of multiple developpers in the last 3 years. Thanks to all of the contributors: Ayaka <ayaka@soulik.info> Frédéric Sureau <frederic.sureau@vodalys.com> Jean-Michel Hautbois <jean-michel.hautbois@veo-labs.com> Nicolas Dufresne <nicolas.dufresne@collabora.com> Pablo Anton <pablo.anton@vodalys-labs.com> https://bugzilla.gnome.org/show_bug.cgi?id=728438
254 lines
7.4 KiB
C
254 lines
7.4 KiB
C
/* GStreamer
|
|
*
|
|
* Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
|
* 2006 Edgard Lima <edgard.lima@gmail.com>
|
|
*
|
|
* gstv4l2.c: plugin for v4l2 elements
|
|
*
|
|
* 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
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifndef _GNU_SOURCE
|
|
# define _GNU_SOURCE /* O_CLOEXEC */
|
|
#endif
|
|
|
|
#include "gst/gst-i18n-plugin.h"
|
|
|
|
#include <gst/gst.h>
|
|
|
|
#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"
|
|
|
|
#include "gstv4l2object.h"
|
|
#include "gstv4l2src.h"
|
|
#include "gstv4l2sink.h"
|
|
#include "gstv4l2radio.h"
|
|
#include "gstv4l2videodec.h"
|
|
#include "gstv4l2h264enc.h"
|
|
#include "gstv4l2deviceprovider.h"
|
|
#include "gstv4l2transform.h"
|
|
|
|
/* used in v4l2_calls.c and v4l2src_calls.c */
|
|
GST_DEBUG_CATEGORY (v4l2_debug);
|
|
#define GST_CAT_DEFAULT v4l2_debug
|
|
|
|
#ifdef GST_V4L2_ENABLE_PROBE
|
|
/* 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);
|
|
|
|
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;
|
|
}
|
|
|
|
gst_caps_append_structure (caps, template);
|
|
|
|
if (alt_t)
|
|
gst_caps_append_structure (caps, alt_t);
|
|
}
|
|
}
|
|
|
|
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;
|
|
guint32 device_caps;
|
|
|
|
it = gst_v4l2_iterator_new ();
|
|
|
|
while (gst_v4l2_iterator_next (it)) {
|
|
GstCaps *src_caps, *sink_caps;
|
|
gchar *basename;
|
|
|
|
if (video_fd >= 0)
|
|
close (video_fd);
|
|
|
|
video_fd = open (it->device_path, O_RDWR | O_CLOEXEC);
|
|
|
|
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;
|
|
}
|
|
|
|
if (vcap.capabilities & V4L2_CAP_DEVICE_CAPS)
|
|
device_caps = vcap.device_caps;
|
|
else
|
|
device_caps = vcap.capabilities;
|
|
|
|
if (!((device_caps & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE)) ||
|
|
/* But legacy driver may expose both CAPTURE and OUTPUT */
|
|
((device_caps &
|
|
(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) &&
|
|
(device_caps &
|
|
(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));
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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);
|
|
else if (gst_v4l2_is_h264_enc (sink_caps, src_caps))
|
|
ret = gst_v4l2_h264_enc_register (plugin, basename, it->device_path,
|
|
sink_caps, src_caps);
|
|
else if (gst_v4l2_is_transform (sink_caps, src_caps))
|
|
ret = gst_v4l2_transform_register (plugin, basename, it->device_path,
|
|
sink_caps, src_caps);
|
|
/* else if ( ... etc. */
|
|
|
|
gst_caps_unref (sink_caps);
|
|
gst_caps_unref (src_caps);
|
|
g_free (basename);
|
|
|
|
if (!ret)
|
|
break;
|
|
}
|
|
|
|
if (video_fd >= 0)
|
|
close (video_fd);
|
|
|
|
gst_v4l2_iterator_free (it);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static gboolean
|
|
plugin_init (GstPlugin * plugin)
|
|
{
|
|
const gchar *paths[] = { "/dev", "/dev/v4l2", NULL };
|
|
const gchar *names[] = { "video", NULL };
|
|
|
|
GST_DEBUG_CATEGORY_INIT (v4l2_debug, "v4l2", 0, "V4L2 API calls");
|
|
|
|
/* 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);
|
|
|
|
if (!gst_element_register (plugin, "v4l2src", GST_RANK_PRIMARY,
|
|
GST_TYPE_V4L2SRC) ||
|
|
!gst_element_register (plugin, "v4l2sink", GST_RANK_NONE,
|
|
GST_TYPE_V4L2SINK) ||
|
|
!gst_element_register (plugin, "v4l2radio", GST_RANK_NONE,
|
|
GST_TYPE_V4L2RADIO) ||
|
|
!gst_device_provider_register (plugin, "v4l2deviceprovider",
|
|
GST_RANK_PRIMARY, GST_TYPE_V4L2_DEVICE_PROVIDER)
|
|
/* etc. */
|
|
#ifdef GST_V4L2_ENABLE_PROBE
|
|
|| !gst_v4l2_probe_and_register (plugin)
|
|
#endif
|
|
)
|
|
return FALSE;
|
|
|
|
#ifdef ENABLE_NLS
|
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
#endif /* ENABLE_NLS */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
GST_VERSION_MINOR,
|
|
video4linux2,
|
|
"elements for Video 4 Linux",
|
|
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|