From 1ba24e1306dd1623bae3400e12e073c9f6fc8d51 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Mon, 10 Sep 2012 16:09:26 -0400 Subject: [PATCH] UVC H264 plugin --- configure.ac | 26 +- sys/Makefile.am | 10 +- sys/uvch264/Makefile.am | 48 + sys/uvch264/gstuvch264-marshal.list | 3 + sys/uvch264/gstuvch264.c | 50 + sys/uvch264/gstuvch264_mjpgdemux.c | 723 ++++ sys/uvch264/gstuvch264_mjpgdemux.h | 66 + sys/uvch264/gstuvch264_src.c | 3180 +++++++++++++++++ sys/uvch264/gstuvch264_src.h | 166 + sys/uvch264/uvc_h264.c | 122 + sys/uvch264/uvc_h264.h | 335 ++ tests/check/Makefile.am | 19 +- tests/check/elements/.gitignore | 1 + tests/check/elements/uvch264demux.c | 696 ++++ .../uvch264demux_data/valid_h264_jpg.h264 | Bin 0 -> 5297 bytes .../uvch264demux_data/valid_h264_jpg.jpg | Bin 0 -> 10690 bytes .../uvch264demux_data/valid_h264_jpg.mjpg | Bin 0 -> 16017 bytes .../uvch264demux_data/valid_h264_yuy2.h264 | Bin 0 -> 5607 bytes .../uvch264demux_data/valid_h264_yuy2.mjpg | Bin 0 -> 34477 bytes .../uvch264demux_data/valid_h264_yuy2.yuy2 | 1 + tests/examples/Makefile.am | 10 +- tests/examples/uvch264/Makefile.am | 36 + tests/examples/uvch264/boolean_property.glade | 94 + tests/examples/uvch264/enum_property.glade | 88 + .../examples/uvch264/enum_property_gtk2.glade | 88 + tests/examples/uvch264/int_property.glade | 147 + tests/examples/uvch264/test-uvch264.c | 673 ++++ tests/examples/uvch264/window.glade | 345 ++ 28 files changed, 6921 insertions(+), 6 deletions(-) create mode 100644 sys/uvch264/Makefile.am create mode 100644 sys/uvch264/gstuvch264-marshal.list create mode 100644 sys/uvch264/gstuvch264.c create mode 100644 sys/uvch264/gstuvch264_mjpgdemux.c create mode 100644 sys/uvch264/gstuvch264_mjpgdemux.h create mode 100644 sys/uvch264/gstuvch264_src.c create mode 100644 sys/uvch264/gstuvch264_src.h create mode 100644 sys/uvch264/uvc_h264.c create mode 100644 sys/uvch264/uvc_h264.h create mode 100644 tests/check/elements/uvch264demux.c create mode 100644 tests/check/elements/uvch264demux_data/valid_h264_jpg.h264 create mode 100644 tests/check/elements/uvch264demux_data/valid_h264_jpg.jpg create mode 100644 tests/check/elements/uvch264demux_data/valid_h264_jpg.mjpg create mode 100644 tests/check/elements/uvch264demux_data/valid_h264_yuy2.h264 create mode 100644 tests/check/elements/uvch264demux_data/valid_h264_yuy2.mjpg create mode 100644 tests/check/elements/uvch264demux_data/valid_h264_yuy2.yuy2 create mode 100644 tests/examples/uvch264/Makefile.am create mode 100644 tests/examples/uvch264/boolean_property.glade create mode 100644 tests/examples/uvch264/enum_property.glade create mode 100644 tests/examples/uvch264/enum_property_gtk2.glade create mode 100644 tests/examples/uvch264/int_property.glade create mode 100644 tests/examples/uvch264/test-uvch264.c create mode 100644 tests/examples/uvch264/window.glade diff --git a/configure.ac b/configure.ac index 47b93d99cc..a468404a2d 100644 --- a/configure.ac +++ b/configure.ac @@ -325,7 +325,7 @@ GST_PLUGINS_NONPORTED=" aiff \ gsettings jasper ladspa \ musepack musicbrainz nas neon ofa openal rsvg sdl sndfile spandsp timidity \ directsound directdraw direct3d9 acm wininet \ - wildmidi xvid lv2 teletextdec sndio" + wildmidi xvid lv2 teletextdec sndio uvch264" AC_SUBST(GST_PLUGINS_NONPORTED) dnl these are all the gst plug-ins, compilable without additional libs @@ -689,6 +689,27 @@ AG_GST_CHECK_FEATURE(VCD, [Video CD], vcdsrc, [ AC_CHECK_HEADER(linux/cdrom.h, HAVE_VCD="yes", HAVE_VCD="no") ]) + +dnl *** UVC H264 *** +translit(dnm, m, l) AM_CONDITIONAL(USE_UVCH264, true) +AG_GST_CHECK_FEATURE(UVCH264, [UVC H264], uvch264, [ + AC_CHECK_HEADER(linux/uvcvideo.h, HAVE_UVCH264=yes, HAVE_UVCH264=no) + AG_GST_PKG_CHECK_MODULES(GST_VIDEO, gstreamer-video-0.10 >= 0.10.36) + PKG_CHECK_MODULES(G_UDEV, gudev-1.0 , [ + AC_DEFINE([HAVE_GUDEV], 1, [Define if gudev is installed]) + HAVE_GUDEV="yes" ], + [HAVE_GUDEV="no"]) + PKG_CHECK_MODULES(LIBUSB, libusb-1.0 , [ + AC_DEFINE([HAVE_LIBUSB], 1, [Define if libusb 1.x is installed]) + HAVE_LIBUSB="yes" ], + [HAVE_LIBUSB="no"]) +]) +AC_SUBST(LIBUDEV_CFLAGS) +AC_SUBST(LIBUDEV_LIBS) +AC_SUBST(LIBUSB_CFLAGS) +AC_SUBST(LIBUSB_LIBS) + + dnl *** ext plug-ins *** dnl keep this list sorted alphabetically ! @@ -1905,6 +1926,7 @@ AM_CONDITIONAL(USE_VP8, false) AM_CONDITIONAL(USE_RTMP, false) AM_CONDITIONAL(USE_TELETEXTDEC, false) AM_CONDITIONAL(USE_SNDIO, false) +AM_CONDITIONAL(USE_UVCH264, false) fi dnl of EXT plugins @@ -2079,6 +2101,7 @@ sys/linsys/Makefile sys/osxvideo/Makefile sys/qtwrapper/Makefile sys/shm/Makefile +sys/uvch264/Makefile sys/vcd/Makefile sys/vdpau/Makefile sys/vdpau/gstvdp/Makefile @@ -2097,6 +2120,7 @@ tests/examples/directfb/Makefile tests/examples/mxf/Makefile tests/examples/scaletempo/Makefile tests/examples/opencv/Makefile +tests/examples/uvch264/Makefile tests/icles/Makefile ext/voamrwbenc/Makefile ext/voaacenc/Makefile diff --git a/sys/Makefile.am b/sys/Makefile.am index d1a29b3443..d79d22325a 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -130,9 +130,15 @@ else WINSCREENCAP_DIR= endif -SUBDIRS = $(ACM_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(D3DVIDEOSINK_DIR) $(DECKLINK_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DIRECTSHOW_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OSX_VIDEO_DIR) $(PVR_DIR) $(QT_DIR) $(SHM_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) $(WINSCREENCAP_DIR) +if USE_UVCH264 +UVCH264_DIR=uvch264 +else +UVCH264_DIR= +endif + +SUBDIRS = $(ACM_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(D3DVIDEOSINK_DIR) $(DECKLINK_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DIRECTSHOW_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OSX_VIDEO_DIR) $(PVR_DIR) $(QT_DIR) $(SHM_DIR) $(UVCH264_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) $(WINSCREENCAP_DIR) DIST_SUBDIRS = acmenc acmmp3dec applemedia avc d3dvideosink decklink directdraw directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ - osxvideo pvr2d qtwrapper shm vcd vdpau wasapi wininet winks winscreencap + osxvideo pvr2d qtwrapper shm uvch264 vcd vdpau wasapi wininet winks winscreencap include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/sys/uvch264/Makefile.am b/sys/uvch264/Makefile.am new file mode 100644 index 0000000000..8ecff113f2 --- /dev/null +++ b/sys/uvch264/Makefile.am @@ -0,0 +1,48 @@ +glib_gen_prefix = __gst_uvc_h264 +glib_gen_basename = gstuvch264 + +include $(top_srcdir)/common/gst-glib-gen.mak + +built_sources = gstuvch264-marshal.c +built_headers = gstuvch264-marshal.h + +BUILT_SOURCES = $(built_sources) $(built_headers) + +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = gstuvch264-marshal.list + + +plugin_LTLIBRARIES = libgstuvch264.la + +libgstuvch264_la_SOURCES = gstuvch264.c \ + gstuvch264_mjpgdemux.c \ + gstuvch264_src.c \ + uvc_h264.c + +nodist_libgstuvch264_la_SOURCES = $(built_sources) + +libgstuvch264_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_VIDEO_CFLAGS) \ + $(GST_CFLAGS) \ + $(G_UDEV_CFLAGS) \ + $(LIBUSB_CFLAGS) \ + -DGST_USE_UNSTABLE_API + +libgstuvch264_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstuvch264_la_LIBTOOLFLAGS = --tag=disable-static + +libgstuvch264_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + $(GST_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) \ + $(GST_VIDEO_LIBS) \ + $(GST_LIBS) \ + $(G_UDEV_LIBS) \ + $(LIBUSB_LIBS) \ + $(top_builddir)/gst-libs/gst/basecamerabinsrc/libgstbasecamerabinsrc-$(GST_MAJORMINOR).la + +noinst_HEADERS = gstuvch264_mjpgdemux.h \ + gstuvch264_src.h \ + uvc_h264.h diff --git a/sys/uvch264/gstuvch264-marshal.list b/sys/uvch264/gstuvch264-marshal.list new file mode 100644 index 0000000000..a9ec0dd267 --- /dev/null +++ b/sys/uvch264/gstuvch264-marshal.list @@ -0,0 +1,3 @@ +BOOLEAN:STRING,POINTER,POINTER,POINTER +BOOLEAN:STRING,POINTER,POINTER +BOOLEAN:STRING,POINTER diff --git a/sys/uvch264/gstuvch264.c b/sys/uvch264/gstuvch264.c new file mode 100644 index 0000000000..77ad73dc4c --- /dev/null +++ b/sys/uvch264/gstuvch264.c @@ -0,0 +1,50 @@ +/* GStreamer + * + * uvch264: a plugin for handling UVC compliant H264 encoding cameras + * + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "gstuvch264_mjpgdemux.h" +#include "gstuvch264_src.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "uvch264_mjpgdemux", GST_RANK_NONE, + GST_TYPE_UVC_H264_MJPG_DEMUX)) + return FALSE; + + if (!gst_element_register (plugin, "uvch264_src", GST_RANK_NONE, + GST_TYPE_UVC_H264_SRC)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "uvch264", + "UVC compliant H264 encoding cameras plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/sys/uvch264/gstuvch264_mjpgdemux.c b/sys/uvch264/gstuvch264_mjpgdemux.c new file mode 100644 index 0000000000..4bc6899818 --- /dev/null +++ b/sys/uvch264/gstuvch264_mjpgdemux.c @@ -0,0 +1,723 @@ +/* GStreamer + * + * uvch264_mjpg_demux: a demuxer for muxed stream in UVC H264 compliant MJPG + * + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-uvch264-mjpgdemux + * @short_description: UVC H264 compliant MJPG demuxer + * + * Parses a MJPG stream from a UVC H264 compliant encoding camera and extracts + * each muxed stream into separate pads. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifndef UVCIOC_GET_LAST_SCR +#include + +struct uvc_last_scr_sample +{ + __u32 dev_frequency; + __u32 dev_stc; + __u16 dev_sof; + struct timespec host_ts; + __u16 host_sof; +}; + +#define UVCIOC_GET_LAST_SCR _IOR('u', 0x23, struct uvc_last_scr_sample) +#endif + +#include "gstuvch264_mjpgdemux.h" + +enum +{ + PROP_0, + PROP_DEVICE_FD, + PROP_NUM_CLOCK_SAMPLES +}; + +#define DEFAULT_NUM_CLOCK_SAMPLES 32 + +static GstStaticPadTemplate mjpgsink_pad_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/jpeg, " + "width = (int) [ 0, MAX ]," + "height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ") + ); + +static GstStaticPadTemplate jpegsrc_pad_template = +GST_STATIC_PAD_TEMPLATE ("jpeg", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/jpeg, " + "width = (int) [ 0, MAX ]," + "height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ") + ); + +static GstStaticPadTemplate h264src_pad_template = +GST_STATIC_PAD_TEMPLATE ("h264", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " + "width = (int) [ 0, MAX ], " + "height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ") + ); + +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, " + "width = (int) [ 0, MAX ], " + "height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ") + ); +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, " + "width = (int) [ 0, MAX ], " + "height = (int) [ 0, MAX ], " "framerate = (fraction) [ 0/1, MAX ] ") + ); + + +GST_DEBUG_CATEGORY_STATIC (uvc_h264_mjpg_demux_debug); +#define GST_CAT_DEFAULT uvc_h264_mjpg_demux_debug + +typedef struct +{ + guint32 dev_stc; + guint32 dev_sof; + GstClockTime host_ts; + guint32 host_sof; +} GstUvcH264ClockSample; + +struct _GstUvcH264MjpgDemuxPrivate +{ + int device_fd; + int num_clock_samples; + GstUvcH264ClockSample *clock_samples; + int last_sample; + int num_samples; + GstPad *sink_pad; + GstPad *jpeg_pad; + GstPad *h264_pad; + GstPad *yuy2_pad; + GstPad *nv12_pad; + GstCaps *h264_caps; + GstCaps *yuy2_caps; + GstCaps *nv12_caps; + guint16 h264_width; + guint16 h264_height; + guint16 yuy2_width; + guint16 yuy2_height; + guint16 nv12_width; + guint16 nv12_height; +}; + +typedef struct +{ + guint16 version; + guint16 header_len; + guint32 type; + guint16 width; + guint16 height; + guint32 frame_interval; + guint16 delay; + guint32 pts; +} __attribute__ ((packed)) AuxiliaryStreamHeader; + +static void gst_uvc_h264_mjpg_demux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +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); + +#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_details_simple (element_class, + "UVC H264 MJPG Demuxer", + "Video/Demuxer", + "Demux UVC H264 auxiliary streams from MJPG images", + "Youness Alaoui "); +} + +static void +gst_uvc_h264_mjpg_demux_class_init (GstUvcH264MjpgDemuxClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + g_type_class_add_private (gobject_class, sizeof (GstUvcH264MjpgDemuxPrivate)); + + gobject_class->set_property = gst_uvc_h264_mjpg_demux_set_property; + gobject_class->get_property = gst_uvc_h264_mjpg_demux_get_property; + gobject_class->dispose = gst_uvc_h264_mjpg_demux_dispose; + + + g_object_class_install_property (gobject_class, PROP_DEVICE_FD, + g_param_spec_int ("device-fd", "device-fd", + "File descriptor of the v4l2 device", + -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_NUM_CLOCK_SAMPLES, + g_param_spec_int ("num-clock-samples", "num-clock-samples", + "Number of clock samples to gather for the PTS synchronization" + " (-1 = unlimited)", + 0, G_MAXINT, DEFAULT_NUM_CLOCK_SAMPLES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_uvc_h264_mjpg_demux_init (GstUvcH264MjpgDemux * self, + GstUvcH264MjpgDemuxClass * g_class) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_UVC_H264_MJPG_DEMUX, + GstUvcH264MjpgDemuxPrivate); + + + self->priv->device_fd = -1; + + /* create the sink and src pads */ + self->priv->sink_pad = + 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_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_element_add_pad (GST_ELEMENT (self), self->priv->jpeg_pad); + + /* H264 */ + self->priv->h264_pad = + gst_pad_new_from_static_template (&h264src_pad_template, "h264"); + gst_pad_use_fixed_caps (self->priv->h264_pad); + gst_element_add_pad (GST_ELEMENT (self), self->priv->h264_pad); + + /* YUY2 */ + self->priv->yuy2_pad = + gst_pad_new_from_static_template (&yuy2src_pad_template, "yuy2"); + gst_pad_use_fixed_caps (self->priv->yuy2_pad); + gst_element_add_pad (GST_ELEMENT (self), self->priv->yuy2_pad); + + /* NV12 */ + self->priv->nv12_pad = + gst_pad_new_from_static_template (&nv12src_pad_template, "nv12"); + 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_width = self->priv->h264_height = 0; + self->priv->yuy2_width = self->priv->yuy2_height = 0; + self->priv->nv12_width = self->priv->nv12_height = 0; +} + +static void +gst_uvc_h264_mjpg_demux_dispose (GObject * object) +{ + GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (object); + + if (self->priv->h264_caps) + gst_caps_unref (self->priv->h264_caps); + self->priv->h264_caps = NULL; + if (self->priv->yuy2_caps) + gst_caps_unref (self->priv->yuy2_caps); + self->priv->yuy2_caps = NULL; + if (self->priv->nv12_caps) + gst_caps_unref (self->priv->nv12_caps); + self->priv->nv12_caps = NULL; + if (self->priv->clock_samples) + g_free (self->priv->clock_samples); + self->priv->clock_samples = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_uvc_h264_mjpg_demux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (object); + + switch (prop_id) { + case PROP_DEVICE_FD: + self->priv->device_fd = g_value_get_int (value); + break; + case PROP_NUM_CLOCK_SAMPLES: + self->priv->num_clock_samples = g_value_get_int (value); + if (self->priv->clock_samples) { + if (self->priv->num_clock_samples) { + self->priv->clock_samples = g_realloc_n (self->priv->clock_samples, + self->priv->num_clock_samples, sizeof (GstUvcH264ClockSample)); + if (self->priv->num_samples > self->priv->num_clock_samples) { + self->priv->num_samples = self->priv->num_clock_samples; + if (self->priv->last_sample >= self->priv->num_samples) + self->priv->last_sample = self->priv->num_samples - 1; + } + } else { + g_free (self->priv->clock_samples); + self->priv->clock_samples = NULL; + self->priv->last_sample = -1; + self->priv->num_samples = 0; + } + } + if (self->priv->num_clock_samples > 0) { + self->priv->clock_samples = g_malloc0_n (self->priv->num_clock_samples, + sizeof (GstUvcH264ClockSample)); + self->priv->last_sample = -1; + self->priv->num_samples = 0; + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +static void +gst_uvc_h264_mjpg_demux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (object); + + switch (prop_id) { + case PROP_DEVICE_FD: + g_value_set_int (value, self->priv->device_fd); + break; + case PROP_NUM_CLOCK_SAMPLES: + g_value_set_int (value, self->priv->num_clock_samples); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + + +static gboolean +gst_uvc_h264_mjpg_demux_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (GST_OBJECT_PARENT (pad)); + + return gst_pad_set_caps (self->priv->jpeg_pad, caps); +} + +static GstCaps * +gst_uvc_h264_mjpg_demux_getcaps (GstPad * pad) +{ + GstUvcH264MjpgDemux *self = GST_UVC_H264_MJPG_DEMUX (GST_OBJECT_PARENT (pad)); + GstCaps *result = NULL; + + 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); + + /* TODO: intersect with template and fixate caps */ + if (result == NULL) + result = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + + return result; +} + +static gboolean +_pts_to_timestamp (GstUvcH264MjpgDemux * self, GstBuffer * buf, guint32 pts) +{ + GstUvcH264MjpgDemuxPrivate *priv = self->priv; + GstUvcH264ClockSample *current_sample = NULL; + GstUvcH264ClockSample *oldest_sample = NULL; + guint32 next_sample; + struct uvc_last_scr_sample sample; + guint32 dev_sof; + + if (self->priv->device_fd == -1 || priv->clock_samples == NULL) + return FALSE; + + if (-1 == ioctl (priv->device_fd, UVCIOC_GET_LAST_SCR, &sample)) { + //GST_WARNING_OBJECT (self, " GET_LAST_SCR error"); + return FALSE; + } + + dev_sof = (guint32) (sample.dev_sof + 2048) << 16; + if (priv->num_samples > 0 && + priv->clock_samples[priv->last_sample].dev_sof == dev_sof) { + current_sample = &priv->clock_samples[priv->last_sample]; + } else { + next_sample = (priv->last_sample + 1) % priv->num_clock_samples; + current_sample = &priv->clock_samples[next_sample]; + current_sample->dev_stc = sample.dev_stc; + current_sample->dev_sof = dev_sof; + current_sample->host_ts = sample.host_ts.tv_sec * GST_SECOND + + sample.host_ts.tv_nsec * GST_NSECOND; + current_sample->host_sof = (guint32) (sample.host_sof + 2048) << 16; + + priv->num_samples++; + priv->last_sample = next_sample; + + /* Debug printing */ + GST_DEBUG_OBJECT (self, "device frequency: %u", sample.dev_frequency); + GST_DEBUG_OBJECT (self, "dev_sof: %u", sample.dev_sof); + GST_DEBUG_OBJECT (self, "dev_stc: %u", sample.dev_stc); + GST_DEBUG_OBJECT (self, "host_ts: %lu -- %" GST_TIME_FORMAT, + current_sample->host_ts, GST_TIME_ARGS (current_sample->host_ts)); + GST_DEBUG_OBJECT (self, "host_sof: %u", sample.host_sof); + GST_DEBUG_OBJECT (self, "PTS: %u", pts); + GST_DEBUG_OBJECT (self, "Diff: %u - %f\n", sample.dev_stc - pts, + (gdouble) (sample.dev_stc - pts) / sample.dev_frequency); + } + + if (priv->num_samples < priv->num_clock_samples) + return FALSE; + + next_sample = (priv->last_sample + 1) % priv->num_clock_samples; + oldest_sample = &priv->clock_samples[next_sample]; + + /* TODO: Use current_sample and oldest_sample to do the + * double linear regression and calculate a new PTS */ + (void) oldest_sample; + + return TRUE; +} + +static GstFlowReturn +gst_uvc_h264_mjpg_demux_chain (GstPad * pad, 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; + 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; + + 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) { + ret = gst_pad_push (self->priv->jpeg_pad, buf); + goto done; + } + + gst_buffer_list_iterator_add_group (jpeg_it); + for (i = 0; i < size - 1; i++) { + /* Check for APP4 (0xe4) marker in the jpeg */ + if (data[i] == 0xff && data[i + 1] == 0xe4) { + guint16 segment_size; + + /* Sanity check sizes and get segment size */ + if (i + 4 >= size) { + GST_ELEMENT_ERROR (self, STREAM, DEMUX, + ("Not enough data to read marker size"), (NULL)); + ret = GST_FLOW_ERROR; + goto done; + } + segment_size = GUINT16_FROM_BE (*((guint16 *) (data + i + 2))); + + if (i + segment_size + 2 >= size) { + GST_ELEMENT_ERROR (self, STREAM, DEMUX, + ("Not enough data to read marker content"), (NULL)); + ret = GST_FLOW_ERROR; + goto done; + } + GST_DEBUG_OBJECT (self, + "Found APP4 marker (%d). JPG: %d-%d - APP4: %d - %d", segment_size, + last_offset, i, i, i + 2 + segment_size); + + /* 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); + } + last_offset = i + 2 + segment_size; + + /* Reset i/segment size to the app4 data (ignore marker header/size) */ + i += 4; + segment_size -= 2; + + /* If this is a new auxiliary stream, initialize everything properly */ + if (aux_buf == NULL) { + if (segment_size < sizeof (aux_header) + sizeof (aux_size)) { + GST_ELEMENT_ERROR (self, STREAM, DEMUX, + ("Not enough data to read aux header"), (NULL)); + ret = GST_FLOW_ERROR; + goto done; + } + + aux_header = *((AuxiliaryStreamHeader *) (data + i)); + /* version should be little endian but it looks more like BE */ + aux_header.version = GUINT16_FROM_BE (aux_header.version); + aux_header.header_len = GUINT16_FROM_LE (aux_header.header_len); + aux_header.width = GUINT16_FROM_LE (aux_header.width); + aux_header.height = GUINT16_FROM_LE (aux_header.height); + aux_header.frame_interval = GUINT32_FROM_LE (aux_header.frame_interval); + aux_header.delay = GUINT16_FROM_LE (aux_header.delay); + aux_header.pts = GUINT32_FROM_LE (aux_header.pts); + GST_DEBUG_OBJECT (self, "New auxiliary stream : v%d - %d bytes - %" + GST_FOURCC_FORMAT " %dx%d -- %d *100ns -- %d ms -- %d", + aux_header.version, aux_header.header_len, + GST_FOURCC_ARGS (aux_header.type), + aux_header.width, aux_header.height, + aux_header.frame_interval, aux_header.delay, aux_header.pts); + aux_size = *((guint32 *) (data + i + aux_header.header_len)); + GST_DEBUG_OBJECT (self, "Auxiliary stream size : %d bytes", aux_size); + + if (aux_size > 0) { + guint16 *width = NULL; + guint16 *height = NULL; + + /* Find the auxiliary stream's pad and caps */ + switch (aux_header.type) { + case GST_MAKE_FOURCC ('H', '2', '6', '4'): + aux_pad = self->priv->h264_pad; + aux_caps = &self->priv->h264_caps; + width = &self->priv->h264_width; + height = &self->priv->h264_height; + break; + case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + aux_pad = self->priv->yuy2_pad; + aux_caps = &self->priv->yuy2_caps; + width = &self->priv->yuy2_width; + height = &self->priv->yuy2_height; + break; + case GST_MAKE_FOURCC ('N', 'V', '1', '2'): + aux_pad = self->priv->nv12_pad; + aux_caps = &self->priv->nv12_caps; + width = &self->priv->nv12_width; + height = &self->priv->nv12_height; + break; + default: + GST_ELEMENT_ERROR (self, STREAM, DEMUX, + ("Unknown auxiliary stream format : %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (aux_header.type)), (NULL)); + ret = GST_FLOW_ERROR; + break; + } + + if (ret != GST_FLOW_OK) + goto done; + + if (*width != aux_header.width || *height != aux_header.height) { + GstCaps *peercaps = gst_pad_peer_get_caps (aux_pad); + 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)) + s = gst_caps_get_structure (peercaps, 0); + if (s) { + /* TODO: make sure it contains the right format/width/height */ + gst_structure_fixate_field_nearest_fraction (s, "framerate", + fps_num, fps_den); + GST_DEBUG ("Fixated struct : %" GST_PTR_FORMAT, s); + gst_structure_get_fraction (s, "framerate", &fps_num, &fps_den); + } + if (peercaps) + gst_caps_unref (peercaps); + + *width = aux_header.width; + *height = aux_header.height; + *aux_caps = gst_caps_make_writable (*aux_caps); + /* FIXME: fps must match the caps and be allowed and represent + our first buffer */ + gst_caps_set_simple (*aux_caps, + "width", G_TYPE_INT, aux_header.width, + "height", G_TYPE_INT, aux_header.height, + "framerate", GST_TYPE_FRACTION, fps_num, fps_den, NULL); + if (!gst_pad_set_caps (aux_pad, *aux_caps)) { + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + } + + /* 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); + } + + i += sizeof (aux_header) + sizeof (aux_size); + segment_size -= sizeof (aux_header) + sizeof (aux_size); + } + + if (segment_size > aux_size) { + GST_ELEMENT_ERROR (self, STREAM, DEMUX, + ("Expected %d auxiliary data, got %d bytes", aux_size, + segment_size), (NULL)); + ret = GST_FLOW_ERROR; + goto done; + } + + if (segment_size > 0) { + sub_buffer = gst_buffer_create_sub (buf, i, segment_size); + GST_BUFFER_DURATION (sub_buffer) = + 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); + + gst_buffer_list_iterator_add (aux_it, sub_buffer); + + 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); + aux_buf = NULL; + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (self, "Error pushing %" GST_FOURCC_FORMAT + " auxiliary data", GST_FOURCC_ARGS (aux_header.type)); + goto done; + } + } + } + + i += segment_size - 1; + } else if (data[i] == 0xff && data[i + 1] == 0xda) { + + /* 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); + last_offset = size; + break; + } + } + gst_buffer_list_iterator_free (jpeg_it); + jpeg_it = NULL; + + if (aux_buf != NULL) { + GST_ELEMENT_ERROR (self, STREAM, DEMUX, + ("Incomplete auxiliary stream. %d bytes missing", aux_size), (NULL)); + ret = GST_FLOW_ERROR; + goto done; + } + + if (last_offset != size) { + /* 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); + jpeg_buf = NULL; + } else { + ret = gst_pad_push_list (self->priv->jpeg_pad, jpeg_buf); + jpeg_buf = NULL; + } + + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (self, "Error pushing jpeg data"); + goto done; + } + +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); + if (jpeg_buf) + gst_buffer_list_unref (jpeg_buf); + + /* We must always unref the input buffer since we never push it out */ + gst_buffer_unref (buf); + + return ret; +} diff --git a/sys/uvch264/gstuvch264_mjpgdemux.h b/sys/uvch264/gstuvch264_mjpgdemux.h new file mode 100644 index 0000000000..8c44452018 --- /dev/null +++ b/sys/uvch264/gstuvch264_mjpgdemux.h @@ -0,0 +1,66 @@ +/* GStreamer + * + * uvch264_mjpg_demux: a demuxer for muxed stream in UVC H264 compliant MJPG + * + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_UVC_H264_MJPG_DEMUX_H__ +#define __GST_UVC_H264_MJPG_DEMUX_H__ + +#include + + +G_BEGIN_DECLS + +#define GST_TYPE_UVC_H264_MJPG_DEMUX \ + (gst_uvc_h264_mjpg_demux_get_type()) +#define GST_UVC_H264_MJPG_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GST_TYPE_UVC_H264_MJPG_DEMUX, \ + GstUvcH264MjpgDemux)) +#define GST_UVC_H264_MJPG_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + GST_TYPE_UVC_H264_MJPG_DEMUX, \ + GstUvcH264MjpgDemuxClass)) +#define GST_IS_UVC_H264_MJPG_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + GST_TYPE_UVC_H264_MJPG_DEMUX)) +#define GST_IS_UVC_H264_MJPG_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + GST_TYPE_UVC_H264_MJPG_DEMUX)) + +typedef struct _GstUvcH264MjpgDemux GstUvcH264MjpgDemux; +typedef struct _GstUvcH264MjpgDemuxPrivate GstUvcH264MjpgDemuxPrivate; +typedef struct _GstUvcH264MjpgDemuxClass GstUvcH264MjpgDemuxClass; + +struct _GstUvcH264MjpgDemux { + GstElement element; + GstUvcH264MjpgDemuxPrivate *priv; +}; + +struct _GstUvcH264MjpgDemuxClass { + GstElementClass parent_class; +}; + +GType gst_uvc_h264_mjpg_demux_get_type (void); + +G_END_DECLS + +#endif /* __GST_UVC_H264_MJPG_DEMUX_H__ */ diff --git a/sys/uvch264/gstuvch264_src.c b/sys/uvch264/gstuvch264_src.c new file mode 100644 index 0000000000..69555d3e1e --- /dev/null +++ b/sys/uvch264/gstuvch264_src.c @@ -0,0 +1,3180 @@ +/* + * GStreamer + * + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +/** + * SECTION:element-uvch264-src + * + * A camera bin src element that wraps v4l2src and implements UVC H264 + * Extension Units (XU) to control the H264 encoder in the camera + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#if defined (HAVE_GUDEV) && defined (HAVE_LIBUSB) +#include +#include + +typedef struct +{ + int8_t bLength; + int8_t bDescriptorType; + int8_t bDescriptorSubType; + int8_t bUnitID; + uint8_t guidExtensionCode[16]; +} __attribute__ ((__packed__)) xu_descriptor; + +#define GUID_FORMAT "02X%02X%02X%02X-%02X%02X%02X%02X-"\ + "%02X%02X%02X%02X-%02X%02X%02X%02X" +#define GUID_ARGS(guid) guid[0], guid[1], guid[2], guid[3], \ + guid[4], guid[5], guid[6], guid[7], \ + guid[8], guid[9], guid[10], guid[11], \ + guid[12], guid[13], guid[14], guid[15] + +#define USB_VIDEO_CONTROL 1 +#define USB_VIDEO_CONTROL_INTERFACE 0x24 +#define USB_VIDEO_CONTROL_XU_TYPE 0x06 +#endif + +#include "gstuvch264_src.h" +#include "gstuvch264-marshal.h" + +#ifndef UVCIOC_XU_FIND_UNIT +/* Define the needed structure if is too old. + * This might fail though if the kernel itself does not support it. + */ +struct uvc_xu_find_unit +{ + __u8 guid[16]; + __u8 unit; +}; +#define UVCIOC_XU_FIND_UNIT _IOWR('u', 0x22, struct uvc_xu_find_unit) +#endif + + +enum +{ + PROP_0, + /* uvch264_src properties */ + PROP_COLORSPACE_NAME, + PROP_JPEG_DECODER_NAME, + PROP_NUM_CLOCK_SAMPLES, + /* v4l2src properties */ + PROP_NUM_BUFFERS, + PROP_DEVICE, + PROP_DEVICE_NAME, + /* Static controls */ + PROP_INITIAL_BITRATE, + PROP_SLICE_UNITS, + PROP_SLICE_MODE, + PROP_IFRAME_PERIOD, + PROP_USAGE_TYPE, + PROP_ENTROPY, + PROP_ENABLE_SEI, + PROP_NUM_REORDER_FRAMES, + PROP_PREVIEW_FLIPPED, + PROP_LEAKY_BUCKET_SIZE, + /* Dynamic controls */ + PROP_RATE_CONTROL, + PROP_FIXED_FRAMERATE, + PROP_MAX_MBPS, /* read-only */ + PROP_LEVEL_IDC, + PROP_PEAK_BITRATE, + PROP_AVERAGE_BITRATE, + PROP_MIN_IFRAME_QP, + PROP_MAX_IFRAME_QP, + PROP_MIN_PFRAME_QP, + PROP_MAX_PFRAME_QP, + PROP_MIN_BFRAME_QP, + PROP_MAX_BFRAME_QP, + PROP_LTR_BUFFER_SIZE, + PROP_LTR_ENCODER_CONTROL, +}; +/* In caps : frame interval (fps), width, height, profile, mux */ +/* Ignored: temporal, spatial, SNR, MVC views, version, reset */ +/* Events: LTR, generate IDR */ + +enum +{ + /* action signals */ + SIGNAL_GET_ENUM_SETTING, + SIGNAL_GET_BOOLEAN_SETTING, + SIGNAL_GET_INT_SETTING, + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL]; + +/* Default values */ +#define DEFAULT_COLORSPACE_NAME "ffmpegcolorspace" +#define DEFAULT_JPEG_DECODER_NAME "jpegdec" +#define DEFAULT_NUM_CLOCK_SAMPLES 0 +#define DEFAULT_NUM_BUFFERS -1 +#define DEFAULT_DEVICE "/dev/video0" +#define DEFAULT_DEVICE_NAME NULL +#define DEFAULT_INITIAL_BITRATE 3000000 +#define DEFAULT_SLICE_UNITS 4 +#define DEFAULT_SLICE_MODE UVC_H264_SLICEMODE_SLICEPERFRAME +#define DEFAULT_IFRAME_PERIOD 10000 +#define DEFAULT_USAGE_TYPE UVC_H264_USAGETYPE_REALTIME +#define DEFAULT_ENTROPY UVC_H264_ENTROPY_CAVLC +#define DEFAULT_ENABLE_SEI FALSE +#define DEFAULT_NUM_REORDER_FRAMES 0 +#define DEFAULT_PREVIEW_FLIPPED FALSE +#define DEFAULT_LEAKY_BUCKET_SIZE 1000 +#define DEFAULT_RATE_CONTROL UVC_H264_RATECONTROL_CBR +#define DEFAULT_FIXED_FRAMERATE FALSE +#define DEFAULT_LEVEL_IDC 40 +#define DEFAULT_PEAK_BITRATE DEFAULT_INITIAL_BITRATE +#define DEFAULT_AVERAGE_BITRATE DEFAULT_INITIAL_BITRATE +#define DEFAULT_MIN_QP 10 +#define DEFAULT_MAX_QP 46 +#define DEFAULT_LTR_BUFFER_SIZE 0 +#define DEFAULT_LTR_ENCODER_CONTROL 0 + +#define NSEC_PER_SEC (G_USEC_PER_SEC * 1000) + + +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_VID_CAPS_STR \ + GST_UVC_H264_SRC_VF_CAPS_STR ";" \ + "video/x-h264, " \ + "width = " GST_VIDEO_SIZE_RANGE ", " \ + "height = " GST_VIDEO_SIZE_RANGE ", " \ + "framerate = " GST_VIDEO_FPS_RANGE ", " \ + "stream-format = (string) { byte-stream, avc }, " \ + "alignment = (string) { au }, " \ + "profile = (string) { high, main, baseline, constrained-baseline }" + +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)); + +static GstStaticPadTemplate imgsrc_template = +GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME, + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_NONE); + +static GstStaticPadTemplate vidsrc_template = +GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME, + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_UVC_H264_SRC_VID_CAPS_STR)); + + +static void gst_uvc_h264_src_dispose (GObject * object); +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_send_event (GstElement * element, + GstEvent * event); +static gboolean gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc * + bcamsrc); +static gboolean gst_uvc_h264_src_set_mode (GstBaseCameraSrc * bcamsrc, + GstCameraBinMode mode); +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 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 void v4l2src_prepare_format (GstElement * v4l2src, gint fd, guint fourcc, + guint width, guint height, 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, + UvcH264StreamFormat stream_format); +static gboolean xu_query (GstUvcH264Src * self, guint selector, guint query, + guchar * data); + +static void set_rate_control (GstUvcH264Src * self); +static void set_level_idc (GstUvcH264Src * self); +static void set_bitrate (GstUvcH264Src * self); +static void set_qp (GstUvcH264Src * self, gint type); +static void set_ltr (GstUvcH264Src * self); +static void update_rate_control (GstUvcH264Src * self); +static guint32 update_level_idc_and_get_max_mbps (GstUvcH264Src * self); +static void update_bitrate (GstUvcH264Src * self); +static gboolean update_qp (GstUvcH264Src * self, gint type); +static void update_ltr (GstUvcH264Src * self); + +static gboolean gst_uvc_h264_src_get_enum_setting (GstUvcH264Src * self, + gchar * property, gint * mask, gint * default_value); +static gboolean gst_uvc_h264_src_get_boolean_setting (GstUvcH264Src * self, + gchar * property, gboolean * changeable, gboolean * def); +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_details_simple (gstelement_class, + "UVC H264 Source", + "Source/Video", + "UVC H264 Encoding camera source", + "Youness Alaoui "); + + /* 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) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseCameraSrcClass *gstbasecamerasrc_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass); + + gobject_class->dispose = gst_uvc_h264_src_dispose; + gobject_class->set_property = gst_uvc_h264_src_set_property; + gobject_class->get_property = gst_uvc_h264_src_get_property; + + gstelement_class->change_state = gst_uvc_h264_src_change_state; + gstelement_class->send_event = gst_uvc_h264_src_send_event; + + gstbasecamerasrc_class->construct_pipeline = + gst_uvc_h264_src_construct_pipeline; + gstbasecamerasrc_class->set_mode = gst_uvc_h264_src_set_mode; + gstbasecamerasrc_class->start_capture = gst_uvc_h264_src_start_capture; + gstbasecamerasrc_class->stop_capture = gst_uvc_h264_src_stop_capture; + + /* Properties */ + g_object_class_install_property (gobject_class, PROP_COLORSPACE_NAME, + g_param_spec_string ("colorspace-name", "colorspace element name", + "The name of the colorspace element", + DEFAULT_COLORSPACE_NAME, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | + GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_JPEG_DECODER_NAME, + g_param_spec_string ("jpeg-decoder-name", "jpeg decoder element name", + "The name of the jpeg decoder element", + DEFAULT_JPEG_DECODER_NAME, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | + GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_NUM_CLOCK_SAMPLES, + g_param_spec_int ("num-clock-samples", "num-clock-samples", + "Number of clock samples to gather for the PTS synchronization" + " (-1 = unlimited)", + 0, G_MAXINT, DEFAULT_NUM_CLOCK_SAMPLES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + + /* v4l2src proxied properties */ + g_object_class_install_property (gobject_class, PROP_NUM_BUFFERS, + g_param_spec_int ("num-buffers", "num-buffers", + "Number of buffers to output before sending EOS (-1 = unlimited)", + -1, G_MAXINT, DEFAULT_NUM_BUFFERS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DEVICE, + g_param_spec_string ("device", "device", + "Device location", + DEFAULT_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, + g_param_spec_string ("device-name", "Device name", + "Name of the device", DEFAULT_DEVICE_NAME, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /* Static controls */ + g_object_class_install_property (gobject_class, PROP_INITIAL_BITRATE, + g_param_spec_uint ("initial-bitrate", "Initial bitrate", + "Initial bitrate in bits/second (static control)", + 0, G_MAXUINT, DEFAULT_INITIAL_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_SLICE_UNITS, + g_param_spec_uint ("slice-units", "Slice units", + "Slice units (static control)", + 0, G_MAXUINT16, DEFAULT_SLICE_UNITS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_SLICE_MODE, + g_param_spec_enum ("slice-mode", "Slice mode", + "Defines the unit of the slice-units property (static control)", + UVC_H264_SLICEMODE_TYPE, + DEFAULT_SLICE_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_IFRAME_PERIOD, + g_param_spec_uint ("iframe-period", "I Frame Period", + "Time between IDR frames in milliseconds (static control)", + 0, G_MAXUINT16, DEFAULT_IFRAME_PERIOD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_USAGE_TYPE, + g_param_spec_enum ("usage-type", "Usage type", + "The usage type (static control)", + UVC_H264_USAGETYPE_TYPE, DEFAULT_USAGE_TYPE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_ENTROPY, + g_param_spec_enum ("entropy", "Entropy", + "Entropy (static control)", + UVC_H264_ENTROPY_TYPE, DEFAULT_ENTROPY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_ENABLE_SEI, + g_param_spec_boolean ("enable-sei", "Enable SEI", + "Enable SEI picture timing (static control)", + DEFAULT_ENABLE_SEI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_NUM_REORDER_FRAMES, + g_param_spec_uint ("num-reorder-frames", "Number of Reorder frames", + "Number of B frames between the references frames (static control)", + 0, G_MAXUINT8, DEFAULT_NUM_REORDER_FRAMES, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_PREVIEW_FLIPPED, + g_param_spec_boolean ("preview-flipped", "Flip preview", + "Horizontal flipped image for non H.264 streams (static control)", + DEFAULT_PREVIEW_FLIPPED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + g_object_class_install_property (gobject_class, PROP_LEAKY_BUCKET_SIZE, + g_param_spec_uint ("leaky-bucket-size", "Size of the leaky bucket size", + "Size of the leaky bucket size in milliseconds (static control)", + 0, G_MAXUINT16, DEFAULT_LEAKY_BUCKET_SIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + /* Dynamic controls */ + g_object_class_install_property (gobject_class, PROP_RATE_CONTROL, + g_param_spec_enum ("rate-control", "Rate control", + "Rate control mode (static & dynamic control)", + UVC_H264_RATECONTROL_TYPE, DEFAULT_RATE_CONTROL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_FIXED_FRAMERATE, + g_param_spec_boolean ("fixed-framerate", "Fixed framerate", + "Fixed framerate (static & dynamic control)", + DEFAULT_FIXED_FRAMERATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MAX_MBPS, + g_param_spec_uint ("max-mbps", "Max macroblocks/second", + "The number of macroblocks per second for the maximum processing rate", + 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_LEVEL_IDC, + g_param_spec_uint ("level-idc", "Level IDC", + "Level IDC (dynamic control)", + 0, G_MAXUINT8, DEFAULT_LEVEL_IDC, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_PEAK_BITRATE, + g_param_spec_uint ("peak-bitrate", "Peak bitrate", + "The peak bitrate in bits/second (dynamic control)", + 0, G_MAXUINT, DEFAULT_PEAK_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_AVERAGE_BITRATE, + g_param_spec_uint ("average-bitrate", "Average bitrate", + "The average bitrate in bits/second (dynamic control)", + 0, G_MAXUINT, DEFAULT_AVERAGE_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MIN_IFRAME_QP, + g_param_spec_int ("min-iframe-qp", "Minimum I frame QP", + "The minimum Quantization step size for I frames (dynamic control)", + -G_MAXINT8, G_MAXINT8, DEFAULT_MIN_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MAX_IFRAME_QP, + g_param_spec_int ("max-iframe-qp", "Minimum I frame QP", + "The minimum Quantization step size for I frames (dynamic control)", + -G_MAXINT8, G_MAXINT8, DEFAULT_MAX_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MIN_PFRAME_QP, + g_param_spec_int ("min-pframe-qp", "Minimum P frame QP", + "The minimum Quantization step size for P frames (dynamic control)", + -G_MAXINT8, G_MAXINT8, DEFAULT_MIN_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MAX_PFRAME_QP, + g_param_spec_int ("max-pframe-qp", "Minimum P frame QP", + "The minimum Quantization step size for P frames (dynamic control)", + -G_MAXINT8, G_MAXINT8, DEFAULT_MAX_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MIN_BFRAME_QP, + g_param_spec_int ("min-bframe-qp", "Minimum B frame QP", + "The minimum Quantization step size for B frames (dynamic control)", + -G_MAXINT8, G_MAXINT8, DEFAULT_MIN_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_MAX_BFRAME_QP, + g_param_spec_int ("max-bframe-qp", "Minimum B frame QP", + "The minimum Quantization step size for B frames (dynamic control)", + -G_MAXINT8, G_MAXINT8, DEFAULT_MAX_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_LTR_BUFFER_SIZE, + g_param_spec_int ("ltr-buffer-size", "LTR Buffer size", + "Total number of Long-Term Reference frames (dynamic control)", + 0, G_MAXUINT8, DEFAULT_LTR_BUFFER_SIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_LTR_ENCODER_CONTROL, + g_param_spec_int ("ltr-encoder-control", "LTR frames controled by device", + "Number of LTR frames the device can control (dynamic control)", + 0, G_MAXUINT8, DEFAULT_LTR_ENCODER_CONTROL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + + _signals[SIGNAL_GET_ENUM_SETTING] = + g_signal_new_class_handler ("get-enum-setting", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_uvc_h264_src_get_enum_setting), + NULL, NULL, __gst_uvc_h264_marshal_BOOLEAN__STRING_POINTER_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, 0); + _signals[SIGNAL_GET_BOOLEAN_SETTING] = + g_signal_new_class_handler ("get-boolean-setting", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_uvc_h264_src_get_boolean_setting), NULL, NULL, + __gst_uvc_h264_marshal_BOOLEAN__STRING_POINTER_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, 0); + _signals[SIGNAL_GET_INT_SETTING] = + g_signal_new_class_handler ("get-int-setting", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_uvc_h264_src_get_int_setting), NULL, NULL, + __gst_uvc_h264_marshal_BOOLEAN__STRING_POINTER_POINTER_POINTER, + 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) +{ + 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_element_add_pad (GST_ELEMENT (self), self->vfsrc); + + self->imgsrc = + gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME, + GST_PAD_SRC); + gst_element_add_pad (GST_ELEMENT (self), self->imgsrc); + + 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_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); + + self->srcpad_event_func = GST_PAD_EVENTFUNC (self->vfsrc); + + gst_pad_set_event_function (self->imgsrc, gst_uvc_h264_src_event); + gst_pad_set_event_function (self->vidsrc, gst_uvc_h264_src_event); + gst_pad_set_event_function (self->vfsrc, gst_uvc_h264_src_event); + + g_signal_connect (self->vidsrc, "linked", + (GCallback) gst_uvc_h264_src_pad_linking_cb, self); + g_signal_connect (self->vidsrc, "unlinked", + (GCallback) gst_uvc_h264_src_pad_linking_cb, self); + g_signal_connect (self->vfsrc, "linked", + (GCallback) gst_uvc_h264_src_pad_linking_cb, self); + g_signal_connect (self->vfsrc, "unlinked", + (GCallback) gst_uvc_h264_src_pad_linking_cb, self); + + self->vid_newseg = FALSE; + self->vf_newseg = FALSE; + self->v4l2_fd = -1; + gst_base_camera_src_set_mode (GST_BASE_CAMERA_SRC (self), MODE_VIDEO); + + self->main_format = UVC_H264_SRC_FORMAT_NONE; + self->main_width = 0; + self->main_height = 0; + self->main_frame_interval = 0; + self->main_stream_format = UVC_H264_STREAMFORMAT_ANNEXB; + self->main_profile = UVC_H264_PROFILE_CONSTRAINED_BASELINE; + self->secondary_format = UVC_H264_SRC_FORMAT_NONE; + self->secondary_width = 0; + self->secondary_height = 0; + self->secondary_frame_interval = 0; + + /* v4l2src properties */ + self->num_buffers = DEFAULT_NUM_BUFFERS; + self->device = g_strdup (DEFAULT_DEVICE); + + /* Static controls */ + self->initial_bitrate = DEFAULT_INITIAL_BITRATE; + self->slice_units = DEFAULT_SLICE_UNITS; + self->slice_mode = DEFAULT_SLICE_MODE; + self->iframe_period = DEFAULT_IFRAME_PERIOD; + self->usage_type = DEFAULT_USAGE_TYPE; + self->entropy = DEFAULT_ENTROPY; + self->enable_sei = DEFAULT_ENABLE_SEI; + self->num_reorder_frames = DEFAULT_NUM_REORDER_FRAMES; + self->preview_flipped = DEFAULT_PREVIEW_FLIPPED; + self->leaky_bucket_size = DEFAULT_LEAKY_BUCKET_SIZE; + + /* Dynamic controls */ + self->rate_control = DEFAULT_RATE_CONTROL; + self->fixed_framerate = DEFAULT_FIXED_FRAMERATE; + self->level_idc = DEFAULT_LEVEL_IDC; + self->peak_bitrate = DEFAULT_PEAK_BITRATE; + self->average_bitrate = DEFAULT_AVERAGE_BITRATE; + self->min_qp[QP_I_FRAME] = DEFAULT_MIN_QP; + self->max_qp[QP_I_FRAME] = DEFAULT_MAX_QP; + self->min_qp[QP_P_FRAME] = DEFAULT_MIN_QP; + self->max_qp[QP_P_FRAME] = DEFAULT_MAX_QP; + self->min_qp[QP_B_FRAME] = DEFAULT_MIN_QP; + self->max_qp[QP_B_FRAME] = DEFAULT_MAX_QP; + self->ltr_buffer_size = DEFAULT_LTR_BUFFER_SIZE; + self->ltr_encoder_control = DEFAULT_LTR_ENCODER_CONTROL; +} + +static void +gst_uvc_h264_src_dispose (GObject * object) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (object); + +#if defined (HAVE_GUDEV) && defined (HAVE_LIBUSB) + if (self->usb_ctx) + libusb_exit (self->usb_ctx); + self->usb_ctx = NULL; +#else + (void) self; +#endif + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_uvc_h264_src_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (object); + + switch (prop_id) { + case PROP_COLORSPACE_NAME: + g_free (self->colorspace_name); + self->colorspace_name = g_value_dup_string (value); + break; + case PROP_JPEG_DECODER_NAME: + g_free (self->jpeg_decoder_name); + self->jpeg_decoder_name = g_value_dup_string (value); + break; + case PROP_NUM_CLOCK_SAMPLES: + self->num_clock_samples = g_value_get_int (value); + if (self->mjpg_demux) + g_object_set (self->mjpg_demux, + "num-clock-samples", self->num_clock_samples, NULL); + break; + /* v4l2 properties */ + case PROP_NUM_BUFFERS: + self->num_buffers = g_value_get_int (value); + if (self->v4l2_src) + g_object_set_property (G_OBJECT (self->v4l2_src), "num-buffers", value); + break; + case PROP_DEVICE: + g_free (self->device); + self->device = g_value_dup_string (value); + if (self->v4l2_src) + g_object_set_property (G_OBJECT (self->v4l2_src), "device", value); + break; + /* Static controls */ + case PROP_INITIAL_BITRATE: + self->initial_bitrate = g_value_get_uint (value); + break; + case PROP_SLICE_UNITS: + self->slice_units = g_value_get_uint (value); + break; + case PROP_SLICE_MODE: + self->slice_mode = g_value_get_enum (value); + break; + case PROP_IFRAME_PERIOD: + self->iframe_period = g_value_get_uint (value); + break; + case PROP_USAGE_TYPE: + self->usage_type = g_value_get_enum (value); + break; + case PROP_ENTROPY: + self->entropy = g_value_get_enum (value); + break; + case PROP_ENABLE_SEI: + self->enable_sei = g_value_get_boolean (value); + break; + case PROP_NUM_REORDER_FRAMES: + self->num_reorder_frames = g_value_get_uint (value); + break; + case PROP_PREVIEW_FLIPPED: + self->preview_flipped = g_value_get_boolean (value); + break; + case PROP_LEAKY_BUCKET_SIZE: + self->leaky_bucket_size = g_value_get_uint (value); + break; + + + /* Dynamic controls */ + case PROP_RATE_CONTROL: + self->rate_control = g_value_get_enum (value); + set_rate_control (self); + update_rate_control (self); + break; + case PROP_FIXED_FRAMERATE: + self->fixed_framerate = g_value_get_boolean (value); + set_rate_control (self); + update_rate_control (self); + break; + case PROP_LEVEL_IDC: + self->level_idc = g_value_get_uint (value); + set_level_idc (self); + update_level_idc_and_get_max_mbps (self); + break; + case PROP_PEAK_BITRATE: + self->peak_bitrate = g_value_get_uint (value); + set_bitrate (self); + update_bitrate (self); + break; + case PROP_AVERAGE_BITRATE: + self->average_bitrate = g_value_get_uint (value); + set_bitrate (self); + update_bitrate (self); + break; + case PROP_MIN_IFRAME_QP: + self->min_qp[QP_I_FRAME] = g_value_get_int (value); + set_qp (self, QP_I_FRAME); + update_qp (self, QP_I_FRAME); + break; + case PROP_MAX_IFRAME_QP: + self->max_qp[QP_I_FRAME] = g_value_get_int (value); + set_qp (self, QP_I_FRAME); + update_qp (self, QP_I_FRAME); + break; + case PROP_MIN_PFRAME_QP: + self->min_qp[QP_P_FRAME] = g_value_get_int (value); + set_qp (self, QP_P_FRAME); + update_qp (self, QP_P_FRAME); + break; + case PROP_MAX_PFRAME_QP: + self->max_qp[QP_P_FRAME] = g_value_get_int (value); + set_qp (self, QP_P_FRAME); + update_qp (self, QP_P_FRAME); + break; + case PROP_MIN_BFRAME_QP: + self->min_qp[QP_B_FRAME] = g_value_get_int (value); + set_qp (self, QP_B_FRAME); + update_qp (self, QP_B_FRAME); + break; + case PROP_MAX_BFRAME_QP: + self->max_qp[QP_B_FRAME] = g_value_get_int (value); + set_qp (self, QP_B_FRAME); + update_qp (self, QP_B_FRAME); + break; + case PROP_LTR_BUFFER_SIZE: + self->ltr_buffer_size = g_value_get_int (value); + set_ltr (self); + update_ltr (self); + break; + case PROP_LTR_ENCODER_CONTROL: + self->ltr_encoder_control = g_value_get_int (value); + set_ltr (self); + update_ltr (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +static void +gst_uvc_h264_src_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (object); + uvcx_video_config_probe_commit_t probe; + + switch (prop_id) { + case PROP_INITIAL_BITRATE: + case PROP_SLICE_UNITS: + case PROP_SLICE_MODE: + case PROP_IFRAME_PERIOD: + case PROP_USAGE_TYPE: + case PROP_ENTROPY: + case PROP_ENABLE_SEI: + case PROP_NUM_REORDER_FRAMES: + case PROP_PREVIEW_FLIPPED: + case PROP_LEAKY_BUCKET_SIZE: + fill_probe_commit (self, &probe, 0, 0, 0, 0, 0); + if (GST_STATE (self) >= GST_STATE_PAUSED) { + xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, + (guchar *) & probe); + } + break; + default: + break; + } + + switch (prop_id) { + case PROP_COLORSPACE_NAME: + g_value_set_string (value, self->colorspace_name); + break; + case PROP_JPEG_DECODER_NAME: + g_value_set_string (value, self->jpeg_decoder_name); + break; + case PROP_NUM_CLOCK_SAMPLES: + g_value_set_int (value, self->num_clock_samples); + break; + /* v4l2src properties */ + case PROP_NUM_BUFFERS: + g_value_set_int (value, self->num_buffers); + break; + case PROP_DEVICE: + g_value_set_string (value, self->device); + break; + case PROP_DEVICE_NAME: + if (self->v4l2_src) + g_object_get_property (G_OBJECT (self->v4l2_src), "device-name", value); + else + g_value_set_static_string (value, ""); + break; + /* Static controls */ + case PROP_INITIAL_BITRATE: + g_value_set_uint (value, probe.dwBitRate); + break; + case PROP_SLICE_UNITS: + g_value_set_uint (value, probe.wSliceUnits); + break; + case PROP_SLICE_MODE: + g_value_set_enum (value, probe.wSliceMode); + break; + case PROP_IFRAME_PERIOD: + g_value_set_uint (value, probe.wIFramePeriod); + break; + case PROP_USAGE_TYPE: + g_value_set_enum (value, probe.bUsageType); + break; + case PROP_ENTROPY: + g_value_set_enum (value, probe.bEntropyCABAC); + break; + case PROP_ENABLE_SEI: + g_value_set_boolean (value, + (probe.bTimestamp == UVC_H264_TIMESTAMP_SEI_ENABLE)); + break; + case PROP_NUM_REORDER_FRAMES: + g_value_set_uint (value, probe.bNumOfReorderFrames); + break; + case PROP_PREVIEW_FLIPPED: + g_value_set_boolean (value, + (probe.bPreviewFlipped == UVC_H264_PREFLIPPED_HORIZONTAL)); + break; + case PROP_LEAKY_BUCKET_SIZE: + g_value_set_uint (value, probe.wLeakyBucketSize); + break; + + /* Dynamic controls */ + case PROP_RATE_CONTROL: + update_rate_control (self); + g_value_set_enum (value, self->rate_control); + break; + case PROP_FIXED_FRAMERATE: + update_rate_control (self); + g_value_set_boolean (value, self->fixed_framerate); + break; + case PROP_MAX_MBPS: + g_value_set_uint (value, update_level_idc_and_get_max_mbps (self)); + break; + case PROP_LEVEL_IDC: + update_level_idc_and_get_max_mbps (self); + g_value_set_uint (value, self->level_idc); + break; + case PROP_PEAK_BITRATE: + update_bitrate (self); + g_value_set_uint (value, self->peak_bitrate); + break; + case PROP_AVERAGE_BITRATE: + update_bitrate (self); + g_value_set_uint (value, self->average_bitrate); + break; + case PROP_MIN_IFRAME_QP: + update_qp (self, QP_I_FRAME); + g_value_set_int (value, self->min_qp[QP_I_FRAME]); + break; + case PROP_MAX_IFRAME_QP: + update_qp (self, QP_I_FRAME); + g_value_set_int (value, self->max_qp[QP_I_FRAME]); + break; + case PROP_MIN_PFRAME_QP: + update_qp (self, QP_P_FRAME); + g_value_set_int (value, self->min_qp[QP_P_FRAME]); + break; + case PROP_MAX_PFRAME_QP: + update_qp (self, QP_P_FRAME); + g_value_set_int (value, self->max_qp[QP_P_FRAME]); + break; + case PROP_MIN_BFRAME_QP: + update_qp (self, QP_B_FRAME); + g_value_set_int (value, self->min_qp[QP_B_FRAME]); + break; + case PROP_MAX_BFRAME_QP: + update_qp (self, QP_B_FRAME); + g_value_set_int (value, self->max_qp[QP_B_FRAME]); + break; + case PROP_LTR_BUFFER_SIZE: + update_ltr (self); + g_value_set_int (value, self->ltr_buffer_size); + break; + case PROP_LTR_ENCODER_CONTROL: + update_ltr (self); + g_value_set_int (value, self->ltr_encoder_control); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +/* Set dynamic controls */ +static void +set_rate_control (GstUvcH264Src * self) +{ + uvcx_rate_control_mode_t req; + + if (!xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_GET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " RATE_CONTROL GET_CUR error"); + return; + } + + req.bRateControlMode = self->rate_control; + if (self->fixed_framerate) + req.bRateControlMode |= UVC_H264_RATECONTROL_FIXED_FRM_FLG; + + if (!xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_SET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " RATE_CONTROL SET_CUR error"); + return; + } +} + +static void +set_level_idc (GstUvcH264Src * self) +{ + uvcx_video_advance_config_t req; + + if (!xu_query (self, UVCX_VIDEO_ADVANCE_CONFIG, UVC_GET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " VIDEO_ADVANCE_CONFIG GET_CUR error"); + return; + } + + req.blevel_idc = self->level_idc; + if (!xu_query (self, UVCX_VIDEO_ADVANCE_CONFIG, UVC_SET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " VIDEO_ADVANCE_CONFIG SET_CUR error"); + return; + } +} + +static void +set_bitrate (GstUvcH264Src * self) +{ + uvcx_bitrate_layers_t req; + + if (!xu_query (self, UVCX_BITRATE_LAYERS, UVC_GET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " BITRATE_LAYERS GET_CUR error"); + return; + } + + req.dwPeakBitrate = self->peak_bitrate; + req.dwAverageBitrate = self->average_bitrate; + if (!xu_query (self, UVCX_BITRATE_LAYERS, UVC_SET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " BITRATE_LAYERS SET_CUR error"); + return; + } +} + +static void +set_qp (GstUvcH264Src * self, gint type) +{ + uvcx_qp_steps_layers_t req; + + req.wLayerID = 0; + switch (type) { + case QP_I_FRAME: + req.bFrameType = UVC_H264_QP_STEPS_I_FRAME_TYPE; + break; + case QP_P_FRAME: + req.bFrameType = UVC_H264_QP_STEPS_P_FRAME_TYPE; + break; + case QP_B_FRAME: + req.bFrameType = UVC_H264_QP_STEPS_B_FRAME_TYPE; + break; + default: + return; + } + req.bMinQp = 0; + req.bMaxQp = 0; + if (!xu_query (self, UVCX_QP_STEPS_LAYERS, UVC_SET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " QP_STEPS_LAYERS SET_CUR error"); + return; + } + + if (!xu_query (self, UVCX_QP_STEPS_LAYERS, UVC_GET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " QP_STEPS_LAYERS GET_CUR error"); + return; + } + + req.bMinQp = self->min_qp[type]; + req.bMaxQp = self->max_qp[type]; + if (!xu_query (self, UVCX_QP_STEPS_LAYERS, UVC_SET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " QP_STEPS_LAYERS SET_CUR error"); + return; + } +} + +static void +set_ltr (GstUvcH264Src * self) +{ + uvcx_ltr_buffer_size_control_t req; + + if (!xu_query (self, UVCX_LTR_BUFFER_SIZE_CONTROL, UVC_GET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " LTR_BUFFER_SIZE GET_CUR error"); + return; + } + + req.bLTRBufferSize = self->ltr_buffer_size; + req.bLTREncoderControl = self->ltr_encoder_control; + if (!xu_query (self, UVCX_LTR_BUFFER_SIZE_CONTROL, UVC_SET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, "LTR_BUFFER_SIZE SET_CUR error"); + return; + } +} + +/* Get Dynamic controls */ + +static void +update_rate_control (GstUvcH264Src * self) +{ + uvcx_rate_control_mode_t req; + + if (!xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_GET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " RATE_CONTROL GET_CUR error"); + return; + } + + if (self->rate_control != (req.bRateControlMode & + ~UVC_H264_RATECONTROL_FIXED_FRM_FLG)) { + self->rate_control = (req.bRateControlMode & + ~UVC_H264_RATECONTROL_FIXED_FRM_FLG); + g_object_notify (G_OBJECT (self), "rate-control"); + } + if (self->fixed_framerate != ((req.bRateControlMode & + UVC_H264_RATECONTROL_FIXED_FRM_FLG) != 0)) { + self->fixed_framerate = ((req.bRateControlMode & + UVC_H264_RATECONTROL_FIXED_FRM_FLG) != 0); + g_object_notify (G_OBJECT (self), "fixed-framerate"); + } +} + + +static guint32 +update_level_idc_and_get_max_mbps (GstUvcH264Src * self) +{ + uvcx_video_advance_config_t req; + + if (!xu_query (self, UVCX_VIDEO_ADVANCE_CONFIG, UVC_GET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " VIDEO_ADVANCE_CONFIG GET_CUR error"); + return 0; + } + + if (self->level_idc != req.blevel_idc) { + self->level_idc = req.blevel_idc; + g_object_notify (G_OBJECT (self), "level-idc"); + } + return req.dwMb_max; +} + +static void +update_bitrate (GstUvcH264Src * self) +{ + uvcx_bitrate_layers_t req; + + if (!xu_query (self, UVCX_BITRATE_LAYERS, UVC_GET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " BITRATE_LAYERS GET_CUR error"); + return; + } + if (self->peak_bitrate != req.dwPeakBitrate) { + self->peak_bitrate = req.dwPeakBitrate; + g_object_notify (G_OBJECT (self), "peak-bitrate"); + } + if (self->average_bitrate != req.dwAverageBitrate) { + self->average_bitrate = req.dwAverageBitrate; + g_object_notify (G_OBJECT (self), "average-bitrate"); + } +} + +static gboolean +update_qp (GstUvcH264Src * self, gint type) +{ + uvcx_qp_steps_layers_t req; + guint8 frame_type; + + req.wLayerID = 0; + switch (type) { + case QP_I_FRAME: + frame_type = UVC_H264_QP_STEPS_I_FRAME_TYPE; + break; + case QP_P_FRAME: + frame_type = UVC_H264_QP_STEPS_P_FRAME_TYPE; + break; + case QP_B_FRAME: + frame_type = UVC_H264_QP_STEPS_B_FRAME_TYPE; + break; + default: + return FALSE; + } + req.bFrameType = frame_type; + req.bMinQp = 0; + req.bMaxQp = 0; + if (!xu_query (self, UVCX_QP_STEPS_LAYERS, UVC_SET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " QP_STEPS_LAYERS SET_CUR error"); + return FALSE; + } + + if (!xu_query (self, UVCX_QP_STEPS_LAYERS, UVC_GET_CUR, (guchar *) & req)) { + GST_WARNING_OBJECT (self, " QP_STEPS_LAYERS GET_CUR error"); + return FALSE; + } + + if (req.bFrameType == frame_type) { + if (self->min_qp[type] != req.bMinQp) { + self->min_qp[type] = req.bMinQp; + switch (type) { + case QP_I_FRAME: + g_object_notify (G_OBJECT (self), "min-iframe-qp"); + break; + case QP_P_FRAME: + g_object_notify (G_OBJECT (self), "min-pframe-qp"); + break; + case QP_B_FRAME: + g_object_notify (G_OBJECT (self), "min-bframe-qp"); + break; + default: + break; + } + } + if (self->max_qp[type] != req.bMaxQp) { + self->max_qp[type] = req.bMaxQp; + switch (type) { + case QP_I_FRAME: + g_object_notify (G_OBJECT (self), "max-iframe-qp"); + break; + case QP_P_FRAME: + g_object_notify (G_OBJECT (self), "max-pframe-qp"); + break; + case QP_B_FRAME: + g_object_notify (G_OBJECT (self), "max-bframe-qp"); + break; + default: + break; + } + } + return TRUE; + } else { + self->min_qp[type] = 0xFF; + self->max_qp[type] = 0xFF; + return FALSE; + } +} + +static void +update_ltr (GstUvcH264Src * self) +{ + uvcx_ltr_buffer_size_control_t req; + + if (!xu_query (self, UVCX_LTR_BUFFER_SIZE_CONTROL, UVC_GET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " LTR_BUFFER_SIZE GET_CUR error"); + return; + } + + if (self->ltr_buffer_size != req.bLTRBufferSize) { + self->ltr_buffer_size = req.bLTRBufferSize; + g_object_notify (G_OBJECT (self), "ltr-buffer-size"); + } + if (self->ltr_encoder_control != req.bLTREncoderControl) { + self->ltr_encoder_control = req.bLTREncoderControl; + g_object_notify (G_OBJECT (self), "ltr-encoder-control"); + } +} + +#define STORE_MIN_DEF_MAX(type) \ + *(type *)min = *((type *) (min_p + offset)); \ + *(type *)def = *((type *) (def_p + offset)); \ + *(type *)max = *((type *) (max_p + offset)); + +static gboolean +probe_setting (GstUvcH264Src * self, uvcx_control_selector_t selector, + guint offset, gint size, gpointer min, gpointer def, gpointer max) +{ + guchar *min_p, *def_p, *max_p; + gboolean ret = FALSE; + __u16 len; + + if (!xu_query (self, selector, UVC_GET_LEN, (guchar *) & len)) { + GST_WARNING_OBJECT (self, "probe_setting GET_LEN error"); + return FALSE; + } + min_p = g_malloc0 (len); + def_p = g_malloc0 (len); + max_p = g_malloc0 (len); + + if (!xu_query (self, selector, UVC_GET_MIN, min_p)) { + GST_WARNING_OBJECT (self, "probe_setting GET_MIN error"); + goto end; + } + if (!xu_query (self, selector, UVC_GET_DEF, def_p)) { + GST_WARNING_OBJECT (self, "probe_setting GET_DEF error"); + goto end; + } + if (!xu_query (self, selector, UVC_GET_MAX, max_p)) { + GST_WARNING_OBJECT (self, "probe_setting GET_MAX error"); + goto end; + } + + switch (size) { + case -1: + STORE_MIN_DEF_MAX (gint8); + ret = TRUE; + break; + case 1: + STORE_MIN_DEF_MAX (guint8); + ret = TRUE; + break; + case -2: + STORE_MIN_DEF_MAX (gint16); + ret = TRUE; + break; + case 2: + STORE_MIN_DEF_MAX (guint16); + ret = TRUE; + break; + case -4: + STORE_MIN_DEF_MAX (gint32); + ret = TRUE; + break; + case 4: + STORE_MIN_DEF_MAX (guint32); + ret = TRUE; + break; + default: + break; + } + +end: + g_free (min_p); + g_free (def_p); + g_free (max_p); + + return ret; +} + +static gboolean +test_enum_setting (GstUvcH264Src * self, guint offset, guint size, + guint16 value) +{ + uvcx_video_config_probe_commit_t cur; + uvcx_video_config_probe_commit_t req; + guchar *req_p = (guchar *) & req; + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, (guchar *) & cur)) { + GST_WARNING_OBJECT (self, " GET_CUR error"); + return FALSE; + } + + req = cur; + + if (size == 1) + *((guint8 *) (req_p + offset)) = (guint8) value; + else + *((guint16 *) (req_p + offset)) = value; + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_SET_CUR, req_p)) { + GST_WARNING_OBJECT (self, " SET_CUR error"); + return FALSE; + } + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, req_p)) { + GST_WARNING_OBJECT (self, " GET_CUR error"); + return FALSE; + } + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_SET_CUR, (guchar *) & cur)) { + GST_WARNING_OBJECT (self, " SET_CUR error"); + return FALSE; + } + + if (size == 1) + return *((guint8 *) (req_p + offset)) == (guint8) value; + else + return *((guint16 *) (req_p + offset)) == value; +} + +static gboolean +gst_uvc_h264_src_get_enum_setting (GstUvcH264Src * self, gchar * property, + gint * mask, gint * default_value) +{ + guint8 min, def, max; + guint8 en; + gboolean ret = FALSE; + + if (g_strcmp0 (property, "slice-mode") == 0) { + guint16 min16, def16, max16; + guint16 en16; + + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, wSliceMode), 2, + &min16, &def16, &max16); + if (ret) { + *default_value = def16; + *mask = 0; + for (en16 = min16; en16 <= max16; en16++) { + if (test_enum_setting (self, offsetof (uvcx_video_config_probe_commit_t, + wSliceMode), 2, en16)) + *mask |= (1 << en16); + } + } + } else if (g_strcmp0 (property, "usage-type") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bUsageType), 1, + &min, &def, &max); + if (ret) { + *default_value = def; + *mask = 0; + for (en = min; en <= max; en++) { + if (test_enum_setting (self, offsetof (uvcx_video_config_probe_commit_t, + bUsageType), 1, en)) + *mask |= (1 << en); + } + } + } else if (g_strcmp0 (property, "entropy") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bEntropyCABAC), 1, + &min, &def, &max); + if (ret) { + *mask = (1 << min) | (1 << max); + *default_value = def; + } + } else if (g_strcmp0 (property, "rate-control") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bRateControlMode), 1, + &min, &def, &max); + if (ret) { + uvcx_rate_control_mode_t cur; + + *default_value = def; + *mask = 0; + + xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_GET_CUR, (guchar *) & cur); + + for (en = min; en <= max; en++) { + uvcx_rate_control_mode_t req = { 0, en }; + + if (xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_SET_CUR, + (guchar *) & req) && + xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_GET_CUR, + (guchar *) & req) && req.bRateControlMode == en) + *mask |= (1 << en); + } + xu_query (self, UVCX_RATE_CONTROL_MODE, UVC_SET_CUR, (guchar *) & cur); + } + } + + return ret; +} + +static gboolean +gst_uvc_h264_src_get_boolean_setting (GstUvcH264Src * self, gchar * property, + gboolean * changeable, gboolean * default_value) +{ + guint8 min, def, max; + gboolean ret = FALSE; + + if (g_strcmp0 (property, "enable-sei") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bTimestamp), 1, + &min, &def, &max); + *changeable = (min != max); + *default_value = (def != 0); + } else if (g_strcmp0 (property, "preview-flipped") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bPreviewFlipped), 1, + &min, &def, &max); + *changeable = (min != max); + *default_value = (def != 0); + } else if (g_strcmp0 (property, "fixed-framerate") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bRateControlMode), 1, + &min, &def, &max); + *changeable = ((max & UVC_H264_RATECONTROL_FIXED_FRM_FLG) != 0); + *default_value = ((def & UVC_H264_RATECONTROL_FIXED_FRM_FLG) != 0); + } + + return ret; +} + +static gboolean +gst_uvc_h264_src_get_int_setting (GstUvcH264Src * self, gchar * property, + gint * min, gint * def, gint * max) +{ + guint32 min32, def32, max32; + guint16 min16, def16, max16; + guint8 min8, def8, max8; + gint8 smin8, sdef8, smax8; + gboolean ret = FALSE; + + GST_DEBUG_OBJECT (self, "Probing int property %s", property); + if (g_strcmp0 (property, "initial-bitrate") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, dwBitRate), 4, + &min32, &def32, &max32); + *min = min32; + *def = def32; + *max = max32; + } else if (g_strcmp0 (property, "slice-units") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, wSliceUnits), 2, + &min16, &def16, &max16); + *min = min16; + *def = def16; + *max = max16; + } else if (g_strcmp0 (property, "iframe-period") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, wIFramePeriod), 2, + &min16, &def16, &max16); + *min = min16; + *def = def16; + *max = max16; + } else if (g_strcmp0 (property, "num-reorder-frames") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, bNumOfReorderFrames), 1, + &min8, &def8, &max8); + *min = min8; + *def = def8; + *max = max8; + } else if (g_strcmp0 (property, "leaky-bucket-size") == 0) { + ret = probe_setting (self, UVCX_VIDEO_CONFIG_PROBE, + offsetof (uvcx_video_config_probe_commit_t, wLeakyBucketSize), 2, + &min16, &def16, &max16); + *min = min16; + *def = def16; + *max = max16; + } else if (g_strcmp0 (property, "level-idc") == 0) { + ret = probe_setting (self, UVCX_VIDEO_ADVANCE_CONFIG, + offsetof (uvcx_video_advance_config_t, blevel_idc), 1, + &min8, &def8, &max8); + *min = min8; + *def = def8; + *max = max8; + } else if (g_strcmp0 (property, "max-mbps") == 0) { + ret = probe_setting (self, UVCX_VIDEO_ADVANCE_CONFIG, + offsetof (uvcx_video_advance_config_t, dwMb_max), 4, + &min32, &def32, &max32); + *min = min32; + *def = def32; + *max = max32; + } else if (g_strcmp0 (property, "peak-bitrate") == 0) { + ret = probe_setting (self, UVCX_BITRATE_LAYERS, + offsetof (uvcx_bitrate_layers_t, dwPeakBitrate), 4, + &min32, &def32, &max32); + *min = min32; + *def = def32; + *max = max32; + } else if (g_strcmp0 (property, "average-bitrate") == 0) { + ret = probe_setting (self, UVCX_BITRATE_LAYERS, + offsetof (uvcx_bitrate_layers_t, dwAverageBitrate), 4, + &min32, &def32, &max32); + *min = min32; + *def = def32; + *max = max32; + } else if (g_strcmp0 (property, "min-iframe-qp") == 0) { + if (update_qp (self, QP_I_FRAME)) + ret = probe_setting (self, UVCX_QP_STEPS_LAYERS, + offsetof (uvcx_qp_steps_layers_t, bMinQp), 1, &smin8, &sdef8, &smax8); + *min = smin8; + *def = sdef8; + *max = smax8; + } else if (g_strcmp0 (property, "max-iframe-qp") == 0) { + if (update_qp (self, QP_I_FRAME)) + ret = probe_setting (self, UVCX_QP_STEPS_LAYERS, + offsetof (uvcx_qp_steps_layers_t, bMaxQp), 1, &smin8, &sdef8, &smax8); + *min = smin8; + *def = sdef8; + *max = smax8; + } else if (g_strcmp0 (property, "min-pframe-qp") == 0) { + if (update_qp (self, QP_P_FRAME)) + ret = probe_setting (self, UVCX_QP_STEPS_LAYERS, + offsetof (uvcx_qp_steps_layers_t, bMinQp), 1, &smin8, &sdef8, &smax8); + *min = smin8; + *def = sdef8; + *max = smax8; + } else if (g_strcmp0 (property, "max-pframe-qp") == 0) { + if (update_qp (self, QP_P_FRAME)) + ret = probe_setting (self, UVCX_QP_STEPS_LAYERS, + offsetof (uvcx_qp_steps_layers_t, bMaxQp), 1, &smin8, &sdef8, &smax8); + *min = smin8; + *def = sdef8; + *max = smax8; + } else if (g_strcmp0 (property, "min-bframe-qp") == 0) { + if (update_qp (self, QP_B_FRAME)) + ret = probe_setting (self, UVCX_QP_STEPS_LAYERS, + offsetof (uvcx_qp_steps_layers_t, bMinQp), 1, &smin8, &sdef8, &smax8); + *min = smin8; + *def = sdef8; + *max = smax8; + } else if (g_strcmp0 (property, "max-bframe-qp") == 0) { + if (update_qp (self, QP_B_FRAME)) + ret = probe_setting (self, UVCX_QP_STEPS_LAYERS, + offsetof (uvcx_qp_steps_layers_t, bMaxQp), 1, &smin8, &sdef8, &smax8); + *min = smin8; + *def = sdef8; + *max = smax8; + } else if (g_strcmp0 (property, "ltr-buffer-size") == 0) { + ret = probe_setting (self, UVCX_LTR_BUFFER_SIZE_CONTROL, + offsetof (uvcx_ltr_buffer_size_control_t, bLTRBufferSize), 1, + &min8, &def8, &max8); + *min = min8; + *def = def8; + *max = max8; + } else if (g_strcmp0 (property, "ltr-encoder-control") == 0) { + ret = probe_setting (self, UVCX_LTR_BUFFER_SIZE_CONTROL, + offsetof (uvcx_ltr_buffer_size_control_t, bLTREncoderControl), 1, + &min8, &def8, &max8); + *min = min8; + *def = def8; + *max = max8; + } + + return ret; +} + +static gboolean +gst_uvc_h264_src_event_probe (GstPad * pad, GstEvent * event, + gpointer user_data) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (user_data); + gboolean ret = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + ret = !self->reconfiguring; + break; + case GST_EVENT_NEWSEGMENT: + if (pad == self->vidsrc) { + ret = !self->vid_newseg; + self->vid_newseg = TRUE; + } else if (pad == self->vfsrc) { + ret = !self->vf_newseg; + self->vf_newseg = TRUE; + } + break; + default: + break; + } + + return ret; +} + +static gboolean +gst_uvc_h264_src_buffer_probe (GstPad * pad, GstBuffer * buffer, + gpointer user_data) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (user_data); + + /* TODO: Check the NALU type and make sure it is a keyframe */ + if (self->key_unit_event) { + GstClockTime ts, running_time, stream_time; + gboolean all_headers; + guint count; + GstEvent *downstream; + + if (gst_video_event_parse_upstream_force_key_unit (self->key_unit_event, + &ts, &all_headers, &count)) { + if (!GST_CLOCK_TIME_IS_VALID (ts)) { + ts = GST_BUFFER_TIMESTAMP (buffer); + } + running_time = gst_segment_to_running_time (&self->segment, + GST_FORMAT_TIME, ts); + + stream_time = gst_segment_to_stream_time (&self->segment, + GST_FORMAT_TIME, ts); + + GST_DEBUG_OBJECT (self, "Sending downstream force-key-unit : %d - %d ts=%" + GST_TIME_FORMAT " running time =%" GST_TIME_FORMAT " stream=%" + GST_TIME_FORMAT, all_headers, count, GST_TIME_ARGS (ts), + GST_TIME_ARGS (running_time), GST_TIME_ARGS (stream_time)); + downstream = gst_video_event_new_downstream_force_key_unit (ts, + running_time, stream_time, all_headers, count); + gst_pad_push_event (self->vidsrc, downstream); + gst_event_replace (&self->key_unit_event, NULL); + } + } + return TRUE; +} + +static gboolean +gst_uvc_h264_src_parse_event (GstUvcH264Src * self, GstPad * pad, + GstEvent * event) +{ + const GstStructure *s = gst_event_get_structure (event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + if (pad == self->vidsrc && self->main_format == UVC_H264_SRC_FORMAT_H264) { + if (gst_video_event_is_force_key_unit (event)) { + uvcx_picture_type_control_t req = { 0, 0 }; + GstClockTime ts; + gboolean all_headers; + + if (gst_video_event_parse_upstream_force_key_unit (event, + &ts, &all_headers, NULL)) { + GST_INFO_OBJECT (self, "Received upstream force-key-unit : %d %" + GST_TIME_FORMAT, all_headers, GST_TIME_ARGS (ts)); + /* TODO: wait until 'ts' time is reached */ + if (all_headers) + req.wPicType = UVC_H264_PICTYPE_IDR_WITH_PPS_SPS; + else + req.wPicType = UVC_H264_PICTYPE_IDR; + + if (!xu_query (self, UVCX_PICTURE_TYPE_CONTROL, UVC_SET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " PICTURE_TYPE_CONTROL SET_CUR error"); + } else { + gst_event_replace (&self->key_unit_event, event); + gst_event_unref (event); + + return TRUE; + } + } + } else if (s && + gst_structure_has_name (s, "uvc-h264-ltr-picture-control")) { + guint put_at, encode_using; + + if (gst_structure_get_uint (s, "put-at", &put_at) && + gst_structure_get_uint (s, "encode-using", &encode_using)) { + uvcx_ltr_picture_control req = { 0, put_at, encode_using }; + + if (!xu_query (self, UVCX_LTR_PICTURE_CONTROL, UVC_SET_CUR, + (guchar *) & req)) { + GST_WARNING_OBJECT (self, " LTR PICTURE_CONTROL SET_CUR error"); + } else { + gst_event_unref (event); + + return TRUE; + } + } + return TRUE; + } else if (s && gst_structure_has_name (s, "uvc-h264-bitrate-control")) { + guint average, peak; + + if (gst_structure_get_uint (s, "average-bitrate", &average) && + gst_structure_get_uint (s, "peak-bitrate", &peak)) { + self->average_bitrate = average; + self->peak_bitrate = peak; + set_bitrate (self); + update_bitrate (self); + + gst_event_unref (event); + + return TRUE; + } + } else if (s && gst_structure_has_name (s, "uvc-h264-qp-control")) { + gint min_qp, max_qp; + gboolean valid_event = FALSE; + + if (gst_structure_get_int (s, "min-iframe-qp", &min_qp) && + gst_structure_get_int (s, "max-iframe-qp", &max_qp)) { + self->min_qp[QP_I_FRAME] = min_qp; + self->max_qp[QP_I_FRAME] = max_qp; + set_qp (self, QP_I_FRAME); + update_qp (self, QP_I_FRAME); + valid_event = TRUE; + } + if (gst_structure_get_int (s, "min-pframe-qp", &min_qp) && + gst_structure_get_int (s, "max-pframe-qp", &max_qp)) { + self->min_qp[QP_P_FRAME] = min_qp; + self->max_qp[QP_P_FRAME] = max_qp; + set_qp (self, QP_P_FRAME); + update_qp (self, QP_P_FRAME); + valid_event = TRUE; + } + if (gst_structure_get_int (s, "min-bframe-qp", &min_qp) && + gst_structure_get_int (s, "max-bframe-qp", &max_qp)) { + self->min_qp[QP_B_FRAME] = min_qp; + self->max_qp[QP_B_FRAME] = max_qp; + set_qp (self, QP_B_FRAME); + update_qp (self, QP_B_FRAME); + valid_event = TRUE; + } + + if (valid_event) { + gst_event_unref (event); + + return TRUE; + } + } else if (s && gst_structure_has_name (s, "uvc-h264-rate-control")) { + UvcH264RateControl rate; + gboolean fixed_framerate; + + if (gst_structure_get_enum (s, "rate-control", + UVC_H264_RATECONTROL_TYPE, (gint *) & rate) && + gst_structure_get_boolean (s, "fixed-framerate", + &fixed_framerate)) { + self->rate_control = rate; + self->fixed_framerate = fixed_framerate; + set_rate_control (self); + update_rate_control (self); + + gst_event_unref (event); + + return TRUE; + } + } else if (s && gst_structure_has_name (s, "uvc-h264-level-idc")) { + guint level_idc; + + if (gst_structure_get_uint (s, "level-idc", &level_idc)) { + self->level_idc = level_idc; + set_level_idc (self); + update_level_idc_and_get_max_mbps (self); + + gst_event_unref (event); + } + } + } + if (s && gst_structure_has_name (s, "renegotiate")) { + GST_DEBUG_OBJECT (self, "Received renegotiate on %s", + GST_PAD_NAME (pad)); + /* TODO: Do not reconstruct pipeline twice if we receive + the event on both pads */ + if (GST_STATE (self) >= GST_STATE_READY) { + /* TODO: diff the caps */ + gst_uvc_h264_src_construct_pipeline (GST_BASE_CAMERA_SRC (self)); + } + return TRUE; + } + break; + default: + break; + } + + return FALSE; +} + +static gboolean +gst_uvc_h264_src_send_event (GstElement * element, GstEvent * event) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (element); + + if (gst_uvc_h264_src_parse_event (self, self->vidsrc, event)) + return TRUE; + + return GST_ELEMENT_CLASS (parent_class)->send_event (element, event); +} + +static gboolean +gst_uvc_h264_src_event (GstPad * pad, GstEvent * event) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (GST_PAD_PARENT (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + if (!self->vid_newseg && pad == self->vidsrc) { + gboolean update; + gdouble rate, applied_rate; + GstFormat format; + gint64 start, stop, position; + + 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); + } + break; + case GST_EVENT_FLUSH_STOP: + if (pad == self->vidsrc) { + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + self->vid_newseg = FALSE; + } + if (pad == self->vfsrc) + self->vf_newseg = FALSE; + break; + default: + if (gst_uvc_h264_src_parse_event (self, pad, event)) + return TRUE; + break; + } + return self->srcpad_event_func (pad, event); +} + +static guint8 +xu_get_id (GstUvcH264Src * self) +{ + struct uvc_xu_find_unit xu; + static const __u8 guid[16] = GUID_UVCX_H264_XU; + + if (self->v4l2_fd == -1) { + GST_WARNING_OBJECT (self, "Can't query XU with fd = -1"); + return 0; + } + + memcpy (xu.guid, guid, 16); + xu.unit = 0; + + if (-1 == ioctl (self->v4l2_fd, UVCIOC_XU_FIND_UNIT, &xu)) { +#if defined (HAVE_GUDEV) && defined (HAVE_LIBUSB) + /* Fallback on libusb */ + GUdevClient *client; + GUdevDevice *udevice; + GUdevDevice *parent; + guint64 busnum; + guint64 devnum; + libusb_device **device_list = NULL; + libusb_device *device = NULL; + ssize_t cnt; + int i, j, k; + + GST_DEBUG_OBJECT (self, "XU_FIND_UNIT ioctl failed. Fallback on libusb"); + + if (self->usb_ctx == NULL) + libusb_init (&self->usb_ctx); + + client = g_udev_client_new (NULL); + if (client) { + udevice = g_udev_client_query_by_device_file (client, self->device); + if (udevice) { + parent = g_udev_device_get_parent_with_subsystem (udevice, "usb", + "usb_device"); + if (parent) { + busnum = g_udev_device_get_sysfs_attr_as_uint64 (parent, "busnum"); + devnum = g_udev_device_get_sysfs_attr_as_uint64 (parent, "devnum"); + + cnt = libusb_get_device_list (self->usb_ctx, &device_list); + for (i = 0; i < cnt; i++) { + if (busnum == libusb_get_bus_number (device_list[i]) && + devnum == libusb_get_device_address (device_list[i])) { + device = libusb_ref_device (device_list[i]); + break; + } + } + libusb_free_device_list (device_list, 1); + g_object_unref (parent); + } + g_object_unref (udevice); + } + g_object_unref (client); + } + + if (device) { + struct libusb_device_descriptor desc; + + if (libusb_get_device_descriptor (device, &desc) == 0) { + for (i = 0; i < desc.bNumConfigurations; ++i) { + struct libusb_config_descriptor *config = NULL; + + if (libusb_get_config_descriptor (device, i, &config) == 0) { + for (j = 0; j < config->bNumInterfaces; j++) { + for (k = 0; k < config->interface[j].num_altsetting; k++) { + const struct libusb_interface_descriptor *interface; + const guint8 *ptr = NULL; + + interface = &config->interface[j].altsetting[k]; + if (interface->bInterfaceClass != LIBUSB_CLASS_VIDEO || + interface->bInterfaceSubClass != USB_VIDEO_CONTROL) + continue; + ptr = interface->extra; + while (ptr - interface->extra + + sizeof (xu_descriptor) < interface->extra_length) { + xu_descriptor *desc = (xu_descriptor *) ptr; + + GST_DEBUG_OBJECT (self, "Found VideoControl interface with " + "unit id %d : %" GUID_FORMAT, desc->bUnitID, + GUID_ARGS (desc->guidExtensionCode)); + if (desc->bDescriptorType == USB_VIDEO_CONTROL_INTERFACE && + desc->bDescriptorSubType == USB_VIDEO_CONTROL_XU_TYPE && + memcmp (desc->guidExtensionCode, guid, 16) == 0) { + guint8 unit_id = desc->bUnitID; + + GST_DEBUG_OBJECT (self, "Found H264 XU unit : %d", unit_id); + + libusb_unref_device (device); + return unit_id; + } + ptr += desc->bLength; + } + } + } + } + } + } + libusb_unref_device (device); + } +#else + GST_WARNING_OBJECT (self, "XU_FIND_UNIT ioctl failed"); +#endif + return 0; + } + + return xu.unit; +} + +static gboolean +xu_query (GstUvcH264Src * self, guint selector, guint query, guchar * data) +{ + struct uvc_xu_control_query xu; + __u16 len; + + if (self->v4l2_fd == -1) { + GST_WARNING_OBJECT (self, "Can't query XU with fd = -1"); + return FALSE; + } + + xu.unit = self->h264_unit_id; + xu.selector = selector; + + xu.query = UVC_GET_LEN; + xu.size = sizeof (len); + xu.data = (unsigned char *) &len; + if (-1 == ioctl (self->v4l2_fd, UVCIOC_CTRL_QUERY, &xu)) { + GST_WARNING_OBJECT (self, "PROBE GET_LEN error"); + return FALSE; + } + + if (query == UVC_GET_LEN) { + *((__u16 *) data) = len; + } else { + xu.query = query; + xu.size = len; + xu.data = data; + if (-1 == ioctl (self->v4l2_fd, UVCIOC_CTRL_QUERY, &xu)) { + return FALSE; + } + } + + return TRUE; +} + +static void +fill_probe_commit (GstUvcH264Src * self, + uvcx_video_config_probe_commit_t * probe, guint32 frame_interval, + guint32 width, guint32 height, guint32 profile, + UvcH264StreamFormat stream_format) +{ + probe->dwFrameInterval = frame_interval; + probe->dwBitRate = self->initial_bitrate; + probe->wWidth = width; + probe->wHeight = height; + probe->wSliceUnits = self->slice_units; + probe->wSliceMode = self->slice_mode; + probe->wProfile = profile; + probe->wIFramePeriod = self->iframe_period; + probe->bUsageType = self->usage_type; + probe->bRateControlMode = self->rate_control; + if (self->fixed_framerate) + probe->bRateControlMode |= UVC_H264_RATECONTROL_FIXED_FRM_FLG; + probe->bStreamFormat = stream_format; + probe->bEntropyCABAC = self->entropy; + probe->bTimestamp = self->enable_sei ? + UVC_H264_TIMESTAMP_SEI_ENABLE : UVC_H264_TIMESTAMP_SEI_DISABLE; + probe->bNumOfReorderFrames = self->num_reorder_frames; + probe->bPreviewFlipped = self->preview_flipped ? + UVC_H264_PREFLIPPED_HORIZONTAL : UVC_H264_PREFLIPPED_DISABLE; + probe->wLeakyBucketSize = self->leaky_bucket_size; +} + +static void +print_probe_commit (GstUvcH264Src * self, + uvcx_video_config_probe_commit_t * probe) +{ + GST_DEBUG_OBJECT (self, " Frame interval : %d *100ns", + probe->dwFrameInterval); + GST_DEBUG_OBJECT (self, " Bit rate : %d", probe->dwBitRate); + GST_DEBUG_OBJECT (self, " Hints : %X", probe->bmHints); + GST_DEBUG_OBJECT (self, " Configuration index : %d", + probe->wConfigurationIndex); + GST_DEBUG_OBJECT (self, " Width : %d", probe->wWidth); + GST_DEBUG_OBJECT (self, " Height : %d", probe->wHeight); + GST_DEBUG_OBJECT (self, " Slice units : %d", probe->wSliceUnits); + GST_DEBUG_OBJECT (self, " Slice mode : %X", probe->wSliceMode); + GST_DEBUG_OBJECT (self, " Profile : %X", probe->wProfile); + GST_DEBUG_OBJECT (self, " IFrame Period : %d ms", probe->wIFramePeriod); + GST_DEBUG_OBJECT (self, " Estimated video delay : %d ms", + probe->wEstimatedVideoDelay); + GST_DEBUG_OBJECT (self, " Estimated max config delay : %d ms", + probe->wEstimatedMaxConfigDelay); + GST_DEBUG_OBJECT (self, " Usage type : %X", probe->bUsageType); + GST_DEBUG_OBJECT (self, " Rate control mode : %X", probe->bRateControlMode); + GST_DEBUG_OBJECT (self, " Temporal scale mode : %X", + probe->bTemporalScaleMode); + GST_DEBUG_OBJECT (self, " Spatial scale mode : %X", + probe->bSpatialScaleMode); + GST_DEBUG_OBJECT (self, " SNR scale mode : %X", probe->bSNRScaleMode); + GST_DEBUG_OBJECT (self, " Stream mux option : %X", probe->bStreamMuxOption); + GST_DEBUG_OBJECT (self, " Stream Format : %X", probe->bStreamFormat); + GST_DEBUG_OBJECT (self, " Entropy CABAC : %X", probe->bEntropyCABAC); + GST_DEBUG_OBJECT (self, " Timestamp : %X", probe->bTimestamp); + GST_DEBUG_OBJECT (self, " Num of reorder frames : %d", + probe->bNumOfReorderFrames); + GST_DEBUG_OBJECT (self, " Preview flipped : %X", probe->bPreviewFlipped); + GST_DEBUG_OBJECT (self, " View : %d", probe->bView); + GST_DEBUG_OBJECT (self, " Stream ID : %X", probe->bStreamID); + GST_DEBUG_OBJECT (self, " Spatial layer ratio : %f", + ((probe->bSpatialLayerRatio & 0xF0) >> 4) + + ((float) (probe->bSpatialLayerRatio & 0x0F)) / 16); + GST_DEBUG_OBJECT (self, " Leaky bucket size : %d ms", + probe->wLeakyBucketSize); +} + +static void +configure_h264 (GstUvcH264Src * self, gint fd) +{ + uvcx_video_config_probe_commit_t probe; + + /* Set the secondary format first, so the last SET_CUR will be for the + * H264 format. This way, we can still get the static control values with + * a GET_CUR. Otherwise all static properties will return 0 because that's + * what the GET_CUR of the raw format returns. + */ + if (self->secondary_format == UVC_H264_SRC_FORMAT_RAW) { + memset (&probe, 0, sizeof (probe)); + probe.dwFrameInterval = self->secondary_frame_interval; + probe.wWidth = self->secondary_width; + probe.wHeight = self->secondary_height; + probe.bStreamMuxOption = 5; + + GST_DEBUG_OBJECT (self, "RAW PROBE SET_CUR : "); + print_probe_commit (self, &probe); + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_SET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE SET_CUR error"); + return; + } + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return; + } + GST_DEBUG_OBJECT (self, "RAW PROBE GET_CUR : "); + print_probe_commit (self, &probe); + + if (!xu_query (self, UVCX_VIDEO_CONFIG_COMMIT, UVC_SET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "COMMIT SET_CUR error"); + return; + } + } + /* Print MIN/MAX/DEF probe values for debugging purposes */ + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_MIN, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return; + } + GST_DEBUG_OBJECT (self, "PROBE GET_MIN : "); + print_probe_commit (self, &probe); + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_MAX, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return; + } + GST_DEBUG_OBJECT (self, "PROBE GET_MAX : "); + print_probe_commit (self, &probe); + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_DEF, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return; + } + GST_DEBUG_OBJECT (self, "PROBE GET_DEF : "); + print_probe_commit (self, &probe); + + fill_probe_commit (self, &probe, self->main_frame_interval, + self->main_width, self->main_height, self->main_profile, + self->main_stream_format); + if (self->secondary_format != UVC_H264_SRC_FORMAT_NONE) + probe.bStreamMuxOption = 3; + else + probe.bStreamMuxOption = 0; + probe.bmHints = UVC_H264_BMHINTS_RESOLUTION | UVC_H264_BMHINTS_PROFILE | + UVC_H264_BMHINTS_FRAME_INTERVAL; + + GST_DEBUG_OBJECT (self, "PROBE SET_CUR : "); + print_probe_commit (self, &probe); + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_SET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE SET_CUR error"); + return; + } + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return; + } + GST_DEBUG_OBJECT (self, "PROBE GET_CUR : "); + print_probe_commit (self, &probe); + + /* Must validate the settings accepted by the encoder */ + if (!xu_query (self, UVCX_VIDEO_CONFIG_COMMIT, UVC_SET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "COMMIT SET_CUR error"); + return; + } +} + +static void +v4l2src_prepare_format (GstElement * v4l2src, gint fd, guint fourcc, + guint width, guint height, 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); + + /* TODO: update dynamic controls on READY state */ + /* Configure dynamic controls */ + set_rate_control (self); + update_rate_control (self); + set_level_idc (self); + update_level_idc_and_get_max_mbps (self); + set_bitrate (self); + update_bitrate (self); + set_qp (self, QP_I_FRAME); + update_qp (self, QP_I_FRAME); + set_qp (self, QP_P_FRAME); + update_qp (self, QP_P_FRAME); + set_qp (self, QP_B_FRAME); + update_qp (self, QP_B_FRAME); + set_ltr (self); + update_ltr (self); + } +} + +static gboolean +_extract_caps_info (GstStructure * structure, guint16 * width, guint16 * height, + guint32 * frame_interval) +{ + gint w, h, fps_n, fps_d; + gboolean ret = TRUE; + + ret &= gst_structure_get_int (structure, "width", &w); + ret &= gst_structure_get_int (structure, "height", &h); + ret &= gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d); + + if (ret) { + *width = w; + *height = h; + /* Interval is in 100ns */ + *frame_interval = GST_TIME_AS_NSECONDS ((fps_d * GST_SECOND) / fps_n) / 100; + } + + return ret; +} + +static guint16 +_extract_profile (GstStructure * structure) +{ + const gchar *profile_str; + guint16 profile; + + profile = UVC_H264_PROFILE_HIGH; + profile_str = gst_structure_get_string (structure, "profile"); + if (profile_str) { + if (!strcmp (profile_str, "constrained-baseline")) { + profile = UVC_H264_PROFILE_CONSTRAINED_BASELINE; + } else if (!strcmp (profile_str, "baseline")) { + profile = UVC_H264_PROFILE_BASELINE; + } else if (!strcmp (profile_str, "main")) { + profile = UVC_H264_PROFILE_MAIN; + } else if (!strcmp (profile_str, "high")) { + profile = UVC_H264_PROFILE_HIGH; + } + } + return profile; +} + +static UvcH264StreamFormat +_extract_stream_format (GstStructure * structure) +{ + const gchar *stream_format; + + stream_format = gst_structure_get_string (structure, "stream-format"); + if (stream_format) { + if (!strcmp (stream_format, "avc")) + return UVC_H264_STREAMFORMAT_NAL; + else if (!strcmp (stream_format, "byte-stream")) + return UVC_H264_STREAMFORMAT_ANNEXB; + } + return UVC_H264_STREAMFORMAT_ANNEXB; +} + +static GstCaps * +_transform_caps (GstUvcH264Src * self, GstCaps * caps, const gchar * name) +{ + GstElement *el = gst_element_factory_make (name, NULL); + GstElement *cf = gst_element_factory_make ("capsfilter", NULL); + GstPad *sink; + + if (!el || !cf || !gst_bin_add (GST_BIN (self), el)) { + if (el) + gst_object_unref (el); + if (cf) + gst_object_unref (cf); + goto done; + } + if (!gst_bin_add (GST_BIN (self), cf)) { + gst_object_unref (cf); + gst_bin_remove (GST_BIN (self), el); + goto done; + } + if (!gst_element_link (el, cf)) + goto error_remove; + + sink = gst_element_get_static_pad (el, "sink"); + if (!sink) + goto error_remove; + g_object_set (cf, "caps", caps, NULL); + + caps = gst_pad_get_caps (sink); + gst_object_unref (sink); + +error_remove: + gst_bin_remove (GST_BIN (self), cf); + gst_bin_remove (GST_BIN (self), el); + +done: + return caps; +} + +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_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); + + 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; + } + + if (h264_caps) + gst_caps_unref (h264_caps); + if (jpg_caps) + gst_caps_unref (jpg_caps); + gst_caps_unref (h264); + gst_caps_unref (jpg); + + + return caps; +} + +static GstCaps * +gst_uvc_h264_src_fixate_caps (GstUvcH264Src * self, GstPad * v4l_pad, + GstCaps * v4l_caps, GstCaps * peer_caps, gboolean primary) +{ + GstCaps *caps = NULL; + GstCaps *icaps = NULL; + GstCaps *tcaps = NULL; + int i; + + if (v4l_caps == NULL || gst_caps_is_any (v4l_caps)) { + GST_DEBUG_OBJECT (self, "v4l caps are invalid. not fixating"); + return NULL; + } + + tcaps = gst_caps_intersect_full (peer_caps, v4l_caps, + 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++) { + /* get intersection */ + GstCaps *ipcaps = gst_caps_copy_nth (icaps, i); + GstStructure *s = gst_caps_get_structure (ipcaps, 0); + + GST_DEBUG_OBJECT (self, "Testing %s: %" GST_PTR_FORMAT, + primary ? "primary" : "secondary", ipcaps); + if (primary && gst_structure_has_name (s, "video/x-h264")) { + uvcx_video_config_probe_commit_t probe; + guint16 width; + guint16 height; + guint32 interval; + guint16 profile; + UvcH264StreamFormat stream_format; + + if (_extract_caps_info (s, &width, &height, &interval)) { + profile = _extract_profile (s); + stream_format = _extract_stream_format (s); + fill_probe_commit (self, &probe, interval, width, height, + profile, stream_format); + probe.bmHints = UVC_H264_BMHINTS_RESOLUTION | + UVC_H264_BMHINTS_PROFILE | UVC_H264_BMHINTS_FRAME_INTERVAL; + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_SET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE SET_CUR error"); + return NULL; + } + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return NULL; + } + GST_DEBUG_OBJECT (self, "Probe gives us %d==%d, %d==%d, %d==%d", + probe.wWidth, width, probe.wHeight, height, + probe.bStreamFormat, stream_format); + if (probe.wWidth == width && probe.wHeight == height && + probe.bStreamFormat == stream_format) { + caps = ipcaps; + break; + } + } + } else if (!primary && self->main_format == UVC_H264_SRC_FORMAT_H264) { + uvcx_video_config_probe_commit_t probe; + guint16 width; + guint16 height; + guint32 interval; + + if (_extract_caps_info (s, &width, &height, &interval)) { + if (gst_structure_has_name (s, "video/x-raw-yuv")) { + guint32 fcc = 0; + guint8 mux = 0; + + if (gst_structure_get_fourcc (s, "format", &fcc)) { + if (fcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')) + mux = 4; + else if (fcc == GST_MAKE_FOURCC ('N', 'V', '1', '2')) + mux = 8; + } + if (mux != 0) { + memset (&probe, 0, sizeof (probe)); + probe.dwFrameInterval = interval; + probe.wWidth = width; + probe.wHeight = height; + probe.bStreamMuxOption = mux | 1; + probe.bmHints = UVC_H264_BMHINTS_RESOLUTION | + UVC_H264_BMHINTS_PROFILE | UVC_H264_BMHINTS_FRAME_INTERVAL; + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_SET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE SET_CUR error"); + return NULL; + } + + if (!xu_query (self, UVCX_VIDEO_CONFIG_PROBE, UVC_GET_CUR, + (guchar *) & probe)) { + GST_WARNING_OBJECT (self, "PROBE GET_CUR error"); + return NULL; + } + GST_DEBUG_OBJECT (self, "Probe gives us %d==%d, %d==%d, %d~=%d", + probe.wWidth, width, probe.wHeight, height, + probe.bStreamMuxOption, mux); + if (probe.wWidth == width && probe.wHeight == height && + (probe.bStreamMuxOption & mux) != 0) { + caps = ipcaps; + break; + } + } + } else if (gst_structure_has_name (s, "image/jpeg")) { + /* HACK ALERT: No way of figuring this one out but it seems the + * camera doesn't allow for h264 muxing and jpeg resolution higher + * than 640x480 so we shouldn't allow it */ + if (width <= 640 && height <= 480) { + caps = ipcaps; + break; + } + } + } + } else { + caps = ipcaps; + break; + } + gst_caps_unref (ipcaps); + } + + 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); + GST_DEBUG_OBJECT (self, "fixated to: %" GST_PTR_FORMAT, caps); + } + + if (gst_caps_is_empty (caps) || gst_caps_is_any (caps)) { + gst_caps_unref (caps); + caps = NULL; + } + } + + return caps; +} + +static void +gst_uvc_h264_src_destroy_pipeline (GstUvcH264Src * self, gboolean v4l2src) +{ + GstIterator *iter = NULL; + gboolean done; + + if (v4l2src && self->v4l2_src) { + gst_bin_remove (GST_BIN (self), self->v4l2_src); + gst_element_set_state (self->v4l2_src, GST_STATE_NULL); + gst_object_unref (self->v4l2_src); + self->v4l2_src = NULL; + self->v4l2_fd = -1; + self->h264_unit_id = 0; + } + if (self->mjpg_demux) { + gst_bin_remove (GST_BIN (self), self->mjpg_demux); + gst_element_set_state (self->mjpg_demux, GST_STATE_NULL); + gst_object_unref (self->mjpg_demux); + self->mjpg_demux = NULL; + } + if (self->jpeg_dec) { + gst_bin_remove (GST_BIN (self), self->jpeg_dec); + gst_element_set_state (self->jpeg_dec, GST_STATE_NULL); + gst_object_unref (self->jpeg_dec); + self->jpeg_dec = NULL; + } + if (self->vid_colorspace) { + gst_bin_remove (GST_BIN (self), self->vid_colorspace); + gst_element_set_state (self->vid_colorspace, GST_STATE_NULL); + gst_object_unref (self->vid_colorspace); + self->vid_colorspace = NULL; + } + if (self->vf_colorspace) { + gst_bin_remove (GST_BIN (self), self->vf_colorspace); + gst_element_set_state (self->vf_colorspace, GST_STATE_NULL); + gst_object_unref (self->vf_colorspace); + self->vf_colorspace = NULL; + } + iter = gst_bin_iterate_elements (GST_BIN (self)); + done = FALSE; + while (!done) { + GstElement *item = NULL; + + switch (gst_iterator_next (iter, (gpointer *) & item)) { + case GST_ITERATOR_OK: + if (item != self->v4l2_src) { + gst_bin_remove (GST_BIN (self), item); + gst_element_set_state (item, GST_STATE_NULL); + } + gst_object_unref (item); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); +} + +static gboolean +ensure_v4l2src (GstUvcH264Src * self) +{ + gchar *device = NULL; + GstClock *v4l2_clock = NULL; + + if (self->v4l2_src == NULL) { + /* Create v4l2 source and set it up */ + self->v4l2_src = gst_element_factory_make ("v4l2src", NULL); + if (!self->v4l2_src || !gst_bin_add (GST_BIN (self), self->v4l2_src)) + goto error; + gst_object_ref (self->v4l2_src); + g_signal_connect (self->v4l2_src, "prepare-format", + (GCallback) v4l2src_prepare_format, self); + } + + g_object_get (self->v4l2_src, "device", &device, NULL); + g_object_set (self->v4l2_src, + "device", self->device, "num-buffers", self->num_buffers, NULL); + + v4l2_clock = gst_element_get_clock (self->v4l2_src); + + /* Set to NULL if the device changed */ + if (g_strcmp0 (device, self->device)) + gst_element_set_state (self->v4l2_src, GST_STATE_NULL); + g_free (device); + + if (gst_element_set_state (self->v4l2_src, GST_STATE_READY) != + GST_STATE_CHANGE_SUCCESS) { + GST_DEBUG_OBJECT (self, "Unable to set v4l2src to READY state"); + goto error_remove; + } + + /* Set/Update the fd and unit id after we go to READY */ + g_object_get (self->v4l2_src, "device-fd", &self->v4l2_fd, NULL); + self->h264_unit_id = xu_get_id (self); + + if (self->h264_unit_id == 0) { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + ("Device is not a valid UVC H264 camera"), (NULL)); + goto error_remove; + } + + /* going to state READY makes v4l2src lose its reference to the clock */ + if (v4l2_clock) { + gst_element_set_clock (self->v4l2_src, v4l2_clock); + gst_element_set_base_time (self->v4l2_src, + gst_element_get_base_time (GST_ELEMENT (self))); + gst_object_unref (v4l2_clock); + } + + return TRUE; + +error_remove: + gst_element_set_state (self->v4l2_src, GST_STATE_NULL); + gst_bin_remove (GST_BIN (self), self->v4l2_src); + +error: + if (self->v4l2_src) + gst_object_unref (self->v4l2_src); + self->v4l2_src = NULL; + self->v4l2_fd = -1; + self->h264_unit_id = 0; + + return FALSE; +} + +static gboolean +gst_uvc_h264_src_construct_pipeline (GstBaseCameraSrc * bcamsrc) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (bcamsrc); + GstIterator *iter = NULL; + gboolean iter_done = FALSE; + GstPad *vf_pad = NULL; + GstCaps *vf_caps = NULL; + GstStructure *vf_struct = NULL; + GstPad *vid_pad = NULL; + GstCaps *vid_caps = NULL; + GstStructure *vid_struct = NULL; + GstCaps *src_caps = NULL; + GstPad *v4l_pad = NULL; + GstCaps *v4l_caps = NULL; + gboolean jpg2raw = FALSE; + + enum + { + RAW_NONE, ENCODED_NONE, NONE_RAW, NONE_ENCODED, + H264_JPG, H264_RAW, H264_JPG2RAW, NONE_NONE, + RAW_RAW, ENCODED_ENCODED, + } type; + + GST_DEBUG_OBJECT (self, "Construct pipeline"); + self->reconfiguring = TRUE; + + if (self->v4l2_src) { + uvcx_encoder_reset req = { 0 }; + + if (!xu_query (self, UVCX_ENCODER_RESET, UVC_SET_CUR, (guchar *) & req)) + GST_WARNING_OBJECT (self, " UVCX_ENCODER_RESET SET_CUR error"); + } + + if (!ensure_v4l2src (self)) + goto error; + + gst_uvc_h264_src_destroy_pipeline (self, FALSE); + + /* Potentially unlink v4l2src to the ghost pads */ + 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); + + GST_DEBUG_OBJECT (self, "vfsrc caps : %" GST_PTR_FORMAT, vf_caps); + GST_DEBUG_OBJECT (self, "vidsrc caps : %" GST_PTR_FORMAT, vid_caps); + if (!self->started) { + GST_DEBUG_OBJECT (self, "video not started. Ignoring vidsrc caps"); + if (vid_caps) + gst_caps_unref (vid_caps); + vid_caps = NULL; + } + + v4l_pad = gst_element_get_static_pad (self->v4l2_src, "src"); + v4l_caps = gst_pad_get_caps (v4l_pad); + 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); + + gst_caps_unref (vid_caps); + vid_caps = gst_uvc_h264_src_fixate_caps (self, v4l_pad, v4l_caps, + trans_caps, TRUE); + gst_caps_unref (trans_caps); + + if (vid_caps) { + vid_struct = gst_caps_get_structure (vid_caps, 0); + } else { + GST_WARNING_OBJECT (self, "Could not negotiate vidsrc caps format"); + gst_object_unref (v4l_pad); + gst_caps_unref (v4l_caps); + goto error_remove; + } + } + GST_DEBUG_OBJECT (self, "Fixated vidsrc caps : %" GST_PTR_FORMAT, vid_caps); + + if (vid_caps && gst_structure_has_name (vid_struct, "video/x-h264")) { + self->main_format = UVC_H264_SRC_FORMAT_H264; + if (!_extract_caps_info (vid_struct, &self->main_width, + &self->main_height, &self->main_frame_interval)) { + gst_object_unref (v4l_pad); + gst_caps_unref (v4l_caps); + goto error_remove; + } + + self->main_stream_format = _extract_stream_format (vid_struct); + self->main_profile = _extract_profile (vid_struct); + } else { + self->main_format = UVC_H264_SRC_FORMAT_NONE; + } + + if (vf_caps) { + GstCaps *trans_caps = gst_uvc_h264_src_transform_caps (self, vf_caps); + + gst_caps_unref (vf_caps); + vf_caps = gst_uvc_h264_src_fixate_caps (self, v4l_pad, v4l_caps, + trans_caps, FALSE); + + /* If we couldn't find a suitable vf cap, try the jpeg2raw pipeline */ + if (!vf_caps && self->main_format == UVC_H264_SRC_FORMAT_H264) { + GstCaps *jpg_caps; + + jpg2raw = TRUE; + jpg_caps = _transform_caps (self, trans_caps, self->jpeg_decoder_name); + + vf_caps = gst_uvc_h264_src_fixate_caps (self, v4l_pad, v4l_caps, + jpg_caps, FALSE); + gst_caps_unref (jpg_caps); + } + gst_caps_unref (trans_caps); + if (vf_caps) { + vf_struct = gst_caps_get_structure (vf_caps, 0); + } else { + GST_WARNING_OBJECT (self, "Could not negotiate vfsrc caps format"); + gst_object_unref (v4l_pad); + gst_caps_unref (v4l_caps); + goto error_remove; + } + } + GST_DEBUG_OBJECT (self, "Fixated vfsrc caps : %" GST_PTR_FORMAT, vf_caps); + gst_object_unref (v4l_pad); + gst_caps_unref (v4l_caps); + + if (vf_caps && vid_caps && + !gst_structure_has_name (vid_struct, "video/x-h264")) { + /* Allow for vfsrc+vidsrc to both be raw or jpeg */ + if (gst_structure_has_name (vid_struct, "image/jpeg") && + gst_structure_has_name (vf_struct, "image/jpeg")) { + self->main_format = UVC_H264_SRC_FORMAT_JPG; + self->secondary_format = UVC_H264_SRC_FORMAT_JPG; + type = ENCODED_ENCODED; + } else if (!gst_structure_has_name (vid_struct, "image/jpeg") && + !gst_structure_has_name (vf_struct, "image/jpeg")) { + self->main_format = UVC_H264_SRC_FORMAT_RAW; + self->secondary_format = UVC_H264_SRC_FORMAT_RAW; + type = RAW_RAW; + } else { + goto error_remove; + } + } else if (vf_caps && vid_caps) { + guint32 smallest_frame_interval; + + if (!_extract_caps_info (vf_struct, &self->secondary_width, + &self->secondary_height, &self->secondary_frame_interval)) + goto error_remove; + + if (jpg2raw == FALSE && gst_structure_has_name (vf_struct, "image/jpeg")) { + type = H264_JPG; + self->secondary_format = UVC_H264_SRC_FORMAT_JPG; + } else { + if (jpg2raw) { + type = H264_JPG2RAW; + self->secondary_format = UVC_H264_SRC_FORMAT_JPG; + } else { + type = H264_RAW; + self->secondary_format = UVC_H264_SRC_FORMAT_RAW; + } + } + smallest_frame_interval = MIN (self->main_frame_interval, + self->secondary_frame_interval); + /* Just to avoid a potential division by zero, set interval to 30 fps */ + if (smallest_frame_interval == 0) + smallest_frame_interval = 333333; + + /* Frame interval is in 100ns units */ + src_caps = gst_caps_new_simple ("image/jpeg", + "width", G_TYPE_INT, self->secondary_width, + "height", G_TYPE_INT, self->secondary_height, + "framerate", GST_TYPE_FRACTION, + NSEC_PER_SEC / smallest_frame_interval, 100, NULL); + } else if (vf_caps || vid_caps) { + self->secondary_format = UVC_H264_SRC_FORMAT_NONE; + if (vid_struct && gst_structure_has_name (vid_struct, "video/x-h264")) { + type = ENCODED_NONE; + } else if (vid_struct && gst_structure_has_name (vid_struct, "image/jpeg")) { + type = ENCODED_NONE; + self->main_format = UVC_H264_SRC_FORMAT_JPG; + } else if (vf_struct && gst_structure_has_name (vf_struct, "image/jpeg")) { + type = NONE_ENCODED; + self->secondary_format = UVC_H264_SRC_FORMAT_JPG; + } else if (vid_struct) { + type = RAW_NONE; + self->main_format = UVC_H264_SRC_FORMAT_RAW; + } else if (vf_struct) { + type = NONE_RAW; + self->secondary_format = UVC_H264_SRC_FORMAT_RAW; + } else { + g_assert_not_reached (); + } + } else { + type = NONE_NONE; + self->main_format = UVC_H264_SRC_FORMAT_NONE; + self->secondary_format = UVC_H264_SRC_FORMAT_NONE; + } + + switch (type) { + case NONE_NONE: + GST_DEBUG_OBJECT (self, "None+None"); + vf_pad = gst_element_get_static_pad (self->v4l2_src, "src"); + break; + case RAW_NONE: + GST_DEBUG_OBJECT (self, "Raw+None"); + self->vid_colorspace = gst_element_factory_make (self->colorspace_name, + NULL); + if (!self->vid_colorspace || + !gst_bin_add (GST_BIN (self), self->vid_colorspace)) + goto error_remove; + gst_object_ref (self->vid_colorspace); + if (!gst_element_link (self->v4l2_src, self->vid_colorspace)) + goto error_remove_all; + vid_pad = gst_element_get_static_pad (self->vid_colorspace, "src"); + break; + case NONE_RAW: + GST_DEBUG_OBJECT (self, "None+Raw"); + self->vf_colorspace = gst_element_factory_make (self->colorspace_name, + NULL); + if (!self->vf_colorspace || + !gst_bin_add (GST_BIN (self), self->vf_colorspace)) + goto error_remove; + gst_object_ref (self->vf_colorspace); + if (!gst_element_link (self->v4l2_src, self->vf_colorspace)) + goto error_remove_all; + vf_pad = gst_element_get_static_pad (self->vf_colorspace, "src"); + break; + case ENCODED_NONE: + GST_DEBUG_OBJECT (self, "Encoded+None"); + vid_pad = gst_element_get_static_pad (self->v4l2_src, "src"); + break; + case NONE_ENCODED: + GST_DEBUG_OBJECT (self, "None+Encoded"); + vf_pad = gst_element_get_static_pad (self->v4l2_src, "src"); + break; + case H264_JPG: + GST_DEBUG_OBJECT (self, "H264+JPG"); + self->mjpg_demux = gst_element_factory_make ("uvch264_mjpgdemux", NULL); + if (!self->mjpg_demux || !gst_bin_add (GST_BIN (self), self->mjpg_demux)) + goto error_remove; + gst_object_ref (self->mjpg_demux); + g_object_set (self->mjpg_demux, "device-fd", self->v4l2_fd, + "num-clock-samples", self->num_clock_samples, NULL); + if (!gst_element_link_filtered (self->v4l2_src, self->mjpg_demux, + src_caps)) + goto error_remove_all; + vid_pad = gst_element_get_static_pad (self->mjpg_demux, "h264"); + vf_pad = gst_element_get_static_pad (self->mjpg_demux, "jpeg"); + break; + case H264_RAW: + GST_DEBUG_OBJECT (self, "H264+Raw"); + self->mjpg_demux = gst_element_factory_make ("uvch264_mjpgdemux", NULL); + self->vf_colorspace = gst_element_factory_make (self->colorspace_name, + NULL); + if (!self->mjpg_demux || !self->vf_colorspace) + goto error_remove; + if (!gst_bin_add (GST_BIN (self), self->mjpg_demux)) + goto error_remove; + gst_object_ref (self->mjpg_demux); + g_object_set (self->mjpg_demux, "device-fd", self->v4l2_fd, + "num-clock-samples", self->num_clock_samples, NULL); + if (!gst_bin_add (GST_BIN (self), self->vf_colorspace)) { + gst_object_unref (self->vf_colorspace); + self->vf_colorspace = NULL; + goto error_remove_all; + } + gst_object_ref (self->vf_colorspace); + if (!gst_element_link_filtered (self->v4l2_src, self->mjpg_demux, + src_caps)) + goto error_remove_all; + if (!gst_element_link_pads (self->mjpg_demux, "yuy2", + self->vf_colorspace, "sink")) + goto error_remove_all; + vid_pad = gst_element_get_static_pad (self->mjpg_demux, "h264"); + vf_pad = gst_element_get_static_pad (self->vf_colorspace, "src"); + break; + case H264_JPG2RAW: + GST_DEBUG_OBJECT (self, "H264+Raw(jpegdec)"); + self->mjpg_demux = gst_element_factory_make ("uvch264_mjpgdemux", NULL); + self->jpeg_dec = gst_element_factory_make (self->jpeg_decoder_name, NULL); + self->vf_colorspace = gst_element_factory_make (self->colorspace_name, + NULL); + if (!self->mjpg_demux || !self->jpeg_dec || !self->vf_colorspace) + goto error_remove; + if (!gst_bin_add (GST_BIN (self), self->mjpg_demux)) + goto error_remove; + gst_object_ref (self->mjpg_demux); + g_object_set (self->mjpg_demux, "device-fd", self->v4l2_fd, + "num-clock-samples", self->num_clock_samples, NULL); + if (!gst_bin_add (GST_BIN (self), self->jpeg_dec)) { + gst_object_unref (self->jpeg_dec); + self->jpeg_dec = NULL; + gst_object_unref (self->vf_colorspace); + self->vf_colorspace = NULL; + goto error_remove_all; + } + gst_object_ref (self->jpeg_dec); + if (!gst_bin_add (GST_BIN (self), self->vf_colorspace)) { + gst_object_unref (self->vf_colorspace); + self->vf_colorspace = NULL; + goto error_remove_all; + } + gst_object_ref (self->vf_colorspace); + if (!gst_element_link_filtered (self->v4l2_src, self->mjpg_demux, + src_caps)) + goto error_remove_all; + if (!gst_element_link_pads (self->mjpg_demux, "jpeg", self->jpeg_dec, + "sink")) + goto error_remove_all; + if (!gst_element_link (self->jpeg_dec, self->vf_colorspace)) + goto error_remove_all; + vid_pad = gst_element_get_static_pad (self->mjpg_demux, "h264"); + vf_pad = gst_element_get_static_pad (self->vf_colorspace, "src"); + break; + case RAW_RAW: + { + GstElement *tee = NULL; + + GST_DEBUG_OBJECT (self, "Raw+Raw"); + tee = gst_element_factory_make ("tee", NULL); + if (!tee || !gst_bin_add (GST_BIN (self), tee)) { + if (tee) + gst_object_unref (tee); + goto error_remove; + } + self->vf_colorspace = gst_element_factory_make (self->colorspace_name, + NULL); + self->vid_colorspace = gst_element_factory_make (self->colorspace_name, + NULL); + if (!self->vf_colorspace || !self->vid_colorspace) + goto error_remove; + if (!gst_bin_add (GST_BIN (self), self->vf_colorspace)) + goto error_remove; + gst_object_ref (self->vf_colorspace); + if (!gst_bin_add (GST_BIN (self), self->vid_colorspace)) { + gst_object_unref (self->vid_colorspace); + self->vid_colorspace = NULL; + goto error_remove_all; + } + gst_object_ref (self->vid_colorspace); + if (!gst_element_link (self->v4l2_src, tee)) + goto error_remove_all; + if (!gst_element_link (tee, self->vf_colorspace)) + goto error_remove_all; + if (!gst_element_link (tee, self->vid_colorspace)) + goto error_remove_all; + vf_pad = gst_element_get_static_pad (self->vf_colorspace, "src"); + vid_pad = gst_element_get_static_pad (self->vid_colorspace, "src"); + } + break; + case ENCODED_ENCODED: + { + GstElement *tee = NULL; + + GST_DEBUG_OBJECT (self, "Encoded+Encoded"); + tee = gst_element_factory_make ("tee", NULL); + if (!tee || !gst_bin_add (GST_BIN (self), tee)) { + if (tee) + gst_object_unref (tee); + goto error_remove; + } + 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"); + } + break; + } + + if (!gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), vid_pad) || + !gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad)) + goto error_remove_all; + if (vid_pad) + gst_object_unref (vid_pad); + if (vf_pad) + gst_object_unref (vf_pad); + vid_pad = vf_pad = NULL; + + if (vf_caps) + gst_caps_unref (vf_caps); + if (vid_caps) + gst_caps_unref (vid_caps); + if (src_caps) + gst_caps_unref (src_caps); + vf_caps = vid_caps = src_caps = NULL; + + /* Sync children states, in sink to source order */ + if (self->vid_colorspace && + !gst_element_sync_state_with_parent (self->vid_colorspace)) + goto error_remove_all; + if (self->vf_colorspace && + !gst_element_sync_state_with_parent (self->vf_colorspace)) + goto error_remove_all; + if (self->jpeg_dec && !gst_element_sync_state_with_parent (self->jpeg_dec)) + goto error_remove_all; + if (self->mjpg_demux && + !gst_element_sync_state_with_parent (self->mjpg_demux)) + goto error_remove_all; + if (self->v4l2_src && !gst_element_sync_state_with_parent (self->v4l2_src)) + goto error_remove_all; + + /* Sync any remaining children states with bin's state */ + iter = gst_bin_iterate_elements (GST_BIN (self)); + iter_done = FALSE; + while (!iter_done) { + GstElement *item = NULL; + + switch (gst_iterator_next (iter, (gpointer *) & item)) { + case GST_ITERATOR_OK: + if (!gst_element_sync_state_with_parent (item)) { + gst_object_unref (item); + gst_iterator_free (iter); + goto error_remove_all; + } + gst_object_unref (item); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + iter_done = TRUE; + break; + case GST_ITERATOR_DONE: + iter_done = TRUE; + break; + } + } + gst_iterator_free (iter); + + self->reconfiguring = FALSE; + return TRUE; + +error_remove_all: + gst_uvc_h264_src_destroy_pipeline (self, FALSE); +error_remove: + gst_element_set_state (self->v4l2_src, GST_STATE_NULL); + gst_bin_remove (GST_BIN (self), self->v4l2_src); + +error: + if (self->v4l2_src) + gst_object_unref (self->v4l2_src); + self->v4l2_src = NULL; + self->v4l2_fd = -1; + self->h264_unit_id = 0; + + if (self->mjpg_demux) + gst_object_unref (self->mjpg_demux); + self->mjpg_demux = NULL; + if (self->jpeg_dec) + gst_object_unref (self->jpeg_dec); + self->jpeg_dec = NULL; + if (self->vid_colorspace) + gst_object_unref (self->vid_colorspace); + self->vid_colorspace = NULL; + if (self->vf_colorspace) + gst_object_unref (self->vf_colorspace); + self->vf_colorspace = NULL; + + if (src_caps) + gst_caps_unref (src_caps); + + if (vf_caps) + gst_caps_unref (vf_caps); + if (vid_caps) + gst_caps_unref (vid_caps); + + if (vid_pad) + gst_object_unref (vid_pad); + if (vf_pad) + gst_object_unref (vf_pad); + + self->reconfiguring = FALSE; + return FALSE; +} + +static GstCaps * +gst_uvc_h264_src_getcaps (GstPad * pad) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (GST_OBJECT_PARENT (pad)); + GstCaps *template = NULL; + GstCaps *result = NULL; + + if (pad == self->vfsrc) + template = gst_static_pad_template_get_caps (&vfsrc_template); + else if (pad == self->vidsrc) + template = gst_static_pad_template_get_caps (&vidsrc_template); + else + template = gst_caps_new_empty (); + + 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 *new_caps = gst_uvc_h264_src_transform_caps (self, v4l_caps); + + result = gst_caps_intersect (new_caps, template); + gst_object_unref (v4l_pad); + gst_caps_unref (v4l_caps); + gst_caps_unref (new_caps); + gst_caps_unref (template); + } else { + result = template; + } + + return result; +} + +static gboolean +gst_uvc_h264_src_set_mode (GstBaseCameraSrc * bcamsrc, GstCameraBinMode mode) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (bcamsrc); + + GST_DEBUG_OBJECT (self, "set mode to %d", mode); + + return (mode == MODE_VIDEO); +} + +static gboolean +gst_uvc_h264_src_start_capture (GstBaseCameraSrc * camerasrc) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (camerasrc); + gboolean ret = TRUE; + + GST_DEBUG_OBJECT (self, "start capture"); + + if (!self->started) { + self->started = TRUE; + if (GST_STATE (self) >= GST_STATE_READY) { + ret = gst_uvc_h264_src_construct_pipeline (GST_BASE_CAMERA_SRC (self)); + if (!ret) { + GST_DEBUG_OBJECT (self, "Could not start capture"); + self->started = FALSE; + gst_uvc_h264_src_construct_pipeline (GST_BASE_CAMERA_SRC (self)); + } + } + } + + return ret; +} + +static void +gst_uvc_h264_src_stop_capture (GstBaseCameraSrc * camerasrc) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (camerasrc); + + GST_DEBUG_OBJECT (self, "stop capture"); + + if (self->started) { + self->started = FALSE; + if (GST_STATE (self) >= GST_STATE_READY) + gst_uvc_h264_src_construct_pipeline (GST_BASE_CAMERA_SRC (self)); + gst_base_camera_src_finish_capture (camerasrc); + } +} + +static void +gst_uvc_h264_src_pad_linking_cb (GstPad * pad, + GstPad * peer, gpointer user_data) +{ + GstUvcH264Src *self = GST_UVC_H264_SRC (user_data); + gchar *pad_name = gst_pad_get_name (pad); + + GST_DEBUG_OBJECT (self, "Pad %s was (un)linked. Renegotiating", pad_name); + g_free (pad_name); + if (GST_STATE (self) >= GST_STATE_READY) + gst_uvc_h264_src_construct_pipeline (GST_BASE_CAMERA_SRC (self)); +} + + +static GstStateChangeReturn +gst_uvc_h264_src_change_state (GstElement * element, GstStateChange trans) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstUvcH264Src *self = GST_UVC_H264_SRC (element); + + switch (trans) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!ensure_v4l2src (self)) { + ret = GST_STATE_CHANGE_FAILURE; + goto end; + } + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + if (!self->v4l2_src) + gst_uvc_h264_src_construct_pipeline (GST_BASE_CAMERA_SRC (self)); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans); + + if (ret == GST_STATE_CHANGE_FAILURE) + goto end; + + switch (trans) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + self->vid_newseg = FALSE; + self->vf_newseg = FALSE; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_uvc_h264_src_destroy_pipeline (self, TRUE); + break; + default: + break; + } + + +end: + return ret; +} diff --git a/sys/uvch264/gstuvch264_src.h b/sys/uvch264/gstuvch264_src.h new file mode 100644 index 0000000000..3eb846bc0e --- /dev/null +++ b/sys/uvch264/gstuvch264_src.h @@ -0,0 +1,166 @@ +/* + * GStreamer + * + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_UVC_H264_SRC_H__ +#define __GST_UVC_H264_SRC_H__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#if defined (HAVE_GUDEV) && defined (HAVE_LIBUSB) +#include +#endif + +#include "uvc_h264.h" + +G_BEGIN_DECLS +#define GST_TYPE_UVC_H264_SRC \ + (gst_uvc_h264_src_get_type()) +#define GST_UVC_H264_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_UVC_H264_SRC, GstUvcH264Src)) +#define GST_UVC_H264_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_UVC_H264_SRC, GstUvcH264SrcClass)) +#define GST_IS_UVC_H264_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_UVC_H264_SRC)) +#define GST_IS_UVC_H264_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_UVC_H264_SRC)) + GType gst_uvc_h264_src_get_type (void); + +typedef struct _GstUvcH264Src GstUvcH264Src; +typedef struct _GstUvcH264SrcClass GstUvcH264SrcClass; + +enum GstVideoRecordingStatus { + GST_VIDEO_RECORDING_STATUS_DONE, + GST_VIDEO_RECORDING_STATUS_STARTING, + GST_VIDEO_RECORDING_STATUS_RUNNING, + GST_VIDEO_RECORDING_STATUS_FINISHING +}; + +enum { + QP_I_FRAME = 0, + QP_P_FRAME, + QP_B_FRAME, + QP_FRAMES +}; + +typedef enum { + UVC_H264_SRC_FORMAT_NONE, + UVC_H264_SRC_FORMAT_JPG, + UVC_H264_SRC_FORMAT_H264, + UVC_H264_SRC_FORMAT_RAW +} GstUvcH264SrcFormat; + +/** + * GstUcH264Src: + * + */ +struct _GstUvcH264Src +{ + GstBaseCameraSrc parent; + + GstPad *vfsrc; + GstPad *imgsrc; + GstPad *vidsrc; + + /* source elements */ + GstElement *v4l2_src; + GstElement *mjpg_demux; + GstElement *jpeg_dec; + GstElement *vid_colorspace; + GstElement *vf_colorspace; + + GstUvcH264SrcFormat main_format; + guint16 main_width; + guint16 main_height; + guint32 main_frame_interval; + UvcH264StreamFormat main_stream_format; + guint16 main_profile; + GstUvcH264SrcFormat secondary_format; + guint16 secondary_width; + guint16 secondary_height; + guint32 secondary_frame_interval; + + int v4l2_fd; + guint8 h264_unit_id; +#if defined (HAVE_GUDEV) && defined (HAVE_LIBUSB) + libusb_context *usb_ctx; +#endif + + GstPadEventFunction srcpad_event_func; + GstEvent *key_unit_event; + GstSegment segment; + + gboolean started; + + /* When restarting the source */ + gboolean reconfiguring; + gboolean vid_newseg; + gboolean vf_newseg; + + gchar *colorspace_name; + gchar *jpeg_decoder_name; + int num_clock_samples; + + /* v4l2src proxied properties */ + guint32 num_buffers; + gchar *device; + + /* Static controls */ + guint32 initial_bitrate; + guint16 slice_units; + UvcH264SliceMode slice_mode; + guint16 iframe_period; + UvcH264UsageType usage_type; + UvcH264Entropy entropy; + gboolean enable_sei; + guint8 num_reorder_frames; + gboolean preview_flipped; + guint16 leaky_bucket_size; + + /* Dynamic controls */ + UvcH264RateControl rate_control; + gboolean fixed_framerate; + guint8 level_idc; + guint32 peak_bitrate; + guint32 average_bitrate; + gint8 min_qp[QP_FRAMES]; + gint8 max_qp[QP_FRAMES]; + guint8 ltr_buffer_size; + guint8 ltr_encoder_control; +}; + + +/** + * GstUvcH264SrcClass: + * + */ +struct _GstUvcH264SrcClass +{ + GstBaseCameraSrcClass parent; +}; + + +#endif /* __GST_UVC_H264_SRC_H__ */ diff --git a/sys/uvch264/uvc_h264.c b/sys/uvch264/uvc_h264.c new file mode 100644 index 0000000000..1c26ae437a --- /dev/null +++ b/sys/uvch264/uvc_h264.c @@ -0,0 +1,122 @@ +/* + * GStreamer + * + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "uvc_h264.h" + +GType +uvc_h264_slicemode_get_type (void) +{ + static GType type = 0; + + static const GEnumValue types[] = { + {UVC_H264_SLICEMODE_IGNORED, "Ignored", "ignored"}, + {UVC_H264_SLICEMODE_BITSPERSLICE, "Bits per slice", "bits/slice"}, + {UVC_H264_SLICEMODE_MBSPERSLICE, "MBs per Slice", "MBs/slice"}, + {UVC_H264_SLICEMODE_SLICEPERFRAME, "Slice Per Frame", "slice/frame"}, + {0, NULL, NULL} + }; + + if (!type) { + type = g_enum_register_static ("UvcH264SliceMode", types); + } + return type; +} + +GType +uvc_h264_usagetype_get_type (void) +{ + static GType type = 0; + + static const GEnumValue types[] = { + {UVC_H264_USAGETYPE_REALTIME, "Realtime (video conferencing)", "realtime"}, + {UVC_H264_USAGETYPE_BROADCAST, "Broadcast", "broadcast"}, + {UVC_H264_USAGETYPE_STORAGE, "Storage", "storage"}, + {UVC_H264_USAGETYPE_UCCONFIG_0, "UCConfig 0", "ucconfig0"}, + {UVC_H264_USAGETYPE_UCCONFIG_1, "UCConfig 1", "ucconfig1"}, + {UVC_H264_USAGETYPE_UCCONFIG_2Q, "UCConfig 2Q", "ucconfig2q"}, + {UVC_H264_USAGETYPE_UCCONFIG_2S, "UCConfig 2S", "ucconfig2s"}, + {UVC_H264_USAGETYPE_UCCONFIG_3, "UCConfig 3", "ucconfig3"}, + {0, NULL, NULL} + }; + + if (!type) { + type = g_enum_register_static ("UvcH264UsageType", types); + } + return type; +} + +GType +uvc_h264_ratecontrol_get_type (void) +{ + static GType type = 0; + + static const GEnumValue types[] = { + {UVC_H264_RATECONTROL_CBR, "Constant bit rate", "cbr"}, + {UVC_H264_RATECONTROL_VBR, "Variable bit rate", "vbr"}, + {UVC_H264_RATECONTROL_CONST_QP, "Constant QP", "qp"}, + {0, NULL, NULL} + }; + + if (!type) { + type = g_enum_register_static ("UvcH264RateControl", types); + } + return type; +} + +GType +uvc_h264_streamformat_get_type (void) +{ + static GType type = 0; + + static const GEnumValue types[] = { + {UVC_H264_STREAMFORMAT_ANNEXB, "Byte stream format (Annex B)", "byte"}, + {UVC_H264_STREAMFORMAT_NAL, "NAL stream format", "nal"}, + {0, NULL, NULL} + }; + + if (!type) { + type = g_enum_register_static ("UvcH264StreamFormat", types); + } + return type; +} + +GType +uvc_h264_entropy_get_type (void) +{ + static GType type = 0; + + static const GEnumValue types[] = { + {UVC_H264_ENTROPY_CAVLC, "CAVLC", "cavlc"}, + {UVC_H264_ENTROPY_CABAC, "CABAC", "cabac"}, + {0, NULL, NULL} + }; + + if (!type) { + type = g_enum_register_static ("UvcH264Entropy", types); + } + return type; +} diff --git a/sys/uvch264/uvc_h264.h b/sys/uvch264/uvc_h264.h new file mode 100644 index 0000000000..d27104ecfe --- /dev/null +++ b/sys/uvch264/uvc_h264.h @@ -0,0 +1,335 @@ +/* + * uvc_h264.h - Definitions of the UVC H.264 Payload specification Version 1.0 + * + * Copyright (c) 2011 USB Implementers Forum, Inc. + * + * Modification into glib-like header by : + * Copyright (C) 2012 Cisco Systems, Inc. + * Author: Youness Alaoui + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _UVC_H264_H_ +#define _UVC_H264_H_ + +/* Header File for the little-endian platform */ + +#include +#include + +/* bmHints defines */ + +#define UVC_H264_BMHINTS_RESOLUTION (0x0001) +#define UVC_H264_BMHINTS_PROFILE (0x0002) +#define UVC_H264_BMHINTS_RATECONTROL (0x0004) +#define UVC_H264_BMHINTS_USAGE (0x0008) +#define UVC_H264_BMHINTS_SLICEMODE (0x0010) +#define UVC_H264_BMHINTS_SLICEUNITS (0x0020) +#define UVC_H264_BMHINTS_MVCVIEW (0x0040) +#define UVC_H264_BMHINTS_TEMPORAL (0x0080) +#define UVC_H264_BMHINTS_SNR (0x0100) +#define UVC_H264_BMHINTS_SPATIAL (0x0200) +#define UVC_H264_BMHINTS_SPATIAL_RATIO (0x0400) +#define UVC_H264_BMHINTS_FRAME_INTERVAL (0x0800) +#define UVC_H264_BMHINTS_LEAKY_BKT_SIZE (0x1000) +#define UVC_H264_BMHINTS_BITRATE (0x2000) +#define UVC_H264_BMHINTS_ENTROPY (0x4000) +#define UVC_H264_BMHINTS_IFRAMEPERIOD (0x8000) + + +#define UVC_H264_QP_STEPS_I_FRAME_TYPE (0x01) +#define UVC_H264_QP_STEPS_P_FRAME_TYPE (0x02) +#define UVC_H264_QP_STEPS_B_FRAME_TYPE (0x04) +#define UVC_H264_QP_STEPS_ALL_FRAME_TYPES (UVC_H264_QP_STEPS_I_FRAME_TYPE | \ + UVC_H264_QP_STEPS_P_FRAME_TYPE | UVC_H264_QP_STEPS_B_FRAME_TYPE) + +/* wSliceMode defines */ + +typedef enum +{ + UVC_H264_SLICEMODE_IGNORED = 0x0000, + UVC_H264_SLICEMODE_BITSPERSLICE = 0x0001, + UVC_H264_SLICEMODE_MBSPERSLICE = 0x0002, + UVC_H264_SLICEMODE_SLICEPERFRAME = 0x0003 +} UvcH264SliceMode; + +#define UVC_H264_SLICEMODE_TYPE (uvc_h264_slicemode_get_type()) + +GType uvc_h264_slicemode_get_type (void); + +/* bUsageType defines */ + +typedef enum { + UVC_H264_USAGETYPE_REALTIME = 0x01, + UVC_H264_USAGETYPE_BROADCAST = 0x02, + UVC_H264_USAGETYPE_STORAGE = 0x03, + UVC_H264_USAGETYPE_UCCONFIG_0 = 0x04, + UVC_H264_USAGETYPE_UCCONFIG_1 = 0x05, + UVC_H264_USAGETYPE_UCCONFIG_2Q = 0x06, + UVC_H264_USAGETYPE_UCCONFIG_2S = 0x07, + UVC_H264_USAGETYPE_UCCONFIG_3 = 0x08, +} UvcH264UsageType; + +#define UVC_H264_USAGETYPE_TYPE (uvc_h264_usagetype_get_type()) + +GType uvc_h264_usagetype_get_type (void); + +/* bRateControlMode defines */ + +typedef enum { + UVC_H264_RATECONTROL_CBR = 0x01, + UVC_H264_RATECONTROL_VBR = 0x02, + UVC_H264_RATECONTROL_CONST_QP = 0x03, +} UvcH264RateControl; + +#define UVC_H264_RATECONTROL_FIXED_FRM_FLG (0x10) + +#define UVC_H264_RATECONTROL_TYPE (uvc_h264_ratecontrol_get_type()) + +GType uvc_h264_ratecontrol_get_type (void); + +/* bStreamFormat defines */ + +typedef enum { + UVC_H264_STREAMFORMAT_ANNEXB = 0x00, + UVC_H264_STREAMFORMAT_NAL = 0x01, +} UvcH264StreamFormat; + +#define UVC_H264_STREAMFORMAT_TYPE (uvc_h264_streamformat_get_type()) + +GType uvc_h264_streamformat_get_type (void); + +/* bEntropyCABAC defines */ + +typedef enum { + UVC_H264_ENTROPY_CAVLC = 0x00, + UVC_H264_ENTROPY_CABAC = 0x01, +} UvcH264Entropy; + +#define UVC_H264_ENTROPY_TYPE (uvc_h264_entropy_get_type()) + +GType uvc_h264_entropy_get_type (void); + +/* bProfile defines */ +#define UVC_H264_PROFILE_CONSTRAINED_BASELINE 0x4240 +#define UVC_H264_PROFILE_BASELINE 0x4200 +#define UVC_H264_PROFILE_MAIN 0x4D00 +#define UVC_H264_PROFILE_HIGH 0x6400 + +/* bTimingstamp defines */ + +#define UVC_H264_TIMESTAMP_SEI_DISABLE (0x00) +#define UVC_H264_TIMESTAMP_SEI_ENABLE (0x01) + +/* bPreviewFlipped defines */ + +#define UVC_H264_PREFLIPPED_DISABLE (0x00) +#define UVC_H264_PREFLIPPED_HORIZONTAL (0x01) + +/* wPicType defines */ +#define UVC_H264_PICTYPE_I_FRAME (0x00) +#define UVC_H264_PICTYPE_IDR (0x01) +#define UVC_H264_PICTYPE_IDR_WITH_PPS_SPS (0x02) + + +/* wLayerID Macro */ + +/* wLayerID + |------------+------------+------------+----------------+------------| + | Reserved | StreamID | QualityID | DependencyID | TemporalID | + | (3 bits) | (3 bits) | (3 bits) | (4 bits) | (3 bits) | + |------------+------------+------------+----------------+------------| + |15 13|12 10|9 7|6 3|2 0| + |------------+------------+------------+----------------+------------| +*/ + +#define xLayerID(stream_id, quality_id, dependency_id, temporal_id) \ + ((((stream_id) & 7) << 10) | \ + (((quality_id) & 7) << 7) | \ + (((dependency_id) & 15) << 3) | \ + ((temporal_id) & 7)) + +/* id extraction from wLayerID */ + +#define xStream_id(layer_id) (((layer_id) >> 10) & 7) +#define xQuality_id(layer_id) (((layer_id) >> 7) & 7) +#define xDependency_id(layer_id) (((layer_id) >> 3) & 15) +#define xTemporal_id(layer_id) ((layer_id)&7) + +/* UVC H.264 control selectors */ + +typedef enum _uvcx_control_selector_t +{ + UVCX_VIDEO_CONFIG_PROBE = 0x01, + UVCX_VIDEO_CONFIG_COMMIT = 0x02, + UVCX_RATE_CONTROL_MODE = 0x03, + UVCX_TEMPORAL_SCALE_MODE = 0x04, + UVCX_SPATIAL_SCALE_MODE = 0x05, + UVCX_SNR_SCALE_MODE = 0x06, + UVCX_LTR_BUFFER_SIZE_CONTROL = 0x07, + UVCX_LTR_PICTURE_CONTROL = 0x08, + UVCX_PICTURE_TYPE_CONTROL = 0x09, + UVCX_VERSION = 0x0A, + UVCX_ENCODER_RESET = 0x0B, + UVCX_FRAMERATE_CONFIG = 0x0C, + UVCX_VIDEO_ADVANCE_CONFIG = 0x0D, + UVCX_BITRATE_LAYERS = 0x0E, + UVCX_QP_STEPS_LAYERS = 0x0F, +} uvcx_control_selector_t; + + +typedef struct _uvcx_video_config_probe_commit_t +{ + guint32 dwFrameInterval; + guint32 dwBitRate; + guint16 bmHints; + guint16 wConfigurationIndex; + guint16 wWidth; + guint16 wHeight; + guint16 wSliceUnits; + guint16 wSliceMode; + guint16 wProfile; + guint16 wIFramePeriod; + guint16 wEstimatedVideoDelay; + guint16 wEstimatedMaxConfigDelay; + guint8 bUsageType; + guint8 bRateControlMode; + guint8 bTemporalScaleMode; + guint8 bSpatialScaleMode; + guint8 bSNRScaleMode; + guint8 bStreamMuxOption; + guint8 bStreamFormat; + guint8 bEntropyCABAC; + guint8 bTimestamp; + guint8 bNumOfReorderFrames; + guint8 bPreviewFlipped; + guint8 bView; + guint8 bReserved1; + guint8 bReserved2; + guint8 bStreamID; + guint8 bSpatialLayerRatio; + guint16 wLeakyBucketSize; +} __attribute__((packed)) uvcx_video_config_probe_commit_t; + + +typedef struct _uvcx_rate_control_mode_t +{ + guint16 wLayerID; + guint8 bRateControlMode; +} __attribute__((packed)) uvcx_rate_control_mode_t; + + +typedef struct _uvcx_temporal_scale_mode_t +{ + guint16 wLayerID; + guint8 bTemporalScaleMode; +} __attribute__((packed)) uvcx_temporal_scale_mode_t; + + +typedef struct _uvcx_spatial_scale_mode_t +{ + guint16 wLayerID; + guint8 bSpatialScaleMode; +} __attribute__((packed)) uvcx_spatial_scale_mode_t; + + +typedef struct _uvcx_snr_scale_mode_t +{ + guint16 wLayerID; + guint8 bSNRScaleMode; + guint8 bMGSSublayerMode; +} __attribute__((packed)) uvcx_snr_scale_mode_t; + + +typedef struct _uvcx_ltr_buffer_size_control_t +{ + guint16 wLayerID; + guint8 bLTRBufferSize; + guint8 bLTREncoderControl; +} __attribute__((packed)) uvcx_ltr_buffer_size_control_t; + +typedef struct _uvcx_ltr_picture_control +{ + guint16 wLayerID; + guint8 bPutAtPositionInLTRBuffer; + guint8 bEncodeUsingLTR; +} __attribute__((packed)) uvcx_ltr_picture_control; + + +typedef struct _uvcx_picture_type_control_t +{ + guint16 wLayerID; + guint16 wPicType; +} __attribute__((packed)) uvcx_picture_type_control_t; + + +typedef struct _uvcx_version_t +{ + guint16 wVersion; +} __attribute__((packed)) uvcx_version_t; + + +typedef struct _uvcx_encoder_reset +{ + guint16 wLayerID; +} __attribute__((packed)) uvcx_encoder_reset; + + +typedef struct _uvcx_framerate_config_t +{ + guint16 wLayerID; + guint32 dwFrameInterval; +} __attribute__((packed)) uvcx_framerate_config_t; + + +typedef struct _uvcx_video_advance_config_t +{ + guint16 wLayerID; + guint32 dwMb_max; + guint8 blevel_idc; + guint8 bReserved; +} __attribute__((packed)) uvcx_video_advance_config_t; + + +typedef struct _uvcx_bitrate_layers_t +{ + guint16 wLayerID; + guint32 dwPeakBitrate; + guint32 dwAverageBitrate; +} __attribute__((packed)) uvcx_bitrate_layers_t; + + +typedef struct _uvcx_qp_steps_layers_t +{ + guint16 wLayerID; + guint8 bFrameType; + guint8 bMinQp; + guint8 bMaxQp; +} __attribute__((packed)) uvcx_qp_steps_layers_t; + + +#ifdef _WIN32 +// GUID of the UVC H.264 extension unit: {A29E7641-DE04-47E3-8B2B-F4341AFF003B} +DEFINE_GUID(GUID_UVCX_H264_XU, 0xA29E7641, 0xDE04, 0x47E3, 0x8B, 0x2B, 0xF4, 0x34, 0x1A, 0xFF, 0x00, 0x3B); +#else +#define GUID_UVCX_H264_XU \ + {0x41, 0x76, 0x9e, 0xa2, 0x04, 0xde, 0xe3, 0x47, 0x8b, 0x2b, 0xF4, 0x34, 0x1A, 0xFF, 0x00, 0x3B} +#endif + +#endif /*_UVC_H264_H_*/ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 84cee4d25b..57adac24d9 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -156,6 +156,20 @@ else check_curl = endif +if USE_UVCH264 +check_uvch264=elements/uvch264demux +else +check_uvch264= +endif +uvch264_dist_data = elements/uvch264demux_data/valid_h264_jpg.mjpg \ + elements/uvch264demux_data/valid_h264_jpg.jpg \ + elements/uvch264demux_data/valid_h264_jpg.h264 \ + elements/uvch264demux_data/valid_h264_yuy2.mjpg \ + elements/uvch264demux_data/valid_h264_yuy2.h264 \ + elements/uvch264demux_data/valid_h264_yuy2.yuy2 + + + VALGRIND_TO_FIX = \ elements/mpeg2enc \ elements/mplex \ @@ -209,6 +223,7 @@ check_PROGRAMS = \ elements/rtpmux \ libs/mpegvideoparser \ libs/h264parser \ + $(check_uvch264) \ libs/vc1parser \ $(check_schro) \ $(check_vp8) \ @@ -332,8 +347,10 @@ elements_assrender_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION elements_mpegtsmux_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_mpegtsmux_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_BASE_LIBS) $(LDADD) +elements_uvch264demux_CFLAGS = -DUVCH264DEMUX_DATADIR="$(srcdir)/elements/uvch264demux_data" \ + $(AM_CFLAGS) -EXTRA_DIST = gst-plugins-bad.supp +EXTRA_DIST = gst-plugins-bad.supp $(uvch264_dist_data) orc_bayer_CFLAGS = $(ORC_CFLAGS) orc_bayer_LDADD = $(ORC_LIBS) -lorc-test-0.4 diff --git a/tests/check/elements/.gitignore b/tests/check/elements/.gitignore index b72807696d..48604ef9ac 100644 --- a/tests/check/elements/.gitignore +++ b/tests/check/elements/.gitignore @@ -44,6 +44,7 @@ schroenc spectrum timidity y4menc +uvch264demux videorecordingbin viewfinderbin voaacenc diff --git a/tests/check/elements/uvch264demux.c b/tests/check/elements/uvch264demux.c new file mode 100644 index 0000000000..a633c694a5 --- /dev/null +++ b/tests/check/elements/uvch264demux.c @@ -0,0 +1,696 @@ +/* GStreamer + * + * unit test for uvch264_demux + * + * Copyright (C) <2012> Collabora Ltd. + * Author: Youness Alaoui + * Copyright (C) <2008> Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +static GstElement *demux; +static GstPad *mjpg_pad, *h264_pad, *yuy2_pad, *nv12_pad, *jpg_pad; +static gboolean have_h264_eos, have_yuy2_eos, have_nv12_eos, have_jpg_eos; +static GstBuffer *buffer_h264, *buffer_yuy2, *buffer_nv12, *buffer_jpg; +static GError *gerror; +static gchar *error_debug; + +static GstStaticPadTemplate mjpg_template = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/jpeg, width=640, height=480, framerate=15/1")); + +static GstStaticPadTemplate sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_ (x) +#define DATADIR STRINGIFY (UVCH264DEMUX_DATADIR) +#define VALID_H264_JPG_MJPG_FILENAME DATADIR "/valid_h264_jpg.mjpg" +#define VALID_H264_JPG_JPG_FILENAME DATADIR "/valid_h264_jpg.jpg" +#define VALID_H264_JPG_H264_FILENAME DATADIR "/valid_h264_jpg.h264" +#define VALID_H264_YUY2_MJPG_FILENAME DATADIR "/valid_h264_yuy2.mjpg" +#define VALID_H264_YUY2_YUY2_FILENAME DATADIR "/valid_h264_yuy2.yuy2" +#define VALID_H264_YUY2_H264_FILENAME DATADIR "/valid_h264_yuy2.h264" + +#define _sink_chain_func(type) \ +static GstFlowReturn \ + _sink_##type##_chain (GstPad * pad, GstBuffer * buffer) \ +{ \ + fail_unless (GST_BUFFER_CAPS (buffer) != NULL); \ + \ + buffer_##type = buffer; \ + \ + return GST_FLOW_OK; \ +} + +#define _sink_event_func(type) \ +static gboolean \ + _sink_##type##_event (GstPad * pad, GstEvent * event) \ +{ \ + if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) \ + have_##type##_eos = TRUE; \ + \ + gst_event_unref (event); \ + \ + return TRUE; \ +} + + +_sink_chain_func (h264); +_sink_chain_func (yuy2); +_sink_chain_func (nv12); +_sink_chain_func (jpg); + +_sink_event_func (h264); +_sink_event_func (yuy2); +_sink_event_func (nv12); +_sink_event_func (jpg); + + +static GstBusSyncReply +_bus_sync_handler (GstBus * bus, GstMessage * message, gpointer data) +{ + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { + fail_unless (gerror == NULL && error_debug == NULL); + fail_unless (GST_MESSAGE_SRC (message) == GST_OBJECT (demux)); + gst_message_parse_error (message, &gerror, &error_debug); + } + return GST_BUS_PASS; +} + +static void +_teardown_test (void) +{ + GstBus *bus; + gst_element_set_state (demux, GST_STATE_NULL); + + bus = GST_ELEMENT_BUS (demux); + gst_bus_set_flushing (bus, TRUE); + gst_object_unref (bus); + + gst_pad_set_active (mjpg_pad, FALSE); + gst_object_unref (mjpg_pad); + if (h264_pad) { + gst_pad_set_active (h264_pad, FALSE); + gst_object_unref (h264_pad); + } + if (yuy2_pad) { + gst_pad_set_active (yuy2_pad, FALSE); + gst_object_unref (yuy2_pad); + } + if (nv12_pad) { + gst_pad_set_active (nv12_pad, FALSE); + gst_object_unref (nv12_pad); + } + if (jpg_pad) { + gst_pad_set_active (jpg_pad, FALSE); + gst_object_unref (jpg_pad); + } + if (gerror) { + g_error_free (gerror); + gerror = NULL; + } + if (error_debug) { + g_free (error_debug); + error_debug = NULL; + } + + gst_object_unref (demux); + mjpg_pad = h264_pad = yuy2_pad = nv12_pad = jpg_pad = NULL; + demux = NULL; +} + +static void +_setup_test (gboolean link_h264, gboolean link_yuy2, gboolean link_nv12, + gboolean link_jpg) +{ + GstBus *bus = gst_bus_new (); + GstPad *sinkpad, *h264pad, *yuy2pad, *nv12pad, *jpgpad; + + have_h264_eos = have_yuy2_eos = have_nv12_eos = have_jpg_eos = FALSE; + buffer_h264 = buffer_yuy2 = buffer_nv12 = buffer_jpg = NULL; + + demux = gst_element_factory_make ("uvch264_mjpgdemux", NULL); + fail_unless (demux != NULL); + + gst_element_set_bus (demux, bus); + gst_bus_set_sync_handler (bus, _bus_sync_handler, NULL); + + mjpg_pad = gst_pad_new_from_static_template (&mjpg_template, "src"); + fail_unless (mjpg_pad != NULL); + sinkpad = gst_element_get_static_pad (demux, "sink"); + fail_unless (sinkpad != NULL); + fail_unless (gst_pad_link (mjpg_pad, sinkpad) == GST_PAD_LINK_OK); + gst_object_unref (sinkpad); + + gst_pad_set_active (mjpg_pad, TRUE); + + if (link_h264) { + h264pad = gst_element_get_static_pad (demux, "h264"); + fail_unless (h264pad != NULL); + + h264_pad = gst_pad_new_from_static_template (&sink_template, "h264"); + fail_unless (h264_pad != NULL); + gst_pad_set_chain_function (h264_pad, _sink_h264_chain); + gst_pad_set_event_function (h264_pad, _sink_h264_event); + + fail_unless (gst_pad_link (h264pad, h264_pad) == GST_PAD_LINK_OK); + gst_object_unref (h264pad); + + gst_pad_set_active (h264_pad, TRUE); + } + if (link_yuy2) { + yuy2pad = gst_element_get_static_pad (demux, "yuy2"); + fail_unless (yuy2pad != NULL); + + yuy2_pad = gst_pad_new_from_static_template (&sink_template, "yuy2"); + fail_unless (yuy2_pad != NULL); + gst_pad_set_chain_function (yuy2_pad, _sink_yuy2_chain); + gst_pad_set_event_function (yuy2_pad, _sink_yuy2_event); + + fail_unless (gst_pad_link (yuy2pad, yuy2_pad) == GST_PAD_LINK_OK); + gst_object_unref (yuy2pad); + + gst_pad_set_active (yuy2_pad, TRUE); + } + if (link_nv12) { + nv12pad = gst_element_get_static_pad (demux, "nv12"); + fail_unless (nv12pad != NULL); + + nv12_pad = gst_pad_new_from_static_template (&sink_template, "nv12"); + fail_unless (nv12_pad != NULL); + gst_pad_set_chain_function (nv12_pad, _sink_nv12_chain); + gst_pad_set_event_function (nv12_pad, _sink_nv12_event); + + fail_unless (gst_pad_link (nv12pad, nv12_pad) == GST_PAD_LINK_OK); + gst_object_unref (nv12pad); + gst_pad_set_active (nv12_pad, TRUE); + } + if (link_jpg) { + jpgpad = gst_element_get_static_pad (demux, "jpeg"); + fail_unless (jpgpad != NULL); + + jpg_pad = gst_pad_new_from_static_template (&sink_template, "jpeg"); + fail_unless (jpg_pad != NULL); + gst_pad_set_chain_function (jpg_pad, _sink_jpg_chain); + gst_pad_set_event_function (jpg_pad, _sink_jpg_event); + + fail_unless (gst_pad_link (jpgpad, jpg_pad) == GST_PAD_LINK_OK); + gst_object_unref (jpgpad); + + gst_pad_set_active (jpg_pad, TRUE); + } + + gst_element_set_state (demux, GST_STATE_PLAYING); +} + +static GstBuffer * +_buffer_from_file (const gchar * filename) +{ + GstBuffer *buffer = gst_buffer_new (); + gchar *contents = NULL; + gsize length = 0; + + fail_unless (g_file_get_contents (filename, &contents, &length, NULL)); + + GST_BUFFER_MALLOCDATA (buffer) = (guint8 *) contents; + GST_BUFFER_DATA (buffer) = (guint8 *) contents; + GST_BUFFER_SIZE (buffer) = length; + GST_BUFFER_OFFSET (buffer) = 0; + + return buffer; +} + +GST_START_TEST (test_valid_h264_jpg) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstCaps *h264_caps; + GstBuffer *buffer; + gchar *h264_data, *jpg_data; + gsize h264_size, jpg_size; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + h264_caps = gst_caps_new_simple ("video/x-h264", + "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 480, + "framerate", GST_TYPE_FRACTION, 15, 1, NULL); + buffer = _buffer_from_file (VALID_H264_JPG_MJPG_FILENAME); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (g_file_get_contents (VALID_H264_JPG_H264_FILENAME, + &h264_data, &h264_size, NULL)); + fail_unless (g_file_get_contents (VALID_H264_JPG_JPG_FILENAME, + &jpg_data, &jpg_size, NULL)); + + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_OK); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos); + fail_unless (have_yuy2_eos); + fail_unless (have_nv12_eos); + fail_unless (have_jpg_eos); + fail_unless (buffer_h264 != NULL); + fail_unless (buffer_jpg != NULL); + fail_unless (buffer_nv12 == NULL); + fail_unless (buffer_yuy2 == NULL); + fail_unless (gerror == NULL && error_debug == NULL); + fail_unless (gst_caps_is_always_compatible (GST_BUFFER_CAPS (buffer_h264), + h264_caps)); + fail_unless (gst_caps_is_always_compatible (GST_BUFFER_CAPS (buffer_jpg), + mjpg_caps)); + fail_unless (GST_BUFFER_SIZE (buffer_h264) == h264_size); + fail_unless (GST_BUFFER_SIZE (buffer_jpg) == jpg_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer_h264), h264_data, + h264_size) == 0); + fail_unless (memcmp (GST_BUFFER_DATA (buffer_jpg), jpg_data, jpg_size) == 0); + + gst_caps_unref (mjpg_caps); + gst_caps_unref (h264_caps); + g_free (h264_data); + g_free (jpg_data); + gst_buffer_unref (buffer_h264); + gst_buffer_unref (buffer_jpg); + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_valid_h264_yuy2) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstCaps *h264_caps; + GstCaps *yuy2_caps; + GstBuffer *buffer; + gchar *h264_data, *yuy2_data; + gsize h264_size, yuy2_size; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + h264_caps = gst_caps_new_simple ("video/x-h264", + "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 480, + "framerate", GST_TYPE_FRACTION, 15, 1, NULL); + yuy2_caps = gst_caps_new_simple ("video/x-raw-yuv", + "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), + "width", G_TYPE_INT, 160, "height", G_TYPE_INT, 90, + "framerate", GST_TYPE_FRACTION, 15, 1, NULL); + buffer = _buffer_from_file (VALID_H264_YUY2_MJPG_FILENAME); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (g_file_get_contents (VALID_H264_YUY2_H264_FILENAME, + &h264_data, &h264_size, NULL)); + fail_unless (g_file_get_contents (VALID_H264_YUY2_YUY2_FILENAME, + &yuy2_data, &yuy2_size, NULL)); + + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_OK); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos); + fail_unless (have_yuy2_eos); + fail_unless (have_nv12_eos); + fail_unless (have_jpg_eos); + fail_unless (buffer_h264 != NULL); + fail_unless (buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL); + fail_unless (buffer_yuy2 != NULL); + fail_unless (gerror == NULL && error_debug == NULL); + fail_unless (gst_caps_is_always_compatible (GST_BUFFER_CAPS (buffer_h264), + h264_caps)); + fail_unless (gst_caps_is_always_compatible (GST_BUFFER_CAPS (buffer_yuy2), + yuy2_caps)); + fail_unless (GST_BUFFER_SIZE (buffer_h264) == h264_size); + fail_unless (GST_BUFFER_SIZE (buffer_yuy2) == yuy2_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer_h264), h264_data, + h264_size) == 0); + fail_unless (memcmp (GST_BUFFER_DATA (buffer_yuy2), yuy2_data, + yuy2_size) == 0); + + gst_caps_unref (mjpg_caps); + gst_caps_unref (yuy2_caps); + gst_caps_unref (h264_caps); + g_free (h264_data); + g_free (yuy2_data); + gst_buffer_unref (buffer_h264); + gst_buffer_unref (buffer_yuy2); + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_no_data) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new (); + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_OK); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg != NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror == NULL && error_debug == NULL); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_data_zero) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memset (GST_BUFFER_DATA (buffer), 0, 1024); + GST_BUFFER_SIZE (buffer) = 1024; + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_OK); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_no_marker_size) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_ERROR); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror != NULL); + fail_unless (gerror->domain == GST_STREAM_ERROR); + fail_unless (gerror->code == GST_STREAM_ERROR_DEMUX); + fail_unless (memcmp (gerror->message, + "Not enough data to read marker size", + strlen (gerror->message)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_not_enough_data) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0xff, 0x00, 0x00 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_ERROR); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror != NULL); + fail_unless (gerror->domain == GST_STREAM_ERROR); + fail_unless (gerror->code == GST_STREAM_ERROR_DEMUX); + fail_unless (memcmp (gerror->message, + "Not enough data to read marker content", + strlen (gerror->message)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_no_aux_header) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0x02, 0x00, 0x00, + 0xff, 0xd9 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_ERROR); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror != NULL); + fail_unless (gerror->domain == GST_STREAM_ERROR); + fail_unless (gerror->code == GST_STREAM_ERROR_DEMUX); + fail_unless (memcmp (gerror->message, + "Not enough data to read aux header", strlen (gerror->message)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_empty_aux_data) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0x1C, 0x00, 0x01, + 0x16, 0x00, 0x48, 0x32, 0x36, 0x34, 0x80, 0x07, + 0x38, 0x04, 0x2a, 0x2c, 0x0a, 0x00, 0x1b, 0x00, + 0x40, 0x62, 0xcb, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xd9 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_OK); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror == NULL); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_unknown_fcc) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0x2C, 0x00, 0x01, + 0x16, 0x00, 0x48, 0x30, 0x30, 0x30, 0x80, 0x07, + 0x38, 0x04, 0x2a, 0x2c, 0x0a, 0x00, 0x1b, 0x00, + 0x40, 0x62, 0xcb, 0x0a, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xd9 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_ERROR); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror != NULL); + fail_unless (gerror->domain == GST_STREAM_ERROR); + fail_unless (gerror->code == GST_STREAM_ERROR_DEMUX); + fail_unless (memcmp (gerror->message, + "Unknown auxiliary stream format : H000", + strlen (gerror->message)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_not_enough_aux_data) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0x1C, 0x00, 0x01, + 0x16, 0x00, 0x48, 0x32, 0x36, 0x34, 0x80, 0x07, + 0x38, 0x04, 0x2a, 0x2c, 0x0a, 0x00, 0x1b, 0x00, + 0x40, 0x62, 0xcb, 0x0a, 0x10, 0x00, 0x00, 0x00, + 0xff, 0xd9 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_ERROR); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror != NULL); + fail_unless (gerror->domain == GST_STREAM_ERROR); + fail_unless (gerror->code == GST_STREAM_ERROR_DEMUX); + fail_unless (memcmp (gerror->message, + "Incomplete auxiliary stream. 16 bytes missing", + strlen (gerror->message)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + +GST_START_TEST (test_too_much_aux_data) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0x3C, 0x00, 0x01, + 0x16, 0x00, 0x48, 0x32, 0x36, 0x34, 0x80, 0x07, + 0x38, 0x04, 0x2a, 0x2c, 0x0a, 0x00, 0x1b, 0x00, + 0x40, 0x62, 0xcb, 0x0a, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xd9 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_ERROR); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 == NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror != NULL); + fail_unless (gerror->domain == GST_STREAM_ERROR); + fail_unless (gerror->code == GST_STREAM_ERROR_DEMUX); + fail_unless (memcmp (gerror->message, + "Expected 16 auxiliary data, got 32 bytes", + strlen (gerror->message)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + + +GST_START_TEST (test_no_sos_marker) +{ + GstCaps *mjpg_caps = gst_static_pad_template_get_caps (&mjpg_template); + GstBuffer *buffer = gst_buffer_new_and_alloc (1024); + const guchar data[] = { + 0xff, 0xd8, 0xff, 0xe4, 0x00, 0x2C, 0x00, 0x01, + 0x16, 0x00, 0x48, 0x32, 0x36, 0x34, 0x80, 0x07, + 0x38, 0x04, 0x2a, 0x2c, 0x0a, 0x00, 0x1b, 0x00, + 0x40, 0x62, 0xcb, 0x0a, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xd9 + }; + const guchar h264_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + _setup_test (TRUE, TRUE, TRUE, TRUE); + + memcpy (GST_BUFFER_DATA (buffer), data, sizeof (data)); + GST_BUFFER_SIZE (buffer) = sizeof (data); + gst_buffer_set_caps (buffer, mjpg_caps); + fail_unless (gst_pad_push (mjpg_pad, buffer) == GST_FLOW_OK); + fail_unless (gst_pad_push_event (mjpg_pad, gst_event_new_eos ())); + + fail_unless (have_h264_eos && have_yuy2_eos && have_nv12_eos && have_jpg_eos); + fail_unless (buffer_h264 != NULL && buffer_jpg == NULL); + fail_unless (buffer_nv12 == NULL && buffer_yuy2 == NULL); + fail_unless (gerror == NULL); + fail_unless (GST_BUFFER_SIZE (buffer_h264) == sizeof (h264_data)); + fail_unless (memcmp (GST_BUFFER_DATA (buffer_h264), h264_data, + sizeof (h264_data)) == 0); + + _teardown_test (); +} + +GST_END_TEST; + +static Suite * +uvch264demux_suite (void) +{ + Suite *s = suite_create ("uvch264demux"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_set_timeout (tc_chain, 180); + tcase_add_test (tc_chain, test_valid_h264_jpg); + tcase_add_test (tc_chain, test_valid_h264_yuy2); + tcase_add_test (tc_chain, test_no_data); + tcase_add_test (tc_chain, test_data_zero); + tcase_add_test (tc_chain, test_no_marker_size); + tcase_add_test (tc_chain, test_not_enough_data); + tcase_add_test (tc_chain, test_no_aux_header); + tcase_add_test (tc_chain, test_empty_aux_data); + tcase_add_test (tc_chain, test_unknown_fcc); + tcase_add_test (tc_chain, test_no_sos_marker); + tcase_add_test (tc_chain, test_not_enough_aux_data); + tcase_add_test (tc_chain, test_too_much_aux_data); + + return s; +} + +GST_CHECK_MAIN (uvch264demux); diff --git a/tests/check/elements/uvch264demux_data/valid_h264_jpg.h264 b/tests/check/elements/uvch264demux_data/valid_h264_jpg.h264 new file mode 100644 index 0000000000000000000000000000000000000000..f57e002d9a06d3f34da526b24276127e8f958eff GIT binary patch literal 5297 zcmZ`-2|QG9_aCW5q^zY-TKp*4m32_aPD0kPgro>r!yvL$KZUGilzo?dosl)!%D%){ zvkYcrm@(V)AKv%>zW?=po_p`P&*z+b?!D)n=YG!@0)a5NsH^_fsA?pD3jM4KfiObo z2d0#h5M^eF@(BWxH$q(&Lf3lOe=~Id>=YdaVTOabl}re1rpz4eTZY^T?xzcZNHW$O z9lS;!StDcVa|CqoCK+37vomH{+A!i%IP!>$DS-w1jM@C>uxdurwQL?$2R7S2rtf8v zN7fGP2xvgNX1Swwd>CDZRfG^;MEj1S#F&<*lE#73lipdj3er8DjDdY#h_ zZ7B=uNd~lEA%Og317Yi67zb!+&wI5u;0x5iZ99He=H-(vI$nj!nkisth=&FRhMmpp zXG9cqVSu_Lw(Z2&Z3w_8mB)qE+K$6)f#!eGzyiQ?{O*{@-{IYOA`7~}mW{qaQ5m2f zZakWll_8L&OG5xbkePfhKza+TOgv6yyDwYj2-1zi zm%A5oMyko(N*_)HAR=qVM?U%*0F=wJOlJbFz~=z--E&Th_Q?Q8ED}q-|6ssRup;ev zm?~-uoVwv$3IF4ij*a(g?6gRL1B!v7ZczW$43dm73CvNqQT7=# zwEmD@sxRczfPDu*_iWVDg|=IfS-`KZ^0A#^;W8v-Qxh(PYk9&-QYMX1S)hF&NPhTV~zoHqsv^<7^)J zGoG+a3TZ8G>asJ>ElQP_K?Hq30Kof8D%URSq2MUShkobU;GG6c$xwH9je%Ba12!Q6 z*{}g>^Tx@;_8rpFj;Ydyh$8gr?TqBT0V>lUaQjjYgM!;&h_k}ev$YE{Tmi8n{KDO1 z!Y}|hi*l(8cvQ(K#&0axos2su8b~I+Y`89RK!9x-*Ua@TzgGzJCY!Y_zoL<~oxr9P zeOMsLE@9ABjsbBVB7s4SOK68I5rxI`W}|S(=i8T;TH)-@9>h$@kRH!n`&o#?sCQ5X zOHsw~&}o!MCW3cvhX7-Wx=@b=r_)zVm*sr z*znc(9vRK%z-AbZau&<5s0umHk!q0Zx7z&T3;N2{{hFnRmb;|F%gd90aew>u@sGPf zBQ~y8i~-Qldtl|H`QRfTOS~!aoMZUZn`K|^5XHReBeqPYksA{}Iyp{#jbiEY(*3)_ zNE%0Yff2?%;HfdD`m^8&uFtMw81QII6=ujkN0iaa;cMI+Ozb^e2Xu$c#FzkG#HN8Gs{mSX1 zlv^KeD*Y(ZP$)JL|53PFn6a=)`72rJo8NTAwRhN=9sY$6qGi8bXS_~*U`Ejw2SE?7wjdu} z7u=n^2zKztZk}FC2G}=8{ZGD536SuB1%jQ^%}4!@xH%8sSut#><8x|60}$npP1|5} z>8U|?ks`f6&k5RUocU>(_B*+8?HO5?FLQ+acJFacM3+w8{JDUw@$x&D)utCFJGd>@ zx4yqI^*T-Pt}f~eq~7}>@P5wUbE%GaVoWS|_UwzhYAKWB55gOowlK(9vqqiWS3`uCXuf!mG}gqv9@zPBcvsRY%G28k(bujVMBIU(UZ9Z9qcp8ZzNm=rL_`C z-2{xDvdl1xm`Lf}!cPFjRC7L8ubZ!O`Y#007X9*XfvvH59!|omm|Lq)XK~()Mw+Kr zt^|)1+Y|>_pfIM@$|~FozlbK6>lDkGEzx_l#9g6*<3{!<|M}wF;8#$P$6^?uVRXhd z%F9Xe%tb?h8%JDJUOVX|3vJ0j=N!+MD#|_Q@2a}uF%V#U^j>G#)q!**bokk_yZK3> zwK#@^Gm>s{3JV2q3>w(;V&hfZ9sM2^)F-Sv%9@ixY)V&gSc<*$mpDJe5yMBx6)5W>WMM0%7NL2A@OxL7#V(BNs@O$M#`zSN$FZ!7L-=hSgzaW z)cN%Ls3SsSG}=W`0NxYZyQzyV6?D$b77AFkjsE5OKK|^J!AJ|$(%j8J7Foj#`>dQz zjaKc_xFL#MiP53VMzC|}u*6GWhF{lIT5cG7PTyglMw|gA`?<#4Wx_Yy`A%=M*2ZtY zq~^o`0obCzE^lgd`YGYQA*_LYUbr!PZTjN{{QGC8N{?PzYMwodV`2ED?7$^S~BkFRpOu$}ipYrFd#agF`ZtmnPI2<8mmHwBWL*f>pn zsI+5{ds8qxE#)qB&%)jJ;tCY33k%0JXVgoQTx&lYGKB=cAhX6gNLSuXskm#+k4vmX z19Bj@)SXOsF*1LM{Yz;_=4h9Xsb1g-0Ao8>rbkdJm>V zd0sS$Y~Jo(SzACtJMg{`RRO( z>*E5K;+hSKy4R_d_hjrcE#iFUk3=7ZUZ3)*N@`*Kor0yEinV=eI!5{W+8PG8m8wzY zk#;9J4lx^$wH=QWBnvy~_ahz4eg-#dI7ruT8y9D4+mxphMt zt-PJYUy4{Ybm!?u^NleT%rJ4qPV^M)!R-IOJP$lDcI0W%UVDypUu^ozIbWy3jsH=V)??t0Dvj< zK#-J3TEEWVD3nO!%o53%f&co}p%6q%k0VcAhYDufxW3|jwg3in61d-(B=dH+N;RSA z_(G&C+lMGeMbHEzRtr*9QklyE0k7(--5)z$Y#hbaQFqBlx|?%%WxHOeutp?D%EoQS@JZhnBw!?m@PRMAEc?#DK+ZhiH% zRMg6aZ8^%RYo;}$=VPdziux~X)76^rII{U>@aZD+7c9EA0-|rao&1?PyKg&w-hfdU zd|!_@(==19JUN(@jn75Iphn<$!R0bW~yjJfd>5L65cC*!GZ&j9rtD)tExu26|WsG!Qc-#ysxzR*eOXgyU8JKXCu-U#1`D#$*c(1QUBxa~x%ToIey>r!Rql?WHZTp;tZ?x83tt?w`Qta! z1whQ3Ow2U)$h36u&H>z_KtaitI6V4(bjVD>>WG&@*<4BV70aNTO3J@ib8?45k9hW? z-eMk#jkl+WuG~E8E|aOO!gjnO>?!A*dXz?Z+m4rTpwk;%W8<-%?|m@{1Ezu&YmOL7 zi8{Kzyx^AZHKm+tb>Trj4 zTv)iLuS^rGb`O)wZAAg-yP|J|zbS$5wufH}=L(O!Bykpn3enVR^5gfg8=S{`WB3QqR*dKE7T9}xyfs1W9Vg8s7C>6_e7u5BY zHLg`i^Ie#1%RA|!Tmzh@2@}O~(k7d`;b%mg^3xY|ji$)KYWmQN+3LS&nE6xisBjvumH# z{TUNwDkFra;6lg!K6u>`b0dQ-m6%cb!CqpBwcn7%H9mZ7 z_R{cbEpfcZ91?O7q`MARdm`Jztiv@JloKv3-cY+PF=(1D2Su;7npJ9% z1GiMQ%U1AN8b>|k&~c8J;Y_|Me21%eRXLnRQ3XCACik{VYrZ|FB2Lsk2Xh~r4_eLR z)lvq2>@74_usYZq1_F07ipCL2GA~c=vHCM9<^L4hg589IR;!k0G3=62XyTF76o2pw zPSlC`HlfIj`hN9A_X2_5^CeyxzQMJP`sQ2P^+%Gy_0oD@c}{@uNy$bKp*WX>Bfla7 zI6#4@I=<8%uYdFoNHPEohp#Tpx(8k7CUqrTP`dF747K+r>;MMBf`r+S2sdQ9=c?0+ zrsbEWX*#A`orS=$2mg933Q#55{~D=|I!PLtsTCIbR-x=OT6Wi+NO3I#hH|-3J~xom zSLe!SX$aaPu67`kqXa}XB~W`vdtTUE!nie-iz7m1aY6^a*C-(f9r~tg5-&a z`?(ItI7sEaWo$EAxQ)0H7Adi#US1LNVZmwAtCR2?*+B0eF@AJx(#h+}Pf>4I|61rZ zA=jO4uqW3&mAw-P@+i>o>>;Jc?shN9dDrZ6)47jcVI}!1V?t?;bHnW}5lSiLERGfe z#^0L1A3J71Qpt!uR3CE@HgAfa$v?GDy@XTgHHc;_yKA7kO?@AuXYRQPec~*B|Hp}J zBL8saFZWrmpNCg_c$Yzl-|Z+ITT>4lj_opR;v+K3+CCD%xBCwL0N65G1i~P3sVA!PiyZe7z4l zQFJAarL3hqIx@ChnUU-10z@5agun(nnGEKx%2Y1Vc^9z@r_j%J%K%NFp8g)0iB&=E zT9#0{;e>1F!Fk*%X>XfOXIN|pi*{j`C&4~hqb$07et}#-uA%~T0==8sIUvFunD{0q z8?DUU9&J3aDJiP1&hf3OsVN;j=)y}$n0?lR{yp@}3<$x58SH*O(_uP&o-X`z&~XN~ zl&(+0_|bc`f75F=|C0Ux!9n=%FOcv9>hAn;zh>AzMfd-Wf7#N711NLw82@&9;JtR= ieCNO;-SxlZf7<>J<^O)^_rIU>fDHVve*X`7_CEkh>X|YC literal 0 HcmV?d00001 diff --git a/tests/check/elements/uvch264demux_data/valid_h264_jpg.jpg b/tests/check/elements/uvch264demux_data/valid_h264_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e23067b90d335bc7c23cdeda6de2e6b2b34693cc GIT binary patch literal 10690 zcmbt)2Ut_h*XIqPOK*a7K>h4z2}noj(v*&L=^a8S(t89% zx&#Qx7vJ~)e*bU3-F&L)J= zZoAP41t)zVqnoxOv{YfrB?-aft|p<&?>kx|jfDeqF#(%)xfek>>~DlRE4`&3g~ zSKrXs)co~ZcTaC$|G?l7YzjU-GduTver_*4&{5WIAwzAY3?Nb@M^!JU{{l zVI2&n1RwxvyTp>oCXrYw&%t#R%#7MuDg`MsU!Io)rv%I2NPuP6b*9kgLoZ7~ZaeDH zfT=5a5t9xM>}XMk-Ir;Drq%PBCm1tr+I*ea#gEfu~ z1ow#6IAi9CbR}RYsHPvU1h^%B$kpX8W?*_*WvOZjnO(@HkcbAK(7x{c? z5gd~poNG7c>&^qhOrj7s4zzl~O!)S5KY0jsc;bGG`@a% z6ipii&@ca0DrQt=SC%evRf~}qKW~|bbUBCH#q9SG{yrNrT1!EMh)ey@Lv%gYjgse|_am(!SN8>Ke?l>EWQIS- zdPLtS;}ht)Q!(V~&hv!MK#_MixvbM^GZ9+kE9IA0M27~Cc z5%Y=62Xf{@$ARMz3VQ3bF1cF71yA{8;8>vWE1z^@f7^}&=8ohiZYCqJnBR6s>VS#< zR3fXkXvc50M%JIBmC#{L_t5~aes&B%{6Hi<1Hnp#rdL1QCY4k5r0sCh-;_^!JlOa` zZ`N(>jh|uSMWE^^B;SZnKX zfRvRdp(rMlm*LLia*>dtmm{OE;s^r?y)BuR%>*y=F~E1nN07HH6bGUktd~CuFo1Q@ zNVisR5Ax3Hm#&+(d*ax^Q_0okf=hm3?Y^9m(eW*3&VH^b4$CCkh)Vfx>}XPH+|x(oqa1?XvxF3>kP1@ql)b~wHc^!lj$&@mQkX(Z$|9mj zr%J1;JJxLxKJ>j8%sD86T7RpJnK*j^KSt#80G|+3mFlgQ%RxBqkRnl|yh;JC(FgV* z>M{h%MwP>mN56uGbr=vxE9Ie)+=<$i zCp>rxK7`zQ2U=(oBxl}Pow)7!ia5zAH!n;CT;qn?$tV5(%MM3J9y?ETw7gM2D7m>R zt@eD;8Ebn4ZE!A%1?7I37M3n$-M~~;vHeBOISg#glRv?(W`C-#@(7HsS=TJY0G&^n zpey>e>d13zj!zC!CY>eWY0D3}S+0zDt~K8Tm}{P)$Xpz&=2^afK*lH^(SBUApH+1u zy6Vzm)-K?=cz3Pn9A0%d)5~UO$xecL=Ryzev~vNYU)AziFT`cR0BZPm{hlfle`m%6 z65^rus*T?1stI+zZqTb;L)NSN{%{qP9sRd}GRgg39LBu^ZC0}!``Cver-cI>4N@>* zObCy4a|(=^8aG$_96>+sfDq-Yk|=iznRDCGxIygT$yF~%9sMq_u17^!3-|Q&)1$jI zXi`9>5Ud*#{Q|eomc z7|*AjR`gqr>?`iazz=hHyDy)=Q}Bn_HA$>+hPaOVgZp?NIk9TA=++qb2`@e6An$oG z`^C$R307RaT7Eh$lCf#@(q);~z3J3#G7+8Qdt&|!Dc7>rh z0CIiVuCq}OgE&|~olWB8+)vubOTEU-RjJ4P*}CPv6GA%6tzZ=sFL`?AaWxS3;Wr5b zZ>ZBBd$6RkC_7u{w);vHvo{x0AM_W2Z*oQpX(Qw#5cPubLc-&f?2$yw=#TNB_4mS< z>5)j5l9c(%vTTq!O0;j5d^66`p>`JkkeSZPdl5gb%YY!!okxy*MfD?c1_LPDKx{jr zhWM{kBlIwUzjImIm-Hg^{zJRTGo_6S7$VQ%c&ObWP#Xrv{ zQV0||g!jmZ(`@;>X%OI_mEndkbAzsGu@4}!Y~Lx04^0D-)!FSr-Zs0-$E;M(9z1eV zUusj}Cqyi;^2j!wRhA9G%-yo5ry!V#BTeC_jqq19RvdmLDnZBOt`6u`dCnKJWgdws zUduhcUZ2cKEu7TXUo}4Qz|SM8<~U<@eGDX-iwc4!#gjOtF&?T+R2ewW^lyxEB;@ca3>L1@r?^2n?cT3d_J#&o7~+( zc6sws{iGKVCn6mk9m7YmaDjm`g+S4s*W@d~noF4gQhZ~v1%3NDS^M*%uP5kjXr=cD znSgs=31d?*z-)0cR9FSPP%kZuCqV5sUdas!=K&V6aY>mlwv`4OiuGtopyEs)xsrMm z=z5C2)MnBN%Fs49&hZG&6`p?|6^tTPBixuNeZ2dfW$BZ=9mV0?`FWW**0ea%w_JJi zJT!=ewMKz^pJmBu4!^w*^-5g5yW$RCh-7OCTgw$qBYIbu)I<`>k{{BA|DF{CJBCvD zbI+XePL(2UyqX%hJm%!Y<9#1^d;W%q&om8V0J`TU1D|)lK28&NuUK!1Bhw31sPf%^q42LuX5V#&tQ!Iy?e#tcOM71@ym2#%gwi5#|Zmk^M#LktMZ019Es@WupNfIPEVR{s{ z4CtrMGAZM2Y$Rx!u-blSIElQLy*oCno$AzB^)RHJrl}R1$WPV=(~dd#h*cli%+GAC zey+Px>v6SBYG8-_3?5`b(9&fc1sLm_?2#sZ=}N<2RBoT?gG%i_trI}Ve(L)@kDlkv zvZpigfy=n_vqi%Uu(H$$yS7^k`U4@;{=8+~j!sT4d>-<5$i+|yPRp&AJH`*6CvjOU zCma!P=p9xSl<}#!oQ9E#JXGr+BOgw)P?FB}FttfIay0Ll;d9zj*RTKD9M2q{(f?kffJAU%0SE}Ley;oN;E%Wk#110`gFB4U=CHo z>)WmS8UEp<;+I8pC;yyVfc0;=+;r!W@@PA znX6;NgunJ$-pxnGecn|W_=s@Dp7LM%;ZVw?1LL8r=w@R3cPRf${f^LsjpkrZllLQZ zpZ6Jrnr_Rshq7kL2HkCk9@ZdZzFFwKEG^W06P}A7t4_&LVir&%;OL;XvtsjjxxD#@ z0LrpmNy`5#Q@YfJ^lf#6yEi#q<=L8;2M{dv2q8ytz9#Zs#!~3jPBwmS!#56&>FDrWjhfF)a za1Rzl0*khUV5SitUX@BzU;v6J#z$~kGET7?CyweR(XmERKUOXyA0_mhTFb|ilo5gt zjNu+gLzknQYW1-CGrBK)bhBbR_xghFfd?d5!``gupGbc7AAf|juc}NBJ(Ik9XJc}y z=qXb+?NTr@#4G?3i!xbfR%L(t+sJHJC()#Hn+7)*{~^O=icVT(`-anx))-%x9&#Gw z8$AKDMb$U7b;Z(IKI8AbC0;O=%0)<2uEeDD`o5M`0)M@6Wnks2QMV?Z`^=0hR_af5W2oi0Y-e4F8!1o{TSzUYmwX@ow3(WsWRd@SAG}sWodo=QWI~ zMT5&v#NBdRgFt6A%b)m1Z!~Y4u}*T#RElPp8LGba!*2;Qq_?OLZecqLS!jFZFFJc5 zl0f@Rk*UzcK{v+lebfcLIIsa{qJWZ}mA}z7pXo84v9axu>EF<_dX0@2ZBU(}{j73p zUBd(N0E%S?I7AHq5v%@17?OLxi;|2lz4G-_BstwC11+;aiM#rO!?E zSd;yk7AP_GRM6;`{{Ryih$Joux+>3F>3e_Y!OS(8y3@6%Tr@Jr($lkW{&_d!NT%Xk z?mB&!4Yz*cXK=Ef&)LjCl0|LZkv3h%70d4C-BrlQIHm64_|-pBg%TfgPp2?t+DGF00&Aft-S9U2f=R~6PYXnV?CfYWH~=-43XtUnV`jH4 zEcX&%g-wy#E_}+VsSMidf~KY4^O(oNZ)9+6iU`r7;sQiH3%p%=q?|JX%4rQ8t&W$9 zjtSVPq+(~SE=69F=Aa_A64uK3$@dKA`M$`ci^an}Y9bnkN^s*wqc6_oo3F&0ONOV0 znj;Zt+RU8S01MgF+H z_f`4whhii8YqI~HcXIWm#<}TVUq>bA5@Uczjw8*{79|HgNJwxc?Ju->8Nc<3T;x%q z8Iig)sh*K~R6XOc1S-bw4Yv^<0oP}yvujdsba}<{Zl;XE?-{7|Av#6!IQ>2k`Q42Z zX?ImbEJ%;>>^rpk=7Wa4X1M>|#GJrglA|{zBSitehM7Y$EfEY|aRaD0yGAHKeDr66 zE;7%S^RnE$|Avpn^7oSTQaOv4TG9baZNBTDmMO)*jY^z1Ok10eP+vPxpBttq%B~df zpyZ^tj!4)rK!0DbH*n5K>LJIFGJJIR-8If~HwO3+y*%T75m;6-%v4^wDD2AQ)j$>5?pXkhGjMwx1{^=aeo_No0PK!i&c;K+-n93`p>gwI=Nw2b+m;M!)}b<$+|*uiYYc=*^m(1cZcanUj2&6zkUsb8M~Cn9wr%f={c<&NjSZJal!Jy+#pD+~Q4Tblb(oYaq;5@Wy53)c+BXDx=YL%%;6_n2>SqV8&EnRqbr zJL&*`LH4BI|GX)Yb^){~mXUSAC+N^#SLzC`1a)Ily@| z)4t^4yK&r!PVJ`FND9qE*9%PGSWV99fTAVy@y2N~#`WY`F_T*_-wC}H94LQ?L=?F* zvT`G3LGjlLeF0={z`N78s0It6rvS!>yyjzdLc=dx!z7rc62#Wz$FY0@;>~SN4WI zU2YFDPZv@N>N4V1vu!jTJSQRa?tlM}Vnb)7|W<|MuSTq+u5uiw>t7Ep%R&5>C9Q8*l86F?Jbq{eY1EdG< zFSz3kTw;JQ3!~ln*MWraxZS2~R(mtBN3o zA2Fs(*wTZWD;Mfl(l>4DYfePlc?+K)m(M|2zK=(wJfFZ?7_(US%|kBT8o#6&OjYGR z#HkeyV$rwVNja~LeJwW~cRjZ^eRKL!7z4YI{&pE(Y423A^ZVATMulhf0wV&wlZISx zzzlufvbzUwPoFz1^h$}0mn`)YoAr!xJl@U6XW(1~fGWhDs+R0QS@ODJL54-Uex{U^ z<<^tZct<;DOTm%1Dp>~{LAy|JW-_-$7@Y*1(@vl?|6cG<0-8R};Kf|~ZiYf|O-XLy z>tZQhaB|=~(8c&hGj6W%5ts~s|9TooC4f#HpuU)f6P}t@K;OEiH z(%x2zO^K|EWX!A&5v8L|lq&N|X+MWjVxQaD3DN%4*6TVo5n}V9j`g-+_oN@)Zv*1J zt7Wu9eWz4Id>pqB2dE3wmutt5)HSt&uJAJ`k+;zBx1*NwteBlnsxhU>gkPmUAuJ9h zQF2T>a%@eGg-I3Fg^<=Hz{+xpuhruh;d$A%|13`>-wPwut@?P^S>KdV!ab%Zkx`dl zY|#2U=VtU%&UG9gKZ)E5>{X;(73d;hOY|A%V%sho`+a=#LBa^)bwcdwo1*ZM{R4mYyi~55pf7 z3Wh=HTJ;z}mpLj)n~VJ`BIOw!grt?-rk1--IAhjcSkfJYKF};#^f*w}tbTv-})aq>q^jgo&WPNLA`Wj`Kt`+<-7AS87$AYG+ z!AvLdyza|4{EVmwIgIk7y!xqtQi6P(;{A6P%ien3#-V+Vl@DO{?@=%rHKS`SNtC1U zw{Fd;wPU{3@Oc`UUg8SNV^h5r+L-$;c2*CL`m9!kDO%c^(NEh0K1oE3#97JS9tuZ& zoqPeoz!w}x23hkp( z_SKTB1A<`j9mar{+v$M@*K*L!Vxk?@G2!_jY{Y#G0h?ZzbQ_RpJ>wwMzG^jeVIkwq z_%+%(%o~60V71Zc`%#@SLQkEu-SolQ8+!O>b5oM_AW84DYehcYwI1@{3k0o=^A$L$ z#Jwh*zv!Ji_!W9(>le-<1(J;Ma|vhmUnIJ*y$T9V?xARnO~{(5tGODOb=fN`5UcgH z^|Yyw*<)Dsd0=ZTFwkVJvp{|r*X_DXFgkHdA1xxF>cT_^x4o>+~}4gXD{9 z@VqJ+u*Mlr_wrycP#cn}n{akQJFs7W|M4zg@Gr@hmbli@JR9CyUlir#UTkdb!SVMD z5OHf094+zdyQ(gu6)EgFCnsN>3469>!a26?5!BON12fH3-|cSl(`ORhj>{&pvEQPX z+!_{ys&M4XKxv-3TGd7XfXpSGc;X~tKNcZN^>4$5?`}nY18Xk!o zV7WqKKK5Cv=Zz4>g7@UACh3_&l(&{FZu>H-zugmH!#r07vNscPm1JRQvH^dx5?$b9TA|(6n{!$ z$JHhiU=FlcUJ)a)y z>o2yf#NqZ0nFq@#G33Rd4NkU*&u-Gs&-fEx-^tt%B+l0^QG?9DxA1& zCApbe^qwMF$ zWSO2t0l_BQOy5Y(*7`8Y%}j}ncfpLcKw)kWmCq-`%wJhHLQ@s7T+!cQLfG)jHKqxi zD9&>F6PE&+rJ6g#llFqN$Awcze3%z+DUqOh$W?m1pz+8n#LX6OphZ7^>+PpR_3(R) zB)}b4Le9K*IApF47th2WpCC(=Lhk$`eA*;dwZ>`n3`DFz(L~RQqs<^OYW8)e;^)Er z$~#_Hnm_JS-4;gWe@e3e5$D&$<_g(uy?hPDzydSYZ;%>DM>N{<+J>-M-*Yu||2(e$ zazHegKjF0EyU4@k6`dpt5~bP2qr#*h>0isBL3#wN?BN#=j#h<4a)d7SttUTMG-e;v zd|IW@_c@^CVQXZNt{rDw*XcbU6e)XN9<_h`@HI?P-lXyghX-;>bsRYXQVNjL0p$`F z=HnIiaf7fM@)N?CsSEI0PJe1O{owjK9e7v3@yEbiHt{zTtY@N;0DLV>g$iGi36`ua z((_EAUsh4OC7x7(lS(&Sa#WCtpx?Z&d}Tgnk1@t+i?rue^u}8vCf3d=4F-f8lGikC z{>P#N<@MK9uGD+K*x!g0yDhxE^P)Z5_bH+?GsI-U3EuOj;edBoLRSIR^?}nCNgR9m z_FKdD^Nw<8Mou1d4^6-Gz+Y14_vI^9k6X;tE{VnvL2Mf6?sCy*-(TjB@ICa+yQ1#H zr)1Y~R14BXpLQ`i%T0w#x4UF*F_G{b=oacnbgwp9CQoDyvhd>cNespMk8-$;GpW`c zrPO)M6vT8IQ1<97jg9m>w$oV46oys_2B#`+dgR)l{NlaUm`nA!J@WC1#+R6=Diu5* zdsE3>G67{=cYGzEO5QlK`Q76i&3E(m>9RttjqX0lFzR!a)cAOn8=7dheFK~5D=+bW zqdjGy*k~V=@owP8q5jiPB@d553nzC!b**-gcRPQ+;pM&VbaarkSVlKCe%h3$l9JKVaA&IwV&1Mc4eXrOBNtZG{5IzsMza?CUm`t2sv`Uo#1B?uNd zhynbz(Di4x#O291jMP088_zaPly-J~r&BE;#3LVFSK?F%&pvT_Lw(<=_Ue#7yZ3}k z5IaU#D>M>xR&p~0S@Z-Mjnkv)X;re_6i49tO!=%@ z3YlJEpN1x%_Im#4c+h#W&|!B(-M>xvKz=-oX!As~l8`EP0%tpR0+gu+NKMM)v4#QI zjbbGJHxE2G6#}1uHo`gd)}=c+<&R}!Ve$dUAr zp-g{|waD5Q-O!CHp_PHB-+wcs1`>LpL?x&W1WVEY^tW+TJ2z4lvbC#(0lau1r6ogy zU)fw@SZK8pr1R_&&O|AZ9cv$8(jOs<*znBe)0V|oc9+CxW3T?eP|q-8{#!T7$B6)dYxM?-d9aerD7sFel6sJ zeh@>vWr*zGP#U}2XnOe=QYQMm{ekz=SM%El7$!g@)(5e690jJW#J2Prae ziE5W{hSqPh%3~dCov(~N%kvz4+k5(Q&rsodW&stZ@51ceb-DKjb*Zs&af`sc7TMTK z6|nE91NnW#k3GJiZ{Bn3XC73q&86Zv?>aM>g4yKh%M-Ij)`}`(&F+}7TQuQq1 zET531%HxDJ(7q^Y*F&E4V-PVn@J}U+UF1xn_NgefPgI$GXb81ME>HI~h4PH?MLOE< zp2!n@%``d5$6Z>^${PxzezT{d2{06nV#`22PBidmfWuI$AA?9HGXd^VfJO(TDvw74 z=uUvNp5!9I*n>uW_Od^)+924&I-8^iGdW*_sO7Lr<+}&4AZjiQP^n}G68=*zCo}Qc zMQnocuoFlJq=>BuFmTjLLFGR`|N0MG-N%Z@o`b?RPovfbi$(!15v#vOBzHdUKd87t z-23wQcG$a{K`kFB0!-N3gXGWvbviiMTl*vYp9(vEQ7a#V=#u{!1)H~fB2SblEKgv} z4eTfackTmhMOYydsHLKO+@ik`W3S*HB|9u8x<3ZMS^;Z7tcbsiN`3Ul7V<>@qY4;m z>96+ut3P`AeboSCHGszsC^*W;Ny9Q?*O`AwP{YdmHwOAA@^Ak(G5lZr{|OH$*pt7Z z2r!ra;m58hP%DKy@&x}_|H&U0{AUHi+Td^X|7QOfMDtgB9oCAosO67A)R(aB;3!3$ z|1vWE9wXpC;QuS*KRpUAbZ{pBq;~$WQ~k>lf0^Uo6?Sa=FL;dsHSEU>i~9S20Jl*_ A)Bpeg literal 0 HcmV?d00001 diff --git a/tests/check/elements/uvch264demux_data/valid_h264_jpg.mjpg b/tests/check/elements/uvch264demux_data/valid_h264_jpg.mjpg new file mode 100644 index 0000000000000000000000000000000000000000..f36b514a85aca4e2a1ab6d6bf54a9a4099b9020f GIT binary patch literal 16017 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⁢%6qAI8{SGNX+C#=Iiw` zYU%=<336O;bh}`Ri8gOJM$c2~G83!6*C0Y=ss3sL;?BCo@Y&2ZWxpmcPsPWHHty-0 z;0<{{%eGxlM&GIsPiw^GN*9#JnAFSv4#^*1wZHD>tAxnyOEImz8WAGX$5;HC-&p$M z2ewq0Lj##8k;694JY;i;kzVgcG2oBRs(=uG^nak}^%MXcy9`~G93%E|NGIwCknLs8 zvh7zo;)^LI?-jksx>d7B@4pt^NM~1HLI|{ZWf;j9u`hoDls>fYFQwmYv55`E{+(w} zy|GTW4S6CaeVQ%d6=9bHv%2sA2i9UNPT6-y@wKel zW4AyjORT;|t?12w!dRsc_PYeY14mrujE@*Zjy9LN!p}|-d9r~I4sc)836-ugzt)A| zKfPT?&5WT&BQj*vFkSrXkgMc@+qKFz7wLU|WM*N4DFi>WA~K!NYSRS0p<>)OdF>XV zws>f*C5L4oqdB{yj&u2lmPwzWKX=So@ehH=OUvpXObKLX7LKz&_=z7ZLHen;#tR;Q zb75+F8cV#Tf&S`A&*ltU`_-S7KJ6*@$$Q)s=BHuB9Myit?ujJ4myto{9X)U}E+eg9 z@|?*9F`+Pm7=_6Ch^a%;akDeAvU^w|=pXW<{NThTboF!Zr!wmPwKYoUE|2YY-)tIw zhdx>}5eQ&%HCUb$Tasw8dg|7u^-%X7sz#*4|wpZCC%%_R$EG-{=D5} zmwU}2$AE~&Q)K%WW=jNqMDB6#xW4EP?3dgqb1sKoE9HwrFy=*S#m^RLblBNoK<(-TL%^ z<4$clX?aW7vs2}7@54*wu{^B}M0bgJX+6WTJh)_%z!`)(a+y(r9kd;*z2eOTDO(n{QEUw+HY zW6kdP@WVm@?rl5f4Lfy=t5+mLf-D!PfK55QI(2LhjEp8XNgI4};exf#b*{rctdOUc zCC)~C&6K_jEg2H}(g(5-9DGNjcN%7LTg7GdKU$j<@)e4hVTxD)*8_kYu87T`sEVq> zWz8f8rx5TCD!BPER$#o}cvwFOyxT-opWhLdCSHH3v+FhW$rOe^_|}Qz1 z)sbV_MjLtEzI4iD1^UDYYS{M_ZYt}F<+vBC25zV|mUI=rW+P>m7Ku!=bsBtf*Uei;}43R^(CXBLH~iLPabdr0&*WY?zIob~4Eh6^t>YohKXvLam%Axo27^3cbOkM5iJiVEoM+R#- z(0R0@H%1|^l-Dos;5b(zYP(5&_HET+zmK`m+ZVKqyYBqTfNM586rW_=r1;;a(|=)d zYP7&mTnaDIc{h*?6F7mD>c*UB;u4g)oHF=Y=Y;RNNE3eb!)FElRY{_QoH67+DT&&t z0-Yy0uA0Er*`5kth90&+YxBcXX}pFM{nJd$#ii7Y?|x+Nd-5s(P4*D_?90T60R9;G zICAByAHi4(z&Zqwt3wo|vB&|9QHbjEbOaQ=(Wu8-avMzzhTg2RL3lX*xxtT23LPpx zJRJ8UV*!B@St|(tpYv)g1^b?Wg+CU00;xk}&=n3iiCoSq`Pc2=XXuIxJv@3J6uNr~ zx!RvI1h5Fa{%et}nVA3LiWS1TFO6-5e!FSp(xD7Mi+(-WXL6uQ9RvMpe+K_og|=Vh z@>f5~xIaQc=k1F((r<=N}xSuh1P?D>P4(e*}P* z0$PCRA^r-L?D&r?r1Ab|6646lzx|olf9P_1DFS+m0Gkz%b)1QjfJQ{enSV!6M346$ z9;ly72mC|ClmEp3H+w+Vp7bplfG+ z^1uC6XerJhm%jRuU5)?r50}CCFP8BS8-e~|{=Xvr3nJ@M9b@cIYUd9+=|3g$R~-MT T(6;fv!PNs4(T5S5>mUCEZL8@v literal 0 HcmV?d00001 diff --git a/tests/check/elements/uvch264demux_data/valid_h264_yuy2.h264 b/tests/check/elements/uvch264demux_data/valid_h264_yuy2.h264 new file mode 100644 index 0000000000000000000000000000000000000000..ae68b8b6a82ad9b8a0ff9332b94e5bc9deeee558 GIT binary patch literal 5607 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 literal 0 HcmV?d00001 diff --git a/tests/check/elements/uvch264demux_data/valid_h264_yuy2.mjpg b/tests/check/elements/uvch264demux_data/valid_h264_yuy2.mjpg new file mode 100644 index 0000000000000000000000000000000000000000..c59e7088f2d7dfc5592085059e50f184301536dc GIT binary patch literal 34477 zcmb8X2VfP|_B|Z1ASz-3sUh_AgwRE4B1i{89=%8j9cdxC_fEehJs}Asp#(zj1QbL? zKt=RnLj}P~KoJy_BJhGhnE%@Q%-mdf`upDJICo}Fn{(EzUCurynBSN`2HhVNWS)8Y zzMx7^2lZ^(wzXFIRHcZhhl3sontk@%ymK{!=)X#npYIa3x68l=2ej2)f`aY~!cUdL z$Adan4eIz<+5TFd=i5-JDWA93=sGDy52~t~r`s0?4K2&7x&PM1OZ&#kpO*|hS+f7u z*JY(n{GPvcY5#S(tkkvt)}_3}6QdK3Z`n4cWZT%$({5eZ{&vOxiF+?4mdXhyM(i6i zLq31?@i8Ssw@_Fn zmTa7co{X7R+t~H(q!p(5{(<_FUcY~ydFAwoZ>P;D{-nEk=knG!YP~k8cK0`eTb81F zLN!;+d#G-DXBE%*B{ZR*u0B5EaJPxQ_uXfjzR=H#dd)M< zFGlX#+o#mL*{<%C8)XIGWDkrz-}yW9XY<;VXAXU9nq{AMJ>U2E{-<7A_uX$B?wssw zc8%$KzThKs)vyzb3oe{6?|8n6D;d9W(Xhu3n0KDsHRHt{@kb7r-}GrzWvXi4ZdP~F zl}lYz^Tzo24Q;CRry?7#K7Xkx-Q-oNT~Ywq{A;C{PCCf0e#utvq!$$J^bd;5qU$l94m^B*j=mfSI0L^d^u&; z^}~D5T-tvv|H9Uhj)zS1bf-?$-X3{8a`Y^G+qW+a+5FqlkyC%Wu)Tl(>!Ho_E+m}H z2{CW4cqs9)=U%Mrem%SV;el!6dmg>`_K&k}Uf5PDkKesAZ$rY{js|-|rJ}^cCq`fY z_w8+eJb3eR|L0@JPn>A(IhD2J;>E|)dIrCgee2eR=}GlxFM6Za*4uAfns%qniJQgw z8!nxHz0*4xUo`g&O+Wbl$7y%e*5$qlA3pbhQuLN~tmwB+e|0d;8_S=ans)4?cRC$? zddJNlXWcq;A+dDy)a^wdB<4&U-r>iD!zV&VUoU;Nd|#)lE5|34JT&ZhYTsp_#64k-AO&~OgrCuPg2v*izdDQ;qA%6vsM}<57Zpi=mG(~Qa9o|!@yO=v zoA=L-yOQ_th1=i$Qm&s&{%qy+ElF2<2Nf-Pq0;XUdAjd9_~2_*4p#lcEbsT!>ETTx zGdi5QbMlsXyWZ)J;PFN|TS|%D+_W_tb~;R-ElV%2__N z%Z5#pl6}6TC+;M49%qc&^yE`H>POY~jv1?`yz~0pXZ!4Zcjt`3OUG_c>ela?=b3}< zkKP@BVa=w)jy9gNTjqb>IjYgR56^ufpUO#`S-$7eYa82rb>;Jh^UVDrA(?H=-_Dla zy0NxV{~9&JPt-oNq2$fu8%}>T=IMjO9(_v*`e8=dGapar9sEYAd8c+{t#dy-@KX0R z&%8VH#?rBOZk!(Tz^BJXb^c@5i{^#PUEi#IzQDYcRp-?6hm#8`jeYZ#A&b?U8Dk&4 z+%C37R)^^KPfg8FnsW11v+P>%&-)#(JazQ!vop=|{%;wh+LpgJ@#j@%8a(^^_QOX% z$=#sLzSZWjyh@4rXBX_z-MP~mHRv?74=nx9%Uas(i`F zZ^q1O{bAv#s`Y;TGcTg@XZO|r^Y=g2o_Ah=u6U-#>G-A1T8-?NaAM^Cn-{k4Jl5z| z$aQl^viZ%JqUw1Uw%vF1uTvrQn#~^&75x4mwTG;K^w<|QKK<(}yr@pomJ_+ z1HV2uDE0S=bzc7HWWBPDN$aO?zJ9R0ZAs}zvuaM+Ty(g`hf^m%qYk*Rqt}Deri2#^ zbLE%req>|I1^M-pvMPBG-MI91ovKoelT#C)FgH&f-dj!{kW@Wx*{*Tf(X$f|_ut(* zv*nlVH+TK(t+dml+hndfTYA$R@bS~-or29(@AX|WtNY29`_3;9tM<**m31Sp=9a#< z{hP|wd@FwG_``qVQ=3mw-ygDj%tPNwM$63I`L~SJ^_7nfx;FXi(PJ;&EN?yaV(Gi> z+P%`w`()pO$aD8sZZc&1ye`+i82`kWU!Hyci5^AM#tqMCuU@P7*QJl!#H215W3GDD z{GzC|fhn6;&8}IMUwUO@i>qLBPU)AA9ejLW z_3^`92YA=s(@9siO~A8IvnTj@yXlKp!^O>@Wc zdM_W@`N(f8&GS_URLN~UJ^X=I4c_@{NZOfB;YCdj*2|Cjy5z=#w<~MC{%Y|{`j!uF z#@rYaZHs-{?xN@2_9$*Ed)SKC}MQgXVyrpBekZp*nqks`+^5 z4vhzO?l)95Zw@*%cJZ&RR-C>5Uc!3S{C2{L;|)vKhcA1$uYB#@&Y{!pJ3cjg(pIT% z=M_EMhv)V!z8q7fX4mP5uD(+x^1zqxq`&)Ro1vpW%Qx>2dN_5rY1VtVcf!X%w%Ikd zpR>AotLo>sR~+frx4iXtMRk6@7F-xCZ+)#OW9+0i4Zmu#AQR@L};(V?ey zE5{ohiW^kDO=t7g6P3O@TL0j%m)&nBXZ^Tu+Qf38@!{?V_WxO~n!krmkDC1G;Hb-o zS}kj@bT6yi#dr4Pp6L;p12&ur{vd1Hm!p~t-FRisDbL#n-W~nJ&g1vkGG4I6AIrdTa$9G$gKY=k5AvZ0?ql-s*`NNyY|x6gR2L*7PM+@etqS~J~cK(Ze8@;Q^(53ez4~3@+Slv7MHBusP2QBC49{hD|%L$!6@O+~lDOr8r zYnAH_9I@NmE9cmIt-5Fb{9NAayI;Hd_VRUOdV2exzrG=M#NcIvG7~mszB6?F9M7a? z6TDBQHQ%cKA8Jtb93w+_pNHbxmKFLHjB9P)1|L= zuN^8;fZx|#Lc5ADy_e$ZQ5 zSXTRH=!si#qs>0=7VnI_HFo=$7dPLYKeHrcamzceha~U)^~JW8yUZEb`CQJWBR_q+ z@~ctLBoy{{Eq^v*WbEJ#+Boy_o;BgMUs}CtNaB&8>c3s_)G6LG^7n>2K?T%!dU4K2kw|r>G9b?$5q0L9czE@Vg{9*5mu`A@H`dwNe z_3%e4a-09F`hnXuAIWgtZW2?pKlO!n=GTw-Rybl0+}ExC%=gZBpHc0PvF7)Wz4`6* z`@f%_zI)xe-8mCS{tH4}^iP8IzELg0kDdGRKUe4E{#n#;%)ojJ#s_zp z{NU}OnY9i%k1V^P?)&;wwQF78J75fZvHFu2e`>#Taj)d-+3$4ph5df&VW}u1_JLW$ zXC7Qx{imASZ(V-l`db^*dfx0fXzf=MclSM4I=XHn*F#|k5B~7Pl2fjOBaSTi;gc?L zhZ{Wc`m|NU3Oj!``E1|6p4mHQ$+6&xm1o~NShYdn^7}5mn-KbC=K5-p1s!HwzEzmn za6|7ZMUTA^bbWlumqXXg{_e~-RpvG{w@#Bc46gZSeEInJ;?MU#yyy0rSYMyC3+;;g z^d7uqS#_>#*tK)(H)p>yU|#*-HfK&e9@cW`{a?1-P`Oshi*NJ{ z{q_T8MePk2gZBOV>YuiVxqE-LobopFPmQbDVcfZ2Ykb_I`;8~3 zH<}YWc-Ek^LA8FEQ)S-B+Es@nUU^D6X>_mD{f)U4GphaHIFCaYw6v-1qFb)<4z%GwgyiwukB2pjCR<{ac@hlTOvW z|5}9haN*{gd&b@<%o$&O{^k}h99hw|_Jk@!k}lOW56%4L<6r7rNPg>s#(NvSKXBl> zea}XpD4m1(=WByIwm9a(`!i*Mq_r0`r&!xP5h!-q_SJB73(#0zu%zohD!|Qgf8t~%jI=_`(y7_(o z@73Qs|J?Fc?Rr@wZcLn%F}cqz`M~>EdImjo|9bV(rZxyr;D7Vx;74EB-)Q`EZycYV z&~e`RYp>0nziaoVHj>i%gn4z^ge&V0-2UEkAgk2u>~$=h{qv!o<>qHs9~!##(;pt$ zcdS{}zZSmJ_4AcCUCSpAn-E)T_TYz-ZcS`a>iAtf^~jq8w!J%|ue^U%^`14qy*Mo5 z{cXjb+CM!q)J#9Mqoi-xo|bX-S|7`N?AU!ZFV9Of&mW6Dabb4s(o>s;xmML|eZkC0 z_KwRqa)0a(*H-PB|J2I2%gW4N<65Ph8r$aGoZ+7AmCLh&Yd7f|(X;itnI8{-?Tb-c z%M(uZ5U2sSJRE%T z<)ZNy2K0`F3D&^1Jm-$M@QqyZ6W6yNnC@V#V)!zZfyN)|O5$ zCj9uHvSU#R<5O0?A9VIgxo7W=!!fNMyEG!Yr2m;a$G>yk@s3*5r`)q;N%m^pLiRQ^x(c6!R_oH_7YxEkfRvX3t4^yjP>^ET}7zNhEX7yg(y=yc7{QhD>b zUR%=}uf6rHr}crG&Z*fSG(GU2rQfwRuiTFOxNDz|PnvhCCI0bIz4bru@2h{>exa-X zi|jZ9AY<43K(jX>1!hnD9%Yt=>gZqZxPTiNf{n0n#3}FHhLk zpcG{WeKBdxv6f$=%;J%;E2|flZyDL`Pm@ZF>{y2QOz$slz4+Zp3J9^ZN`-5t^_@QL zkC~&lZQHUA|1Eff{@Z?>{>#tL=O0}8zn#OMMKlKCmrMJ&x{V#+6aQ12BAyi(m9*~P z5lS|Rlq{l*ze2}8atZNHiiyzwWuixczr(g!Eg!!`)82qcXTbfBWu^WO+5cWqDo!1DT2sz%s#2Mz{M1Zssx?-d=uOqeS~IF+G|`)=O{osb zP)akc5#_n*8LcCSYB$vBT@CS$Mh5zUU)-S_^&~{+Lu&@a2suVie_E}DJHkq&*4BU? zQgAT7^%S6_)HW>a)H=Y5QDe;N==IfldOeK}AP4N~X~Dn`*a5W$YOo@%zzq0>XblXU zKrBSH&{PQ}%4x`FL-ezO(U6{i4B?3p;R+lvvPKwNBOnVLkqbn$Ccp&d&>$q4 zb$T-}t0^OdUlk}Cjg^K#lrV1O=I=t^NCC1ceKq<{qAwvxq3;LWNNx?96dS;5fOU{yA*qatunS}ds1HJJ7!Ak-uJn}&D|}fR z8}PIG&v^}i0zFeI?pT8}e;}7&6l?=)J;u!D7@^9R0(OF|17qw4UT(4@gf8)mFltO3 zr}v_L(lU69LHNL)>8xK!R)}|$#*8EB4L0f{oorq(BPQ^wr3)?+>qrN(&4A<*dbNNT z={?wA#*J(Q+gTt-I*+s+*o?-$&ks+xeoedq&R*hr`BL|jj>&OP;dxJ@Y4si;) zo$Vd4A~_|Fq73R&ON~eZ(Hf)1x~wU+3t8nnqZxEvGp)J1x!z03Ep|C zu4ihC-G$mpb%VM^FHv9D_`r1s@M%bWMW2P1t^;`@KEN-7Cr0dGpN#U6O8ysk1%}bM z2qQ=oOB3mHvVNqEfVd&@89!i1+_U7CG@^aL9&UhV0b5ZrP`&$(cr$yii{xE!GRA0=+;jG?qxqjUsuazEUnWij`IRDtV22 zwX#NAtE|)4s%vm=(AF#K^^NLgZL_*b+p2Ccwy0ZmU5H6;+>2#8^WBAVp}s^eG?rpHYq`8!U#=A4T%i@q#X4cP zLSC(}QPyf})pc&dj8E`vy|O{w00cK`n^a(@ZBaJsblpZc;ukzqOBCQpi~|FA(1|;G z+5y~v9q|z7PVjUG2 z6Y{$qs8I^iAo;>R6W0g<@`+hW$rAxGNN3AI_`xcWOez9pLIQW*WoiVq)a_i0tcmBJx9sa^IVJd zJSE>KPzp(2)g{Ujb*Z{s2Y$v16(?~Ivb#zl+}5aTWyr0zjxocpTC9SP8aN32HV^}q zEnwj$4H!a}x9VH*3;e*ntw4+z2-e{Qeta!~G?O-@Uq)5if&*odyP0Mmu+f&kLMV^~ z6H?^;;Rz=gNAeg9eNFkqL}nXG6N+4dJQ(DNStr>J@_0fZx0dA~KZV$**1`N%%qH4N z67f$roNmajPxm>%KRM0FkTbOmSEf5#S!m=+IYu5hxCs0!kbs{;`fjOvnX(jeyIe1V z46jgDFn+5^_o=HOzs1H{;vX<1FmMZebj^fk#_$w@HOO z8(USv5{FLdB-5}V#692#sn+oe{%!?!+qDhA4>1OK1cV5oWJxFU5GjyNq!6#ju99CO zA(dpBe0Su)`cfVE%2c|ExCf@ez_I~LEs!Idj@SSsmtrjVr8z}2+2vCu-Ecd-x{u`7 z1^k>T?lgt?mks_c)N;YWJelxYq%D>U+|YgMQe~+M{iov>=Q6cODsrz>Rzm-=4%AjF z(1F@oeI4^|tzN7FKW(kC9`6#btW6hQmT>aN)`N*vvk;5_d+F? z`IjdzcIQL);WP??U7@m61BM1TXe^hD+(p0-JXB#rpaU5{Fpv1RT3to@P+g}2I~51? zAnC;okY}JF(KPRs)>7KS;%UW7A%H$P4)tQI2~+rfe+aT zmQDH<=rPBbi@*o0gAYi)AMqBN&c>J|m%JTHCG6obvp-2VVxB=bTKt0_sU~Vd(&2{HVdB!5(w^%7K3gELWQJ3gTw57&UX&JCv23xWM z*uj5MR+24&9Vvz!!;0vu!Mim=c7Y$+64+&ft{W7_P|NppmY&IIx1e2ShXtB%gKHz{ zMAmOx;3<<{B%J7Aji|#elYPOl4O+BBEsz>}$*MNu*#vQqI2H^K7STvx7|fOy=)fN) zY;X|2aIiGvw;^*9Xt7LFUNG1kjPme+d7j4pGEf8q@eBKlIT=L*>TAz9uGL{(AajHrS(a&9>&&)^t#aBdXp`A$e#`8(NekT@C1^#+CTYHH z8oX-451NoL1P9?KvcE`}LVsqejXcB^!5^Riz7Yms7IYq1MqE?~#RiaNnh(?`ADZ)7 zmYIW8j_~DpChmYc>Jc}2)&a>y#FS&pY(WrJA^jJu4%H-=>eieFY=}qq5Pp(RPjvx5 zDbt-PXK5tAIZ~dMCoMJ>DFr0Iz^_n)4c4IlH0VN&WLWU8$h`vi0W;uLtV4=b*kpYb z`80F|7a_;ci|dVb(1p3~wsOntc8T$6Q=ReYQyed6wp8MBW+bhLE+k1N{t;?h;b$`^ z*;bQW6BkQ>-xkfMG)f`vSeybn!I;Z|U(j_TGS1Qq#Kc^!0mcnPl$^aAbdm{b{g{?eQwuUb4UJ6UuAI zwm^9z3VcrwLRf|Y39m85tz$)oAA+v9cl+&(E;Pmxk}=AR1eq-=d5`Or8QOZlDx_%Vg> zV+!RZ`cn9VJP&~$T&677ie$oXrHTW3eI;AnV$y<`%MpI$SFe^K!(bus(^_Y>NE(|q z#u1-B%{e)3iepM@yfi7jgCjO)^#by6fE*+gUJiWj&4i?ac^^_4L1ClS*}#wDD8wd0 zh-8*xAL5C19CNZh)?FwaIlzo#BZL%7EhH6X>e=b2&#WY7(%g;NN7|2L^W;7ALAgyd zheL+^>bedNdNCaV|MWB$`7r4k_=mY}wy{vo(Q}~(7b%PM#mZu>K*`rE83umKz`tcA z!+H_sapVhK9ko?63xC2FeO=rJ_Iv2>+xhgdH%rIfkB9X1dN|JMc9%y1Lwq15nsZy zji$Y}Lz<28uALU*d#}2}zJ$=cJ5D;NIk<`1E+^bBo&~!b0nq?Gc6w5Y#t9nz!PKJZ|uh zEDPj!qdpcfd0|tTcZ>|J3IZuwrvb-s!Ve~CBrL%{w}lTsGq%K9JPG9LQa*GbwL<*k z_%lbsn1ist6e+Dsj76>KGQg8{1NzVHgZw7T@MAEoOMwrAd58fUqA!#d8t|m`JXQG7 zz)#Ot3dn}gT-Z%^1YR`760{=V2py=egbiU1DyzUhun^b*Gv*@fFzG+NLwcNJV%m7e z*upsHT3`n3fFaLU$V+6KOwx;!v?B1^tPiF+1lvl|TR?&0 zdsh&97<(AZb; zk9E3yiqn7(ZNPqz{v+&C-Klaq{1?ct0zXjA(ICGx4^i_V!}*BcEmoih)qKQaFb9DR zhA*R1ET%|Z3HgOA<4QJ}d>NX5SQz4tJR9<8idDBeM(vjwKM%ZfPRg3-n3Ntb#b-`Q z8k5<=xduLBrnjx!HhYZ&JOuyz^JUV1;3DC-MenVIc*sY_sDTXmWP}Fu&l(}K3CIdQ z5mJN^og~c^E5X1C#OwtyaM@+Pm6a=%$S@saAC|mc!yf=%y?(F zMV%6$|9Flf5G1b#%wu`pqIc6me6R>?2?!6;e}ZxRL@eS}Yq9c0xFMgA1_q)eNh;|w z$|1{uC*&|Xs15v}|5yhK4_e5tR#%}`g0*KQmrr)-hK@O$NAel4zwQ({)srS+9zt;# zp2tCkp$Ewh=c+Ux7W~Tx2Z15!L9)XJ&4-s`KD=B)Y(|Afge=3Rz?R@79wJV|`jGiY zzRglCF0;*oSJK8hCuab|^mtc%`XpD(!uCnzU3bWuFz=a#gA)+B;ph$9WGe=XIWxt; z^k*Qwj33)w)>^FnC`vCFCVrV+jFCW)e+fyTP>0qQIl2!7b&3*Du81uQ`@!)8UPI^E zFX0U7Ck{u*&Scq%II7DH8HNp!5l59^Luf86X9yh#8HOLI=g5#__=4b}x(GHnA9J}v z_<|Hug&j7Qu|46K0%BW;t->#aEjD0Ngf0Z{7)R2C@UNj2^$a~+i%E@_)3mYb@U#U< z6VrP+VzVYXX^yg5o2JjlmH3AUwust5j^P~|6hFkAS#Lqsl{Fn9Kr5`^7W2#E8Sd+2 zy!-^IV~s}~V}$4z2Qd({3tAuH9V(P7bR+DC6+aMjU$VcnK2G{iB@a79Npj0BMc06z zTc$XS&y!5}InzC9kl|FeA(?tM@FQEC3tvXWW8h0GixEqJKh5(Ij>jOT%K8wt1bz*C zLarpdAp2RYu5sdY<> zb;f2!CACP4cEn~Exe&32o}}2b?km$M!<%_is+IdCmMHGV}0x z8>*xjy5vS2i}>fGSPX0k`+@FEr|_f6j$|u2S`KUp&F5f^AlwK%~S+0SK*ob#L-$z<|HRsY4Q|@rcX$UhmT{p zF=Gavz)zty074Sc9O1bE6<`KDtOE!M4%Q<^@mRqcl%cVU_#NF51{Ry>9(pfWJX?9x zhVcK`cOm=hua7tJ`x>Z2^&}^JK$nI%KH^xCN1v^9m0J4RyQXN#S}Mh|*dcRqA}lm8?W&DiIC}o1)R9Ti*_{^5HKMifjL}ZSUWF9~ZbFXN zvwno%2VOxc+4sYUfSS;LjFQfX30#O#<^bb<+`%97Fw*grTn}%Mv6&qmsqH#XmM%L z&Y1KTj@WdUW0}@MjmnI6MP`O4My5tMnezc6|?OSc0%A~$s^-q*-8s5plQjRg%rs%4*Eq& z!KjM8qs%|rrEBk7!z_-pAvi1apdUeXwll$SQ5+RAtjH1IU#K+QIo%zW(%cp4Nmiph z&85&}1@{qZxHnD3n4OWy!(^8`%n|O3c4n&)T0}}5Bt24%NQ-miC{b#3a-=g`jxwTr zk#epUqeiDiITp!LdK7RhP@}bow1}ke4Dc^K(pjKH8KJl?CF>$TtBuHbCGq9V7aU}t z;VJ6F(nd+~siWjEsjoO+OvB0^Y%fm4EQ&E}t>B)m{e;cn@m27QeG^M|E999FWK@Oq z0s182Vm}pQNwO{BT^!rIdY^H32tU8 z?SNmjl%++1pHXs_5@SGS9r;?E7M>RF>`g1mF#GLN@Ex+F90jl>@D;UGZHAGGNKT&q zyq4f8R-!W|B0k;C(bhW-QK7X8#jw`^*VV8;^~qBcz8%>Di&MlrUgxR6Pw)vWWBKOM z13N!Y34210ugps3rR~3v{UAG{)iQV$iToJC4<+ksqZ6H8hpgz5q#Byk#uMskE_YGg zsqm+HO$1zVhWTcxtx`ghB9bS%{*@m3YM8IBoTfzT;VBW03@sb6u3>6;YPb}h9OcM{ zzKTeRa(U%p?#Q$#SBes&M`gq&rK+)7L`FnXt{SIBrH3bGBZ?TC9qx$8h;~L~#U?J{ z`5tUEd>fjxv`roFY?W7(Fes&~{GxZIvRYp4#+n}N2(QjVx(hA#2?Q~I_-u>`aAEw2 zU&J=TiCJf1M)@SQ#67;pl~5zj;aS*j`i1nu_p@Y}d}!X!f!b6PECk+aZ%0D1%Ox3# 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 literal 0 HcmV?d00001 diff --git a/tests/check/elements/uvch264demux_data/valid_h264_yuy2.yuy2 b/tests/check/elements/uvch264demux_data/valid_h264_yuy2.yuy2 new file mode 100644 index 0000000000..b62889135b --- /dev/null +++ b/tests/check/elements/uvch264demux_data/valid_h264_yuy2.yuy2 @@ -0,0 +1 @@ +€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ €€€ ƒ€ !€€€!€!€!€ €!€ ‚  €#€#€"€!€"€#€"#‚!""""!"€$€#€#€$#€"€$€$€#€%$€"#$"€%€#€"‚##$""€"€%€#€%€!"€%ƒ"€"€#€#"#€"€  €#€ €"!‚!€!!‚"!"€€!€€%€€€€€€€€€€€€€€€€€€~~‚€€€€€€€€€€€€€€€€~€€€‚€€€€ €€€ €€ € €!€‚ € €!!€€  !€!€  €!€#€!!€!€"!##€#‚#"€"!ƒ#€""€$"€$#!€#"‚$€#€%$~##"‚" "€!"‚!€"‚"€! ƒ!€!ƒ"€!€#€!!€€!€ €""|!€!‚‚ € € €€€ €€€€€‚€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€ € € €€ €€ € € €€€ €!€€€!€!€"€ "!€!€"€"€!€#!€!!‚ €"!~$"€$€"€""€!€#€$"€"‚##%#€$€!€#€$€!" ‚"~ #!€#!ƒ  €! !~!"! €!€€ €€"   €‚ € €ƒ€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€}ƒ€€€€€€€€€€ € €€€€ €! €€€! !€!€€ !€ €"€!€!€ € ! !€"€"‚%‚! ‚"€"€"""#€"$€!€ ‚#€#‚$"€""€"$€"€"€$€"€"!€"€" €!€ €!ƒ €!€ !€!…! „   ‚ €"€!€€ € €!€€  €‚!€€€ €€€€€€€€€€€€€€€€€€€~€€€€€‚€€€€€€€€€€€€ € !€€€€€€€ € € € €€!€ €  €!€ €!  ~!!€ €"#€##"!€!"€ €!""!€""€"!€"€&€$€!"!"‚# !!€   €!€ " € € € €€€€€€!!€ € €  !~€€!€!€€€ ~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ € €!!€!€!" €!€ €"€!€€ €€!€!€€  €!€ € € €!€€€"€!€ €!€"€!!"€!€"€"€#€"€"$€#"€"€""€$€"€"€$€##%€#€"##€%€#"!ƒ "ƒ"#""€ €"€ € €"€€ €€!‚€ €‚€€€€ € €‚€ €€€€€€€€€€€€€€€€€€€€€€€€~€€€ €€€ €"€"€##!##€"€"€$€#€"!"€#€#"€"!€#€#€"€!€"#‚"!‚!€!€! !!€"€"!"€"€!€$€"#$€#"#€$€$##‚#€"€!€$‚%€$%€&ƒ&%(~'‚+‚&€$‚%‚"€"€#€# €!! €"!"€ € ƒ € €‚€€€€ €€~€€ ‚€!!€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ €"~ €"€#"$€%€$€&%€%€$%€%(€%‚%$€%€%'€%€%€%%€&%€%€&€#€%‚$$%€%€$~#&(€%$€%&€$‚%&€'~(„'(.~3‹?€E‡U|c…s€•ƒ˜€Ÿ§«¯³€³€³€³³€°€ € €€€€€€€€€€€€€€‚€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€~€€ € €€ ‚ €"!€#$ƒ"€"%€%%€'€&€'&€'‚(,„+,ˆ1~5…8€9†;AD€R†T~Y†d~kƒv}€„‰}•‚›¡‚¥¦©}«ª«€­~¬‚­€­­€¯¯€¯€¯€°€±®€®€¯±²€²€²€±€²€¯€¯€´·µ²€²€±­€(%€%€%$"#€##€"€!$#€!  € €" €€"€!€ "‚  !"‚!€!€ € ‚~€€ €€€€‚€€€€€€€€€ƒ"}%ƒ*{-†1{=ƒD|N…Q{X„azf„mzr„wzx‚}z€…ƒ}†Š}‚Ž}”|•˜|——˜‚™|œ‚}ŸŸ} ‚ ~¡¡}£ƒ¢~£€¤~¥¤£¤§€¦~¦¨ª€ª©€¬«‚«¬­~¬€­~®€­­®­®¯¯~¯€°€°±±€²€°±€°²²€²³€²€²°€®°~³€´€´´€´€®€$€#€"€"‚!€# !€!€!~!‚"‚!€ €€€ € €€  €‚ € € € €  € ‚! €€€‚€!!€€€€€€€€€€hxƒxƒƒ†x‡‚…y‡‚†zˆ„‰zŒ‚zŽ‚{‘‚’z“’|“”z”ƒ•}”ƒ•}–ƒ˜}˜™}˜‚š}šƒœ}œœ~œ~ž€ }  ~¡ž}¢£~¤€¤¥ƒ¦~¦¦~¦¦¦¦~§§¨¨€©«¬€«~ª«¬€­¬­€­®€¯¯~­®€®€¯¯€°¯€°€¯®€¯°€²²€²±²±±‚­€°€³€´²±€¬€&€$€ €!‚  €€  !€‚ € €€€‚€"€ €‚€€ ‚€ € ‚!‚ €€  € €€‚€‚ƒ€€€€€€€€\x}†€zƒ‚{‚ƒ„z‡‡zˆ‚ˆy‡‚‰z„Ž|‚’{’‚‘~““}”“}––}–ƒ—}˜™|š›}œ€›~ž€Ÿ~ € ~  ¡‚¢~£¢¢€£~¤¤¦€¦¦€££€¥~¦§§€¨¨¨ªª€ª«~««¬€¬€­€­€®€®­~®¯¯€¯°€°€°€®°€°°€°€²€±²€²±€¯€®€¬€°€²€²€±«€#"€"€!€ € €€! €€"€€€!€ ‚€€‚!€€ ~€  €€‚€ €€ €€€€€€€€€€€Hv}ˆx‚ƒ|…z†ƒ‡{‡…y†‚Œ||Ž‚|’‘~’“{•“~–‚—~˜—˜™~™šœ›~‚ž‚ž}  } ‚¡~¢€£~£¤}¥¤€¥€¥~¦¥€¥ ¤€¤€¦¦§€§¨€©~«€©©€ª€ª€¬¬¬€¬­€®®€¬€®®€®€®€¯€®€°€®€¯€®€¯±°€±±°€°€°€®€«€«€¬€®€¯€ª€$"#"€ € €"€€!€!€ € € €€€"€‚€ €€ €€ € €‚‚€€€€€€€€€€‚€€€7||†€z‚zƒ‚†z††yˆ‚ˆ}Š‚Œ}€Œ{ƒ~‚’|‘‚“~“”}–—|˜—~˜‚™|šƒ›~šœ~œ}œž~ž } ‚¢~¡¡¡£~ ‚¤~¤¤€¥€¤€¤€£¡€¢£€¥~¥§€©€¨€¨€©€ª©ª€«©€ª€­«€¬©€©­¬‚¬®®€®€®­«€­®€¯±€²±°€¯€°°€¯ª€¬€ª€®€§€$#‚!~ € €€€€‚€ €€€ ‚€€€€€‚|€ €€~€#€ ‚‚€€€€€~€€€€€€%}{†|‚ƒz…†z‡‡{ˆ‚ˆzˆƒŠ~|‚|€’|’“}“”}”€•}—ƒ™}˜š~šš}šššš›|ƒŸ~Ÿ ~  ~¡€¢~¢£€¤¥¤€¤€¤ €¡€¢€¤‚¦~¥¥€¦€§~©€¨©©~©ª¬€«©€«€©€ª~ª€¬€¬­¬€¬€¬¬‚®¬¬®€¯€¯°€®€°€°€°~¯€°®ª«±€¦€!€!€! €€€ €€€ ƒ€€€€‚€€€€€€€€€!€$€€€€€€€€€€€€€ }y…zƒ‚z„‚…z‡‚‡|‰‚‰{‰‚Œ}€Œ}ŽŽ}ƒ~’‚“~““}•ƒ–}˜–}˜‚˜}š˜~š€š}š‚œ~œ‚œ}ƒ~Ÿž~ž ~¡‚¢~¢£€¤€¤€¥€¤€¢¡¢€£¥¥€¦¦¦§¦€¦§¦¨€©¨€¨€§¨©€ª€©«ª¬€¬¬€­€®€­€ª€®€­€®®€®€®€¯€°€¯¯€°€®€­¬€°€ #‚!€!€€€!€ €€€€€€€‚€€‚€€ €€€€€"  €€€€€€€€€€€w€€zƒƒy„ƒ…{‡‡|ˆ‚Š{‹‚‹{‹ƒ{Ž‚{‘’|’ƒ“|“‚”}•ƒ•~—‚—|—™™™€š‚š|›‚œ}››}žž~ŸŸ~Ÿ€Ÿ~   ¡¢¢¢€¢€£€¢£€¥¥¦¦€¤¥‚¦¦€§~¦€¦§€–„?|9Œ7v7:v€†§{¨€ª€¬€«€«€««¬­€­¬€¬€¬€¯€®€°°¯¯€¯€¯€­~«€¯—|"…"€ !€€€€€€€‚€€‚€€€€€€€€€€€ €€€€‚€€€€€€€€€€€k~{€zƒƒ…z…‚†{‡‚‡z‰ƒŠ|Š‚Œ~Œ|Ž‚}“‚’~“”}”‚”}—ƒ˜~™˜~™™~š›š€š}›|}‚ž€Ÿ}Ÿ  €¡¢€£€¡¡ ¡€¢€¡¡¢¤£¤€¦€¦€¦¥€¥j€46w3‘5v53t26xƒ¨|ª€«««€­­€¬‚­¬€¬­¬€¬€®€®­­­®€¬€¬ª€!„ €€€€€€€~‚€ƒ€€"€€€€€€€€€€€ €€€€€€€€€W€|{ƒ€z‚‚z„„{‡‚‡|†‰{Š‚Œ|ƒ}Ž€~’‚‘|“€“|•‚”~—–}˜˜~˜€™~™€šš€š~š€œ~œ‚ž~ŸŸ~ƒž}Ÿ¡ €¡ ¢£¡~¢¡~£¢~¢¡€£€£¥¥¦‚¥~¥’ƒ6}27wfŠizf‹hyT2~1‘lx¦€©€©««¬¬€¬€¬~¬€¬€«€«¬¬€­€®€¯€®€¯€¬«€†~!ˆ€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€‚A€|y~ƒ{‚‚{„‚„{…‚‡|ˆ‚Š|‰‚‹}Œ}Ž~€~’‚“|“•}••}——}——}˜™~š‚š|š€›œž}žŸ|ž‚ž~‚ž} € } ¢€¡¡¡¡£€¢£€£~¤¤}££€¤€¤¢?Œ4w9ŽhygŠgvhŒh{j‰dw<Ž4x{„©~©©€««ª«€«€«€«€ª€ª€©«€¬€¬€¬¬€««€¬}€  !€€€€€€€€€€€€‚€€€‚‚€€€€€€€€€€‚€€€€€€€€€€€~-{x~…€|„€}ƒ„…{†ƒ†|‡ˆ|‰Œ|ƒ€|~““~“•|–„—}–˜|˜˜}™š~˜š}šƒœ|›‚›~œ€}ž‚ž~ŸŸŸ€ Ÿ€¡€¢€ ¢¢~£€¢£€¢¢€¢}¤£€£¤¢€4Ž0x_Š^yf‹jzgŠhyh‰f|e‹:y/‘¢v¨€©€«€«ª€©ª¨ªª«~«‚«€«€¬€¬€¬¬€¬€¬p€ €€€€€€€€€€€€€€€ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€#€yy}ƒ€z{‚…}†€‡|ˆŠ{ˆƒŠ~‹€}‚Ž}‚}‘’}“‚”~”•~•€—~–€˜~˜€™~™š}›€››€œ~œ~ž€ž~ž~ŸŸ€Ÿ€ € € ¡€ ¡€ €¡€¢£€¢£€£€¡€£€ €21ybŽguhˆf|h‹dwhŠgshŒey/‘Tw¨€©€©€§€©¨€§€§©€©ª¬€«€«¬€«€­¬«€ªf€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€w{{ƒ€{‚ƒz…‚…|‡ƒ‡{‡‚‰~ˆ‚Š|€Œ~‚~Ž}’’~’€“}“€”}•€–}–€—}™‚™}™ƒš~™€š~›‚œ€œ~žž€žž~ž€ € € ¡¡¢ ¢¢€¡€£¢£€¢€£€¤€¡€¡4Ž0wcŒcxhŒhzeŒe|e‹gxf‹fz<•5w¦€§§€¨€©€§~¨€§¨€«¬€©€ª€«¬€¬­€¬«€¨_ „€€€€€€€€€‚€‚€€€ƒ€€€€€€€€€€€€€€ƒ€€€€€€€€€€€€€€k}{‚}‚‚{„ƒ†{†ƒ‡}‡‡}ˆˆ{‹ƒŒ}‚}Ž|‚€’““”}”€–}––~—€™~™™|šƒšš€›œ~€€ž~Ÿ €Ÿ€Ÿ€ ~¡€  ¡¢¡€¡€ ¡€¢¤€¤¢:‹1ydŠbyg‹fxeŽgyeŒgwh‹hzX1z¤€§©€§§€©€¨€¨¨€¨««€«€¬€«€¬«€««€ªS €€€€€‚€€€€€€€‚€€€€€€€€€€€€€‚€€€€€€€€€€€_z{†~z‚ƒ‚z‚‚„{†ƒ†|ˆˆ{‰ƒ‰}‹|‚Ž~Ž‚‘€’}““}““~”–}•‚•~–˜–™}šš~š€›šœ~Ÿ~œŸž~Ÿ  € €¡€  ¡¢€¡€¡¢€¡€¢€ ƒƒ.Mfse‹gzeŽdye‹f|fhx^0z£¦€…6}6£y§€¨¨€©€ª€ª€¬¬€¬€¬¬€««€«J~„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~Ny{‰~y€„‚}ƒ‚ƒz†‚‡{‡‚ˆ{‰‰}Š‚}~Ž}~‘‚’““}“ƒ“~•€•––~˜˜˜€š}š™€š›œ‚œ€ž€œœ€œŸž€   €¡€¡€ € ¢¢£€ €¢€¢ 8€2ŒfwgŠcyaŒe|fŠf{dŒgxP.s¥€¥€JfzC”>u§€§€¨€§©€ª€ª€«¬«€ª€«¬€«C€†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€;|{†}{‚z„‚„|†‚†|‡ƒˆ}‰„Š|‹‚‹|}ŽŽ}Ž~~“’’“}•‚•~––~––~˜‚™}™š}š‚ššœ›œ|œ›œ€~žŸ€žŸ € } ¡¢ €¡€¢€¡¡š€15vaŠ_zgŒcya‹f{f‹e{.8x¥€¥:Žcs9Su¦¦€¦¦¨€©€«ª€ª©€«©ª€ª9€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€~€€€€€€€€‚€€€€€€€*}z‡}zƒ{ƒƒ‚z…‚…}‡‚†|‡‚‰|‰‚‰}ŒƒŒ}‚~Ž‚Ž~‘}‘€’~’‚’“”~–€–|—˜}—˜}˜š}›‚›}™ƒš~šš››~œœœ~Ÿ‚ž€ž€Ÿ€Ÿ€ € €Ÿ € ¡€¡  ¡Œ)€.‰f}Š€_YŠvxd‹Iy/Ždz¦€¦€¢€jt€¦€§€§€§€¦€¨€©€ª€©€©~©‚ª€ª¨©0€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~xƒ}{~€{ƒƒƒ}…†z†„‡|‡‚‰z‹„Š}Š‚‰}‚}~~’}‘“}“”}••~•‚˜}——~˜€™~ššš€›~šš~šœ~›€›~œ€œž~ž€  €  €Ÿ “€›¡€ €¡€™/‰/z6JxQIs0Ž-x-’y¦€¥£€£€¦€£–‚—¢€¤€¦€¦€§€¨§€§€¦¦‚§€§€(€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€‚‚€€€€€€€€€€€€€s|{„€}‚‚‚|„ƒ†}„ƒ‡}‡ƒ‰}ˆ‚‰|ŒƒŒ|Œ~Ž‚€}‘“}‘’~“’}“‚”~•–}–—~––€˜€š|š‚™}š‚š~šš}šœ€›}‚ž›~ŸŸ€Ÿ€Ÿ~žŸ    ~  €CŠ/z/,x0Š-y/{£€¥£€¤ €PeŒNz˜€¦~¦¦€¦€§€¦€§¨€£§€§‚"€‚€€€€€€€€€€€€€€€€€€€€€€€€€€‚€~€€€€€€€€€€l€z}}‚|~‚|ƒ‚…z†„‡|†‡|ˆ‰}ˆ‚Š|ƒz‚Ž}‚}~‘€‘~‘‚“|“““€’~“–|”‚–}––˜˜~–€“€„}}‚~ššš›€œ~€œ~œ›œ€Ÿ€Ÿ€ Ÿ€Ÿ€ŸŸ€   € ¡Ÿ€¢¢€]€.ez¡¢€¢€£€W‹cxfŠby‚ƒ¥~¥€¤¦€§~¨¨¦€¦€¦€¤€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€Wxz{‚{€ƒ|„ƒ…|„p~A|2†8{h€‡€‰{‹ƒ}Œ€|ƒ}~Š0…*}U~†R†1y3‹_z•|•–~••~—–~”‚€ƒ5{2‰9yš~ššššœ€›~œœ~œž~ŸŸ€ŸŸž€€€Ÿ¡~ ¡¢¡€w:ŒR{M‹<|‚w~,Œcxg‹Fw—€¥¦€¦‚¦€§§€§€¦€¦§¦€‚€‚€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€‚€€€€€€€€€>zz|„}{‚zƒ‚|P*„)|(†+|+…0|‡z‰‚‹{}‚Œ}Ž}‚.ˆ-z,†‹z;‹-y,ŠN~“”“~“‘€”€”~–Š€2~1‡1{•€y€€˜€š~š€•~\€M‰^{•€›P€FˆA{2+‰.}-/w:•w € ŸA€`]te‰[x->vr‚-€)Œ]z¥€¦¦€¦¦€¦¦¦€¦€¦£€£€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€)€u|{„}}€€{‚‚}|)‚&†(|Z~.€+„,|^|‹ƒŠ}}P†A|W„~|Œ‚,‡*~+ˆ}{I…,|-‰=|‘~‹5†.}.…1{D†’{€0€/…2}B….|.‹7w˜ˆ€1Š-z.‹0y/‹{y›€0,-{+)w+‰*z1‹,w.‹1x € €‡ƒ]`bygˆb{1Ž t¡ Ÿ~/‚›¤¤€¤¤€¥‚¥¥¢£‚¥¡€€€‚€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ s{xƒ|{€ƒ{€…~}(&‡'{z|Q‚*†*{6‚ˆc†*v)‡){*ƒ+|`*ˆ(z*„j|b†*|+Š3{‘}1€+‰*{0‡,|0‰My’.Aˆ,xm/-‰.z•3€.‹,x‰€,,‹.z™~-*Š+~(&x*‹(y–~)‚(Œ+y›ž€‘YbŠbwdˆ\~u¢€¡€¡ž€.€JŠ<~¢€šš€£€¥¤€¤€¤€£~ ‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€pzu‚{{ƒ€{‚~}*‚&†'|kzkƒ(ˆ&{+„‡-„&|'‡~{+„*)…Š*‹(z+‡X}{ƒ(y)†-z*€*ˆ1z‰‚+*Š1y‘~,-‡+z},‚,†+}’€.-Š,y”},,‹,x—-€*Š*x)Œ)w+‹+z—~,‚'‹+zš€Ÿ};€ZŒawTDy  ¡€¡XŠ\|f‰\}^‰azGŠ¤~¤€£¤€£‚œ €€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€k{zƒ|{|ƒ}}€€{.'„%}S}{„'…)~)‰„|*†(|(†}{)‡*|(…xz>&q'‡G}†€(+ˆ)y~+*‰0}Š€**ˆ+z‘}+€+†*|~7‚,†-|Ž€,€+‰,}Ž~.‚+)y–€1€)Š)zJ‚—~˜~‚7ˆ(y),y—€€ž€‰€l‚‰ž€“B€u„ ~-Œdzf‹Cy6…`|€¤~£¥£€¡¢ž‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€‚€€€€€]wz…y}~||‚€|5„'„&}@ƒ‰ƒ}…‚†{‡‚*Š*z(†n3ˆ'x*‡d{V‹'o(†6}Š~+)ˆ*yŒ€€Œ~X)‰)|)‡,y~/‚*ˆ,{…}Xƒ+Š,{ˆ~.,Œ/x‰/€*Œ*x’1€,‹)w€˜~/)y*Œ-z*Ž/w™€~œž€ž€ž€6‰bz3“uRˆA}G…Ÿ~¢}¢ƒ¤€¥¦€¤€¥€¥¢€˜€€€€€€~€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€‚€€€€Jyu‡{{|‚~z}‚~}N‚'…%|1„ƒ‚‚{‚„|…‚6‡'{(„Z}Bˆ&{'‡Ryt‚'y(†*~}((„){ˆp€)ˆ'z/†+~)‡+{/€+‰+{w~/€4‰+yz€2€,Ž*y~€2)*y’6€,Œ+x€\€('x‚€8€+,w–}€€ž€ €Ÿ € €w2€-Žœu Ÿ€¡€ ¡žœ ¡~Ÿ  ‚Ÿ‘€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}2|y…{z~‚}z‚€{gƒ&ƒ'|*„„~.‡%{(ƒc}N‚&}'‡F}d'{&†>{‚)'‰(|‹)€*…(z‚}:)†'z…*‚(ˆ+z†€6*ˆ*|o€1Z„,q€4,‰)xs€2*,yŽ:+‹)y‚€M(Š(z‹€:€+‘+v•Ÿž€›™—˜€—™‘–›€š€œ€~›€›€ž€ €œ€ ~ž€œ}‘‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€#~x…z{|{{|}{x‚%†%}'„}4‡&}(…P}e‚%}&†5}vƒ({'ˆ2y‰~))‡)yŠ.)‡*zx}B€)†)|ƒ}*)†+{|B€*‡+}d€j@‡9{l€Y2‡.~f„3|+‹.x–~N€,‹Cy†w9Ž,yM„2|-Ž+vŽ€ššš››€šœ››€›€›~žžž€ž€   ‚ŸžŸ~™‚€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€r…z{|ƒ|{}‚~z}ƒ%…%}%…t}:†${)…P~~&&…+||&~(‡8zŒ}*€,†({Š}5+†*|a€\€)‰)y8‰+~*†){6‡.}+‡-}:‰mz1‰,yy~“-Š+~/‹+z*‰8y˜B)Œ'yo‘*Œ'y)Š6z+Š-zˆ€šš€š›€š€š€œ€œŸ€€}žžœž›€ž€ž ~ Ÿ € —€€€€€€€€€€€€€€€€€€‚€€€€‚€€€€€€€€€€€€€€€€€€€€€€€m€r}yƒ{z{ƒ}{|‚L%{&„%~%…%|&„p|‡|7‚(‡({*…'|(„pŠ~*‚*‰){…~D€+†*|L€‰-ˆ*|,ˆD|-ˆ,x.†-~/†1|U‚m~F…U|’“‹€V€@ƒE}v}““~†€€‡€”~˜€–€Š€•˜€™}˜‚˜™€šœšš€š››€œ€›€œ€€ž€~œœ€žžœ€œ€žž~”‚€€€€€€€€€€€€€€€€€€€€€€€‚€€€~€€€€€‚€€€€€€€€€€€gw|z{{|ƒ~}}~|Qƒ&…'|%ƒ*}f}…†{†‚a}/-‡?w}Š‚Š}y~}€‹~Œ‚‰~‹€€~Ž}~’€’~““~”€”~””}““€”€”~““•€•~—€—~—˜˜€™˜—€™€š~™™˜™}™š™š€š›š€›€šš€ššš~œ~œœ~Ÿ~‚œ“€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ€€€€€€€€€€€€€€€€€€€€€€€S€u|w‚xzz||~~|~€|‚|ƒ‚†}…‚†}†‚‡}ˆ€‰ˆ‚Š|‹Œ~€ŽŽ~ŽŽ~€~‚‘~’“}’‚’’““”“““€“€””€–€•~–——€——˜€—€–—–˜€—š€——€˜€™€™€™€›€š€š€š€š€šš€œ€š€›››œ€œœ€€œœ€€‘€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€6uzxƒz||{~~€||ƒƒ}…†|†‚‡|‡‚ˆ}‰Š|Š‚Š}‹‹Œ€Œ~Ž}Ž}‚~ŽŽ~Ž~€’‚‘‚”~“Ž’~}K„~‘u€“€}†~e…‚‘‹€XDŠ”|€€™€š™€š~™š›€š€š€š€š€š€š›€›€›€œ‚š€›œšœ~››}‹ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€#€u{w‚x||ƒ~{‚€{€€|‚zƒ€…}††{†‡|„^€„€`€_€†~^}v}\‹ƒ€‹`€sQk‚ˆ~Š\\ˆŠ|qzˆi~~€M…Š~ŒL€d‚l“~m€[€S€}S”€l€†‘’€UY]z€—˜˜€š™€™šš€š€™€˜™€š€˜šššš€š›€š›~šš€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€r}v{y{„}|}‚~}‚€|€‚‚{‚„|„€ƒ{„‚†{dƒ†}Uƒ>~‚…ƒ~‰}Uy€mŽ~F‚†}NŒ~‚€ƒ}imR~ˆ~a‚’}zƒ’‘€s€_€Ž€€K‹{z“b‚q…z|Oƒ`“€eˆ€ŒR€Wƒ‘w€—ƒ˜€˜€š~™š€™€›ššš€š˜™€™š€ššššš€šš™€p€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€nzxy|{ƒ|z~ƒ€{€{€|ƒ|‚~…„{tƒ„}N‚U…‡{Š|t‹~\ƒx_|~ˆ‚W†Yy‰j_€~h€P€iƒ„f€ˆ‘u‰~’}c“~“€Œ”€“€’””‚“”–––€˜€˜™™šš€™š~š‚š™™™™™™š‚š€š€›šš~š˜~c€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€gyxƒx}z‚{{|„}|€}ƒ~‚ƒ{…„…|ƒ€E‚gx€ˆ‰|fm‚‹}…‚‹}…‚‡|Œ‚‡}ŽƒŒ}Œ‚}€}Ž~‚‘€‘~‘‘’’€’}’’“‚““’“€””””€–~•••”~”‚—~˜ƒ˜~—€™˜—~˜‚˜™™š‚™™€™~š‚˜~š‚š~š››™~U€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€Xxu…xxy„z}z€{}€ƒ€z‚{‚zƒ‚z„€†}„‡|‡‡~ˆ‰{ˆ‚‰}‰‹~‹Œ}‹}Ž}}Ž|ŽŽ€~‚€‘~‘€}‘’’‚’‘’’€““€“~“”~””€•€•~–‚–~”–™€˜˜—˜€™˜€––—€˜––—•p}<€>2‚*‚#~€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€B|t‚wzy‚zz}‚||}€{€„€}€{€‚‚|……~„„{„ƒ†|‡‚‡|‡‰}‰€Š|ŠŠ|Œ‚‹~‹ŒŒ|‚~}ŽŽ~Ž€~‚}~‘~‘‘‘€‘“‘€’€”~“‚’~”€“~”•~”“~“”~••–••—–€—€––~––€–~–~‚u|ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€1|r„wzy‚z|{{{~~z„€}€{€ƒ{‚‚ƒ}‚€„|…‚†{†‚‡}ˆ‰}ˆƒ‰}‰‰|‰€‹}ŒƒŠ}Œ~‹}~€ŽŽ~~€Ž~‘~‘‚€‘€‘‘‘‘’’’“€“€‘’€“€“•€”€–••‚—•€•~•–~–•€”–‘~}ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€%r„u{y‚y{x‚{||ƒ}|}ƒ~|€€}‚ƒ~ƒ€ƒ}ƒ„}†‡|‡‚ˆ}‡‚ˆ}ˆ‹~‹Š~‹ŠŠ‹‹‚Œ~‹€|‚Œ~ŽŽ~u€sƒ~ŽK~Ž‹€Ž€‘~€o€“Œ€…€T\ƒ“|•~“€”€”€•€•€”€”€””–~–”€•~•~“€y|‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ€€€€€€€€ pƒt}w‚y{yƒ{{|{~~~|€{‚~€|ƒƒ}ƒ‚…}†‡}‡‡€‡€‡}‡‰Š€Š~Š‚‹~‹‚Švhz~€^†R~]€€F……{N‡k}a…p}<‡6~@ˆM}W€m€m„o‘€]|‚m“‚€yU€[€’}“~“€””€“€“€“€“€””“•”•“€”~~‚€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€juzvƒw|z‚{y|‚{{}}~ƒ~}€|€‚|ƒ|ƒƒƒ~„‚†††‡€‡~†‡|ˆ‰}‡€ˆ}ˆˆ€ƒ=ŠAzAˆ8}{‚D~`ˆ‚U‚|g†y{m_@ˆ@|JƒCoyMu€te€‹€lŠ€}~Š€‡€‘’“““€“€’€“”€““€“€“€”~”ƒ“’v|‚€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€dqzu„xzxƒz{z|~|‚}{}‚|~~ƒ||||ƒ€ƒ~…€…|……{††~‡‡}‡ƒ‡~‡ˆ}ˆ‚‰~‰x~€‚€†‹~ˆ‹~‹‹~ŒŒ~€€Ž€Ž€Ž~‘‘‘€€‘‘’€‘€‘€““€““€““€”€“€“~“€“€“€“‘|}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€O€s|uuzx„w{xz{{‚}}}ƒ~{}‚~~€{€ƒ|ƒ}„‚~ƒ~„~ƒ€„~…‚‡|‡‚‡}ˆ‡~‡€‡‰‚‰~‹Š~‰‚Œ~‹ŒŒ€Œ}Œ€}Œ€ŒŒŒ€~Ž€Ž~Ž€Ž€‘€€’€‘€‘€€“~“€““’’€‘“ƒ’€’‘Žz|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€5€q|uƒtyu„v{wƒy|{‚{|{„}|‚}zƒ€~|‚ƒƒ|‚„~ƒ€„|„‚…}…ƒ‡}‡‚‡ˆ‰|‰‚‡|‰‚Š~‹Š}‹Š~‰‹~‹‹€ŒŒ‹Š~Œ‚‹~Œ~~€€€Ž€‘€‘~‘‚‘~‘‘€‘‘€““““€““€’~€‘~Ž€{}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€"€p{rƒtyuƒwzwƒvzzƒz{|‚}|}‚}{}{~{‚|}€ƒ{‚ƒ‚|ƒƒ„|ƒ‡|‡‚†~‡‡}‡‡}‡‚‡~ˆ‚ˆ~‰‚‰~Šˆ‰ŠŠ‰}‰‰}‹Š‹Š~‹ŒŒŒ€Ž~}€€‘€’}’”––~••~–‚”’l|„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€mzq‚qytu|uw|y‚z{{ƒ{|}‚|z}~|}ƒ~{}€|€€|€‚z€{‚ƒ}ƒ‚ƒ~ƒ‚„~†‚‡~†‚†~€†}‡‚ˆ}‡‡}‡‚ˆ~‡‡}ˆ‰|‡ˆ~ˆ‰Š€ˆŠ‚‹~‹€Š‹‹}Œ‚‹€Œ€ŒŒ€Œ~ŒŒ~Š‰~‰€ˆ~ˆˆ€ˆ„€€‚|zƒw~pk_‚4€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€iup…q|qƒs{u‚uyv‚x{x‚zy{{{|‚}{~‚||~ƒ}{|}„|€|€€{~‚€|‚}ƒ€‚||ƒ||zy~}€|ƒ~|{„|~xƒr}uƒl|d„^S‚M€FB9€5€0€,'€$€$€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€\ym†nzq„qzqƒrzsƒs}uu|u„u|tƒuyr„tyqƒm|jƒc}a\|P‚H<‚5€.€&&€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€'€0.~)(€&€"€ €€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ€€€€€€€€‚~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ \ No newline at end of file diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index eef73e6c90..d166a66057 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -1,5 +1,11 @@ +if USE_UVCH264 +UVCH264_DIR=uvch264 +else +UVCH264_DIR= +endif + if HAVE_GTK -GTK_EXAMPLES=mxf scaletempo camerabin2 +GTK_EXAMPLES=mxf scaletempo camerabin2 $(UVCH264_DIR) else GTK_EXAMPLES= endif @@ -13,6 +19,6 @@ endif OPENCV_EXAMPLES=opencv SUBDIRS= $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) -DIST_SUBDIRS= camerabin2 directfb mxf scaletempo opencv +DIST_SUBDIRS= camerabin2 directfb mxf scaletempo opencv uvch264 include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/tests/examples/uvch264/Makefile.am b/tests/examples/uvch264/Makefile.am new file mode 100644 index 0000000000..e02b88821a --- /dev/null +++ b/tests/examples/uvch264/Makefile.am @@ -0,0 +1,36 @@ +TEST_UVCH264_GLADE_FILES = window.glade \ + boolean_property.glade \ + enum_property.glade \ + int_property.glade + +if HAVE_GTK + +TEST_UVCH264_EXAMPLES = test-uvch264 + +test_uvch264_SOURCES = test-uvch264.c +test_uvch264_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_VIDEO_CFLAGS) \ + $(GST_CFLAGS) \ + $(GTK_CFLAGS) \ + $(GMODULE_EXPORT_CFLAGS) \ + -DGST_USE_UNSTABLE_API +test_uvch264_LDADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + $(GST_VIDEO_LIBS) \ + $(GST_LIBS) \ + -lgstinterfaces-@GST_MAJORMINOR@ \ + $(GTK_LIBS) \ + $(GMODULE_EXPORT_LIBS) + +noinst_DATA = $(TEST_UVCH264_GLADE_FILES) + +else +TEST_UVCH264_EXAMPLES = +endif + +noinst_PROGRAMS = $(TEST_UVCH264_EXAMPLES) + +EXTRA_DIST = $(TEST_UVCH264_GLADE_FILES) + diff --git a/tests/examples/uvch264/boolean_property.glade b/tests/examples/uvch264/boolean_property.glade new file mode 100644 index 0000000000..d391a58c65 --- /dev/null +++ b/tests/examples/uvch264/boolean_property.glade @@ -0,0 +1,94 @@ + + + + + True + False + + + True + False + 18 + + + False + True + 0 + + + + + Disabled + True + True + True + False + + + + False + True + 1 + + + + + Get + True + True + True + False + + + + False + True + 2 + + + + + Set + True + True + True + False + + + + False + True + 3 + + + + + True + False + Default + 8 + + + False + True + 4 + + + + + Disabled + True + False + True + True + False + + + + False + True + 5 + + + + diff --git a/tests/examples/uvch264/enum_property.glade b/tests/examples/uvch264/enum_property.glade new file mode 100644 index 0000000000..0dfb9747f9 --- /dev/null +++ b/tests/examples/uvch264/enum_property.glade @@ -0,0 +1,88 @@ + + + + + True + False + + + True + False + 18 + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + Get + True + True + True + False + + + + False + True + 2 + + + + + Set + True + True + True + False + + + + False + True + 3 + + + + + True + False + Default + 8 + + + False + True + 4 + + + + + True + False + True + ● + True + + + False + True + 5 + + + + diff --git a/tests/examples/uvch264/enum_property_gtk2.glade b/tests/examples/uvch264/enum_property_gtk2.glade new file mode 100644 index 0000000000..487efbe5cb --- /dev/null +++ b/tests/examples/uvch264/enum_property_gtk2.glade @@ -0,0 +1,88 @@ + + + + + True + False + + + True + False + 18 + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + Get + True + True + True + False + + + + False + True + 2 + + + + + Set + True + True + True + False + + + + False + True + 3 + + + + + True + False + Default + 8 + + + False + True + 4 + + + + + True + False + True + ● + True + + + False + True + 5 + + + + diff --git a/tests/examples/uvch264/int_property.glade b/tests/examples/uvch264/int_property.glade new file mode 100644 index 0000000000..422ce1cdc4 --- /dev/null +++ b/tests/examples/uvch264/int_property.glade @@ -0,0 +1,147 @@ + + + + + True + False + + + True + False + 18 + + + False + True + 0 + + + + + True + True + ● + 10 + True + + + False + True + 1 + + + + + Get + True + True + True + False + + + + False + True + 2 + + + + + Set + True + True + True + False + + + + False + True + 3 + + + + + True + False + Minimum + 8 + + + False + True + 4 + + + + + True + False + True + ● + 10 + True + + + False + True + 5 + + + + + True + False + Default + 8 + + + False + True + 6 + + + + + True + False + True + ● + 10 + True + + + False + True + 7 + + + + + True + False + Maximum + + + False + True + 8 + + + + + True + False + True + ● + 10 + True + + + False + True + 9 + + + + diff --git a/tests/examples/uvch264/test-uvch264.c b/tests/examples/uvch264/test-uvch264.c new file mode 100644 index 0000000000..78d8ac110d --- /dev/null +++ b/tests/examples/uvch264/test-uvch264.c @@ -0,0 +1,673 @@ +#include +#include +#include +#include +#include + +#define WINDOW_GLADE "window.glade" +#define INT_PROPERTY_GLADE "int_property.glade" +#define ENUM_PROPERTY_GLADE "enum_property.glade" +#define BOOL_PROPERTY_GLADE "boolean_property.glade" + +#define PROPERTY_TO_VBOX \ + properties[i].dynamic ? GTK_BOX (dynamic_vbox) : GTK_BOX (static_vbox) + +#define GET_WIDGET(object, type, name) \ + type (gtk_builder_get_object ((object)->builder, name)) + +#define GET_PROP_WIDGET(type, name) GET_WIDGET (&(properties[i]), type, name) + +static guint h264_xid, preview_xid; + +typedef struct +{ + GtkBuilder *builder; + GstElement *src; + enum + { NONE, INT, ENUM, BOOL } type; + const gchar *property_name; + gboolean readonly; + gboolean dynamic; +} Prop; + +typedef struct +{ + GtkBuilder *builder; + GstElement *bin; + GstElement *src; + GstElement *identity; + GstElement *vid_capsfilter; + GstElement *vf_capsfilter; +} Main; + +Prop properties[] = { + {NULL, NULL, INT, "initial-bitrate", FALSE, FALSE}, + {NULL, NULL, INT, "slice-units", FALSE, FALSE}, + {NULL, NULL, ENUM, "slice-mode", FALSE, FALSE}, + {NULL, NULL, INT, "iframe-period", FALSE, FALSE}, + {NULL, NULL, ENUM, "usage-type", FALSE, FALSE}, + {NULL, NULL, ENUM, "entropy", FALSE, FALSE}, + {NULL, NULL, BOOL, "enable-sei", FALSE, FALSE}, + {NULL, NULL, INT, "num-reorder-frames", FALSE, FALSE}, + {NULL, NULL, BOOL, "preview-flipped", FALSE, FALSE}, + {NULL, NULL, INT, "leaky-bucket-size", FALSE, FALSE}, + {NULL, NULL, INT, "num-clock-samples", FALSE, TRUE}, + {NULL, NULL, ENUM, "rate-control", FALSE, TRUE}, + {NULL, NULL, BOOL, "fixed-framerate", FALSE, TRUE}, + {NULL, NULL, INT, "max-mbps", TRUE, TRUE}, + {NULL, NULL, INT, "level-idc", FALSE, TRUE}, + {NULL, NULL, INT, "peak-bitrate", FALSE, TRUE}, + {NULL, NULL, INT, "average-bitrate", FALSE, TRUE}, + {NULL, NULL, INT, "min-iframe-qp", FALSE, TRUE}, + {NULL, NULL, INT, "max-iframe-qp", FALSE, TRUE}, + {NULL, NULL, INT, "min-pframe-qp", FALSE, TRUE}, + {NULL, NULL, INT, "max-pframe-qp", FALSE, TRUE}, + {NULL, NULL, INT, "min-bframe-qp", FALSE, TRUE}, + {NULL, NULL, INT, "max-bframe-qp", FALSE, TRUE}, + {NULL, NULL, INT, "ltr-buffer-size", FALSE, TRUE}, + {NULL, NULL, INT, "ltr-encoder-control", FALSE, TRUE}, +}; + +static void set_drop_probability (Main * self); +static void get_all_properties (void); +static void probe_all_properties (gboolean playing); + +/* Callbacks */ +void on_button_toggled (GtkToggleButton * button, gpointer user_data); +void on_get_button_clicked (GtkButton * button, gpointer user_data); +void on_set_button_clicked (GtkButton * button, gpointer user_data); +void on_button_ready_clicked (GtkButton * button, gpointer user_data); +void on_button_null_clicked (GtkButton * button, gpointer user_data); +void on_button_playing_clicked (GtkButton * button, gpointer user_data); +void on_iframe_button_clicked (GtkButton * button, gpointer user_data); +void on_renegotiate_button_clicked (GtkButton * button, gpointer user_data); +void on_start_capture_button_clicked (GtkButton * button, gpointer user_data); +void on_stop_capture_button_clicked (GtkButton * button, gpointer user_data); +void on_window_destroyed (GtkWindow * window, gpointer user_data); + +static GstEvent * +new_upstream_force_key_unit (GstClockTime running_time, + gboolean all_headers, guint count) +{ + GstEvent *force_key_unit_event; + GstStructure *s; + + s = gst_structure_new ("GstForceKeyUnit", + "running-time", GST_TYPE_CLOCK_TIME, running_time, + "all-headers", G_TYPE_BOOLEAN, all_headers, + "count", G_TYPE_UINT, count, NULL); + force_key_unit_event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s); + + return force_key_unit_event; +} + +void +on_get_button_clicked (GtkButton * button, gpointer user_data) +{ + Prop *property = user_data; + + switch (property->type) { + case INT: + { + gchar *val; + gint val_int; + g_object_get (property->src, property->property_name, &val_int, NULL); + val = g_strdup_printf ("%d", val_int); + gtk_entry_set_text (GET_WIDGET (property, GTK_ENTRY, "value"), val); + g_free (val); + } + break; + case ENUM: + { + GParamSpec *param; + gint val; + + g_object_get (property->src, property->property_name, &val, NULL); + param = g_object_class_find_property (G_OBJECT_GET_CLASS (property->src), + property->property_name); + if (G_IS_PARAM_SPEC_ENUM (param)) { + GEnumValue *values; + guint i = 0; + + values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values; + + while (values[i].value_name) { + if (values[i].value == val) { + gtk_combo_box_set_active (GET_WIDGET (property, + (GtkComboBox *), "value"), i); + break; + } + i++; + } + } + } + break; + case BOOL: + { + gboolean val; + + g_object_get (property->src, property->property_name, &val, NULL); + gtk_toggle_button_set_active (GET_WIDGET (property, + (GtkToggleButton *), "value"), val); + } + break; + case NONE: + default: + break; + } +} + +void +on_set_button_clicked (GtkButton * button, gpointer user_data) +{ + Prop *property = user_data; + + switch (property->type) { + case INT: + { + int val_int; + const gchar *val; + + val = gtk_entry_get_text (GET_WIDGET (property, GTK_ENTRY, "value")); + val_int = (int) g_ascii_strtoll (val, NULL, 0); + g_object_set (property->src, property->property_name, val_int, NULL); + } + break; + case ENUM: + { + GParamSpec *param; + + param = g_object_class_find_property (G_OBJECT_GET_CLASS (property->src), + property->property_name); + if (G_IS_PARAM_SPEC_ENUM (param)) { + GEnumValue *values; + guint val = 0; + + values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values; + + val = gtk_combo_box_get_active (GET_WIDGET (property, + (GtkComboBox *), "value")); + g_object_set (property->src, property->property_name, + values[val].value, NULL); + } + } + break; + case BOOL: + { + gboolean val; + + val = gtk_toggle_button_get_active (GET_WIDGET (property, + (GtkToggleButton *), "value")); + g_object_set (property->src, property->property_name, val, NULL); + } + break; + case NONE: + default: + break; + } + get_all_properties (); +} + +void +on_button_toggled (GtkToggleButton * button, gpointer user_data) +{ + if (gtk_toggle_button_get_active (button)) + gtk_button_set_label (GTK_BUTTON (button), " Enabled "); + else + gtk_button_set_label (GTK_BUTTON (button), " Disabled "); +} + +static gboolean +set_caps (Main * self, gboolean send_event) +{ + const gchar *h264_filter; + const gchar *raw_filter; + GstCaps *h264_caps = NULL; + GstCaps *raw_caps = NULL; + gboolean ret = TRUE; + + h264_filter = gtk_entry_get_text (GET_WIDGET (self, GTK_ENTRY, "h264_caps")); + raw_filter = + gtk_entry_get_text (GET_WIDGET (self, GTK_ENTRY, "preview_caps")); + if (h264_filter) + h264_caps = gst_caps_from_string (h264_filter); + if (raw_filter) + raw_caps = gst_caps_from_string (raw_filter); + + g_debug ("H264 caps : %s", gst_caps_to_string (h264_caps)); + g_debug ("Preview caps : %s", gst_caps_to_string (raw_caps)); + if (!h264_caps || !raw_caps) { + g_debug ("Invalid caps"); + ret = FALSE; + goto end; + } + + g_object_set (self->vid_capsfilter, "caps", h264_caps, NULL); + g_object_set (self->vf_capsfilter, "caps", raw_caps, NULL); + + if (send_event) { + gst_element_send_event (GST_ELEMENT (self->src), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("renegotiate", NULL))); + } + +end: + if (h264_caps) + gst_caps_unref (h264_caps); + if (raw_caps) + gst_caps_unref (raw_caps); + + return ret; +} + +void +on_button_ready_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + + set_caps (self, FALSE); + gst_element_set_state (self->bin, GST_STATE_READY); + probe_all_properties (FALSE); + get_all_properties (); +} + +void +on_button_null_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + + gst_element_set_state (self->bin, GST_STATE_NULL); + probe_all_properties (FALSE); + get_all_properties (); +} + +void +on_button_playing_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + + if (gst_element_set_state (self->bin, GST_STATE_PLAYING) == + GST_STATE_CHANGE_FAILURE) { + g_debug ("Unable to go to state PLAYING"); + } + set_caps (self, FALSE); + probe_all_properties (TRUE); + get_all_properties (); + + set_drop_probability (self); +} + +void +on_iframe_button_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + GstEvent *event; + gboolean pps_sps; + + set_drop_probability (self); + pps_sps = gtk_toggle_button_get_active (GET_WIDGET (self, (GtkToggleButton *), + "pps_sps")); + + event = new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, pps_sps, 0); + gst_element_send_event (GST_ELEMENT (self->src), event); +} + +void +on_renegotiate_button_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + + set_caps (self, TRUE); + probe_all_properties (GST_STATE (self->bin) >= GST_STATE_PAUSED); + get_all_properties (); +} + +void +on_start_capture_button_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + + set_caps (self, FALSE); + g_signal_emit_by_name (G_OBJECT (self->src), "start-capture", NULL); + probe_all_properties (GST_STATE (self->bin) >= GST_STATE_PAUSED); + get_all_properties (); +} + +void +on_stop_capture_button_clicked (GtkButton * button, gpointer user_data) +{ + Main *self = user_data; + + set_caps (self, FALSE); + g_signal_emit_by_name (G_OBJECT (self->src), "stop-capture", NULL); + probe_all_properties (GST_STATE (self->bin) >= GST_STATE_PAUSED); + get_all_properties (); +} + +void +on_window_destroyed (GtkWindow * window, gpointer user_data) +{ + gtk_main_quit (); +} + +static gboolean +_bus_callback (GstBus * bus, GstMessage * message, gpointer user_data) +{ + const GstStructure *s = gst_message_get_structure (message); + GstObject *source = NULL; + + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT && + gst_structure_has_name (s, "prepare-xwindow-id")) { + source = GST_MESSAGE_SRC (message); + if (!g_strcmp0 (gst_object_get_name (source), "h264_sink")) + gst_x_overlay_set_window_handle (GST_X_OVERLAY (source), h264_xid); + else + gst_x_overlay_set_window_handle (GST_X_OVERLAY (source), preview_xid); + } + + return TRUE; +} + +static void +set_drop_probability (Main * self) +{ + const gchar *drop; + gdouble drop_probability = 0.0; + + drop = gtk_entry_get_text (GET_WIDGET (self, GTK_ENTRY, "drop")); + drop_probability = g_ascii_strtod (drop, NULL); + g_debug ("Setting drop probability to : %f", drop_probability); + g_object_set (self->identity, "drop-probability", drop_probability, NULL); +} + +static void +get_all_properties (void) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (properties); i++) + on_get_button_clicked (NULL, &properties[i]); + +} + +static void +probe_all_properties (gboolean playing) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + gboolean return_value, changeable, default_bool; + guint mask, minimum, maximum, default_int; + GParamSpec *param; + + /* When playing, ignore static controls */ + if (playing && !properties[i].dynamic) + continue; + + switch (properties[i].type) { + case INT: + g_signal_emit_by_name (G_OBJECT (properties[i].src), "get-int-setting", + properties[i].property_name, &minimum, &default_int, &maximum, + &return_value, NULL); + if (return_value) { + gchar *min, *def, *max; + + min = g_strdup_printf ("%d", minimum); + def = g_strdup_printf ("%d", default_int); + max = g_strdup_printf ("%d", maximum); + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "minimum"), min); + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), def); + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "maximum"), max); + g_free (min); + g_free (def); + g_free (max); + } else { + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "minimum"), ""); + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), ""); + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "maximum"), ""); + } + break; + case ENUM: + g_signal_emit_by_name (G_OBJECT (properties[i].src), "get-enum-setting", + properties[i].property_name, &mask, &default_int, &return_value, + NULL); + param = + g_object_class_find_property (G_OBJECT_GET_CLASS (properties + [i].src), properties[i].property_name); + if (G_IS_PARAM_SPEC_ENUM (param)) { + GEnumValue *values; + guint j = 0; + + values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values; + + if (return_value) { + while (values[j].value_name) { + if (values[j].value == default_int) { + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), + values[j].value_name); + break; + } + j++; + } + } else { + gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), ""); + } + + j = 0; + while (values[j].value_name) { +#if !GTK_CHECK_VERSION (2, 24, 0) + gtk_combo_box_remove_text (GET_PROP_WIDGET ((GtkComboBox *), + "value"), 0); +#else + gtk_combo_box_text_remove (GET_PROP_WIDGET ((GtkComboBoxText *), + "value"), 0); +#endif + j++; + } + + j = 0; + while (values[j].value_name) { + gchar *val; + if (return_value && (mask & (1 << values[j].value)) != 0) + val = g_strdup_printf ("**%s**", values[j].value_name); + else + val = g_strdup (values[j].value_name); + +#if !GTK_CHECK_VERSION (2, 24, 0) + gtk_combo_box_append_text (GET_PROP_WIDGET ((GtkComboBox *), + "value"), val); +#else + gtk_combo_box_text_append_text (GET_PROP_WIDGET ((GtkComboBoxText + *), "value"), val); +#endif + g_free (val); + j++; + } + } + break; + case BOOL: + g_signal_emit_by_name (G_OBJECT (properties[i].src), + "get-boolean-setting", properties[i].property_name, + &changeable, &default_bool, &return_value, NULL); + if (return_value) { + gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "value"), + changeable); + gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "get"), + changeable); + gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), + changeable); + gtk_toggle_button_set_active (GET_PROP_WIDGET ((GtkToggleButton *), + "default"), default_bool); + } + break; + case NONE: + default: + break; + } + } +} + +int +main (int argc, char *argv[]) +{ + Main self = { NULL, NULL, NULL, NULL }; + GstBus *bus = NULL; + GtkWidget *window, *static_vbox, *dynamic_vbox, *da; + gchar *drop; + gdouble drop_probability; + GdkWindow *gdk_win = NULL; + const char *device = "/dev/video0"; + GError *error = NULL; + int i; + + gtk_init (&argc, &argv); + gst_init (&argc, &argv); + + if (argc > 1) + device = argv[1]; + else + g_print ("Usage : %s [device]\nUsing default device : %s\n", + argv[0], device); + + + self.bin = gst_parse_launch ("uvch264_src name=src src.vidsrc ! queue ! " + "capsfilter name=vid_cf ! identity name=identity ! ffdec_h264 ! " + "xvimagesink name=h264_sink async=false " + "src.vfsrc ! queue ! capsfilter name=vf_cf ! " + "xvimagesink name=preview_sink async=false", NULL); + + if (!self.bin) + return -1; + + /* Listen to the bus for messages */ + bus = gst_element_get_bus (self.bin); + gst_bus_add_watch (bus, _bus_callback, self.bin); + gst_object_unref (bus); + + self.src = gst_bin_get_by_name (GST_BIN (self.bin), "src"); + self.identity = gst_bin_get_by_name (GST_BIN (self.bin), "identity"); + self.vid_capsfilter = gst_bin_get_by_name (GST_BIN (self.bin), "vid_cf"); + self.vf_capsfilter = gst_bin_get_by_name (GST_BIN (self.bin), "vf_cf"); + + self.builder = gtk_builder_new (); + gtk_builder_add_from_file (self.builder, WINDOW_GLADE, &error); + if (error) { + g_debug ("Unable to load glade file : %s", error->message); + goto end; + } + gtk_builder_connect_signals (self.builder, &self); + + g_object_get (self.identity, "drop-probability", &drop_probability, NULL); + drop = g_strdup_printf ("%f", drop_probability); + gtk_entry_set_text (GET_WIDGET (&self, GTK_ENTRY, "drop"), drop); + g_free (drop); + window = GET_WIDGET (&self, GTK_WIDGET, "window"); + static_vbox = GET_WIDGET (&self, GTK_WIDGET, "static"); + dynamic_vbox = GET_WIDGET (&self, GTK_WIDGET, "dynamic"); + da = GET_WIDGET (&self, GTK_WIDGET, "h264"); + gtk_widget_realize (da); + gdk_win = gtk_widget_get_window (da); + h264_xid = GDK_WINDOW_XID (gdk_win); + da = GET_WIDGET (&self, GTK_WIDGET, "preview"); + gtk_widget_realize (da); + gdk_win = gtk_widget_get_window (da); + preview_xid = GDK_WINDOW_XID (gdk_win); + + set_caps (&self, FALSE); + + g_object_set (self.src, "device", device, NULL); + if (gst_element_set_state (self.bin, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + g_debug ("Unable to go to state READY"); + goto end; + } + + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + switch (properties[i].type) { + case INT: + properties[i].src = self.src; + properties[i].builder = gtk_builder_new (); + gtk_builder_add_from_file (properties[i].builder, INT_PROPERTY_GLADE, + NULL); + gtk_builder_connect_signals (properties[i].builder, &properties[i]); + gtk_box_pack_start (PROPERTY_TO_VBOX, + GET_PROP_WIDGET (GTK_WIDGET, "int-property"), TRUE, TRUE, 2); + gtk_label_set_label (GET_PROP_WIDGET (GTK_LABEL, "label"), + properties[i].property_name); + if (properties[i].readonly) + gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), FALSE); + break; + case ENUM: + properties[i].src = self.src; + properties[i].builder = gtk_builder_new (); +#if !GTK_CHECK_VERSION (2, 24, 0) + gtk_builder_add_from_file (properties[i].builder, + "enum_property_gtk2.glade", NULL); +#else + gtk_builder_add_from_file (properties[i].builder, ENUM_PROPERTY_GLADE, + NULL); +#endif + gtk_builder_connect_signals (properties[i].builder, &properties[i]); + gtk_box_pack_start (PROPERTY_TO_VBOX, + GET_PROP_WIDGET (GTK_WIDGET, "enum-property"), TRUE, TRUE, 2); + gtk_label_set_label (GET_PROP_WIDGET (GTK_LABEL, "label"), + properties[i].property_name); +#if !GTK_CHECK_VERSION (2, 24, 0) + { + GtkComboBox *combo_box; + GtkCellRenderer *cell; + GtkListStore *store; + + combo_box = GET_PROP_WIDGET ((GtkComboBox *), "value"); + store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store)); + g_object_unref (store); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell, + "text", 0, NULL); + } +#endif + if (properties[i].readonly) + gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), FALSE); + break; + case BOOL: + properties[i].src = self.src; + properties[i].builder = gtk_builder_new (); + gtk_builder_add_from_file (properties[i].builder, BOOL_PROPERTY_GLADE, + NULL); + gtk_builder_connect_signals (properties[i].builder, &properties[i]); + gtk_box_pack_start (PROPERTY_TO_VBOX, + GET_PROP_WIDGET (GTK_WIDGET, "boolean-property"), TRUE, TRUE, 2); + gtk_label_set_label (GET_PROP_WIDGET (GTK_LABEL, "label"), + properties[i].property_name); + if (properties[i].readonly) + gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), FALSE); + break; + case NONE: + default: + break; + } + } + probe_all_properties (FALSE); + get_all_properties (); + + gtk_widget_show (window); + gtk_main (); + +end: + g_object_unref (G_OBJECT (self.builder)); + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + if (properties[i].builder) + g_object_unref (G_OBJECT (properties[i].builder)); + } + gst_element_set_state (self.bin, GST_STATE_NULL); + gst_object_unref (self.src); + gst_object_unref (self.identity); + gst_object_unref (self.vid_capsfilter); + gst_object_unref (self.vf_capsfilter); + gst_object_unref (self.bin); + + return 0; +} diff --git a/tests/examples/uvch264/window.glade b/tests/examples/uvch264/window.glade new file mode 100644 index 0000000000..8b7624a9ff --- /dev/null +++ b/tests/examples/uvch264/window.glade @@ -0,0 +1,345 @@ + + + + + False + Test for uvch264_src + + + + True + False + + + True + False + vertical + + + True + False + True + + + State NULL + True + True + True + False + + + + False + True + 0 + + + + + State READY + True + True + True + False + + + + False + True + 1 + + + + + State PLAYING + True + True + True + False + + + + False + True + 2 + + + + + Start capture + True + True + True + False + + + + False + True + 3 + + + + + Stop capture + True + True + True + False + + + + False + True + 4 + + + + + Renegotiate + True + True + True + False + + + + False + True + 5 + + + + + False + True + 0 + + + + + True + False + Static controls + + + False + True + 1 + + + + + True + False + vertical + + + + + + True + True + 2 + + + + + True + False + Dynamic controls + + + False + True + 3 + + + + + True + False + vertical + + + + + + True + True + 4 + + + + + True + False + + + True + False + Drop probability % (between 0.0 and 1.0) + + + False + True + 0 + + + + + True + True + ● + + + False + True + 1 + + + + + With SPS/PPS + True + True + False + False + 0 + True + + + False + True + 2 + + + + + Request keyframe + True + True + True + False + + + + False + True + 3 + + + + + False + True + 5 + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + H264 + + + False + True + 0 + + + + + True + True + ● + video/x-h264,width=640,height=480,profile=constrained-baseline,stream-format=bytestream,framerate=15/1 + + + False + True + 1 + + + + + 320 + 240 + True + True + False + False + center + center + + + False + False + 2 + + + + + True + False + Preview + + + False + True + 3 + + + + + True + True + ● + video/x-raw-yuv,width=320,height=240,format=(fourcc)YUY2,framerate=15/1 + + + False + True + 4 + + + + + 320 + 240 + True + True + False + False + center + center + + + False + False + 5 + + + + + True + True + 1 + + + + + + -- 2.34.1