uvch264src: Port to gstreamer 1.0

This commit is contained in:
Sjoerd Simons 2013-01-18 22:49:19 +01:00 committed by Sjoerd Simons
parent 381fcda68b
commit 14637e2a27
4 changed files with 236 additions and 273 deletions

View file

@ -1,6 +1,3 @@
glib_gen_prefix = __gst_uvc_h264
glib_gen_basename = gstuvch264
plugin_LTLIBRARIES = libgstuvch264.la
libgstuvch264_la_SOURCES = gstuvch264.c \
@ -27,7 +24,7 @@ libgstuvch264_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
$(GST_LIBS) \
$(G_UDEV_LIBS) \
$(LIBUSB_LIBS) \
$(top_builddir)/gst-libs/gst/basecamerabinsrc/libgstbasecamerabinsrc-$(GST_MAJORMINOR).la
$(top_builddir)/gst-libs/gst/basecamerabinsrc/libgstbasecamerabinsrc-@GST_API_VERSION@.la
noinst_HEADERS = gstuvch264_mjpgdemux.h \
gstuvch264_src.h \

View file

@ -45,6 +45,6 @@ plugin_init (GstPlugin * plugin)
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"uvch264",
uvch264,
"UVC compliant H264 encoding cameras plugin",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -96,8 +96,8 @@ static GstStaticPadTemplate yuy2src_pad_template =
GST_STATIC_PAD_TEMPLATE ("yuy2",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw-yuv, "
"format = (fourcc) YUY2, "
GST_STATIC_CAPS ("video/x-raw, "
"format = (string) YUY2, "
"width = (int) [ 0, MAX ], "
"height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ")
);
@ -105,8 +105,8 @@ static GstStaticPadTemplate nv12src_pad_template =
GST_STATIC_PAD_TEMPLATE ("nv12",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw-yuv, "
"format = (fourcc) NV21, "
GST_STATIC_CAPS ("video/x-raw, "
"format = (string) NV21, "
"width = (int) [ 0, MAX ], "
"height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ")
);
@ -164,53 +164,22 @@ static void gst_uvc_h264_mjpg_demux_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_uvc_h264_mjpg_demux_dispose (GObject * object);
static GstFlowReturn gst_uvc_h264_mjpg_demux_chain (GstPad * pad,
GstBuffer * buffer);
static gboolean gst_uvc_h264_mjpg_demux_sink_setcaps (GstPad * pad,
GstCaps * caps);
static GstCaps *gst_uvc_h264_mjpg_demux_getcaps (GstPad * pad);
GstObject * parent, GstBuffer * buffer);
static gboolean gst_uvc_h264_mjpg_demux_sink_event (GstPad * pad,
GstObject * parent, GstEvent * event);
static gboolean gst_uvc_h264_mjpg_demux_query (GstPad * pad,
GstObject * parent, GstQuery * query);
#define _do_init(x) \
GST_DEBUG_CATEGORY_INIT (uvc_h264_mjpg_demux_debug, \
"uvch264_mjpgdemux", 0, "UVC H264 MJPG Demuxer");
GST_BOILERPLATE_FULL (GstUvcH264MjpgDemux, gst_uvc_h264_mjpg_demux, GstElement,
GST_TYPE_ELEMENT, _do_init);
static void
gst_uvc_h264_mjpg_demux_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GstPadTemplate *pt;
/* do not use gst_element_class_add_static_pad_template to stay compatible
* with gstreamer 0.10.35 */
pt = gst_static_pad_template_get (&mjpgsink_pad_template);
gst_element_class_add_pad_template (element_class, pt);
gst_object_unref (pt);
pt = gst_static_pad_template_get (&jpegsrc_pad_template);
gst_element_class_add_pad_template (element_class, pt);
gst_object_unref (pt);
pt = gst_static_pad_template_get (&h264src_pad_template);
gst_element_class_add_pad_template (element_class, pt);
gst_object_unref (pt);
pt = gst_static_pad_template_get (&yuy2src_pad_template);
gst_element_class_add_pad_template (element_class, pt);
gst_object_unref (pt);
pt = gst_static_pad_template_get (&nv12src_pad_template);
gst_element_class_add_pad_template (element_class, pt);
gst_object_unref (pt);
gst_element_class_set_static_metadata (element_class,
"UVC H264 MJPG Demuxer",
"Video/Demuxer",
"Demux UVC H264 auxiliary streams from MJPG images",
"Youness Alaoui <youness.alaoui@collabora.co.uk>");
}
#define gst_uvc_h264_mjpg_demux_parent_class parent_class
G_DEFINE_TYPE (GstUvcH264MjpgDemux, gst_uvc_h264_mjpg_demux, GST_TYPE_ELEMENT);
static void
gst_uvc_h264_mjpg_demux_class_init (GstUvcH264MjpgDemuxClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (gobject_class, sizeof (GstUvcH264MjpgDemuxPrivate));
@ -218,6 +187,26 @@ gst_uvc_h264_mjpg_demux_class_init (GstUvcH264MjpgDemuxClass * klass)
gobject_class->get_property = gst_uvc_h264_mjpg_demux_get_property;
gobject_class->dispose = gst_uvc_h264_mjpg_demux_dispose;
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&mjpgsink_pad_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&jpegsrc_pad_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&h264src_pad_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&yuy2src_pad_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&nv12src_pad_template));
gst_element_class_set_static_metadata (element_class,
"UVC H264 MJPG Demuxer",
"Video/Demuxer",
"Demux UVC H264 auxiliary streams from MJPG images",
"Youness Alaoui <youness.alaoui@collabora.co.uk>");
g_object_class_install_property (gobject_class, PROP_DEVICE_FD,
g_param_spec_int ("device-fd", "device-fd",
@ -230,11 +219,13 @@ gst_uvc_h264_mjpg_demux_class_init (GstUvcH264MjpgDemuxClass * klass)
" (-1 = unlimited)",
0, G_MAXINT, DEFAULT_NUM_CLOCK_SAMPLES,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
GST_DEBUG_CATEGORY_INIT (uvc_h264_mjpg_demux_debug,
"uvch264_mjpgdemux", 0, "UVC H264 MJPG Demuxer");
}
static void
gst_uvc_h264_mjpg_demux_init (GstUvcH264MjpgDemux * self,
GstUvcH264MjpgDemuxClass * g_class)
gst_uvc_h264_mjpg_demux_init (GstUvcH264MjpgDemux * self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_UVC_H264_MJPG_DEMUX,
GstUvcH264MjpgDemuxPrivate);
@ -247,17 +238,17 @@ gst_uvc_h264_mjpg_demux_init (GstUvcH264MjpgDemux * self,
gst_pad_new_from_static_template (&mjpgsink_pad_template, "sink");
gst_pad_set_chain_function (self->priv->sink_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_chain));
gst_pad_set_setcaps_function (self->priv->sink_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_sink_setcaps));
gst_pad_set_getcaps_function (self->priv->sink_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_getcaps));
gst_pad_set_event_function (self->priv->sink_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_sink_event));
gst_pad_set_query_function (self->priv->sink_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_query));
gst_element_add_pad (GST_ELEMENT (self), self->priv->sink_pad);
/* JPEG */
self->priv->jpeg_pad =
gst_pad_new_from_static_template (&jpegsrc_pad_template, "jpeg");
gst_pad_set_getcaps_function (self->priv->jpeg_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_getcaps));
gst_pad_set_query_function (self->priv->jpeg_pad,
GST_DEBUG_FUNCPTR (gst_uvc_h264_mjpg_demux_query));
gst_element_add_pad (GST_ELEMENT (self), self->priv->jpeg_pad);
/* H264 */
@ -278,11 +269,11 @@ gst_uvc_h264_mjpg_demux_init (GstUvcH264MjpgDemux * self,
gst_pad_use_fixed_caps (self->priv->nv12_pad);
gst_element_add_pad (GST_ELEMENT (self), self->priv->nv12_pad);
self->priv->h264_caps = gst_caps_new_simple ("video/x-h264", NULL);
self->priv->yuy2_caps = gst_caps_new_simple ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), NULL);
self->priv->nv12_caps = gst_caps_new_simple ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
self->priv->h264_caps = gst_caps_new_empty_simple ("video/x-h264");
self->priv->yuy2_caps = gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, "YUY2", NULL);
self->priv->nv12_caps = gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, "NV12", NULL);
self->priv->h264_width = self->priv->h264_height = 0;
self->priv->yuy2_width = self->priv->yuy2_height = 0;
self->priv->nv12_width = self->priv->nv12_height = 0;
@ -369,31 +360,40 @@ gst_uvc_h264_mjpg_demux_get_property (GObject * object,
}
}
static gboolean
gst_uvc_h264_mjpg_demux_sink_setcaps (GstPad * pad, GstCaps * caps)
gst_uvc_h264_mjpg_demux_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event)
{
GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (GST_OBJECT_PARENT (pad));
GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (parent);
return gst_pad_set_caps (self->priv->jpeg_pad, caps);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
return gst_pad_push_event (self->priv->jpeg_pad, event);
default:
break;
}
return gst_pad_event_default (pad, parent, event);
}
static GstCaps *
gst_uvc_h264_mjpg_demux_getcaps (GstPad * pad)
static gboolean
gst_uvc_h264_mjpg_demux_query (GstPad * pad, GstObject * parent,
GstQuery * query)
{
GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (GST_OBJECT_PARENT (pad));
GstCaps *result = NULL;
GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (parent);
gboolean ret = FALSE;
if (pad == self->priv->jpeg_pad)
result = gst_pad_peer_get_caps (self->priv->sink_pad);
else if (pad == self->priv->sink_pad)
result = gst_pad_peer_get_caps (self->priv->jpeg_pad);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CAPS:
if (pad == self->priv->sink_pad)
ret = gst_pad_peer_query (self->priv->jpeg_pad, query);
else
ret = gst_pad_peer_query (self->priv->sink_pad, query);
break;
default:
ret = gst_pad_query_default (pad, parent, query);
}
/* TODO: intersect with template and fixate caps */
if (result == NULL)
result = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
return result;
return ret;
}
static gboolean
@ -456,35 +456,37 @@ _pts_to_timestamp (GstUvcH264MjpgDemux * self, GstBuffer * buf, guint32 pts)
}
static GstFlowReturn
gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
gst_uvc_h264_mjpg_demux_chain (GstPad * pad,
GstObject * parent, GstBuffer * buf)
{
GstUvcH264MjpgDemux *self;
GstFlowReturn ret = GST_FLOW_OK;
GstBufferList *jpeg_buf = gst_buffer_list_new ();
GstBufferListIterator *jpeg_it = gst_buffer_list_iterate (jpeg_buf);
GstBufferList *aux_buf = NULL;
GstBufferListIterator *aux_it = NULL;
GstBuffer *jpeg_buf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_METADATA,
0, 0);
GstBuffer *aux_buf = NULL;
AuxiliaryStreamHeader aux_header = { 0 };
GstBuffer *sub_buffer = NULL;
guint32 aux_size = 0;
GstPad *aux_pad = NULL;
GstCaps **aux_caps = NULL;
guint last_offset;
guint i;
guchar *data;
guint size;
gsize size;
GstMapInfo info;
self = GST_UVC_H264_MJPG_DEMUX (GST_PAD_PARENT (pad));
last_offset = 0;
data = GST_BUFFER_DATA (buf);
size = GST_BUFFER_SIZE (buf);
if (data == NULL || size == 0) {
size = gst_buffer_get_size (buf);
if (size == 0) {
ret = gst_pad_push (self->priv->jpeg_pad, buf);
goto done;
}
gst_buffer_list_iterator_add_group (jpeg_it);
gst_buffer_map (buf, &info, GST_MAP_READ);
data = info.data;
for (i = 0; i < size - 1; i++) {
/* Check for APP4 (0xe4) marker in the jpeg */
if (data[i] == 0xff && data[i + 1] == 0xe4) {
@ -511,9 +513,9 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
/* Add JPEG data between the last offset and this market */
if (i - last_offset > 0) {
sub_buffer = gst_buffer_create_sub (buf, last_offset, i - last_offset);
gst_buffer_copy_metadata (sub_buffer, buf, GST_BUFFER_COPY_ALL);
gst_buffer_list_iterator_add (jpeg_it, sub_buffer);
GstMemory *m = gst_memory_copy (info.memory, last_offset,
i - last_offset);
gst_buffer_append_memory (jpeg_buf, m);
}
last_offset = i + 2 + segment_size;
@ -584,16 +586,18 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
goto done;
if (*width != aux_header.width || *height != aux_header.height) {
GstCaps *peercaps = gst_pad_peer_get_caps (aux_pad);
GstCaps *peercaps = gst_pad_peer_query_caps (aux_pad, NULL);
GstStructure *s = NULL;
gint fps_num = 1000000000 / aux_header.frame_interval;
gint fps_den = 100;
/* TODO: intersect with pad template */
GST_DEBUG ("peercaps : %" GST_PTR_FORMAT, peercaps);
if (peercaps && !gst_caps_is_any (peercaps))
if (peercaps && !gst_caps_is_any (peercaps)) {
peercaps = gst_caps_make_writable (peercaps);
s = gst_caps_get_structure (peercaps, 0);
if (s) {
}
if (s && gst_structure_has_field (s, "framerate")) {
/* TODO: make sure it contains the right format/width/height */
gst_structure_fixate_field_nearest_fraction (s, "framerate",
fps_num, fps_den);
@ -619,9 +623,7 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
}
/* Create new auxiliary buffer list and adjust i/segment size */
aux_buf = gst_buffer_list_new ();
aux_it = gst_buffer_list_iterate (aux_buf);
gst_buffer_list_iterator_add_group (aux_it);
aux_buf = gst_buffer_new ();
}
i += sizeof (aux_header) + sizeof (aux_size);
@ -637,26 +639,24 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
}
if (segment_size > 0) {
sub_buffer = gst_buffer_create_sub (buf, i, segment_size);
GST_BUFFER_DURATION (sub_buffer) =
GstMemory *m;
m = gst_memory_copy (info.memory, i, segment_size);
GST_BUFFER_DURATION (aux_buf) =
aux_header.frame_interval * 100 * GST_NSECOND;
gst_buffer_copy_metadata (sub_buffer, buf, GST_BUFFER_COPY_TIMESTAMPS);
gst_buffer_set_caps (sub_buffer, *aux_caps);
_pts_to_timestamp (self, sub_buffer, aux_header.pts);
_pts_to_timestamp (self, aux_buf, aux_header.pts);
gst_buffer_list_iterator_add (aux_it, sub_buffer);
gst_buffer_append_memory (aux_buf, m);
aux_size -= segment_size;
/* Push completed aux data */
if (aux_size == 0) {
gst_buffer_list_iterator_free (aux_it);
aux_it = NULL;
GST_DEBUG_OBJECT (self, "Pushing %" GST_FOURCC_FORMAT
" auxiliary buffer %" GST_PTR_FORMAT,
GST_FOURCC_ARGS (aux_header.type), *aux_caps);
ret = gst_pad_push_list (aux_pad, aux_buf);
ret = gst_pad_push (aux_pad, aux_buf);
aux_buf = NULL;
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (self, "Error pushing %" GST_FOURCC_FORMAT
@ -668,19 +668,17 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
i += segment_size - 1;
} else if (data[i] == 0xff && data[i + 1] == 0xda) {
GstMemory *m;
/* The APP4 markers must be before the SOS marker, so this is the end */
GST_DEBUG_OBJECT (self, "Found SOS marker.");
sub_buffer = gst_buffer_create_sub (buf, last_offset, size - last_offset);
gst_buffer_copy_metadata (sub_buffer, buf, GST_BUFFER_COPY_ALL);
gst_buffer_list_iterator_add (jpeg_it, sub_buffer);
m = gst_memory_copy (info.memory, last_offset, size - last_offset);
gst_buffer_append_memory (jpeg_buf, m);
last_offset = size;
break;
}
}
gst_buffer_list_iterator_free (jpeg_it);
jpeg_it = NULL;
if (aux_buf != NULL) {
GST_ELEMENT_ERROR (self, STREAM, DEMUX,
@ -693,10 +691,10 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
/* this means there was no SOS marker in the jpg, so we assume the JPG was
just a container */
GST_DEBUG_OBJECT (self, "SOS marker wasn't found. MJPG is container only");
gst_buffer_list_unref (jpeg_buf);
gst_buffer_unref (jpeg_buf);
jpeg_buf = NULL;
} else {
ret = gst_pad_push_list (self->priv->jpeg_pad, jpeg_buf);
ret = gst_pad_push (self->priv->jpeg_pad, jpeg_buf);
jpeg_buf = NULL;
}
@ -707,14 +705,10 @@ gst_uvc_h264_mjpg_demux_chain (GstPad * pad, GstBuffer * buf)
done:
/* In case of error, unref whatever was left */
if (aux_it)
gst_buffer_list_iterator_free (aux_it);
if (aux_buf)
gst_buffer_list_unref (aux_buf);
if (jpeg_it)
gst_buffer_list_iterator_free (jpeg_it);
gst_buffer_unref (aux_buf);
if (jpeg_buf)
gst_buffer_list_unref (jpeg_buf);
gst_buffer_unref (jpeg_buf);
/* We must always unref the input buffer since we never push it out */
gst_buffer_unref (buf);

View file

@ -41,7 +41,6 @@
#include <string.h>
#include "gstuvch264_src.h"
#include "gstuvch264-marshal.h"
#include <gudev/gudev.h>
#include <libusb.h>
#ifndef LIBUSB_CLASS_VIDEO
@ -154,40 +153,11 @@ static guint _signals[LAST_SIGNAL];
GST_DEBUG_CATEGORY (uvc_h264_src_debug);
#define GST_CAT_DEFAULT uvc_h264_src_debug
GST_BOILERPLATE (GstUvcH264Src, gst_uvc_h264_src,
GstBaseCameraSrc, GST_TYPE_BASE_CAMERA_SRC);
#define GST_UVC_H264_SRC_VF_CAPS_STR \
GST_VIDEO_CAPS_RGB ";" \
GST_VIDEO_CAPS_RGB";" \
GST_VIDEO_CAPS_BGR";" \
GST_VIDEO_CAPS_RGBx";" \
GST_VIDEO_CAPS_xRGB";" \
GST_VIDEO_CAPS_BGRx";" \
GST_VIDEO_CAPS_xBGR";" \
GST_VIDEO_CAPS_RGBA";" \
GST_VIDEO_CAPS_ARGB";" \
GST_VIDEO_CAPS_BGRA";" \
GST_VIDEO_CAPS_ABGR";" \
GST_VIDEO_CAPS_RGB_16";" \
GST_VIDEO_CAPS_RGB_15";" \
"video/x-raw-rgb, bpp = (int)8, depth = (int)8, " \
"width = "GST_VIDEO_SIZE_RANGE" , " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
"framerate = "GST_VIDEO_FPS_RANGE ";" \
GST_VIDEO_CAPS_GRAY8";" \
GST_VIDEO_CAPS_GRAY16("BIG_ENDIAN")";" \
GST_VIDEO_CAPS_GRAY16("LITTLE_ENDIAN")";" \
GST_VIDEO_CAPS_YUV ("{ I420 , NV12 , NV21 , YV12 , YUY2 ," \
" Y42B , Y444 , YUV9 , YVU9 , Y41B , Y800 , Y8 , GREY ," \
" Y16 , UYVY , YVYU , IYU1 , v308 , AYUV, A420}") ";" \
"image/jpeg, " \
"width = " GST_VIDEO_SIZE_RANGE ", " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
"framerate = " GST_VIDEO_FPS_RANGE
#define gst_uvc_h264_src_parent_class parent_class
G_DEFINE_TYPE (GstUvcH264Src, gst_uvc_h264_src, GST_TYPE_BASE_CAMERA_SRC);
#define GST_UVC_H264_SRC_VID_CAPS_STR \
GST_UVC_H264_SRC_VF_CAPS_STR ";" \
GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ";" \
"video/x-h264, " \
"width = " GST_VIDEO_SIZE_RANGE ", " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
@ -200,7 +170,7 @@ static GstStaticPadTemplate vfsrc_template =
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_UVC_H264_SRC_VF_CAPS_STR));
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)));
static GstStaticPadTemplate imgsrc_template =
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
@ -220,7 +190,8 @@ static void gst_uvc_h264_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_uvc_h264_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static gboolean gst_uvc_h264_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_uvc_h264_src_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_uvc_h264_src_send_event (GstElement * element,
GstEvent * event);
static gboolean gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc *
@ -231,17 +202,18 @@ static gboolean gst_uvc_h264_src_start_capture (GstBaseCameraSrc * camerasrc);
static void gst_uvc_h264_src_stop_capture (GstBaseCameraSrc * camerasrc);
static GstStateChangeReturn gst_uvc_h264_src_change_state (GstElement * element,
GstStateChange trans);
static gboolean gst_uvc_h264_src_buffer_probe (GstPad * pad,
GstBuffer * buffer, gpointer user_data);
static gboolean gst_uvc_h264_src_event_probe (GstPad * pad,
GstEvent * event, gpointer user_data);
static GstPadProbeReturn gst_uvc_h264_src_buffer_probe (GstPad * pad,
GstPadProbeInfo * info, gpointer user_data);
static GstPadProbeReturn gst_uvc_h264_src_event_probe (GstPad * pad,
GstPadProbeInfo * info, gpointer user_data);
static void gst_uvc_h264_src_pad_linking_cb (GstPad * pad,
GstPad * peer, gpointer user_data);
static GstCaps *gst_uvc_h264_src_getcaps (GstPad * pad);
static gboolean gst_uvc_h264_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static void v4l2src_prepare_format (GstElement * v4l2src, gint fd, guint fourcc,
guint width, guint height, gpointer user_data);
static void v4l2src_prepare_format (GstElement * v4l2src, gint fd,
GstCaps * caps, gpointer user_data);
static void fill_probe_commit (GstUvcH264Src * self,
uvcx_video_config_probe_commit_t * probe, guint32 frame_interval,
guint32 width, guint32 height, guint32 profile,
@ -267,36 +239,6 @@ static gboolean gst_uvc_h264_src_get_boolean_setting (GstUvcH264Src * self,
static gboolean gst_uvc_h264_src_get_int_setting (GstUvcH264Src * self,
gchar * property, gint * min, gint * def, gint * max);
static void
gst_uvc_h264_src_base_init (gpointer g_class)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
GstPadTemplate *pt;
GST_DEBUG_CATEGORY_INIT (uvc_h264_src_debug, "uvch264_src",
0, "UVC H264 Compliant camera bin source");
gst_element_class_set_static_metadata (gstelement_class,
"UVC H264 Source",
"Source/Video",
"UVC H264 Encoding camera source",
"Youness Alaoui <youness.alaoui@collabora.co.uk>");
/* Don't use gst_element_class_add_static_pad_template in order to keep
* the plugin compatible with gst 0.10.35 */
pt = gst_static_pad_template_get (&vidsrc_template);
gst_element_class_add_pad_template (gstelement_class, pt);
gst_object_unref (pt);
pt = gst_static_pad_template_get (&imgsrc_template);
gst_element_class_add_pad_template (gstelement_class, pt);
gst_object_unref (pt);
pt = gst_static_pad_template_get (&vfsrc_template);
gst_element_class_add_pad_template (gstelement_class, pt);
gst_object_unref (pt);
}
static void
gst_uvc_h264_src_class_init (GstUvcH264SrcClass * klass)
{
@ -304,6 +246,8 @@ gst_uvc_h264_src_class_init (GstUvcH264SrcClass * klass)
GstElementClass *gstelement_class;
GstBaseCameraSrcClass *gstbasecamerasrc_class;
parent_class = g_type_class_peek_parent (klass);
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass);
@ -321,6 +265,24 @@ gst_uvc_h264_src_class_init (GstUvcH264SrcClass * klass)
gstbasecamerasrc_class->start_capture = gst_uvc_h264_src_start_capture;
gstbasecamerasrc_class->stop_capture = gst_uvc_h264_src_stop_capture;
GST_DEBUG_CATEGORY_INIT (uvc_h264_src_debug, "uvch264_src",
0, "UVC H264 Compliant camera bin source");
gst_element_class_set_static_metadata (gstelement_class,
"UVC H264 Source",
"Source/Video",
"UVC H264 Encoding camera source",
"Youness Alaoui <youness.alaoui@collabora.co.uk>");
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&vidsrc_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&imgsrc_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&vfsrc_template));
/* Properties */
g_object_class_install_property (gobject_class, PROP_COLORSPACE_NAME,
g_param_spec_string ("colorspace-name", "colorspace element name",
@ -519,17 +481,16 @@ gst_uvc_h264_src_class_init (GstUvcH264SrcClass * klass)
G_CALLBACK (gst_uvc_h264_src_get_int_setting), NULL, NULL, NULL,
G_TYPE_BOOLEAN, 4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER,
G_TYPE_POINTER, 0);
}
static void
gst_uvc_h264_src_init (GstUvcH264Src * self, GstUvcH264SrcClass * klass)
gst_uvc_h264_src_init (GstUvcH264Src * self)
{
self->vfsrc =
gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
GST_PAD_SRC);
gst_pad_set_getcaps_function (self->vfsrc,
GST_DEBUG_FUNCPTR (gst_uvc_h264_src_getcaps));
gst_pad_set_query_function (self->vfsrc,
GST_DEBUG_FUNCPTR (gst_uvc_h264_src_query));
gst_element_add_pad (GST_ELEMENT (self), self->vfsrc);
self->imgsrc =
@ -540,15 +501,15 @@ gst_uvc_h264_src_init (GstUvcH264Src * self, GstUvcH264SrcClass * klass)
self->vidsrc =
gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
GST_PAD_SRC);
gst_pad_set_getcaps_function (self->vidsrc,
GST_DEBUG_FUNCPTR (gst_uvc_h264_src_getcaps));
gst_pad_set_query_function (self->vidsrc,
GST_DEBUG_FUNCPTR (gst_uvc_h264_src_query));
gst_element_add_pad (GST_ELEMENT (self), self->vidsrc);
gst_pad_add_buffer_probe (self->vidsrc,
(GCallback) gst_uvc_h264_src_buffer_probe, self);
gst_pad_add_event_probe (self->vfsrc,
(GCallback) gst_uvc_h264_src_event_probe, self);
gst_pad_add_event_probe (self->vidsrc,
(GCallback) gst_uvc_h264_src_event_probe, self);
gst_pad_add_probe (self->vidsrc, GST_PAD_PROBE_TYPE_BUFFER,
gst_uvc_h264_src_buffer_probe, self, NULL);
gst_pad_add_probe (self->vfsrc, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
gst_uvc_h264_src_event_probe, self, NULL);
gst_pad_add_probe (self->vidsrc, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
gst_uvc_h264_src_event_probe, self, NULL);
self->srcpad_event_func = GST_PAD_EVENTFUNC (self->vfsrc);
@ -1536,23 +1497,24 @@ gst_uvc_h264_src_get_int_setting (GstUvcH264Src * self, gchar * property,
return ret;
}
static gboolean
gst_uvc_h264_src_event_probe (GstPad * pad, GstEvent * event,
static GstPadProbeReturn
gst_uvc_h264_src_event_probe (GstPad * pad, GstPadProbeInfo * info,
gpointer user_data)
{
GstUvcH264Src *self = GST_UVC_H264_SRC (user_data);
gboolean ret = TRUE;
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
GstEvent *event = info->data;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
ret = !self->reconfiguring;
ret = self->reconfiguring ? GST_PAD_PROBE_DROP : GST_PAD_PROBE_OK;
break;
case GST_EVENT_NEWSEGMENT:
case GST_EVENT_SEGMENT:
if (pad == self->vidsrc) {
ret = !self->vid_newseg;
ret = self->vid_newseg ? GST_PAD_PROBE_DROP : GST_PAD_PROBE_OK;
self->vid_newseg = TRUE;
} else if (pad == self->vfsrc) {
ret = !self->vf_newseg;
ret = self->vf_newseg ? GST_PAD_PROBE_DROP : GST_PAD_PROBE_OK;
self->vf_newseg = TRUE;
}
break;
@ -1563,11 +1525,12 @@ gst_uvc_h264_src_event_probe (GstPad * pad, GstEvent * event,
return ret;
}
static gboolean
gst_uvc_h264_src_buffer_probe (GstPad * pad, GstBuffer * buffer,
static GstPadProbeReturn
gst_uvc_h264_src_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
gpointer user_data)
{
GstUvcH264Src *self = GST_UVC_H264_SRC (user_data);
GstBuffer *buffer = info->data;
/* TODO: Check the NALU type and make sure it is a keyframe */
if (self->key_unit_event) {
@ -1760,22 +1723,17 @@ gst_uvc_h264_src_send_event (GstElement * element, GstEvent * event)
}
static gboolean
gst_uvc_h264_src_event (GstPad * pad, GstEvent * event)
gst_uvc_h264_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstUvcH264Src *self = GST_UVC_H264_SRC (GST_PAD_PARENT (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_NEWSEGMENT:
case GST_EVENT_SEGMENT:
if (!self->vid_newseg && pad == self->vidsrc) {
gboolean update;
gdouble rate, applied_rate;
GstFormat format;
gint64 start, stop, position;
const GstSegment *s;
gst_event_parse_new_segment_full (event, &update, &rate,
&applied_rate, &format, &start, &stop, &position);
gst_segment_set_newsegment (&self->segment, update, rate, format,
start, stop, position);
gst_event_parse_segment (event, &s);
gst_segment_copy_into (s, &self->segment);
}
break;
case GST_EVENT_FLUSH_STOP:
@ -1791,7 +1749,7 @@ gst_uvc_h264_src_event (GstPad * pad, GstEvent * event)
return TRUE;
break;
}
return self->srcpad_event_func (pad, event);
return self->srcpad_event_func (pad, parent, event);
}
static guint8
@ -2094,14 +2052,11 @@ configure_h264 (GstUvcH264Src * self, gint fd)
}
static void
v4l2src_prepare_format (GstElement * v4l2src, gint fd, guint fourcc,
guint width, guint height, gpointer user_data)
v4l2src_prepare_format (GstElement * v4l2src, gint fd, GstCaps * caps,
gpointer user_data)
{
GstUvcH264Src *self = GST_UVC_H264_SRC (user_data);
GST_DEBUG_OBJECT (self, "v4l2src prepare-format with FCC %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (fourcc));
if (self->main_format == UVC_H264_SRC_FORMAT_H264) {
/* TODO: update static controls and g_object_notify those that changed */
configure_h264 (self, fd);
@ -2237,7 +2192,7 @@ _transform_caps (GstUvcH264Src * self, GstCaps * caps, const gchar * name)
goto error_remove;
GST_DEBUG_OBJECT (self, "Transforming: %" GST_PTR_FORMAT, caps);
out_caps = gst_pad_get_caps (sink);
caps = gst_pad_query_caps (sink, NULL);
gst_object_unref (sink);
GST_DEBUG_OBJECT (self, "Result: %" GST_PTR_FORMAT, out_caps);
@ -2257,33 +2212,30 @@ done:
static GstCaps *
gst_uvc_h264_src_transform_caps (GstUvcH264Src * self, GstCaps * caps)
{
GstCaps *h264 = gst_caps_new_simple ("video/x-h264", NULL);
GstCaps *jpg = gst_caps_new_simple ("image/jpeg", NULL);
GstCaps *h264 = gst_caps_new_empty_simple ("video/x-h264");
GstCaps *jpg = gst_caps_new_empty_simple ("image/jpeg");
GstCaps *h264_caps = gst_caps_intersect (h264, caps);
GstCaps *jpg_caps = gst_caps_intersect (jpg, caps);
/* TODO: Keep caps order after transformation */
caps = _transform_caps (self, caps, self->colorspace_name);
caps = gst_caps_make_writable (caps);
if (!gst_caps_is_empty (h264_caps)) {
GstCaps *temp = gst_caps_union (caps, h264_caps);
gst_caps_unref (caps);
caps = temp;
}
if (!gst_caps_is_empty (jpg_caps)) {
GstCaps *temp = gst_caps_union (caps, jpg_caps);
gst_caps_unref (caps);
caps = temp;
gst_caps_append (caps, h264_caps);
} else {
gst_caps_unref (h264_caps);
}
if (h264_caps)
gst_caps_unref (h264_caps);
if (jpg_caps)
if (!gst_caps_is_empty (jpg_caps)) {
gst_caps_append (caps, jpg_caps);
} else {
gst_caps_unref (jpg_caps);
}
gst_caps_unref (h264);
gst_caps_unref (jpg);
return caps;
}
@ -2305,7 +2257,6 @@ gst_uvc_h264_src_fixate_caps (GstUvcH264Src * self, GstPad * v4l_pad,
GST_CAPS_INTERSECT_FIRST);
GST_DEBUG_OBJECT (self, "intersect: %" GST_PTR_FORMAT, tcaps);
icaps = gst_caps_normalize (tcaps);
gst_caps_unref (tcaps);
/* Prefer the first caps we are compatible with that the peer proposed */
for (i = 0; i < gst_caps_get_size (icaps); i++) {
@ -2358,14 +2309,14 @@ gst_uvc_h264_src_fixate_caps (GstUvcH264Src * self, GstPad * v4l_pad,
guint32 interval;
if (_extract_caps_info (s, &width, &height, &interval)) {
if (gst_structure_has_name (s, "video/x-raw-yuv")) {
guint32 fcc = 0;
if (gst_structure_has_name (s, "video/x-raw")) {
guint8 mux = 0;
const gchar *format = gst_structure_get_string (s, "format");
if (gst_structure_get_fourcc (s, "format", &fcc)) {
if (fcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'))
if ((format = gst_structure_get_string (s, "format"))) {
if (g_strcmp0 (format, "YUY2") == 0)
mux = 4;
else if (fcc == GST_MAKE_FOURCC ('N', 'V', '1', '2'))
else if (g_strcmp0 (format, "NV12") == 0)
mux = 8;
}
if (mux != 0) {
@ -2416,11 +2367,10 @@ gst_uvc_h264_src_fixate_caps (GstUvcH264Src * self, GstPad * v4l_pad,
if (caps) {
caps = gst_caps_make_writable (caps);
gst_caps_truncate (caps);
/* now fixate */
if (!gst_caps_is_empty (caps)) {
gst_pad_fixate_caps (v4l_pad, caps);
caps = gst_caps_fixate (caps);
GST_DEBUG_OBJECT (self, "fixated to: %" GST_PTR_FORMAT, caps);
}
@ -2474,16 +2424,19 @@ gst_uvc_h264_src_destroy_pipeline (GstUvcH264Src * self, gboolean v4l2src)
iter = gst_bin_iterate_elements (GST_BIN (self));
done = FALSE;
while (!done) {
GstElement *item = NULL;
GValue data = { 0, };
switch (gst_iterator_next (iter, (gpointer *) & item)) {
switch (gst_iterator_next (iter, &data)) {
case GST_ITERATOR_OK:
if (item != self->v4l2_src) {
gst_bin_remove (GST_BIN (self), item);
gst_element_set_state (item, GST_STATE_NULL);
{
GstElement *child = g_value_get_object (&data);
if (child != self->v4l2_src) {
gst_bin_remove (GST_BIN (self), child);
gst_element_set_state (child, GST_STATE_NULL);
}
gst_object_unref (item);
g_value_reset (&data);
break;
}
case GST_ITERATOR_RESYNC:
gst_iterator_resync (iter);
break;
@ -2608,8 +2561,8 @@ gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), NULL);
vf_caps = gst_pad_peer_get_caps (self->vfsrc);
vid_caps = gst_pad_peer_get_caps (self->vidsrc);
vf_caps = gst_pad_peer_query_caps (self->vfsrc, NULL);
vid_caps = gst_pad_peer_query_caps (self->vidsrc, NULL);
GST_DEBUG_OBJECT (self, "vfsrc caps : %" GST_PTR_FORMAT, vf_caps);
GST_DEBUG_OBJECT (self, "vidsrc caps : %" GST_PTR_FORMAT, vid_caps);
@ -2621,7 +2574,7 @@ gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
}
v4l_pad = gst_element_get_static_pad (self->v4l2_src, "src");
v4l_caps = gst_pad_get_caps (v4l_pad);
v4l_caps = gst_pad_query_caps (v4l_pad, NULL);
GST_DEBUG_OBJECT (self, "v4l2src caps : %" GST_PTR_FORMAT, v4l_caps);
if (vid_caps) {
GstCaps *trans_caps = gst_uvc_h264_src_transform_caps (self, vid_caps);
@ -2926,8 +2879,8 @@ gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
}
if (!gst_element_link (self->v4l2_src, tee))
goto error_remove_all;
vf_pad = gst_element_get_request_pad (tee, "src%d");
vid_pad = gst_element_get_request_pad (tee, "src%d");
vf_pad = gst_element_get_request_pad (tee, "src_%u");
vid_pad = gst_element_get_request_pad (tee, "src_%u");
}
break;
}
@ -2968,16 +2921,18 @@ gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
iter = gst_bin_iterate_elements (GST_BIN (self));
iter_done = FALSE;
while (!iter_done) {
GstElement *item = NULL;
GstElement *child = NULL;
GValue data = { 0, };
switch (gst_iterator_next (iter, (gpointer *) & item)) {
switch (gst_iterator_next (iter, &data)) {
case GST_ITERATOR_OK:
if (!gst_element_sync_state_with_parent (item)) {
gst_object_unref (item);
child = g_value_get_object (&data);
if (!gst_element_sync_state_with_parent (child)) {
g_value_reset (&data);
gst_iterator_free (iter);
goto error_remove_all;
}
gst_object_unref (item);
g_value_reset (&data);
break;
case GST_ITERATOR_RESYNC:
gst_iterator_resync (iter);
@ -3039,9 +2994,9 @@ error:
}
static GstCaps *
gst_uvc_h264_src_getcaps (GstPad * pad)
gst_uvc_h264_src_getcaps (GstPad * pad, GstObject * parent)
{
GstUvcH264Src *self = GST_UVC_H264_SRC (GST_OBJECT_PARENT (pad));
GstUvcH264Src *self = GST_UVC_H264_SRC (parent);
GstCaps *template = NULL;
GstCaps *result = NULL;
@ -3054,7 +3009,7 @@ gst_uvc_h264_src_getcaps (GstPad * pad)
if (self->v4l2_src) {
GstPad *v4l_pad = gst_element_get_static_pad (self->v4l2_src, "src");
GstCaps *v4l_caps = gst_pad_get_caps (v4l_pad);
GstCaps *v4l_caps = gst_pad_query_caps (v4l_pad, NULL);
GstCaps *new_caps = gst_uvc_h264_src_transform_caps (self, v4l_caps);
result = gst_caps_intersect (new_caps, template);
@ -3069,6 +3024,23 @@ gst_uvc_h264_src_getcaps (GstPad * pad)
return result;
}
static gboolean
gst_uvc_h264_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
gboolean ret = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CAPS:
gst_query_set_caps_result (query, gst_uvc_h264_src_getcaps (pad, parent));
ret = TRUE;
default:
ret = gst_pad_query_default (pad, parent, query);
break;
}
return ret;
}
static gboolean
gst_uvc_h264_src_set_mode (GstBaseCameraSrc * bcamsrc, GstCameraBinMode mode)
{