From e4f74290df504c1df57090f0244bd99b53c11749 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 10 Oct 2013 23:47:38 +1100 Subject: [PATCH 01/77] rpicamsrc: Initial commit Simple modified gst-template to use BaseCameraSrc Incorporate Broadcom mmal headers --- sys/rpicamsrc/gstrpicamsrc.c | 227 +++++++++++++++++++++++++++++++++++ sys/rpicamsrc/gstrpicamsrc.h | 83 +++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 sys/rpicamsrc/gstrpicamsrc.c create mode 100644 sys/rpicamsrc/gstrpicamsrc.h diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c new file mode 100644 index 0000000000..391d678366 --- /dev/null +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -0,0 +1,227 @@ +/* + * GStreamer + * Copyright (C) 2013 Jan Schmidt + * + * 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. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * 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-rpicamsrc + * + * Source element for capturing from the Raspberry Pi camera module + * + * + * Example launch line + * |[ + * gst-launch -v -m rpicamsrc ! fakesink + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "gstrpicamsrc.h" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +GST_DEBUG_CATEGORY_STATIC (gst_rpi_cam_src_debug); +#define GST_CAT_DEFAULT gst_rpi_cam_src_debug + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, +}; + +#define RAW_AND_JPEG_CAPS \ + GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ";" \ + "image/jpeg," \ + "width = " GST_VIDEO_SIZE_RANGE "," \ + "height = " GST_VIDEO_SIZE_RANGE "," \ + "framerate = " GST_VIDEO_FPS_RANGE + +#define H264_CAPS \ + "video/x-h264, " \ + "width = " GST_VIDEO_SIZE_RANGE ", " \ + "height = " GST_VIDEO_SIZE_RANGE ", " \ + "framerate = " GST_VIDEO_FPS_RANGE ", " \ + "stream-format = (string) { byte-stream }, " \ + "alignment = (string) { au }, " \ + "profile = (string) { baseline, main, high }" + +static GstStaticPadTemplate video_src_template = + GST_STATIC_PAD_TEMPLATE ("vidsrc", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS) + ); +static GstStaticPadTemplate viewfind_src_template = + GST_STATIC_PAD_TEMPLATE ("vfsrc", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS) + ); +static GstStaticPadTemplate image_src_template = + GST_STATIC_PAD_TEMPLATE ("imgsrc", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (RAW_AND_JPEG_CAPS) + ); + +#define gst_rpi_cam_src_parent_class parent_class +G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_BASE_CAMERA_SRC); + +static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean +gst_rpi_cam_src_setup_pipeline (GstBaseCameraSrc *parent) +{ + GstRpiCamSrc *self = GST_RPICAMSRC(parent); + g_print ("In setup_pipeline\n"); +} + +static void +gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseCameraSrcClass *basecamsrc_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + basecamsrc_class = (GstBaseCameraSrcClass *) klass; + + gobject_class->set_property = gst_rpi_cam_src_set_property; + gobject_class->get_property = gst_rpi_cam_src_get_property; + + gst_element_class_set_static_metadata (gstelement_class, + "Raspberry Pi Camera Source", + "Source/Video", + "Raspberry Pi camera module source", + "Jan Schmidt "); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&viewfind_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&video_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&image_src_template)); + + basecamsrc_class->setup_pipeline = gst_rpi_cam_src_setup_pipeline; +} + +static void +gst_rpi_cam_src_init (GstRpiCamSrc *src) +{ +} + +static void +gst_rpi_cam_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rpi_cam_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +rpicamsrc_init (GstPlugin * rpicamsrc) +{ + GST_DEBUG_CATEGORY_INIT (gst_rpi_cam_src_debug, "rpicamsrc", + 0, "rpicamsrc debug"); + + return gst_element_register (rpicamsrc, "rpicamsrc", GST_RANK_NONE, + GST_TYPE_RPICAMSRC); +} + +#ifndef PACKAGE +#define PACKAGE "gstrpicamsrc" +#endif + +GST_PLUGIN_DEFINE ( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + rpicamsrc, + "Raspberry Pi Camera Source", + rpicamsrc_init, + VERSION, + "LGPL", + "GStreamer", + "http://gstreamer.net/" +) diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h new file mode 100644 index 0000000000..6917bb1293 --- /dev/null +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -0,0 +1,83 @@ +/* + * GStreamer + * Copyright (C) 2013 Jan Schmidt + * + * 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. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * 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_RPICAMSRC_H__ +#define __GST_RPICAMSRC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RPICAMSRC (gst_rpi_cam_src_get_type()) +#define GST_RPICAMSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RPICAMSRC,GstRpiCamSrc)) +#define GST_RPICAMSRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RPICAMSRC,GstRpiCamSrcClass)) +#define GST_IS_RPICAMSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RPICAMSRC)) +#define GST_IS_RPICAMSRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RPICAMSRC)) + +typedef struct _GstRpiCamSrc GstRpiCamSrc; +typedef struct _GstRpiCamSrcClass GstRpiCamSrcClass; + +struct _GstRpiCamSrc +{ + GstBaseCameraSrc parent; + + GstPad *viewfind_srcpad; + GstPad *video_srcpad; + GstPad *image_srcpad; +}; + +struct _GstRpiCamSrcClass +{ + GstBaseCameraSrcClass parent_class; +}; + +GType gst_rpi_cam_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_RPICAMSRC_H__ */ From f4af399350b69578087af7018f3e1ed94c25ae2d Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 11 Oct 2013 19:17:05 +1100 Subject: [PATCH 02/77] rpicamsrc: checkpoint --- sys/rpicamsrc/RaspiCamControl.c | 1120 +++++++++++++++++++++++ sys/rpicamsrc/RaspiCamControl.h | 184 ++++ sys/rpicamsrc/RaspiCapture.c | 1211 ++++++++++++++++++++++++ sys/rpicamsrc/RaspiCapture.h | 82 ++ sys/rpicamsrc/RaspiPreview.c | 286 ++++++ sys/rpicamsrc/RaspiPreview.h | 57 ++ sys/rpicamsrc/RaspiStill.c | 1516 +++++++++++++++++++++++++++++++ sys/rpicamsrc/RaspiStillYUV.c | 957 +++++++++++++++++++ sys/rpicamsrc/gstrpicamsrc.c | 57 +- sys/rpicamsrc/gstrpicamsrc.h | 8 +- 10 files changed, 5444 insertions(+), 34 deletions(-) create mode 100644 sys/rpicamsrc/RaspiCamControl.c create mode 100644 sys/rpicamsrc/RaspiCamControl.h create mode 100644 sys/rpicamsrc/RaspiCapture.c create mode 100644 sys/rpicamsrc/RaspiCapture.h create mode 100644 sys/rpicamsrc/RaspiPreview.c create mode 100644 sys/rpicamsrc/RaspiPreview.h create mode 100644 sys/rpicamsrc/RaspiStill.c create mode 100644 sys/rpicamsrc/RaspiStillYUV.c diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c new file mode 100644 index 0000000000..f379015d4b --- /dev/null +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -0,0 +1,1120 @@ +/* + * Copyright (c) 2013 Jan Schmidt +Portions: +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vc_vchi_gencmd.h" +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "RaspiCamControl.h" + +#if 0 +/// Structure to cross reference exposure strings against the MMAL parameter equivalent +static XREF_T exposure_map[] = +{ + {"auto", MMAL_PARAM_EXPOSUREMODE_AUTO}, + {"night", MMAL_PARAM_EXPOSUREMODE_NIGHT}, + {"nightpreview", MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW}, + {"backlight", MMAL_PARAM_EXPOSUREMODE_BACKLIGHT}, + {"spotlight", MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT}, + {"sports", MMAL_PARAM_EXPOSUREMODE_SPORTS}, + {"snow", MMAL_PARAM_EXPOSUREMODE_SNOW}, + {"beach", MMAL_PARAM_EXPOSUREMODE_BEACH}, + {"verylong", MMAL_PARAM_EXPOSUREMODE_VERYLONG}, + {"fixedfps", MMAL_PARAM_EXPOSUREMODE_FIXEDFPS}, + {"antishake", MMAL_PARAM_EXPOSUREMODE_ANTISHAKE}, + {"fireworks", MMAL_PARAM_EXPOSUREMODE_FIREWORKS} +}; + +static const int exposure_map_size = sizeof(exposure_map) / sizeof(exposure_map[0]); + +/// Structure to cross reference awb strings against the MMAL parameter equivalent +static XREF_T awb_map[] = +{ + {"off", MMAL_PARAM_AWBMODE_OFF}, + {"auto", MMAL_PARAM_AWBMODE_AUTO}, + {"sun", MMAL_PARAM_AWBMODE_SUNLIGHT}, + {"cloud", MMAL_PARAM_AWBMODE_CLOUDY}, + {"shade", MMAL_PARAM_AWBMODE_SHADE}, + {"tungsten", MMAL_PARAM_AWBMODE_TUNGSTEN}, + {"fluorescent", MMAL_PARAM_AWBMODE_FLUORESCENT}, + {"incandescent", MMAL_PARAM_AWBMODE_INCANDESCENT}, + {"flash", MMAL_PARAM_AWBMODE_FLASH}, + {"horizon", MMAL_PARAM_AWBMODE_HORIZON} +}; + +static const int awb_map_size = sizeof(awb_map) / sizeof(awb_map[0]); + +/// Structure to cross reference image effect against the MMAL parameter equivalent +static XREF_T imagefx_map[] = +{ + {"none", MMAL_PARAM_IMAGEFX_NONE}, + {"negative", MMAL_PARAM_IMAGEFX_NEGATIVE}, + {"solarise", MMAL_PARAM_IMAGEFX_SOLARIZE}, + {"sketch", MMAL_PARAM_IMAGEFX_SKETCH}, + {"denoise", MMAL_PARAM_IMAGEFX_DENOISE}, + {"emboss", MMAL_PARAM_IMAGEFX_EMBOSS}, + {"oilpaint", MMAL_PARAM_IMAGEFX_OILPAINT}, + {"hatch", MMAL_PARAM_IMAGEFX_HATCH}, + {"gpen", MMAL_PARAM_IMAGEFX_GPEN}, + {"pastel", MMAL_PARAM_IMAGEFX_PASTEL}, + {"watercolour", MMAL_PARAM_IMAGEFX_WATERCOLOUR}, + {"film", MMAL_PARAM_IMAGEFX_FILM}, + {"blur", MMAL_PARAM_IMAGEFX_BLUR}, + {"saturation", MMAL_PARAM_IMAGEFX_SATURATION}, + {"colourswap", MMAL_PARAM_IMAGEFX_COLOURSWAP}, + {"washedout", MMAL_PARAM_IMAGEFX_WASHEDOUT}, + {"posterise", MMAL_PARAM_IMAGEFX_POSTERISE}, + {"colourpoint", MMAL_PARAM_IMAGEFX_COLOURPOINT}, + {"colourbalance", MMAL_PARAM_IMAGEFX_COLOURBALANCE}, + {"cartoon", MMAL_PARAM_IMAGEFX_CARTOON} + }; + +static const int imagefx_map_size = sizeof(imagefx_map) / sizeof(imagefx_map[0]); + +static XREF_T metering_mode_map[] = +{ + {"average", MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE}, + {"spot", MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT}, + {"backlit", MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT}, + {"matrix", MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX} +}; + +static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]); + + +#define CommandSharpness 0 +#define CommandContrast 1 +#define CommandBrightness 2 +#define CommandSaturation 3 +#define CommandISO 4 +#define CommandVideoStab 5 +#define CommandEVComp 6 +#define CommandExposure 7 +#define CommandAWB 8 +#define CommandImageFX 9 +#define CommandColourFX 10 +#define CommandMeterMode 11 +#define CommandRotation 12 +#define CommandHFlip 13 +#define CommandVFlip 14 +#define CommandROI 15 + +static COMMAND_LIST cmdline_commands[] = +{ + {CommandSharpness, "-sharpness", "sh", "Set image sharpness (-100 to 100)", 1}, + {CommandContrast, "-contrast", "co", "Set image contrast (-100 to 100)", 1}, + {CommandBrightness, "-brightness","br", "Set image brightness (0 to 100)", 1}, + {CommandSaturation, "-saturation","sa", "Set image saturation (-100 to 100)", 1}, + {CommandISO, "-ISO", "ISO","Set capture ISO", 1}, + {CommandVideoStab, "-vstab", "vs", "Turn on video stablisation", 0}, + {CommandEVComp, "-ev", "ev", "Set EV compensation", 1}, + {CommandExposure, "-exposure", "ex", "Set exposure mode (see Notes)", 1}, + {CommandAWB, "-awb", "awb","Set AWB mode (see Notes)", 1}, + {CommandImageFX, "-imxfx", "ifx","Set image effect (see Notes)", 1}, + {CommandColourFX, "-colfx", "cfx","Set colour effect (U:V)", 1}, + {CommandMeterMode, "-metering", "mm", "Set metering mode (see Notes)", 1}, + {CommandRotation, "-rotation", "rot","Set image rotation (0-359)", 1}, + {CommandHFlip, "-hflip", "hf", "Set horizontal flip", 0}, + {CommandVFlip, "-vflip", "vf", "Set vertical flip", 0}, + {CommandROI, "-roi", "roi","Set region of interest (x,y,w,d as normalised coordinates [0.0-1.0])", 1} +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +static const int exposure_map_size = 1; +static const int awb_map_size = 1; +static const int metering_mode_map_size = 1; + +#define parameter_reset -99999 + +/** + * Update the passed in parameter according to the rest of the parameters + * passed in. + * + * + * @return 0 if reached end of cycle for this parameter, !0 otherwise + */ +static int update_cycle_parameter(int *option, int min, int max, int increment) +{ + vcos_assert(option); + if (!option) + return 0; + + if (*option == parameter_reset) + *option = min - increment; + + *option += increment; + + if (*option > max) + { + *option = parameter_reset; + return 0; + } + else + return 1; +} + + +/** + * Test/Demo code to cycle through a bunch of camera settings + * This code is pretty hacky so please don't complain!! + * It only does stuff that should have a visual impact (hence demo!) + * This will override any user supplied parameters + * + * Each call of this function will move on to the next setting + * + * @param camera Pointer to the camera to change settings on. + * @return 0 if reached end of complete sequence, !0 otherwise + */ + +int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera) +{ + static int parameter = 0; + static int parameter_option = parameter_reset; // which value the parameter currently has + + vcos_assert(camera); + + // We are going to cycle through all the relevant entries in the parameter block + // and send options to the camera. + if (parameter == 0) + { + // sharpness + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_sharpness(camera, parameter_option); + else + { + raspicamcontrol_set_sharpness(camera, 0); + parameter++; + } + } + else + if (parameter == 1) + { + // contrast + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_contrast(camera, parameter_option); + else + { + raspicamcontrol_set_contrast(camera, 0); + parameter++; + } + } + else + if (parameter == 2) + { + // brightness + if (update_cycle_parameter(¶meter_option, 0, 100, 10)) + raspicamcontrol_set_brightness(camera, parameter_option); + else + { + raspicamcontrol_set_brightness(camera, 50); + parameter++; + } + } + else + if (parameter == 3) + { + // contrast + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_saturation(camera, parameter_option); + else + { + parameter++; + raspicamcontrol_set_saturation(camera, 0); + } + } + else + if (parameter == 4) + { + // EV + if (update_cycle_parameter(¶meter_option, -10, 10, 4)) + raspicamcontrol_set_exposure_compensation(camera, parameter_option); + else + { + raspicamcontrol_set_exposure_compensation(camera, 0); + parameter++; + } + } + else + if (parameter == 5) + { + // MMAL_PARAM_EXPOSUREMODE_T + if (update_cycle_parameter(¶meter_option, 0, exposure_map_size, 1)) + raspicamcontrol_set_exposure_mode(camera, exposure_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_exposure_mode(camera, MMAL_PARAM_EXPOSUREMODE_AUTO); + parameter++; + } + } + else + if (parameter == 6) + { + // MMAL_PARAM_AWB_T + if (update_cycle_parameter(¶meter_option, 0, awb_map_size, 1)) + raspicamcontrol_set_awb_mode(camera, awb_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_awb_mode(camera, MMAL_PARAM_AWBMODE_AUTO); + parameter++; + } + } + if (parameter == 7) + { + // MMAL_PARAM_IMAGEFX_T + if (update_cycle_parameter(¶meter_option, 0, imagefx_map_size, 1)) + raspicamcontrol_set_imageFX(camera, imagefx_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_imageFX(camera, MMAL_PARAM_IMAGEFX_NONE); + parameter++; + } + } + if (parameter == 8) + { + MMAL_PARAM_COLOURFX_T colfx = {0,0,0}; + switch (parameter_option) + { + case parameter_reset : + parameter_option = 1; + colfx.u = 128; + colfx.v = 128; + break; + case 1 : + parameter_option = 2; + colfx.u = 100; + colfx.v = 200; + break; + case 2 : + parameter_option = parameter_reset; + colfx.enable = 0; + parameter++; + break; + } + raspicamcontrol_set_colourFX(camera, &colfx); + } + + // Orientation + if (parameter == 9) + { + switch (parameter_option) + { + case parameter_reset: + raspicamcontrol_set_rotation(camera, 90); + parameter_option = 1; + break; + + case 1 : + raspicamcontrol_set_rotation(camera, 180); + parameter_option = 2; + break; + + case 2 : + raspicamcontrol_set_rotation(camera, 270); + parameter_option = 3; + break; + + case 3 : + { + raspicamcontrol_set_rotation(camera, 0); + raspicamcontrol_set_flips(camera, 1,0); + parameter_option = 4; + break; + } + case 4 : + { + raspicamcontrol_set_flips(camera, 0,1); + parameter_option = 5; + break; + } + case 5 : + { + raspicamcontrol_set_flips(camera, 1, 1); + parameter_option = 6; + break; + } + case 6 : + { + raspicamcontrol_set_flips(camera, 0, 0); + parameter_option = parameter_reset; + parameter++; + break; + } + } + } + + if (parameter == 10) + { + parameter = 1; + return 0; + } + + return 1; +} +#endif + + +#if 0 +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandSharpness : // sharpness - needs single number parameter + sscanf(arg2, "%d", ¶ms->sharpness); + used = 2; + break; + + case CommandContrast : // contrast - needs single number parameter + sscanf(arg2, "%d", ¶ms->contrast); + used = 2; + break; + + case CommandBrightness : // brightness - needs single number parameter + sscanf(arg2, "%d", ¶ms->brightness); + used = 2; + break; + + case CommandSaturation : // saturation - needs single number parameter + sscanf(arg2, "%d", ¶ms->saturation); + used = 2; + break; + + case CommandISO : // ISO - needs single number parameter + sscanf(arg2, "%d", ¶ms->ISO); + used = 2; + break; + + case CommandVideoStab : // video stabilisation - if here, its on + params->videoStabilisation = 1; + used = 1; + break; + + case CommandEVComp : // EV - needs single number parameter + sscanf(arg2, "%d", ¶ms->exposureCompensation); + used = 2; + break; + + case CommandExposure : // exposure mode - needs string + params->exposureMode = exposure_mode_from_string(arg2); + used = 2; + break; + + case CommandAWB : // AWB mode - needs single number parameter + params->awbMode = awb_mode_from_string(arg2); + used = 2; + break; + + case CommandImageFX : // Image FX - needs string + params->imageEffect = imagefx_mode_from_string(arg2); + used = 2; + break; + + case CommandColourFX : // Colour FX - needs string "u:v" + sscanf(arg2, "%d:%d", ¶ms->colourEffects.u, ¶ms->colourEffects.u); + params->colourEffects.enable = 1; + used = 2; + break; + + case CommandMeterMode: + params->exposureMeterMode = metering_mode_from_string(arg2); + used = 2; + break; + + case CommandRotation : // Rotation - degree + sscanf(arg2, "%d", ¶ms->rotation); + used = 2; + break; + + case CommandHFlip : + params->hflip = 1; + used = 1; + break; + + case CommandVFlip : + params->vflip = 1; + used = 1; + break; + + case CommandROI : + { + double x,y,w,h; + int args; + + args = sscanf(arg2, "%lf,%lf,%lf,%lf", &x,&y,&w,&h); + + if (args != 4 || x > 1.0 || y > 1.0 || w > 1.0 || h > 1.0) + { + return 0; + } + + // Make sure we stay within bounds + if (x + w > 1.0) + w = 1 - x; + + if (y + h > 1.0) + h = 1 - y; + + params->roi.x = x; + params->roi.y = y; + params->roi.w = w; + params->roi.h = h; + + used = 2; + break; + } + + } + + return used; +} + +/** + * Display help for command line options + */ +void raspicamcontrol_display_help() +{ + int i; + + fprintf(stderr, "\nImage parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + fprintf(stderr, "\n\nNotes\n\nExposure mode options :\n%s", exposure_map[0].mode ); + + for (i=1;iexposureMode, exposure_map, exposure_map_size); + const char *awb_mode = raspicli_unmap_xref(params->awbMode, awb_map, awb_map_size); + const char *image_effect = raspicli_unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); + const char *metering_mode = raspicli_unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); + + fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); + fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); + fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); + fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); + fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); + fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h); +} +#endif + +/** + * Convert a MMAL status return value to a simple boolean of success + * ALso displays a fault if code is not success + * + * @param status The error code to convert + * @return 0 if status is sucess, 1 otherwise + */ +int mmal_status_to_int(MMAL_STATUS_T status) +{ + if (status == MMAL_SUCCESS) + return 0; + else + { + switch (status) + { + case MMAL_ENOMEM : vcos_log_error("Out of memory"); break; + case MMAL_ENOSPC : vcos_log_error("Out of resources (other than memory)"); break; + case MMAL_EINVAL: vcos_log_error("Argument is invalid"); break; + case MMAL_ENOSYS : vcos_log_error("Function not implemented"); break; + case MMAL_ENOENT : vcos_log_error("No such file or directory"); break; + case MMAL_ENXIO : vcos_log_error("No such device or address"); break; + case MMAL_EIO : vcos_log_error("I/O error"); break; + case MMAL_ESPIPE : vcos_log_error("Illegal seek"); break; + case MMAL_ECORRUPT : vcos_log_error("Data is corrupt \attention FIXME: not POSIX"); break; + case MMAL_ENOTREADY :vcos_log_error("Component is not ready \attention FIXME: not POSIX"); break; + case MMAL_ECONFIG : vcos_log_error("Component is not configured \attention FIXME: not POSIX"); break; + case MMAL_EISCONN : vcos_log_error("Port is already connected "); break; + case MMAL_ENOTCONN : vcos_log_error("Port is disconnected"); break; + case MMAL_EAGAIN : vcos_log_error("Resource temporarily unavailable. Try again later"); break; + case MMAL_EFAULT : vcos_log_error("Bad address"); break; + default : vcos_log_error("Unknown status error"); break; + } + + return 1; + } +} + +/** + * Give the supplied parameter block a set of default values + * @params Pointer to parameter block + */ +void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params) +{ + vcos_assert(params); + + params->sharpness = 0; + params->contrast = 0; + params->brightness = 50; + params->saturation = 0; + params->ISO = 0; // 0 = auto + params->videoStabilisation = 0; + params->exposureCompensation = 0; + params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; + params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; + params->awbMode = MMAL_PARAM_AWBMODE_AUTO; + params->imageEffect = MMAL_PARAM_IMAGEFX_NONE; + params->colourEffects.enable = 0; + params->colourEffects.u = 128; + params->colourEffects.v = 128; + params->rotation = 0; + params->hflip = params->vflip = 0; + params->roi.x = params->roi.y = 0.0; + params->roi.w = params->roi.h = 1.0; +} + +/** + * Get all the current camera parameters from specified camera component + * @param camera Pointer to camera component + * @param params Pointer to parameter block to accept settings + * @return 0 if successful, non-zero if unsuccessful + */ +int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params) +{ + vcos_assert(camera); + vcos_assert(params); + + if (!camera || !params) + return 1; + +/* TODO : Write these get functions + params->sharpness = raspicamcontrol_get_sharpness(camera); + params->contrast = raspicamcontrol_get_contrast(camera); + params->brightness = raspicamcontrol_get_brightness(camera); + params->saturation = raspicamcontrol_get_saturation(camera); + params->ISO = raspicamcontrol_get_ISO(camera); + params->videoStabilisation = raspicamcontrol_get_video_stabilisation(camera); + params->exposureCompensation = raspicamcontrol_get_exposure_compensation(camera); + params->exposureMode = raspicamcontrol_get_exposure_mode(camera); + params->awbMode = raspicamcontrol_get_awb_mode(camera); + params->imageEffect = raspicamcontrol_get_image_effect(camera); + params->colourEffects = raspicamcontrol_get_colour_effect(camera); + params->thumbnailConfig = raspicamcontrol_get_thumbnail_config(camera); +*/ + return 0; +} + +/** + * Set the specified camera to all the specified settings + * @param camera Pointer to camera component + * @param params Pointer to parameter block containing parameters + * @return 0 if successful, none-zero if unsuccessful. + */ +int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params) +{ + int result; + + result = raspicamcontrol_set_saturation(camera, params->saturation); + result += raspicamcontrol_set_sharpness(camera, params->sharpness); + result += raspicamcontrol_set_contrast(camera, params->contrast); + result += raspicamcontrol_set_brightness(camera, params->brightness); + result += raspicamcontrol_set_ISO(camera, params->ISO); + result += raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation); + result += raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation); + result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); + result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); + result += raspicamcontrol_set_awb_mode(camera, params->awbMode); + result += raspicamcontrol_set_imageFX(camera, params->imageEffect); + result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); + //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason + result += raspicamcontrol_set_rotation(camera, params->rotation); + result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); + result += raspicamcontrol_set_ROI(camera, params->roi); + + return result; +} + +/** + * Adjust the saturation level for images + * @param camera Pointer to camera component + * @param saturation Value to adjust, -100 to 100 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation) +{ + int ret = 0; + + if (!camera) + return 1; + + if (saturation >= -100 && saturation <= 100) + { + MMAL_RATIONAL_T value = {saturation, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SATURATION, value)); + } + else + { + vcos_log_error("Invalid saturation value"); + ret = 1; + } + + return ret; +} + +/** + * Set the sharpness of the image + * @param camera Pointer to camera component + * @param sharpness Sharpness adjustment -100 to 100 + */ +int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness) +{ + int ret = 0; + + if (!camera) + return 1; + + if (sharpness >= -100 && sharpness <= 100) + { + MMAL_RATIONAL_T value = {sharpness, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SHARPNESS, value)); + } + else + { + vcos_log_error("Invalid sharpness value"); + ret = 1; + } + + return ret; +} + +/** + * Set the contrast adjustment for the image + * @param camera Pointer to camera component + * @param contrast Contrast adjustment -100 to 100 + * @return + */ +int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast) +{ + int ret = 0; + + if (!camera) + return 1; + + if (contrast >= -100 && contrast <= 100) + { + MMAL_RATIONAL_T value = {contrast, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_CONTRAST, value)); + } + else + { + vcos_log_error("Invalid contrast value"); + ret = 1; + } + + return ret; +} + +/** + * Adjust the brightness level for images + * @param camera Pointer to camera component + * @param brightness Value to adjust, 0 to 100 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness) +{ + int ret = 0; + + if (!camera) + return 1; + + if (brightness >= 0 && brightness <= 100) + { + MMAL_RATIONAL_T value = {brightness, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_BRIGHTNESS, value)); + } + else + { + vcos_log_error("Invalid brightness value"); + ret = 1; + } + + return ret; +} + +/** + * Adjust the ISO used for images + * @param camera Pointer to camera component + * @param ISO Value to set TODO : + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_ISO, ISO)); +} + +/** + * Adjust the metering mode for images + * @param camera Pointer to camera component + * @param saturation Value from following + * - MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T m_mode ) +{ + MMAL_PARAMETER_EXPOSUREMETERINGMODE_T meter_mode = {{MMAL_PARAMETER_EXP_METERING_MODE,sizeof(meter_mode)}, + m_mode}; + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &meter_mode.hdr)); +} + + +/** + * Set the video stabilisation flag. Only used in video mode + * @param camera Pointer to camera component + * @param saturation Flag 0 off 1 on + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_VIDEO_STABILISATION, vstabilisation)); +} + +/** + * Adjust the exposure compensation for images (EV) + * @param camera Pointer to camera component + * @param exp_comp Value to adjust, -10 to +10 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_int32(camera->control, MMAL_PARAMETER_EXPOSURE_COMP , exp_comp)); +} + + +/** + * Set exposure mode for images + * @param camera Pointer to camera component + * @param mode Exposure mode to set from + * - MMAL_PARAM_EXPOSUREMODE_OFF, + * - MMAL_PARAM_EXPOSUREMODE_AUTO, + * - MMAL_PARAM_EXPOSUREMODE_NIGHT, + * - MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + * - MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + * - MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + * - MMAL_PARAM_EXPOSUREMODE_SPORTS, + * - MMAL_PARAM_EXPOSUREMODE_SNOW, + * - MMAL_PARAM_EXPOSUREMODE_BEACH, + * - MMAL_PARAM_EXPOSUREMODE_VERYLONG, + * - MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + * - MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + * - MMAL_PARAM_EXPOSUREMODE_FIREWORKS, + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode) +{ + MMAL_PARAMETER_EXPOSUREMODE_T exp_mode = {{MMAL_PARAMETER_EXPOSURE_MODE,sizeof(exp_mode)}, mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &exp_mode.hdr)); +} + + +/** + * Set the aWB (auto white balance) mode for images + * @param camera Pointer to camera component + * @param awb_mode Value to set from + * - MMAL_PARAM_AWBMODE_OFF, + * - MMAL_PARAM_AWBMODE_AUTO, + * - MMAL_PARAM_AWBMODE_SUNLIGHT, + * - MMAL_PARAM_AWBMODE_CLOUDY, + * - MMAL_PARAM_AWBMODE_SHADE, + * - MMAL_PARAM_AWBMODE_TUNGSTEN, + * - MMAL_PARAM_AWBMODE_FLUORESCENT, + * - MMAL_PARAM_AWBMODE_INCANDESCENT, + * - MMAL_PARAM_AWBMODE_FLASH, + * - MMAL_PARAM_AWBMODE_HORIZON, + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode) +{ + MMAL_PARAMETER_AWBMODE_T param = {{MMAL_PARAMETER_AWB_MODE,sizeof(param)}, awb_mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + +/** + * Set the image effect for the images + * @param camera Pointer to camera component + * @param imageFX Value from + * - MMAL_PARAM_IMAGEFX_NONE, + * - MMAL_PARAM_IMAGEFX_NEGATIVE, + * - MMAL_PARAM_IMAGEFX_SOLARIZE, + * - MMAL_PARAM_IMAGEFX_POSTERIZE, + * - MMAL_PARAM_IMAGEFX_WHITEBOARD, + * - MMAL_PARAM_IMAGEFX_BLACKBOARD, + * - MMAL_PARAM_IMAGEFX_SKETCH, + * - MMAL_PARAM_IMAGEFX_DENOISE, + * - MMAL_PARAM_IMAGEFX_EMBOSS, + * - MMAL_PARAM_IMAGEFX_OILPAINT, + * - MMAL_PARAM_IMAGEFX_HATCH, + * - MMAL_PARAM_IMAGEFX_GPEN, + * - MMAL_PARAM_IMAGEFX_PASTEL, + * - MMAL_PARAM_IMAGEFX_WATERCOLOUR, + * - MMAL_PARAM_IMAGEFX_FILM, + * - MMAL_PARAM_IMAGEFX_BLUR, + * - MMAL_PARAM_IMAGEFX_SATURATION, + * - MMAL_PARAM_IMAGEFX_COLOURSWAP, + * - MMAL_PARAM_IMAGEFX_WASHEDOUT, + * - MMAL_PARAM_IMAGEFX_POSTERISE, + * - MMAL_PARAM_IMAGEFX_COLOURPOINT, + * - MMAL_PARAM_IMAGEFX_COLOURBALANCE, + * - MMAL_PARAM_IMAGEFX_CARTOON, + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX) +{ + MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr)); +} + +/* TODO :what to do with the image effects parameters? + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)}, + imageFX, 0, {0}}; +mmal_port_parameter_set(camera->control, &imfx_param.hdr); + */ + +/** + * Set the colour effect for images (Set UV component) + * @param camera Pointer to camera component + * @param colourFX Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white) + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX) +{ + MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0}; + + if (!camera) + return 1; + + colfx.enable = colourFX->enable; + colfx.u = colourFX->u; + colfx.v = colourFX->v; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr)); + +} + + +/** + * Set the rotation of the image + * @param camera Pointer to camera component + * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only) + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation) +{ + int ret; + int my_rotation = ((rotation % 360 ) / 90) * 90; + + ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation); + mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation); + mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation); + + return ret; +} + +/** + * Set the flips state of the image + * @param camera Pointer to camera component + * @param hflip If true, horizontally flip the image + * @param vflip If true, vertically flip the image + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip) +{ + MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE}; + + if (hflip && vflip) + mirror.value = MMAL_PARAM_MIRROR_BOTH; + else + if (hflip) + mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL; + else + if (vflip) + mirror.value = MMAL_PARAM_MIRROR_VERTICAL; + + mmal_port_parameter_set(camera->output[0], &mirror.hdr); + mmal_port_parameter_set(camera->output[1], &mirror.hdr); + return mmal_port_parameter_set(camera->output[2], &mirror.hdr); +} + +/** + * Set the ROI of the sensor to use for captures/preview + * @param camera Pointer to camera component + * @param rect Normalised coordinates of ROI rectangle + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) +{ + MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}}; + + crop.rect.x = (65536 * rect.x); + crop.rect.y = (65536 * rect.y); + crop.rect.width = (65536 * rect.w); + crop.rect.height = (65536 * rect.h); + + return mmal_port_parameter_set(camera->control, &crop.hdr); +} + + +/** + * Asked GPU how much memory it has allocated + * + * @return amount of memory in MB + */ +static int raspicamcontrol_get_mem_gpu(void) +{ + char response[80] = ""; + int gpu_mem = 0; + if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0) + vc_gencmd_number_property(response, "gpu", &gpu_mem); + return gpu_mem; +} + +/** + * Ask GPU about its camera abilities + * @param supported None-zero if software supports the camera + * @param detected None-zero if a camera has been detected + */ +static void raspicamcontrol_get_camera(int *supported, int *detected) +{ + char response[80] = ""; + if (vc_gencmd(response, sizeof response, "get_camera") == 0) + { + if (supported) + vc_gencmd_number_property(response, "supported", supported); + if (detected) + vc_gencmd_number_property(response, "detected", detected); + } +} + +/** + * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities + * @param supported None-zero if software supports the camera + * @param detected None-zero if a camera has been detected + */ +void raspicamcontrol_check_configuration(int min_gpu_mem) +{ + int gpu_mem = raspicamcontrol_get_mem_gpu(); + int supported = 0, detected = 0; + raspicamcontrol_get_camera(&supported, &detected); + if (!supported) + vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n"); + else if (gpu_mem < min_gpu_mem) + vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem); + else if (!detected) + vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n"); + else + vcos_log_error("Failed to run camera app. Please check for firmware updates\n"); +} + diff --git a/sys/rpicamsrc/RaspiCamControl.h b/sys/rpicamsrc/RaspiCamControl.h new file mode 100644 index 0000000000..847b4470c0 --- /dev/null +++ b/sys/rpicamsrc/RaspiCamControl.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2013 Jan Schmidt +Portions: +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPICAMCONTROL_H_ +#define RASPICAMCONTROL_H_ + +/* Various parameters + * + * Exposure Mode + * MMAL_PARAM_EXPOSUREMODE_OFF, + MMAL_PARAM_EXPOSUREMODE_AUTO, + MMAL_PARAM_EXPOSUREMODE_NIGHT, + MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + MMAL_PARAM_EXPOSUREMODE_SPORTS, + MMAL_PARAM_EXPOSUREMODE_SNOW, + MMAL_PARAM_EXPOSUREMODE_BEACH, + MMAL_PARAM_EXPOSUREMODE_VERYLONG, + MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + MMAL_PARAM_EXPOSUREMODE_FIREWORKS, + * + * AWB Mode + * MMAL_PARAM_AWBMODE_OFF, + MMAL_PARAM_AWBMODE_AUTO, + MMAL_PARAM_AWBMODE_SUNLIGHT, + MMAL_PARAM_AWBMODE_CLOUDY, + MMAL_PARAM_AWBMODE_SHADE, + MMAL_PARAM_AWBMODE_TUNGSTEN, + MMAL_PARAM_AWBMODE_FLUORESCENT, + MMAL_PARAM_AWBMODE_INCANDESCENT, + MMAL_PARAM_AWBMODE_FLASH, + MMAL_PARAM_AWBMODE_HORIZON, + * + * Image FX + MMAL_PARAM_IMAGEFX_NONE, + MMAL_PARAM_IMAGEFX_NEGATIVE, + MMAL_PARAM_IMAGEFX_SOLARIZE, + MMAL_PARAM_IMAGEFX_POSTERIZE, + MMAL_PARAM_IMAGEFX_WHITEBOARD, + MMAL_PARAM_IMAGEFX_BLACKBOARD, + MMAL_PARAM_IMAGEFX_SKETCH, + MMAL_PARAM_IMAGEFX_DENOISE, + MMAL_PARAM_IMAGEFX_EMBOSS, + MMAL_PARAM_IMAGEFX_OILPAINT, + MMAL_PARAM_IMAGEFX_HATCH, + MMAL_PARAM_IMAGEFX_GPEN, + MMAL_PARAM_IMAGEFX_PASTEL, + MMAL_PARAM_IMAGEFX_WATERCOLOUR, + MMAL_PARAM_IMAGEFX_FILM, + MMAL_PARAM_IMAGEFX_BLUR, + MMAL_PARAM_IMAGEFX_SATURATION, + MMAL_PARAM_IMAGEFX_COLOURSWAP, + MMAL_PARAM_IMAGEFX_WASHEDOUT, + MMAL_PARAM_IMAGEFX_POSTERISE, + MMAL_PARAM_IMAGEFX_COLOURPOINT, + MMAL_PARAM_IMAGEFX_COLOURBALANCE, + MMAL_PARAM_IMAGEFX_CARTOON, + + */ + + + +// There isn't actually a MMAL structure for the following, so make one +typedef struct +{ + int enable; /// Turn colourFX on or off + int u,v; /// U and V to use +} MMAL_PARAM_COLOURFX_T; + +typedef struct +{ + int enable; + int width,height; + int quality; +} MMAL_PARAM_THUMBNAIL_CONFIG_T; + +typedef struct +{ + double x; + double y; + double w; + double h; +} PARAM_FLOAT_RECT_T; + +/// struct contain camera settings +typedef struct +{ + int sharpness; /// -100 to 100 + int contrast; /// -100 to 100 + int brightness; /// 0 to 100 + int saturation; /// -100 to 100 + int ISO; /// TODO : what range? + int videoStabilisation; /// 0 or 1 (false or true) + int exposureCompensation; /// -10 to +10 ? + MMAL_PARAM_EXPOSUREMODE_T exposureMode; + MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode; + MMAL_PARAM_AWBMODE_T awbMode; + MMAL_PARAM_IMAGEFX_T imageEffect; + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters; + MMAL_PARAM_COLOURFX_T colourEffects; + int rotation; /// 0-359 + int hflip; /// 0 or 1 + int vflip; /// 0 or 1 + PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect +} RASPICAM_CAMERA_PARAMETERS; + + +void raspicamcontrol_check_configuration(int min_gpu_mem); + +int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2); +void raspicamcontrol_display_help(); +int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera); + +int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params); +int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params); +void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params); + +void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params); + +void raspicamcontrol_check_configuration(int min_gpu_mem); + +// Individual setting functions +int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation); +int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness); +int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast); +int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness); +int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO); +int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode); +int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation); +int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); +int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); +int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); +int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); +int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); +int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); +int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); +int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); + +//Individual getting functions +int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera); +MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera); +MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera); +MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera); +MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera); + + +#endif /* RASPICAMCONTROL_H_ */ diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c new file mode 100644 index 0000000000..d6f9b12f1a --- /dev/null +++ b/sys/rpicamsrc/RaspiCapture.c @@ -0,0 +1,1211 @@ +/* + * Copyright (c) 2013 Jan Schmidt +Portions: +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiCapture.c + * + * Modification of the RaspiVid command line capture program for GStreamer + * use. + * + * \date 28th Feb 2013, 11 Oct 2013 + * \Author: James Hughes, Jan Schmidt + * + * Description + * + * 3 components are created; camera, preview and video encoder. + * Camera component has three ports, preview, video and stills. + * This program connects preview and stills to the preview and video + * encoder. Using mmal we don't need to worry about buffers between these + * components, but we do need to handle buffers from the encoder, which + * are simply written straight to the file in the requisite buffer callback. + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the (generic) preview window + */ + + +#include +#include +#include +#include +#include + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" + +#include + +/// Camera number to use - we only have one camera, indexed from 0. +#define CAMERA_NUMBER 0 + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + +// Video format information +#define VIDEO_FRAME_RATE_NUM 30 +#define VIDEO_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +// Max bitrate we allow for recording +const int MAX_BITRATE = 30000000; // 30Mbits/s + +/// Interval at which we check for an failure abort during capture +const int ABORT_INTERVAL = 100; // ms + + +int mmal_status_to_int(MMAL_STATUS_T status); + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + int bitrate; /// Requested bitrate + int framerate; /// Requested frame rate (fps) + int intraperiod; /// Intra-refresh period (key frame rate) + char *filename; /// filename of output file + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either + /// the camera output or the encoder output (with compression artifacts) + int profile; /// H264 profile to use for encoding + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port +} RASPIVID_STATE; + +/** Struct used to pass information in encoder port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + RASPIVID_STATE *pstate; /// pointer to our state in case required in callback + int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture +} PORT_USERDATA; + +#if 0 +/// Structure to cross reference H264 profile strings against the MMAL parameter equivalent +static XREF_T profile_map[] = +{ + {"baseline", MMAL_VIDEO_PROFILE_H264_BASELINE}, + {"main", MMAL_VIDEO_PROFILE_H264_MAIN}, + {"high", MMAL_VIDEO_PROFILE_H264_HIGH}, +// {"constrained", MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this? +}; + +static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]); + + +static void display_valid_parameters(char *app_name); + +/// Command ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandBitrate 3 +#define CommandOutput 4 +#define CommandVerbose 5 +#define CommandTimeout 6 +#define CommandDemoMode 7 +#define CommandFramerate 8 +#define CommandPreviewEnc 9 +#define CommandIntraPeriod 10 +#define CommandProfile 11 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -')", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate,"-framerate", "fps","Specify the frames per second to record", 1}, + { CommandPreviewEnc,"-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, + { CommandIntraPeriod,"-intra", "g", "Specify the intra refresh period (key frame rate/GoP size)", 1}, + { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); +#endif + +static void dump_state(RASPIVID_STATE *state); + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +void raspi_capture_default_state(RASPIVID_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPIVID_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 1920; // Default to 1080p + state->height = 1080; + state->bitrate = 17000000; // This is a decent default bitrate for 1080p + state->framerate = VIDEO_FRAME_RATE_NUM; + state->intraperiod = 0; // Not set + state->demoMode = 0; + state->demoInterval = 250; // ms + state->immutableInput = 1; + state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); + + dump_state(state); +} + +/** + * Dump image state parameters to printf. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_state(RASPIVID_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); + fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->bitrate, state->framerate, state->timeout); + //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->profile, profile_map, profile_map_size)); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +#if 0 +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return Non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandBitrate: // 1-100 + if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1) + { + if (state->bitrate > MAX_BITRATE) + { + state->bitrate = MAX_BITRATE; + } + i++; + } + else + valid = 0; + + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 1); + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len); + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder/capture + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // TODO : What limits do we need for timeout? + i++; + } + else + valid = 0; + break; + } + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + if (state->demoInterval == 0) + state->demoInterval = 250; // ms + + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandFramerate: // fps to record + { + if (sscanf(argv[i + 1], "%u", &state->framerate) == 1) + { + // TODO : What limits do we need for fps 1 - 30 - 120?? + i++; + } + else + valid = 0; + break; + } + + case CommandPreviewEnc: + state->immutableInput = 0; + break; + + case CommandIntraPeriod: // key frame rate + { + if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1) + i++; + else + valid = 0; + break; + } + + case CommandProfile: // H264 profile + { + state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size); + + if( state->profile == -1) + state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + + i++; + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i]); + return 1; + } + + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + int i; + + fprintf(stderr, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n"); + fprintf(stderr, "\nusage: %s [options]\n\n", app_name); + + fprintf(stderr, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Profile options + fprintf(stderr, "\n\nH264 Profile options :\n%s", profile_map[0].mode ); + + for (i=1;icmd == MMAL_EVENT_PARAMETER_CHANGED) + { + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for encoder + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + + vcos_assert(pData->file_handle); + + if (buffer->length) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + if (bytes_written != buffer->length) + { + vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); + pData->abort = 1; + } + } + else + { + vcos_log_error("Received a encoder buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the encoder port"); + } +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 0, + .max_preview_video_w = state->width, + .max_preview_video_h = state->height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + // Now set up the port formats + + // Set the encode format on the Preview port + // HW limitations mean we need the preview to be the same size as the required recorded output + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = state->width; + format->es->video.height = state->height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = state->framerate; + format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the encode format on the video port + + format = video_port->format; + format->encoding_variant = MMAL_ENCODING_I420; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = state->width; + format->es->video.height = state->height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = state->framerate; + format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + + // Set the encode format on the still port + + format = still_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->es->video.width = state->width; + format->es->video.height = state->height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = 1; + format->es->video.frame_rate.den = 1; + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPIVID_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Create the encoder component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *encoder = 0; + MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create video encoder component"); + goto error; + } + + if (!encoder->input_num || !encoder->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Video encoder doesn't have input/output ports"); + goto error; + } + + encoder_input = encoder->input[0]; + encoder_output = encoder->output[0]; + + // We want same format on input and output + mmal_format_copy(encoder_output->format, encoder_input->format); + + // Only supporting H264 at the moment + encoder_output->format->encoding = MMAL_ENCODING_H264; + + encoder_output->format->bitrate = state->bitrate; + + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + + if (encoder_output->buffer_size < encoder_output->buffer_size_min) + encoder_output->buffer_size = encoder_output->buffer_size_min; + + encoder_output->buffer_num = encoder_output->buffer_num_recommended; + + if (encoder_output->buffer_num < encoder_output->buffer_num_min) + encoder_output->buffer_num = encoder_output->buffer_num_min; + + // Commit the port changes to the output port + status = mmal_port_format_commit(encoder_output); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on video encoder output port"); + goto error; + } + + + // Set the rate control parameter + if (0) + { + MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set ratecontrol"); + goto error; + } + + } + + if (state->intraperiod) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set intraperiod"); + goto error; + } + + } + + { + MMAL_PARAMETER_VIDEO_PROFILE_T param; + param.hdr.id = MMAL_PARAMETER_PROFILE; + param.hdr.size = sizeof(param); + + param.profile[0].profile = state->profile; + param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 profile"); + goto error; + } + } + + + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS) + { + vcos_log_error("Unable to set immutable input flag"); + // Continue rather than abort.. + } + + // Enable component + status = mmal_component_enable(encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable video encoder component"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); + } + + state->encoder_pool = pool; + state->encoder_component = encoder; + + if (state->verbose) + fprintf(stderr, "Encoder component done\n"); + + return status; + + error: + if (encoder) + mmal_component_destroy(encoder); + + return status; +} + +/** + * Destroy the encoder component + * + * @param state Pointer to state control struct + * + */ +static void destroy_encoder_component(RASPIVID_STATE *state) +{ + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + } + + if (state->encoder_component) + { + mmal_component_destroy(state->encoder_component); + state->encoder_component = NULL; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + + +int raspi_capture_start() +{ + // Our main data storage vessel.. + RASPIVID_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + MMAL_PORT_T *encoder_output_port = NULL; + FILE *output_file = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY); + + raspi_capture_default_state(&state); + + if (state.verbose) + { + dump_state(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + preview_input_port = state.preview_parameters.preview_component->input[0]; + encoder_input_port = state.encoder_component->input[0]; + encoder_output_port = state.encoder_component->output[0]; + + if (state.preview_parameters.wantPreview ) + { + if (state.verbose) + { + fprintf(stderr, "Connecting camera preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect camera to preview + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + } + else + { + status = MMAL_SUCCESS; + } + + if (status == MMAL_SUCCESS) + { + if (state.verbose) + fprintf(stderr, "Connecting camera stills port to encoder input port\n"); + + // Now connect the camera to the encoder + status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } + + if (state.filename) + { + if (state.filename[0] == '-') + { + output_file = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + if (state.verbose) + fprintf(stderr, "Opening output file \"%s\"\n", state.filename); + + output_file = fopen(state.filename, "wb"); + } + + if (!output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); + } + } + + // Set up our userdata - this is passed though to the callback where we need the information. + callback_data.file_handle = output_file; + callback_data.pstate = &state; + callback_data.abort = 0; + + encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling encoder output port\n"); + + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + + if (state.verbose) + fprintf(stderr, "Running in demo mode\n"); + + for (i=0;state.timeout == 0 || iqueue); + int q; + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + + } + } + + // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example + // out of storage space) + // Going to check every ABORT_INTERVAL milliseconds + + for (wait = 0; state.timeout == 0 || wait < state.timeout; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (callback_data.abort) + break; + } + + if (state.verbose) + fprintf(stderr, "Finished capture\n"); + } + else + { + if (state.timeout) + vcos_sleep(state.timeout); + else + for (;;) vcos_sleep(ABORT_INTERVAL); + } + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_still_port); + check_disable_port(encoder_output_port); + + if (state.preview_parameters.wantPreview ) + mmal_connection_destroy(state.preview_connection); + mmal_connection_destroy(state.encoder_connection); + + // Can now close our file. Note disabling ports may flush buffers which causes + // problems if we have already closed the file! + if (output_file && output_file != stdout) + fclose(output_file); + + /* Disable components */ + if (state.encoder_component) + mmal_component_disable(state.encoder_component); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + destroy_encoder_component(&state); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h new file mode 100644 index 0000000000..f24ab4b834 --- /dev/null +++ b/sys/rpicamsrc/RaspiCapture.h @@ -0,0 +1,82 @@ +/* + * GStreamer + * Copyright (C) 2013 Jan Schmidt + * + * 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. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * 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 __RASPICAPTURE_H__ +#define __RASPICAPTURE_H__ + +#include +#include + +#include "interface/mmal/mmal_common.h" +#include "interface/mmal/mmal_types.h" +#include "interface/mmal/mmal_parameters_camera.h" +#include "interface/mmal/mmal_component.h" +#include "RaspiCamControl.h" +#include "RaspiPreview.h" + +G_BEGIN_DECLS + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + int bitrate; /// Requested bitrate + int framerate; /// Requested frame rate (fps) + int intraperiod; /// Intra-refresh period (key frame rate) + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either + /// the camera output or the encoder output (with compression artifacts) + int profile; /// H264 profile to use for encoding + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters +} RASPIVID_STATE; + +int raspi_capture_start(); + +G_END_DECLS + +#endif diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c new file mode 100644 index 0000000000..08d2a74e1c --- /dev/null +++ b/sys/rpicamsrc/RaspiPreview.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2013 Jan Schmidt +Portions: +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +#include "RaspiPreview.h" + +#if 0 +#define CommandPreview 1 +#define CommandFullScreen 2 +#define CommandOpacity 3 +#define CommandDisablePreview 4 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandPreview, "-preview", "p", "Preview window settings <'x,y,w,h'>", 1 }, + { CommandFullScreen, "-fullscreen", "f", "Fullscreen preview mode", 0 }, + { CommandOpacity, "-opacity", "op", "Preview window opacity (0-255)", 1}, + { CommandDisablePreview,"-nopreview", "n", "Do not display a preview window", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); +#endif + +/** + * Create the preview component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) +{ + MMAL_COMPONENT_T *preview = 0; + MMAL_PORT_T *preview_port = NULL; + MMAL_STATUS_T status; + + if (!state->wantPreview) + { + // No preview required, so create a null sink component to take its place + status = mmal_component_create("vc.null_sink", &preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create null sink component"); + goto error; + } + } + else + { + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, + &preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create preview component"); + goto error; + } + + if (!preview->input_num) + { + status = MMAL_ENOSYS; + vcos_log_error("No input ports found on component"); + goto error; + } + + preview_port = preview->input[0]; + + MMAL_DISPLAYREGION_T param; + param.hdr.id = MMAL_PARAMETER_DISPLAYREGION; + param.hdr.size = sizeof(MMAL_DISPLAYREGION_T); + + param.set = MMAL_DISPLAY_SET_LAYER; + param.layer = PREVIEW_LAYER; + + param.set |= MMAL_DISPLAY_SET_ALPHA; + param.alpha = state->opacity; + + if (state->wantFullScreenPreview) + { + param.set |= MMAL_DISPLAY_SET_FULLSCREEN; + param.fullscreen = 1; + } + else + { + param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN); + param.fullscreen = 0; + param.dest_rect = state->previewWindow; + } + + status = mmal_port_parameter_set(preview_port, ¶m.hdr); + + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + vcos_log_error("unable to set preview port parameters (%u)", status); + goto error; + } + } + + /* Enable component */ + status = mmal_component_enable(preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable preview/null sink component (%u)", status); + goto error; + } + + state->preview_component = preview; + + return status; + +error: + + if (preview) + mmal_component_destroy(preview); + + return status; +} + + +/** + * Destroy the preview component + * + * @param state Pointer to state control struct + * + */ +void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state) +{ + if (state->preview_component) + { + mmal_component_destroy(state->preview_component); + state->preview_component = NULL; + } +} + +/** + * Assign set of default parameters to the passed in parameter block + * + * @param state Pointer to parameter block + * + */ +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state) +{ + state->wantPreview = 1; + state->wantFullScreenPreview = 1; + state->opacity = 255; + state->previewWindow.x = 0; + state->previewWindow.y = 0; + state->previewWindow.width = 1024; + state->previewWindow.height = 768; + state->preview_component = NULL; +} + +/** + * Dump parameters as human readable to stdout + * + * @param state Pointer to parameter block + * + */ +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state) +{ + fprintf(stderr, "Preview %s, Full screen %s\n", state->wantPreview ? "Yes" : "No", + state->wantFullScreenPreview ? "Yes" : "No"); + + fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", state->previewWindow.x, + state->previewWindow.y, state->previewWindow.width, + state->previewWindow.height, state->opacity); +}; + +#if 0 +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandPreview: // Preview window + { + int tmp; + + params->wantPreview = 1; + + tmp = sscanf(arg2, "%d,%d,%d,%d", + ¶ms->previewWindow.x, ¶ms->previewWindow.y, + ¶ms->previewWindow.width, ¶ms->previewWindow.height); + + // Failed to get any window parameters, so revert to full screen + if (tmp == 0) + params->wantFullScreenPreview = 1; + else + params->wantFullScreenPreview = 0; + + used = 2; + + break; + } + + case CommandFullScreen: // Want full screen preview mode (overrides display rect) + params->wantPreview = 1; + params->wantFullScreenPreview = 1; + + used = 1; + break; + + case CommandOpacity: // Define preview window opacity + if (sscanf(arg2, "%u", ¶ms->opacity) != 1) + params->opacity = 255; + else + used = 2; + break; + + case CommandDisablePreview: // Turn off preview output + params->wantPreview = 0; + used = 1; + break; + } + + return used; +} + +/** + * Display help for command line options + */ +void raspipreview_display_help() +{ + fprintf(stderr, "\nPreview parameter commands\n\n"); + raspicli_display_help(cmdline_commands, cmdline_commands_size); +} +#endif diff --git a/sys/rpicamsrc/RaspiPreview.h b/sys/rpicamsrc/RaspiPreview.h new file mode 100644 index 0000000000..c986d2d862 --- /dev/null +++ b/sys/rpicamsrc/RaspiPreview.h @@ -0,0 +1,57 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPIPREVIEW_H_ +#define RASPIPREVIEW_H_ + +/// Layer that preview window should be displayed on +#define PREVIEW_LAYER 2 +#define PREVIEW_FRAME_RATE_NUM 30 +#define PREVIEW_FRAME_RATE_DEN 1 + +#define FULL_RES_PREVIEW_FRAME_RATE_NUM 15 +#define FULL_RES_PREVIEW_FRAME_RATE_DEN 1 + + +typedef struct +{ + int wantPreview; /// Display a preview + int wantFullScreenPreview; /// 0 is use previewRect, non-zero to use full screen + int opacity; /// Opacity of window - 0 = transparent, 255 = opaque + MMAL_RECT_T previewWindow; /// Destination rectangle for the preview window. + MMAL_COMPONENT_T *preview_component; /// Pointer to the created preview display component +} RASPIPREVIEW_PARAMETERS; + +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state); +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2); +void raspipreview_display_help(); + +#endif /* RASPIPREVIEW_H_ */ diff --git a/sys/rpicamsrc/RaspiStill.c b/sys/rpicamsrc/RaspiStill.c new file mode 100644 index 0000000000..62d0b25b39 --- /dev/null +++ b/sys/rpicamsrc/RaspiStill.c @@ -0,0 +1,1516 @@ +/* + * Copyright (c) 2013 Jan Schmidt +Portions: +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiStill.c + * Command line program to capture a still frame and encode it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 31 Jan 2013 + * \Author: James Hughes + * + * Description + * + * 3 components are created; camera, preview and JPG encoder. + * Camera component has three ports, preview, video and stills. + * This program connects preview and stills to the preview and jpg + * encoder. Using mmal we don't need to worry about buffers between these + * components, but we do need to handle buffers from the encoder, which + * are simply written straight to the file in the requisite buffer callback. + * + * We use the RaspiCamControl code to handle the specific camera settings. + */ + +// We use some GNU extensions (asprintf, basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.2" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +/// Camera number to use - we only have one camera, indexed from 0. +#define CAMERA_NUMBER 0 + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + + +// Stills format information +#define STILLS_FRAME_RATE_NUM 15 +#define STILLS_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +#define MAX_USER_EXIF_TAGS 32 +#define MAX_EXIF_PAYLOAD_LENGTH 128 + +int mmal_status_to_int(MMAL_STATUS_T status); + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + int quality; /// JPEG quality setting (1-100) + int wantRAW; /// Flag for whether the JPEG metadata also contains the RAW bayer image + char *filename; /// filename of output file + char *linkname; /// filename of output file + MMAL_PARAM_THUMBNAIL_CONFIG_T thumbnailConfig; + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + MMAL_FOURCC_T encoding; /// Encoding to use for the output file. + const char *exifTags[MAX_USER_EXIF_TAGS]; /// Array of pointers to tags supplied from the command line + int numExifTags; /// Number of supplied tags + int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse + int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps. + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component + MMAL_COMPONENT_T *null_sink_component; /// Pointer to the null sink component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + +} RASPISTILL_STATE; + +/** Struct used to pass information in encoder port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) + RASPISTILL_STATE *pstate; /// pointer to our state in case required in callback +} PORT_USERDATA; + +static void display_valid_parameters(char *app_name); +static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag); + +/// Comamnd ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandQuality 3 +#define CommandRaw 4 +#define CommandOutput 5 +#define CommandVerbose 6 +#define CommandTimeout 7 +#define CommandThumbnail 8 +#define CommandDemoMode 9 +#define CommandEncoding 10 +#define CommandExifTag 11 +#define CommandTimelapse 12 +#define CommandFullResPreview 13 +#define CommandLink 14 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width ", 1 }, + { CommandHeight, "-height", "h", "Set image height ", 1 }, + { CommandQuality, "-quality", "q", "Set jpeg quality <0 to 100>", 1 }, + { CommandRaw, "-raw", "r", "Add raw bayer data to jpeg metadata", 0 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -'). If not specified, no file is saved", 1 }, + { CommandLink, "-latest", "l", "Link latest complete image to filename ", 1}, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down (if not specified, set to 5s)", 1 }, + { CommandThumbnail,"-thumb", "th", "Set thumbnail parameters (x:y:quality)", 1}, + { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 0}, + { CommandEncoding,"-encoding", "e", "Encoding to use for output file (jpg, bmp, gif, png)", 1}, + { CommandExifTag, "-exif", "x", "EXIF tag to apply to captures (format as 'key=value')", 1}, + { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every ms", 1}, + { CommandFullResPreview,"-fullpreview", "fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +static struct +{ + char *format; + MMAL_FOURCC_T encoding; +} encoding_xref[] = +{ + {"jpg", MMAL_ENCODING_JPEG}, + {"bmp", MMAL_ENCODING_BMP}, + {"gif", MMAL_ENCODING_GIF}, + {"png", MMAL_ENCODING_PNG} +}; + +static int encoding_xref_size = sizeof(encoding_xref) / sizeof(encoding_xref[0]); + + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPISTILL_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + state->timeout = 5000; // 5s delay before take image + state->width = 2592; + state->height = 1944; + state->quality = 85; + state->wantRAW = 0; + state->filename = NULL; + state->linkname = NULL; + state->verbose = 0; + state->thumbnailConfig.enable = 1; + state->thumbnailConfig.width = 64; + state->thumbnailConfig.height = 48; + state->thumbnailConfig.quality = 35; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->camera_component = NULL; + state->encoder_component = NULL; + state->preview_connection = NULL; + state->encoder_connection = NULL; + state->encoder_pool = NULL; + state->encoding = MMAL_ENCODING_JPEG; + state->numExifTags = 0; + state->timelapse = 0; + state->fullResPreview = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); +} + +/** + * Dump image state parameters to stderr. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPISTILL_STATE *state) +{ + int i; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, quality %d, filename %s\n", state->width, + state->height, state->quality, state->filename); + fprintf(stderr, "Time delay %d, Raw %s\n", state->timeout, + state->wantRAW ? "yes" : "no"); + fprintf(stderr, "Thumbnail enabled %s, width %d, height %d, quality %d\n", + state->thumbnailConfig.enable ? "Yes":"No", state->thumbnailConfig.width, + state->thumbnailConfig.height, state->thumbnailConfig.quality); + fprintf(stderr, "Link to latest frame enabled "); + if (state->linkname) + { + fprintf(stderr, " yes, -> %s\n", state->linkname); + } + else + { + fprintf(stderr, " no\n"); + } + fprintf(stderr, "Full resolution preview %s\n\n", state->fullResPreview ? "Yes": "No"); + + if (state->numExifTags) + { + fprintf(stderr, "User supplied EXIF tags :\n"); + + for (i=0;inumExifTags;i++) + { + fprintf(stderr, "%s", state->exifTags[i]); + if (i != state->numExifTags-1) + fprintf(stderr, ","); + } + fprintf(stderr, "\n\n"); + } + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + // exit straight away if help requested + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandQuality: // Quality = 1-100 + if (sscanf(argv[i + 1], "%u", &state->quality) == 1) + { + if (state->quality > 100) + { + fprintf(stderr, "Setting max quality = 100\n"); + state->quality = 100; + } + i++; + } + else + valid = 0; + + break; + + case CommandRaw: // Add raw bayer data in metadata + state->wantRAW = 1; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len); + i++; + } + else + valid = 0; + break; + } + + case CommandLink : + { + int len = strlen(argv[i+1]); + if (len) + { + state->linkname = malloc(len + 10); + vcos_assert(state->linkname); + if (state->linkname) + strncpy(state->linkname, argv[i + 1], len); + i++; + } + else + valid = 0; + break; + + } + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // TODO : What limits do we need for timeout? + i++; + } + else + valid = 0; + break; + } + case CommandThumbnail : // thumbnail parameters - needs string "x:y:quality" + sscanf(argv[i + 1], "%d:%d:%d", &state->thumbnailConfig.width,&state->thumbnailConfig.height, + &state->thumbnailConfig.quality); + i++; + break; + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandEncoding : + { + int len = strlen(argv[i + 1]); + valid = 0; + + if (len) + { + int j; + for (j=0;jencoding = encoding_xref[j].encoding; + valid = 1; + i++; + break; + } + } + } + break; + } + + case CommandExifTag: + store_exif_tag(state, argv[i+1]); + i++; + break; + + case CommandTimelapse: + if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1) + valid = 0; + else + i++; + break; + + case CommandFullResPreview: + state->fullResPreview = 1; + break; + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i]); + return 1; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stderr, "Runs camera for specific time, and take JPG capture at end if requested\n\n"); + fprintf(stderr, "usage: %s [options]\n\n", app_name); + + fprintf(stderr, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + fprintf(stderr, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * No actions taken in current version + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for encoder + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + int complete = 0; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + + if (buffer->length && pData->file_handle) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + // We need to check we wrote what we wanted - it's possible we have run out of storage. + if (bytes_written != buffer->length) + { + vcos_log_error("Unable to write buffer to file - aborting"); + complete = 1; + } + + // Now flag if we have completed + if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) + complete = 1; + } + else + { + vcos_log_error("Received a encoder buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_BUFFER_HEADER_T *new_buffer; + + new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + + if (new_buffer) + { + status = mmal_port_send_buffer(port, new_buffer); + } + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the encoder port"); + } + + if (complete) + vcos_semaphore_post(&(pData->complete_semaphore)); + +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct. camera_component member set to the created camera_component if successfull. + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 1, + .max_preview_video_w = state->preview_parameters.previewWindow.width, + .max_preview_video_h = state->preview_parameters.previewWindow.height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + + if (state->fullResPreview) + { + cam_config.max_preview_video_w = state->width; + cam_config.max_preview_video_h = state->height; + } + + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + // Now set up the port formats + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if (state->fullResPreview) + { + // In this mode we are forcing the preview to be generated from the full capture resolution. + // This runs at a max of 15fps with the OV5647 sensor. + format->es->video.width = state->width; + format->es->video.height = state->height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN; + } + else + { + // use our normal preview mode - probably 1080p30 + format->es->video.width = state->preview_parameters.previewWindow.width; + format->es->video.height = state->preview_parameters.previewWindow.height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->preview_parameters.previewWindow.width; + format->es->video.crop.height = state->preview_parameters.previewWindow.height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + } + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the same format on the video port (which we dont use here) + mmal_format_full_copy(video_port->format, format); + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + format = still_port->format; + + // Set our stills format on the stills (for encoder) port + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = state->width; + format->es->video.height = state->height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; + format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; + + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPISTILL_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + + + +/** + * Create the encoder component, set up its ports + * + * @param state Pointer to state control struct. encoder_component member set to the created camera_component if successfull. + * + * @return a MMAL_STATUS, MMAL_SUCCESS if all OK, something else otherwise + */ +static MMAL_STATUS_T create_encoder_component(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *encoder = 0; + MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create JPEG encoder component"); + goto error; + } + + if (!encoder->input_num || !encoder->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("JPEG encoder doesn't have input/output ports"); + goto error; + } + + encoder_input = encoder->input[0]; + encoder_output = encoder->output[0]; + + // We want same format on input and output + mmal_format_copy(encoder_output->format, encoder_input->format); + + // Specify out output format + encoder_output->format->encoding = state->encoding; + + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + + if (encoder_output->buffer_size < encoder_output->buffer_size_min) + encoder_output->buffer_size = encoder_output->buffer_size_min; + + encoder_output->buffer_num = encoder_output->buffer_num_recommended; + + if (encoder_output->buffer_num < encoder_output->buffer_num_min) + encoder_output->buffer_num = encoder_output->buffer_num_min; + + // Commit the port changes to the output port + status = mmal_port_format_commit(encoder_output); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on video encoder output port"); + goto error; + } + + // Set the JPEG quality level + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, state->quality); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set JPEG quality"); + goto error; + } + + // Set up any required thumbnail + { + MMAL_PARAMETER_THUMBNAIL_CONFIG_T param_thumb = {{MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, sizeof(MMAL_PARAMETER_THUMBNAIL_CONFIG_T)}, 0, 0, 0, 0}; + + if ( state->thumbnailConfig.width > 0 && state->thumbnailConfig.height > 0 ) + { + // Have a valid thumbnail defined + param_thumb.enable = 1; + param_thumb.width = state->thumbnailConfig.width; + param_thumb.height = state->thumbnailConfig.height; + param_thumb.quality = state->thumbnailConfig.quality; + } + status = mmal_port_parameter_set(encoder->control, ¶m_thumb.hdr); + } + + // Enable component + status = mmal_component_enable(encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable video encoder component"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); + } + + state->encoder_pool = pool; + state->encoder_component = encoder; + + if (state->verbose) + fprintf(stderr, "Encoder component done\n"); + + return status; + + error: + + if (encoder) + mmal_component_destroy(encoder); + + return status; +} + +/** + * Destroy the encoder component + * + * @param state Pointer to state control struct + * + */ +static void destroy_encoder_component(RASPISTILL_STATE *state) +{ + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + } + + if (state->encoder_component) + { + mmal_component_destroy(state->encoder_component); + state->encoder_component = NULL; + } +} + + +/** + * Add an exif tag to the capture + * + * @param state Pointer to state control struct + * @param exif_tag String containing a "key=value" pair. + * @return Returns a MMAL_STATUS_T giving result of operation + */ +static MMAL_STATUS_T add_exif_tag(RASPISTILL_STATE *state, const char *exif_tag) +{ + MMAL_STATUS_T status; + MMAL_PARAMETER_EXIF_T *exif_param = (MMAL_PARAMETER_EXIF_T*)calloc(sizeof(MMAL_PARAMETER_EXIF_T) + MAX_EXIF_PAYLOAD_LENGTH, 1); + + vcos_assert(state); + vcos_assert(state->encoder_component); + + // Check to see if the tag is present or is indeed a key=value pair. + if (!exif_tag || strchr(exif_tag, '=') == NULL || strlen(exif_tag) > MAX_EXIF_PAYLOAD_LENGTH-1) + return MMAL_EINVAL; + + exif_param->hdr.id = MMAL_PARAMETER_EXIF; + + strncpy((char*)exif_param->data, exif_tag, MAX_EXIF_PAYLOAD_LENGTH-1); + + exif_param->hdr.size = sizeof(MMAL_PARAMETER_EXIF_T) + strlen((char*)exif_param->data); + + status = mmal_port_parameter_set(state->encoder_component->output[0], &exif_param->hdr); + + free(exif_param); + + return status; +} + +/** + * Add a basic set of EXIF tags to the capture + * Make, Time etc + * + * @param state Pointer to state control struct + * + */ +static void add_exif_tags(RASPISTILL_STATE *state) +{ + time_t rawtime; + struct tm *timeinfo; + char time_buf[32]; + char exif_buf[128]; + int i; + + add_exif_tag(state, "IFD0.Model=RP_OV5647"); + add_exif_tag(state, "IFD0.Make=RaspberryPi"); + + time(&rawtime); + timeinfo = localtime(&rawtime); + + snprintf(time_buf, sizeof(time_buf), + "%04d:%02d:%02d %02d:%02d:%02d", + timeinfo->tm_year+1900, + timeinfo->tm_mon+1, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec); + + snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeDigitized=%s", time_buf); + add_exif_tag(state, exif_buf); + + snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeOriginal=%s", time_buf); + add_exif_tag(state, exif_buf); + + snprintf(exif_buf, sizeof(exif_buf), "IFD0.DateTime=%s", time_buf); + add_exif_tag(state, exif_buf); + + // Now send any user supplied tags + + for (i=0;inumExifTags && i < MAX_USER_EXIF_TAGS;i++) + { + if (state->exifTags[i]) + { + add_exif_tag(state, state->exifTags[i]); + } + } +} + +/** + * Stores an EXIF tag in the state, incrementing various pointers as necessary. + * Any tags stored in this way will be added to the image file when add_exif_tags + * is called + * + * Will not store if run out of storage space + * + * @param state Pointer to state control struct + * @param exif_tag EXIF tag string + * + */ +static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag) +{ + if (state->numExifTags < MAX_USER_EXIF_TAGS) + { + state->exifTags[state->numExifTags] = exif_tag; + state->numExifTags++; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + + +/** + * Allocates and generates a filename based on the + * user-supplied pattern and the frame number. + * On successful return, finalName and tempName point to malloc()ed strings + * which must be freed externally. (On failure, returns nulls that + * don't need free()ing.) + * + * @param finalName pointer receives an + * @param pattern sprintf pattern with %d to be replaced by frame + * @param frame for timelapse, the frame number + * @return Returns a MMAL_STATUS_T giving result of operation +*/ + +MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame) +{ + *finalName = NULL; + *tempName = NULL; + if (0 > asprintf(finalName, pattern, frame) || + 0 > asprintf(tempName, "%s~", *finalName)) + { + if (*finalName != NULL) + { + free(*finalName); + } + return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right + } + return MMAL_SUCCESS; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + // Going to abort on all signals + vcos_log_error("Aborting program\n"); + + // Need to close any open stuff... + + exit(130); +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPISTILL_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + MMAL_PORT_T *encoder_output_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stderr, "\%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. + // Camera and encoder are different in stills/video, but preview + // is the same so handed off to a separate module + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + encoder_input_port = state.encoder_component->input[0]; + encoder_output_port = state.encoder_component->output[0]; + + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + + if (status == MMAL_SUCCESS) + { + VCOS_STATUS_T vcos_status; + + if (state.verbose) + fprintf(stderr, "Connecting camera stills port to encoder input port\n"); + + // Now connect the camera to the encoder + status = connect_ports(camera_still_port, encoder_input_port, &state.encoder_connection); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } + + // Set up our userdata - this is passed though to the callback where we need the information. + // Null until we open our filename + callback_data.file_handle = NULL; + callback_data.pstate = &state; + vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0); + + vcos_assert(vcos_status == VCOS_SUCCESS); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + for (i=0;iuserdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling encoder output port\n"); + + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + + // Send all the buffers to the encoder output port + num = mmal_queue_length(state.encoder_pool->queue); + + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + } + + if (state.verbose) + fprintf(stderr, "Starting capture %d\n", frame); + + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to start capture", __func__); + } + else + { + // Wait for capture to complete + // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error + // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic + vcos_semaphore_wait(&callback_data.complete_semaphore); + if (state.verbose) + fprintf(stderr, "Finished capture %d\n", frame); + } + + // Ensure we don't die if get callback with no open file + callback_data.file_handle = NULL; + + if (output_file != stdout) + { + fclose(output_file); + vcos_assert(use_filename != NULL && final_filename != NULL); + if (0 != rename(use_filename, final_filename)) + { + vcos_log_error("Could not rename temp file to: %s; %s", + final_filename,strerror(errno)); + } + if (state.linkname) + { + char *use_link; + char *final_link; + status = create_filenames(&final_link, &use_link, state.linkname, frame); + + // Create hard link if possible, symlink otherwise + if (status != MMAL_SUCCESS + || (0 != link(final_filename, use_link) + && 0 != symlink(final_filename, use_link)) + || 0 != rename(use_link, final_link)) + { + vcos_log_error("Could not link as filename: %s; %s", + state.linkname,strerror(errno)); + } + if (use_link) free(use_link); + if (final_link) free(final_link); + } + } + // Disable encoder output port + status = mmal_port_disable(encoder_output_port); + } + + if (use_filename) + { + free(use_filename); + use_filename = NULL; + } + if (final_filename) + { + free(final_filename); + final_filename = NULL; + } + } // end for (frame) + + vcos_semaphore_delete(&callback_data.complete_semaphore); + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_video_port); + check_disable_port(encoder_output_port); + + mmal_connection_destroy(state.preview_connection); + + mmal_connection_destroy(state.encoder_connection); + + /* Disable components */ + if (state.encoder_component) + mmal_component_disable(state.encoder_component); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + destroy_encoder_component(&state); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + diff --git a/sys/rpicamsrc/RaspiStillYUV.c b/sys/rpicamsrc/RaspiStillYUV.c new file mode 100644 index 0000000000..aad9edb7b2 --- /dev/null +++ b/sys/rpicamsrc/RaspiStillYUV.c @@ -0,0 +1,957 @@ +/* + * Copyright (c) 2013 Jan Schmidt +Portions: +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file RaspiStillYUV.c + * Command line program to capture a still frame and dump uncompressed it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * 2 components are created; camera and preview. + * Camera component has three ports, preview, video and stills. + * Preview is connected using standard mmal connections, the stills output + * is written straight to the file in YUV 420 format via the requisite buffer + * callback. video port is not used + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the generic preview + */ + +// We use some GNU extensions (basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.2" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +/// Camera number to use - we only have one camera, indexed from 0. +#define CAMERA_NUMBER 0 + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + + +// Stills format information +#define STILLS_FRAME_RATE_NUM 3 +#define STILLS_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +int mmal_status_to_int(MMAL_STATUS_T status); + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + char *filename; /// filename of output file + int verbose; /// !0 if want detailed run information + int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse + int useRGB; /// Output RGB data rather than YUV + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *null_sink_component; /// Pointer to the camera component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera stills port +} RASPISTILLYUV_STATE; + + +/** Struct used to pass information in camera still port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) + RASPISTILLYUV_STATE *pstate; /// pointer to our state in case required in callback +} PORT_USERDATA; + +static void display_valid_parameters(char *app_name); + +/// Comamnd ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandOutput 3 +#define CommandVerbose 4 +#define CommandTimeout 5 +#define CommandTimelapse 6 +#define CommandUseRGB 7 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width ", 1 }, + { CommandHeight, "-height", "h", "Set image height ", 1 }, + { CommandOutput, "-output", "o", "Output filename . If not specifed, no image is saved", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down. If not specified set to 5s", 1 }, + { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every ms", 1}, + { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPISTILLYUV_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPISTILLYUV_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 2592; + state->height = 1944; + state->timelapse = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); +} + +/** + * Dump image state parameters to stderr. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPISTILLYUV_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); + fprintf(stderr, "Time delay %d, Timelapse %d\n", state->timeout, state->timelapse); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; // set 0 if we have a bad parameter + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 1); + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len); + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // TODO : What limits do we need for timeout? + i++; + } + else + valid = 0; + break; + } + + case CommandTimelapse: + if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1) + valid = 0; + else + i++; + break; + + case CommandUseRGB: // display lots of data during run + state->useRGB = 1; + break; + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i]); + return 1; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + * + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stderr, "Runs camera for specific time, and take uncompressed YUV capture at end if requested\n\n"); + fprintf(stderr, "usage: %s [options]\n\n", app_name); + + fprintf(stderr, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + fprintf(stderr, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + fprintf(stderr, "Camera control callback cmd=0x%08x", buffer->cmd); + + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for camera output port + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + int complete = 0; + // We pass our file handle and other stuff in via the userdata field. + + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + + if (buffer->length) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + // We need to check we wrote what we wanted - it's possible we have run out of storage. + if (bytes_written != buffer->length) + { + vcos_log_error("Unable to write buffer to file - aborting"); + complete = 1; + } + + // Check end of frame or error + if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) + complete = 1; + } + else + { + vcos_log_error("Received a camera still buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue); + + // and back to the port from there. + if (new_buffer) + { + status = mmal_port_send_buffer(port, new_buffer); + } + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return the buffer to the camera still port"); + } + + if (complete) + { + vcos_semaphore_post(&(pData->complete_semaphore)); + } +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return 0 if failed, pointer to component if successful + * + */ +static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + if (!camera->output_num) + { + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 1, + .max_preview_video_w = state->preview_parameters.previewWindow.width, + .max_preview_video_h = state->preview_parameters.previewWindow.height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + // Now set up the port formats + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->es->video.width = state->preview_parameters.previewWindow.width; + format->es->video.height = state->preview_parameters.previewWindow.height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->preview_parameters.previewWindow.width; + format->es->video.crop.height = state->preview_parameters.previewWindow.height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + + status = mmal_port_format_commit(preview_port); + + if (status) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the same format on the video port (which we dont use here) + mmal_format_full_copy(video_port->format, format); + status = mmal_port_format_commit(video_port); + + if (status) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + format = still_port->format; + + // Set our stills format on the stills port + if (state->useRGB) + { + format->encoding = MMAL_ENCODING_BGR24; + format->encoding_variant = MMAL_ENCODING_BGR24; + } + else + { + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + } + format->es->video.width = state->width; + format->es->video.height = state->height; + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; + format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; + + if (still_port->buffer_size < still_port->buffer_size_min) + still_port->buffer_size = still_port->buffer_size_min; + + still_port->buffer_num = still_port->buffer_num_recommended; + + status = mmal_port_format_commit(still_port); + + if (status) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(still_port, still_port->buffer_num, still_port->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name); + } + + state->camera_pool = pool; + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPISTILLYUV_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + // Going to abort on all signals + vcos_log_error("Aborting program\n"); + + // Need to close any open stuff... + + exit(255); +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPISTILLYUV_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + FILE *output_file = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + default_status(&state); + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have two components. Camera and Preview + // Camera is different in stills/video, but preview + // is the same so handed off to a separate module + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + + if (status == MMAL_SUCCESS) + { + VCOS_STATUS_T vcos_status; + + if (state.filename) + { + if (state.verbose) + fprintf(stderr, "Opening output file %s\n", state.filename); + + output_file = fopen(state.filename, "wb"); + if (!output_file) + { + // Notify user, carry on but discarding output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); + } + } + + // Set up our userdata - this is passed though to the callback where we need the information. + callback_data.file_handle = output_file; + callback_data.pstate = &state; + + vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0); + vcos_assert(vcos_status == VCOS_SUCCESS); + + camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling camera still output port\n"); + + // Enable the camera still output port and tell it its callback function + status = mmal_port_enable(camera_still_port, camera_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup camera output"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Starting video preview\n"); + + int num_iterations = state.timelapse ? state.timeout / state.timelapse : 1; + int frame; + FILE *output_file = NULL; + + for (frame = 1;frame<=num_iterations; frame++) + { + if (state.timelapse) + vcos_sleep(state.timelapse); + else + vcos_sleep(state.timeout); + + // Open the file + if (state.filename) + { + if (state.filename[0] == '-') + { + output_file = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + char *use_filename = state.filename; + + if (state.timelapse) + asprintf(&use_filename, state.filename, frame); + + if (state.verbose) + fprintf(stderr, "Opening output file %s\n", use_filename); + + output_file = fopen(use_filename, "wb"); + + if (!output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename); + } + + // asprintf used in timelapse mode allocates its own memory which we need to free + if (state.timelapse) + free(use_filename); + } + + callback_data.file_handle = output_file; + } + + // And only do the capture if we have specified a filename and its opened OK + if (output_file) + { + // Send all the buffers to the camera output port + { + int num = mmal_queue_length(state.camera_pool->queue); + int q; + + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(camera_still_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to camera output port (%d)", q); + } + } + + if (state.verbose) + fprintf(stderr, "Starting capture %d\n", frame); + + // Fire the capture + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to start capture", __func__); + } + else + { + // Wait for capture to complete + // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error + // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic + vcos_semaphore_wait(&callback_data.complete_semaphore); + + if (state.verbose) + fprintf(stderr, "Finished capture %d\n", frame); + } + + // Ensure we don't die if get callback with no open file + callback_data.file_handle = NULL; + + if (output_file != stdout) + fclose(output_file); + } + } + vcos_semaphore_delete(&callback_data.complete_semaphore); + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + if (output_file) + fclose(output_file); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_video_port); + + mmal_connection_destroy(state.preview_connection); + + /* Disable components */ + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + + + diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 391d678366..94ee7a77b5 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -62,6 +62,7 @@ #include #include "gstrpicamsrc.h" +#include "RaspiCapture.h" #include "bcm_host.h" #include "interface/vcos/vcos.h" @@ -95,7 +96,6 @@ enum "width = " GST_VIDEO_SIZE_RANGE "," \ "height = " GST_VIDEO_SIZE_RANGE "," \ "framerate = " GST_VIDEO_FPS_RANGE - #define H264_CAPS \ "video/x-h264, " \ "width = " GST_VIDEO_SIZE_RANGE ", " \ @@ -106,49 +106,34 @@ enum "profile = (string) { baseline, main, high }" static GstStaticPadTemplate video_src_template = - GST_STATIC_PAD_TEMPLATE ("vidsrc", + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS) ); -static GstStaticPadTemplate viewfind_src_template = - GST_STATIC_PAD_TEMPLATE ("vfsrc", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS) - ); -static GstStaticPadTemplate image_src_template = - GST_STATIC_PAD_TEMPLATE ("imgsrc", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (RAW_AND_JPEG_CAPS) - ); #define gst_rpi_cam_src_parent_class parent_class -G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_BASE_CAMERA_SRC); +G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_PUSH_SRC); static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); - -static gboolean -gst_rpi_cam_src_setup_pipeline (GstBaseCameraSrc *parent) -{ - GstRpiCamSrc *self = GST_RPICAMSRC(parent); - g_print ("In setup_pipeline\n"); -} +static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent); +static GstFlowReturn gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf); static void gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; - GstBaseCameraSrcClass *basecamsrc_class; + GstBaseSrcClass *basesrc_class; + GstPushSrcClass *pushsrc_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; - basecamsrc_class = (GstBaseCameraSrcClass *) klass; + basesrc_class = (GstBaseSrcClass *) klass; + pushsrc_class = (GstPushSrcClass *) klass; gobject_class->set_property = gst_rpi_cam_src_set_property; gobject_class->get_property = gst_rpi_cam_src_get_property; @@ -159,19 +144,18 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "Raspberry Pi camera module source", "Jan Schmidt "); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&viewfind_src_template)); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&video_src_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&image_src_template)); - basecamsrc_class->setup_pipeline = gst_rpi_cam_src_setup_pipeline; + basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start); + pushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fill_buffer); } static void gst_rpi_cam_src_init (GstRpiCamSrc *src) { + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); + gst_base_src_set_live (GST_BASE_SRC (src), TRUE); } static void @@ -210,6 +194,21 @@ rpicamsrc_init (GstPlugin * rpicamsrc) GST_TYPE_RPICAMSRC); } +static gboolean +gst_rpi_cam_src_start (GstBaseSrc *parent) +{ + GstRpiCamSrc *src = GST_RPICAMSRC(parent); + g_print ("In start()\n"); + raspi_capture_start(); + return TRUE; +} + +static GstFlowReturn +gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf) +{ + return GST_FLOW_ERROR; +} + #ifndef PACKAGE #define PACKAGE "gstrpicamsrc" #endif diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index 6917bb1293..b8eb2e6a24 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -45,7 +45,7 @@ #define __GST_RPICAMSRC_H__ #include -#include +#include G_BEGIN_DECLS @@ -64,16 +64,14 @@ typedef struct _GstRpiCamSrcClass GstRpiCamSrcClass; struct _GstRpiCamSrc { - GstBaseCameraSrc parent; + GstPushSrc parent; - GstPad *viewfind_srcpad; GstPad *video_srcpad; - GstPad *image_srcpad; }; struct _GstRpiCamSrcClass { - GstBaseCameraSrcClass parent_class; + GstPushSrcClass parent_class; }; GType gst_rpi_cam_src_get_type (void); From 7ffb618b20563be43c868230abd926bf21a70b86 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 12 Oct 2013 12:42:07 +1100 Subject: [PATCH 03/77] rpicamsrc: Checkpoint. Version which writes directly to test.out Switch to plain basesrc for parent class --- sys/rpicamsrc/RaspiCamControl.c | 4 + sys/rpicamsrc/RaspiCapture.c | 580 ++++++++++++++++---------------- sys/rpicamsrc/RaspiCapture.h | 12 +- sys/rpicamsrc/RaspiStill.c | 2 +- sys/rpicamsrc/gstrpicamsrc.c | 44 ++- sys/rpicamsrc/gstrpicamsrc.h | 4 + 6 files changed, 333 insertions(+), 313 deletions(-) diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c index f379015d4b..80ad6fa843 100644 --- a/sys/rpicamsrc/RaspiCamControl.c +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -187,6 +187,7 @@ static int update_cycle_parameter(int *option, int min, int max, int increment) else return 1; } +#endif /** @@ -203,6 +204,9 @@ static int update_cycle_parameter(int *option, int min, int max, int increment) int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera) { + return 1; +} +#if 0 static int parameter = 0; static int parameter_option = parameter_reset; // which value the parameter currently has diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index d6f9b12f1a..348a5c8355 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -57,6 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include + #include "bcm_host.h" #include "interface/vcos/vcos.h" @@ -68,6 +69,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "interface/mmal/util/mmal_default_components.h" #include "interface/mmal/util/mmal_connection.h" +#include "RaspiCapture.h" #include "RaspiCamControl.h" #include "RaspiPreview.h" @@ -97,42 +99,32 @@ const int ABORT_INTERVAL = 100; // ms int mmal_status_to_int(MMAL_STATUS_T status); -/** Structure containing all state information for the current run +/** Struct used to pass information in encoder port userdata to callback */ typedef struct { - int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds - int width; /// Requested width of image - int height; /// requested height of image - int bitrate; /// Requested bitrate - int framerate; /// Requested frame rate (fps) - int intraperiod; /// Intra-refresh period (key frame rate) - char *filename; /// filename of output file - int verbose; /// !0 if want detailed run information - int demoMode; /// Run app in demo mode - int demoInterval; /// Interval between camera settings changes - int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either - /// the camera output or the encoder output (with compression artifacts) - int profile; /// H264 profile to use for encoding - RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters - RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + RASPIVID_STATE *state; /// pointer to our state in case required in callback + int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture +} PORT_USERDATA; + +struct RASPIVID_STATE_T +{ + RASPIVID_CONFIG config; + + FILE *output_file; MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder - MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port -} RASPIVID_STATE; + MMAL_PORT_T *camera_still_port; + MMAL_PORT_T *encoder_output_port; -/** Struct used to pass information in encoder port userdata to callback - */ -typedef struct -{ - FILE *file_handle; /// File handle to write buffer data to. - RASPIVID_STATE *pstate; /// pointer to our state in case required in callback - int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture -} PORT_USERDATA; + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + + PORT_USERDATA callback_data; +}; #if 0 /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent @@ -189,36 +181,27 @@ static void dump_state(RASPIVID_STATE *state); * * @param state Pointer to state structure to assign defaults to */ -void raspi_capture_default_state(RASPIVID_STATE *state) +void raspicapture_default_config(RASPIVID_CONFIG *config) { - if (!state) - { - vcos_assert(0); - return; - } - - // Default everything to zero - memset(state, 0, sizeof(RASPIVID_STATE)); // Now set anything non-zero - state->timeout = 5000; // 5s delay before take image - state->width = 1920; // Default to 1080p - state->height = 1080; - state->bitrate = 17000000; // This is a decent default bitrate for 1080p - state->framerate = VIDEO_FRAME_RATE_NUM; - state->intraperiod = 0; // Not set - state->demoMode = 0; - state->demoInterval = 250; // ms - state->immutableInput = 1; - state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + config->timeout = 5000; // 5s delay before take image + config->width = 1920; // Default to 1080p + config->height = 1080; + config->bitrate = 17000000; // This is a decent default bitrate for 1080p + config->framerate = VIDEO_FRAME_RATE_NUM; + config->intraperiod = 0; // Not set + config->demoMode = 0; + config->demoInterval = 250; // ms + config->immutableInput = 1; + config->profile = MMAL_VIDEO_PROFILE_H264_HIGH; // Setup preview window defaults - raspipreview_set_defaults(&state->preview_parameters); + raspipreview_set_defaults(&config->preview_parameters); // Set up the camera_parameters to default - raspicamcontrol_set_defaults(&state->camera_parameters); + raspicamcontrol_set_defaults(&config->camera_parameters); - dump_state(state); } /** @@ -234,12 +217,12 @@ static void dump_state(RASPIVID_STATE *state) return; } - fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); - fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->bitrate, state->framerate, state->timeout); - //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->profile, profile_map, profile_map_size)); + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->config.width, state->config.height, state->config.filename); + fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->config.bitrate, state->config.framerate, state->config.timeout); + //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->config.profile, profile_map, profile_map_size)); - raspipreview_dump_parameters(&state->preview_parameters); - raspicamcontrol_dump_parameters(&state->camera_parameters); + raspipreview_dump_parameters(&state->config.preview_parameters); + //raspicamcontrol_dump_parameters(&state->config.camera_parameters); } #if 0 @@ -521,18 +504,19 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf // We pass our file handle and other stuff in via the userdata field. PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + RASPIVID_STATE *state = pData->state; if (pData) { int bytes_written = buffer->length; - vcos_assert(pData->file_handle); + vcos_assert(state->output_file); if (buffer->length) { mmal_buffer_header_mem_lock(buffer); - bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + bytes_written = fwrite(buffer->data, 1, buffer->length, state->output_file); mmal_buffer_header_mem_unlock(buffer); } @@ -554,9 +538,9 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf // and send one back to the port (if still open) if (port->is_enabled) { - MMAL_STATUS_T status; + MMAL_STATUS_T status = MMAL_SUCCESS; - new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + new_buffer = mmal_queue_get(state->encoder_pool->queue); if (new_buffer) status = mmal_port_send_buffer(port, new_buffer); @@ -616,12 +600,12 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = { { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, - .max_stills_w = state->width, - .max_stills_h = state->height, + .max_stills_w = state->config.width, + .max_stills_h = state->config.height, .stills_yuv422 = 0, .one_shot_stills = 0, - .max_preview_video_w = state->width, - .max_preview_video_h = state->height, + .max_preview_video_w = state->config.width, + .max_preview_video_h = state->config.height, .num_preview_video_frames = 3, .stills_capture_circular_buffer_height = 0, .fast_preview_resume = 0, @@ -641,13 +625,13 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) format->encoding_variant = MMAL_ENCODING_I420; format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->width; - format->es->video.height = state->height; + format->es->video.width = state->config.width; + format->es->video.height = state->config.height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->width; - format->es->video.crop.height = state->height; - format->es->video.frame_rate.num = state->framerate; + format->es->video.crop.width = state->config.width; + format->es->video.crop.height = state->config.height; + format->es->video.frame_rate.num = state->config.framerate; format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; status = mmal_port_format_commit(preview_port); @@ -664,13 +648,13 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) format->encoding_variant = MMAL_ENCODING_I420; format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->width; - format->es->video.height = state->height; + format->es->video.width = state->config.width; + format->es->video.height = state->config.height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->width; - format->es->video.crop.height = state->height; - format->es->video.frame_rate.num = state->framerate; + format->es->video.crop.width = state->config.width; + format->es->video.crop.height = state->config.height; + format->es->video.frame_rate.num = state->config.framerate; format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; status = mmal_port_format_commit(video_port); @@ -693,12 +677,12 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; - format->es->video.width = state->width; - format->es->video.height = state->height; + format->es->video.width = state->config.width; + format->es->video.height = state->config.height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->width; - format->es->video.crop.height = state->height; + format->es->video.crop.width = state->config.width; + format->es->video.crop.height = state->config.height; format->es->video.frame_rate.num = 1; format->es->video.frame_rate.den = 1; @@ -723,11 +707,11 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) goto error; } - raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + raspicamcontrol_set_all_parameters(camera, &state->config.camera_parameters); state->camera_component = camera; - if (state->verbose) + if (state->config.verbose) fprintf(stderr, "Camera component done\n"); return status; @@ -794,7 +778,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) // Only supporting H264 at the moment encoder_output->format->encoding = MMAL_ENCODING_H264; - encoder_output->format->bitrate = state->bitrate; + encoder_output->format->bitrate = state->config.bitrate; encoder_output->buffer_size = encoder_output->buffer_size_recommended; @@ -829,9 +813,9 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (state->intraperiod) + if (state->config.intraperiod) { - MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod}; + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config.intraperiod}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); if (status != MMAL_SUCCESS) { @@ -846,7 +830,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) param.hdr.id = MMAL_PARAMETER_PROFILE; param.hdr.size = sizeof(param); - param.profile[0].profile = state->profile; + param.profile[0].profile = state->config.profile; param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -858,7 +842,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS) + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->config.immutableInput) != MMAL_SUCCESS) { vcos_log_error("Unable to set immutable input flag"); // Continue rather than abort.. @@ -884,7 +868,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) state->encoder_pool = pool; state->encoder_component = encoder; - if (state->verbose) + if (state->config.verbose) fprintf(stderr, "Encoder component done\n"); return status; @@ -954,258 +938,262 @@ static void check_disable_port(MMAL_PORT_T *port) mmal_port_disable(port); } - -int raspi_capture_start() +void raspicapture_init() { - // Our main data storage vessel.. - RASPIVID_STATE state; - int exit_code = EX_OK; - - MMAL_STATUS_T status = MMAL_SUCCESS; - MMAL_PORT_T *camera_preview_port = NULL; - MMAL_PORT_T *camera_video_port = NULL; - MMAL_PORT_T *camera_still_port = NULL; - MMAL_PORT_T *preview_input_port = NULL; - MMAL_PORT_T *encoder_input_port = NULL; - MMAL_PORT_T *encoder_output_port = NULL; - FILE *output_file = NULL; - bcm_host_init(); // Register our application with the logging system vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY); +} - raspi_capture_default_state(&state); +RASPIVID_STATE * +raspi_capture_start(RASPIVID_CONFIG *config) +{ + // Our main data storage vessel.. + RASPIVID_STATE *state; + //int exit_code = EX_OK; - if (state.verbose) - { - dump_state(&state); - } + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; - // OK, we have a nice set of parameters. Now set up our components - // We have three components. Camera, Preview and encoder. + /* Default everything to zero */ + state = calloc(1, sizeof(RASPIVID_STATE)); - if ((status = create_camera_component(&state)) != MMAL_SUCCESS) - { - vcos_log_error("%s: Failed to create camera component", __func__); - exit_code = EX_SOFTWARE; - } - else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) - { - vcos_log_error("%s: Failed to create preview component", __func__); - destroy_camera_component(&state); - exit_code = EX_SOFTWARE; - } - else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) - { - vcos_log_error("%s: Failed to create encode component", __func__); - raspipreview_destroy(&state.preview_parameters); - destroy_camera_component(&state); - exit_code = EX_SOFTWARE; - } - else - { - PORT_USERDATA callback_data; + /* Apply passed in config */ + state->config = *config; - if (state.verbose) - fprintf(stderr, "Starting component connection stage\n"); + if (state->config.verbose) + { + dump_state(state); + } - camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; - camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; - camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; - preview_input_port = state.preview_parameters.preview_component->input[0]; - encoder_input_port = state.encoder_component->input[0]; - encoder_output_port = state.encoder_component->output[0]; + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. - if (state.preview_parameters.wantPreview ) - { - if (state.verbose) - { - fprintf(stderr, "Connecting camera preview port to preview input port\n"); - fprintf(stderr, "Starting video preview\n"); - } + if ((status = create_camera_component(state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + return NULL; + } - // Connect camera to preview - status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); - } - else - { - status = MMAL_SUCCESS; - } + if ((status = raspipreview_create(&state->config.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(state); + return NULL; + } - if (status == MMAL_SUCCESS) - { - if (state.verbose) - fprintf(stderr, "Connecting camera stills port to encoder input port\n"); + if ((status = create_encoder_component(state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state->config.preview_parameters); + destroy_camera_component(state); + return NULL; + } - // Now connect the camera to the encoder - status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection); + if (state->config.verbose) + fprintf(stderr, "Starting component connection stage\n"); - if (status != MMAL_SUCCESS) - { - vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); - goto error; - } + camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + preview_input_port = state->config.preview_parameters.preview_component->input[0]; + encoder_input_port = state->encoder_component->input[0]; + state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + state->encoder_output_port = state->encoder_component->output[0]; - if (state.filename) - { - if (state.filename[0] == '-') - { - output_file = stdout; + if (state->config.preview_parameters.wantPreview ) + { + if (state->config.verbose) + { + fprintf(stderr, "Connecting camera preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } - // Ensure we don't upset the output stream with diagnostics/info - state.verbose = 0; - } - else - { - if (state.verbose) - fprintf(stderr, "Opening output file \"%s\"\n", state.filename); + // Connect camera to preview + status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection); + if (status != MMAL_SUCCESS) + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + goto error; + } + } - output_file = fopen(state.filename, "wb"); - } + if (state->config.verbose) + fprintf(stderr, "Connecting camera stills port to encoder input port\n"); - if (!output_file) - { - // Notify user, carry on but discarding encoded output buffers - vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); - } - } + // Now connect the camera to the encoder + status = connect_ports(camera_video_port, encoder_input_port, &state->encoder_connection); - // Set up our userdata - this is passed though to the callback where we need the information. - callback_data.file_handle = output_file; - callback_data.pstate = &state; - callback_data.abort = 0; + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } - encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + if (state->config.filename) + { + if (state->config.filename[0] == '-') + { + state->output_file = stdout; - if (state.verbose) - fprintf(stderr, "Enabling encoder output port\n"); + // Ensure we don't upset the output stream with diagnostics/info + state->config.verbose = 0; + } + else + { + if (state->config.verbose) + fprintf(stderr, "Opening output file \"%s\"\n", state->config.filename); - // Enable the encoder output port and tell it its callback function - status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + state->output_file = fopen(state->config.filename, "wb"); + } - if (status != MMAL_SUCCESS) - { - vcos_log_error("Failed to setup encoder output"); - goto error; - } + if (!state->output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state->config.filename); + } + } - if (state.demoMode) - { - // Run for the user specific time.. - int num_iterations = state.timeout / state.demoInterval; - int i; + // Set up our userdata - this is passed though to the callback where we need the information. + state->callback_data.state = state; + state->callback_data.abort = 0; - if (state.verbose) - fprintf(stderr, "Running in demo mode\n"); + state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data; - for (i=0;state.timeout == 0 || iconfig.verbose) + fprintf(stderr, "Enabling encoder output port\n"); - if (state.verbose) - fprintf(stderr, "Starting video capture\n"); + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback); - if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) - { - goto error; - } + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } - // Send all the buffers to the encoder output port - { - int num = mmal_queue_length(state.encoder_pool->queue); - int q; - for (q=0;qqueue); + if (state->config.demoMode) + { + // Run for the user specific time.. + int num_iterations = state->config.timeout / state->config.demoInterval; + int i; - if (!buffer) - vcos_log_error("Unable to get a required buffer %d from pool queue", q); + if (state->config.verbose) + fprintf(stderr, "Running in demo mode\n"); - if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) - vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + for (i=0;state->config.timeout == 0 || icamera_component); + vcos_sleep(state->config.demoInterval); + } + } + else + { + // Only encode stuff if we have a filename and it opened + if (state->output_file) + { + int wait; - } - } + if (state->config.verbose) + fprintf(stderr, "Starting video capture\n"); - // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example - // out of storage space) - // Going to check every ABORT_INTERVAL milliseconds + if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + goto error; + } - for (wait = 0; state.timeout == 0 || wait < state.timeout; wait+= ABORT_INTERVAL) - { - vcos_sleep(ABORT_INTERVAL); - if (callback_data.abort) - break; - } + // Send all the buffers to the encoder output port + { + int num = mmal_queue_length(state->encoder_pool->queue); + int q; + for (q=0;qencoder_pool->queue); - if (state.verbose) - fprintf(stderr, "Finished capture\n"); - } - else - { - if (state.timeout) - vcos_sleep(state.timeout); - else - for (;;) vcos_sleep(ABORT_INTERVAL); - } - } - } - else - { - mmal_status_to_int(status); - vcos_log_error("%s: Failed to connect camera to preview", __func__); - } + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + + } + } + + // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example + // out of storage space) + // Going to check every ABORT_INTERVAL milliseconds + + for (wait = 0; state->config.timeout == 0 || wait < state->config.timeout; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (state->callback_data.abort) + break; + } + + if (state->config.verbose) + fprintf(stderr, "Finished capture\n"); + } + else + { + if (state->config.timeout) + vcos_sleep(state->config.timeout); + else + for (;;) vcos_sleep(ABORT_INTERVAL); + } + } + + return state; error: + raspi_capture_stop(state); - mmal_status_to_int(status); + if (status != MMAL_SUCCESS) { + mmal_status_to_int(status); + raspicamcontrol_check_configuration(128); + } - if (state.verbose) - fprintf(stderr, "Closing down\n"); - - // Disable all our ports that are not handled by connections - check_disable_port(camera_still_port); - check_disable_port(encoder_output_port); - - if (state.preview_parameters.wantPreview ) - mmal_connection_destroy(state.preview_connection); - mmal_connection_destroy(state.encoder_connection); - - // Can now close our file. Note disabling ports may flush buffers which causes - // problems if we have already closed the file! - if (output_file && output_file != stdout) - fclose(output_file); - - /* Disable components */ - if (state.encoder_component) - mmal_component_disable(state.encoder_component); - - if (state.preview_parameters.preview_component) - mmal_component_disable(state.preview_parameters.preview_component); - - if (state.camera_component) - mmal_component_disable(state.camera_component); - - destroy_encoder_component(&state); - raspipreview_destroy(&state.preview_parameters); - destroy_camera_component(&state); - - if (state.verbose) - fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); - } - - if (status != MMAL_SUCCESS) - raspicamcontrol_check_configuration(128); - - return exit_code; + return NULL; +} + +void +raspi_capture_stop(RASPIVID_STATE *state) +{ + if (state->config.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(state->camera_still_port); + check_disable_port(state->encoder_output_port); + + if (state->config.preview_parameters.wantPreview ) + mmal_connection_destroy(state->preview_connection); + mmal_connection_destroy(state->encoder_connection); + + // Can now close our file. Note disabling ports may flush buffers which causes + // problems if we have already closed the file! + if (state->output_file && state->output_file != stdout) + fclose(state->output_file); + + /* Disable components */ + if (state->encoder_component) + mmal_component_disable(state->encoder_component); + + if (state->config.preview_parameters.preview_component) + mmal_component_disable(state->config.preview_parameters.preview_component); + + if (state->camera_component) + mmal_component_disable(state->camera_component); + + destroy_encoder_component(state); + raspipreview_destroy(&state->config.preview_parameters); + destroy_camera_component(state); + + if (state->config.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + + free(state); } diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index f24ab4b834..554b898f65 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -60,6 +60,9 @@ G_BEGIN_DECLS */ typedef struct { + char *filename; + int verbose; /// !0 if want detailed run information + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds int width; /// Requested width of image int height; /// requested height of image @@ -73,9 +76,14 @@ typedef struct int profile; /// H264 profile to use for encoding RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters -} RASPIVID_STATE; +} RASPIVID_CONFIG; -int raspi_capture_start(); +typedef struct RASPIVID_STATE_T RASPIVID_STATE; + +void raspicapture_init(); +void raspicapture_default_config(RASPIVID_CONFIG *config); +RASPIVID_STATE *raspi_capture_start(RASPIVID_CONFIG *config); +void raspi_capture_stop(RASPIVID_STATE *state); G_END_DECLS diff --git a/sys/rpicamsrc/RaspiStill.c b/sys/rpicamsrc/RaspiStill.c index 62d0b25b39..32747b8667 100644 --- a/sys/rpicamsrc/RaspiStill.c +++ b/sys/rpicamsrc/RaspiStill.c @@ -290,7 +290,7 @@ static void dump_status(RASPISTILL_STATE *state) } raspipreview_dump_parameters(&state->preview_parameters); - raspicamcontrol_dump_parameters(&state->camera_parameters); + //raspicamcontrol_dump_parameters(&state->camera_parameters); } /** diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 94ee7a77b5..4ac7ebd7d6 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -109,7 +109,7 @@ static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS) + GST_STATIC_CAPS (/*RAW_AND_JPEG_CAPS "; "*/ H264_CAPS) ); #define gst_rpi_cam_src_parent_class parent_class @@ -120,6 +120,7 @@ static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent); +static gboolean gst_rpi_cam_src_stop (GstBaseSrc *parent); static GstFlowReturn gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf); static void @@ -148,7 +149,10 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gst_static_pad_template_get (&video_src_template)); basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start); + basesrc_class->stop = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_stop); pushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fill_buffer); + + raspicapture_init(); } static void @@ -156,6 +160,11 @@ gst_rpi_cam_src_init (GstRpiCamSrc *src) { gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (src), TRUE); + raspicapture_default_config(&src->capture_config); + + src->capture_config.verbose = 1; + src->capture_config.filename = "test.out"; + } static void @@ -184,22 +193,19 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, } } -static gboolean -rpicamsrc_init (GstPlugin * rpicamsrc) -{ - GST_DEBUG_CATEGORY_INIT (gst_rpi_cam_src_debug, "rpicamsrc", - 0, "rpicamsrc debug"); - - return gst_element_register (rpicamsrc, "rpicamsrc", GST_RANK_NONE, - GST_TYPE_RPICAMSRC); -} - static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent) { GstRpiCamSrc *src = GST_RPICAMSRC(parent); - g_print ("In start()\n"); - raspi_capture_start(); + src->capture_state = raspi_capture_start(&src->capture_config); + return TRUE; +} + +static gboolean +gst_rpi_cam_src_stop (GstBaseSrc *parent) +{ + GstRpiCamSrc *src = GST_RPICAMSRC(parent); + raspi_capture_stop(src->capture_state); return TRUE; } @@ -209,6 +215,16 @@ gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf) return GST_FLOW_ERROR; } +static gboolean +plugin_init (GstPlugin * rpicamsrc) +{ + GST_DEBUG_CATEGORY_INIT (gst_rpi_cam_src_debug, "rpicamsrc", + 0, "rpicamsrc debug"); + + return gst_element_register (rpicamsrc, "rpicamsrc", GST_RANK_NONE, + GST_TYPE_RPICAMSRC); +} + #ifndef PACKAGE #define PACKAGE "gstrpicamsrc" #endif @@ -218,7 +234,7 @@ GST_PLUGIN_DEFINE ( GST_VERSION_MINOR, rpicamsrc, "Raspberry Pi Camera Source", - rpicamsrc_init, + plugin_init, VERSION, "LGPL", "GStreamer", diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index b8eb2e6a24..e321df40b8 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -46,6 +46,7 @@ #include #include +#include "RaspiCapture.h" G_BEGIN_DECLS @@ -67,6 +68,9 @@ struct _GstRpiCamSrc GstPushSrc parent; GstPad *video_srcpad; + + RASPIVID_CONFIG capture_config; + RASPIVID_STATE *capture_state; }; struct _GstRpiCamSrcClass From 1416631df9b9f5ddb38a09c49f1ec8304da2039f Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 12 Oct 2013 19:23:03 +1100 Subject: [PATCH 04/77] rpicamsrc: First version which generates buffers on the src pad Fixed to 1920x1080 h264 regardless of caps. --- sys/rpicamsrc/RaspiCapture.c | 108 +++++++++++++++++++++-------------- sys/rpicamsrc/RaspiCapture.h | 1 + sys/rpicamsrc/gstrpicamsrc.c | 69 ++++++++++++++++++++-- 3 files changed, 129 insertions(+), 49 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 348a5c8355..e9db8dcf5c 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -57,6 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include "bcm_host.h" #include "interface/vcos/vcos.h" @@ -124,6 +125,8 @@ struct RASPIVID_STATE_T MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port PORT_USERDATA callback_data; + + MMAL_QUEUE_T *encoded_buffer_q; }; #if 0 @@ -499,57 +502,62 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf */ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { - MMAL_BUFFER_HEADER_T *new_buffer; - - // We pass our file handle and other stuff in via the userdata field. - PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; RASPIVID_STATE *state = pData->state; - if (pData) - { - int bytes_written = buffer->length; - - vcos_assert(state->output_file); - - if (buffer->length) - { - mmal_buffer_header_mem_lock(buffer); - - bytes_written = fwrite(buffer->data, 1, buffer->length, state->output_file); - - mmal_buffer_header_mem_unlock(buffer); - } - - if (bytes_written != buffer->length) - { - vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); - pData->abort = 1; - } - } - else + if (pData == NULL) { vcos_log_error("Received a encoder buffer callback with no state"); + // release buffer back to the pool + mmal_buffer_header_release(buffer); + return; } - // release buffer back to the pool - mmal_buffer_header_release(buffer); - - // and send one back to the port (if still open) - if (port->is_enabled) - { - MMAL_STATUS_T status = MMAL_SUCCESS; - - new_buffer = mmal_queue_get(state->encoder_pool->queue); - - if (new_buffer) - status = mmal_port_send_buffer(port, new_buffer); - - if (!new_buffer || status != MMAL_SUCCESS) - vcos_log_error("Unable to return a buffer to the encoder port"); - } + /* Send buffer to GStreamer element for pushing to the pipeline */ + mmal_queue_put(state->encoded_buffer_q, buffer); } +GstFlowReturn +raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp) +{ + GstBuffer *buf; + MMAL_BUFFER_HEADER_T *buffer; + GstFlowReturn ret = GST_FLOW_ERROR; + + /* FIXME: Use our own interruptible cond wait: */ + buffer = mmal_queue_wait(state->encoded_buffer_q); + + mmal_buffer_header_mem_lock(buffer); + buf = gst_buffer_new_allocate(NULL, buffer->length, NULL); + if (buf) { + gst_buffer_fill(buf, 0, buffer->data, buffer->length); + g_print("Buffer of size %u\n", (guint) buffer->length); + ret = GST_FLOW_OK; + } + + mmal_buffer_header_mem_unlock(buffer); + + *bufp = buf; + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (state->encoder_output_port->is_enabled) + { + MMAL_STATUS_T status = MMAL_SUCCESS; + + buffer = mmal_queue_get(state->encoder_pool->queue); + if (buffer) + status = mmal_port_send_buffer(state->encoder_output_port, buffer); + + if (!buffer || status != MMAL_SUCCESS) { + vcos_log_error("Unable to return a buffer to the encoder port"); + ret = GST_FLOW_ERROR; + } + } + + return ret; +} /** * Create the camera component, set up its ports @@ -785,6 +793,8 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (encoder_output->buffer_size < encoder_output->buffer_size_min) encoder_output->buffer_size = encoder_output->buffer_size_min; + g_print("encoder buffer size is %u\n", (guint)encoder_output->buffer_size); + encoder_output->buffer_num = encoder_output->buffer_num_recommended; if (encoder_output->buffer_num < encoder_output->buffer_num_min) @@ -859,7 +869,6 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) /* Create pool of buffer headers for the output port to consume */ pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); - if (!pool) { vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); @@ -888,6 +897,11 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) */ static void destroy_encoder_component(RASPIVID_STATE *state) { + /* Empty the buffer header q */ + while (mmal_queue_length(state->encoded_buffer_q)) + (void)(mmal_queue_get(state->encoded_buffer_q)); + mmal_queue_destroy(state->encoded_buffer_q); + // Get rid of any port buffers first if (state->encoder_pool) { @@ -1004,6 +1018,8 @@ raspi_capture_start(RASPIVID_CONFIG *config) state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; state->encoder_output_port = state->encoder_component->output[0]; + state->encoded_buffer_q = mmal_queue_create(); + if (state->config.preview_parameters.wantPreview ) { if (state->config.verbose) @@ -1096,7 +1112,7 @@ raspi_capture_start(RASPIVID_CONFIG *config) // Only encode stuff if we have a filename and it opened if (state->output_file) { - int wait; + //int wait; if (state->config.verbose) fprintf(stderr, "Starting video capture\n"); @@ -1127,6 +1143,7 @@ raspi_capture_start(RASPIVID_CONFIG *config) // out of storage space) // Going to check every ABORT_INTERVAL milliseconds +#if 0 for (wait = 0; state->config.timeout == 0 || wait < state->config.timeout; wait+= ABORT_INTERVAL) { vcos_sleep(ABORT_INTERVAL); @@ -1136,13 +1153,16 @@ raspi_capture_start(RASPIVID_CONFIG *config) if (state->config.verbose) fprintf(stderr, "Finished capture\n"); +#endif } else { +#if 0 if (state->config.timeout) vcos_sleep(state->config.timeout); else for (;;) vcos_sleep(ABORT_INTERVAL); +#endif } } diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 554b898f65..4069ab3eb2 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -83,6 +83,7 @@ typedef struct RASPIVID_STATE_T RASPIVID_STATE; void raspicapture_init(); void raspicapture_default_config(RASPIVID_CONFIG *config); RASPIVID_STATE *raspi_capture_start(RASPIVID_CONFIG *config); +GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf); void raspi_capture_stop(RASPIVID_STATE *state); G_END_DECLS diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 4ac7ebd7d6..b9753f3e40 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -121,7 +121,11 @@ static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent); static gboolean gst_rpi_cam_src_stop (GstBaseSrc *parent); -static GstFlowReturn gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf); +static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc * src, + GstQuery * query); +static GstFlowReturn gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf); +static GstCaps *gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter); +static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps); static void gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) @@ -150,7 +154,10 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start); basesrc_class->stop = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_stop); - pushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fill_buffer); + basesrc_class->decide_allocation = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_decide_allocation); + basesrc_class->get_caps = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_get_caps); + basesrc_class->fixate = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fixate); + pushsrc_class->create = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_create); raspicapture_init(); } @@ -197,6 +204,7 @@ static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent) { GstRpiCamSrc *src = GST_RPICAMSRC(parent); + g_print ("In src_start()\n"); src->capture_state = raspi_capture_start(&src->capture_config); return TRUE; } @@ -209,10 +217,61 @@ gst_rpi_cam_src_stop (GstBaseSrc *parent) return TRUE; } -static GstFlowReturn -gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf) +static GstCaps * +gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter) { - return GST_FLOW_ERROR; + g_print ("In get_caps\n"); + //if (src->state == NULL) + return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src)); + /* Retrieve limiting parameters from the camera module, max width/height fps-range */ +} + +static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc *bsrc, + GstQuery * query) +{ + g_print ("In decide_allocation\n"); + return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); +} + +static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) +{ + GstStructure *structure; + gint i; + + GST_DEBUG_OBJECT (basesrc, "fixating caps %" GST_PTR_FORMAT, caps); + + caps = gst_caps_make_writable (caps); + + for (i = 0; i < gst_caps_get_size (caps); ++i) { + structure = gst_caps_get_structure (caps, i); + + /* Fixate to the camera's 1920x1080 resolution if possible */ + gst_structure_fixate_field_nearest_int (structure, "width", 1920); + gst_structure_fixate_field_nearest_int (structure, "height", 1080); + gst_structure_fixate_field_nearest_fraction (structure, "framerate", + G_MAXINT, 1); + gst_structure_fixate_field (structure, "format"); + } + + GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, caps); + + caps = GST_BASE_SRC_CLASS (parent_class)->fixate (basesrc, caps); + + return caps; +} + +static GstFlowReturn +gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf) +{ + GstRpiCamSrc *src = GST_RPICAMSRC(parent); + GstFlowReturn ret; + + /* FIXME: Use custom allocator */ + ret = raspi_capture_fill_buffer(src->capture_state, buf); + if (*buf) + g_print ("Made buffer of size %" G_GSIZE_FORMAT "\n", gst_buffer_get_size(*buf)); + + return ret; } static gboolean From cde8b331994e8afad302a3f8d3b40c8aac608d1c Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 13 Oct 2013 01:20:51 +1100 Subject: [PATCH 05/77] rpicamsrc: Initial caps nego and properties. Support caps negotiation for H.264 frame size and framerate. Add bitrate, saturation, brightness, contrast, sharpness properties. --- sys/rpicamsrc/RaspiCamControl.c | 14 +- sys/rpicamsrc/RaspiCapture.c | 367 +++++++++++++++----------------- sys/rpicamsrc/RaspiCapture.h | 8 +- sys/rpicamsrc/gstrpicamsrc.c | 153 +++++++++++-- sys/rpicamsrc/gstrpicamsrc.h | 1 + 5 files changed, 328 insertions(+), 215 deletions(-) diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c index 80ad6fa843..c1b246510e 100644 --- a/sys/rpicamsrc/RaspiCamControl.c +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -565,6 +565,7 @@ void raspicamcontrol_display_help() fprintf(stderr, "\n"); } +#endif /** * Dump contents of camera parameter structure to stdout for debugging/verbose logging * @@ -572,19 +573,18 @@ void raspicamcontrol_display_help() */ void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params) { - const char *exp_mode = raspicli_unmap_xref(params->exposureMode, exposure_map, exposure_map_size); - const char *awb_mode = raspicli_unmap_xref(params->awbMode, awb_map, awb_map_size); - const char *image_effect = raspicli_unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); - const char *metering_mode = raspicli_unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); + //const char *exp_mode = raspicli_unmap_xref(params->exposureMode, exposure_map, exposure_map_size); + //const char *awb_mode = raspicli_unmap_xref(params->awbMode, awb_map, awb_map_size); + //const char *image_effect = raspicli_unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); + //const char *metering_mode = raspicli_unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); - fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); - fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); + //fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); + //fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h); } -#endif /** * Convert a MMAL status return value to a simple boolean of success diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index e9db8dcf5c..164ed6923c 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -110,7 +110,7 @@ typedef struct struct RASPIVID_STATE_T { - RASPIVID_CONFIG config; + RASPIVID_CONFIG *config; FILE *output_file; @@ -119,6 +119,7 @@ struct RASPIVID_STATE_T MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + MMAL_PORT_T *camera_video_port; MMAL_PORT_T *camera_still_port; MMAL_PORT_T *encoder_output_port; @@ -164,7 +165,6 @@ static COMMAND_LIST cmdline_commands[] = { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, - { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -')", 1 }, { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, @@ -192,7 +192,8 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) config->width = 1920; // Default to 1080p config->height = 1080; config->bitrate = 17000000; // This is a decent default bitrate for 1080p - config->framerate = VIDEO_FRAME_RATE_NUM; + config->fps_n = VIDEO_FRAME_RATE_NUM; + config->fps_d = VIDEO_FRAME_RATE_DEN; config->intraperiod = 0; // Not set config->demoMode = 0; config->demoInterval = 250; // ms @@ -220,12 +221,12 @@ static void dump_state(RASPIVID_STATE *state) return; } - fprintf(stderr, "Width %d, Height %d, filename %s\n", state->config.width, state->config.height, state->config.filename); - fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->config.bitrate, state->config.framerate, state->config.timeout); - //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->config.profile, profile_map, profile_map_size)); + fprintf(stderr, "Width %d, Height %d\n", state->config->width, state->config->height); + fprintf(stderr, "bitrate %d, framerate %d/%d, time delay %d\n", state->config->bitrate, state->config->fps_n, state->config->fps_d, state->config->timeout); + //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->config->profile, profile_map, profile_map_size)); - raspipreview_dump_parameters(&state->config.preview_parameters); - //raspicamcontrol_dump_parameters(&state->config.camera_parameters); + raspipreview_dump_parameters(&state->config->preview_parameters); + raspicamcontrol_dump_parameters(&state->config->camera_parameters); } #if 0 @@ -426,12 +427,6 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) return 1; } - // Always disable verbose if output going to stdout - if (state->filename && state->filename[0] == '-') - { - state->verbose = 0; - } - return 0; } @@ -531,7 +526,6 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp) buf = gst_buffer_new_allocate(NULL, buffer->length, NULL); if (buf) { gst_buffer_fill(buf, 0, buffer->data, buffer->length); - g_print("Buffer of size %u\n", (guint) buffer->length); ret = GST_FLOW_OK; } @@ -569,9 +563,7 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp) */ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) { - MMAL_COMPONENT_T *camera = 0; - MMAL_ES_FORMAT_T *format; - MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_COMPONENT_T *camera = NULL; MMAL_STATUS_T status; /* Create the component */ @@ -590,9 +582,6 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) goto error; } - preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; - video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; - still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; // Enable the camera, and tell it its control callback function status = mmal_port_enable(camera->control, camera_control_callback); @@ -603,24 +592,48 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) goto error; } + state->camera_component = camera; + + return status; + +error: + if (camera) + mmal_component_destroy(camera); + + return status; +} + +MMAL_STATUS_T +raspi_capture_set_format_and_start(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *camera = NULL; + MMAL_STATUS_T status; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + // set up the camera configuration + + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = { - MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = - { - { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, - .max_stills_w = state->config.width, - .max_stills_h = state->config.height, - .stills_yuv422 = 0, - .one_shot_stills = 0, - .max_preview_video_w = state->config.width, - .max_preview_video_h = state->config.height, - .num_preview_video_frames = 3, - .stills_capture_circular_buffer_height = 0, - .fast_preview_resume = 0, - .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC - }; - mmal_port_parameter_set(camera->control, &cam_config.hdr); - } + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->config->width, + .max_stills_h = state->config->height, + .stills_yuv422 = 0, + .one_shot_stills = 0, + .max_preview_video_w = state->config->width, + .max_preview_video_h = state->config->height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + + camera = state->camera_component; + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + mmal_port_parameter_set(camera->control, &cam_config.hdr); // Now set up the port formats @@ -633,14 +646,14 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) format->encoding_variant = MMAL_ENCODING_I420; format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->config.width; - format->es->video.height = state->config.height; + format->es->video.width = state->config->width; + format->es->video.height = state->config->height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config.width; - format->es->video.crop.height = state->config.height; - format->es->video.frame_rate.num = state->config.framerate; - format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + format->es->video.crop.width = state->config->width; + format->es->video.crop.height = state->config->height; + format->es->video.frame_rate.num = state->config->fps_n; + format->es->video.frame_rate.den = state->config->fps_d; status = mmal_port_format_commit(preview_port); @@ -656,14 +669,14 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) format->encoding_variant = MMAL_ENCODING_I420; format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->config.width; - format->es->video.height = state->config.height; + format->es->video.width = state->config->width; + format->es->video.height = state->config->height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config.width; - format->es->video.crop.height = state->config.height; - format->es->video.frame_rate.num = state->config.framerate; - format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + format->es->video.crop.width = state->config->width; + format->es->video.crop.height = state->config->height; + format->es->video.frame_rate.num = state->config->fps_n; + format->es->video.frame_rate.den = state->config->fps_d; status = mmal_port_format_commit(video_port); @@ -685,12 +698,12 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; - format->es->video.width = state->config.width; - format->es->video.height = state->config.height; + format->es->video.width = state->config->width; + format->es->video.height = state->config->height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config.width; - format->es->video.crop.height = state->config.height; + format->es->video.crop.width = state->config->width; + format->es->video.crop.height = state->config->height; format->es->video.frame_rate.num = 1; format->es->video.frame_rate.den = 1; @@ -715,20 +728,12 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) goto error; } - raspicamcontrol_set_all_parameters(camera, &state->config.camera_parameters); + raspicamcontrol_set_all_parameters(camera, &state->config->camera_parameters); - state->camera_component = camera; - - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Camera component done\n"); - return status; - error: - - if (camera) - mmal_component_destroy(camera); - return status; } @@ -786,7 +791,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) // Only supporting H264 at the moment encoder_output->format->encoding = MMAL_ENCODING_H264; - encoder_output->format->bitrate = state->config.bitrate; + encoder_output->format->bitrate = state->config->bitrate; encoder_output->buffer_size = encoder_output->buffer_size_recommended; @@ -823,9 +828,9 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (state->config.intraperiod) + if (state->config->intraperiod) { - MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config.intraperiod}; + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config->intraperiod}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); if (status != MMAL_SUCCESS) { @@ -840,7 +845,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) param.hdr.id = MMAL_PARAMETER_PROFILE; param.hdr.size = sizeof(param); - param.profile[0].profile = state->config.profile; + param.profile[0].profile = state->config->profile; param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -852,7 +857,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->config.immutableInput) != MMAL_SUCCESS) + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->config->immutableInput) != MMAL_SUCCESS) { vcos_log_error("Unable to set immutable input flag"); // Continue rather than abort.. @@ -877,7 +882,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) state->encoder_pool = pool; state->encoder_component = encoder; - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Encoder component done\n"); return status; @@ -898,8 +903,10 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) static void destroy_encoder_component(RASPIVID_STATE *state) { /* Empty the buffer header q */ - while (mmal_queue_length(state->encoded_buffer_q)) - (void)(mmal_queue_get(state->encoded_buffer_q)); + while (mmal_queue_length(state->encoded_buffer_q)) { + MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q); + mmal_buffer_header_release(buffer); + } mmal_queue_destroy(state->encoded_buffer_q); // Get rid of any port buffers first @@ -961,29 +968,22 @@ void raspicapture_init() } RASPIVID_STATE * -raspi_capture_start(RASPIVID_CONFIG *config) +raspi_capture_setup(RASPIVID_CONFIG *config) { // Our main data storage vessel.. RASPIVID_STATE *state; - //int exit_code = EX_OK; MMAL_STATUS_T status = MMAL_SUCCESS; - MMAL_PORT_T *camera_preview_port = NULL; - MMAL_PORT_T *camera_video_port = NULL; - MMAL_PORT_T *preview_input_port = NULL; - MMAL_PORT_T *encoder_input_port = NULL; /* Default everything to zero */ state = calloc(1, sizeof(RASPIVID_STATE)); /* Apply passed in config */ - state->config = *config; - - if (state->config.verbose) - { - dump_state(state); - } + state->config = config; + /* So far, all we can do is create the camera component. Actual + * config and connection of encoders etc happens in _start() + */ // OK, we have a nice set of parameters. Now set up our components // We have three components. Camera, Preview and encoder. @@ -993,7 +993,7 @@ raspi_capture_start(RASPIVID_CONFIG *config) return NULL; } - if ((status = raspipreview_create(&state->config.preview_parameters)) != MMAL_SUCCESS) + if ((status = raspipreview_create(&state->config->preview_parameters)) != MMAL_SUCCESS) { vcos_log_error("%s: Failed to create preview component", __func__); destroy_camera_component(state); @@ -1003,26 +1003,47 @@ raspi_capture_start(RASPIVID_CONFIG *config) if ((status = create_encoder_component(state)) != MMAL_SUCCESS) { vcos_log_error("%s: Failed to create encode component", __func__); - raspipreview_destroy(&state->config.preview_parameters); + raspipreview_destroy(&state->config->preview_parameters); destroy_camera_component(state); return NULL; } - if (state->config.verbose) + state->encoded_buffer_q = mmal_queue_create(); + + return state; +} + +gboolean +raspi_capture_start(RASPIVID_STATE *state) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + + if (state->config->verbose) + { + dump_state(state); + } + + if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) { + return FALSE; + } + + if (state->config->verbose) fprintf(stderr, "Starting component connection stage\n"); camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; - camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT]; - preview_input_port = state->config.preview_parameters.preview_component->input[0]; + preview_input_port = state->config->preview_parameters.preview_component->input[0]; encoder_input_port = state->encoder_component->input[0]; + + state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT]; state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; state->encoder_output_port = state->encoder_component->output[0]; - state->encoded_buffer_q = mmal_queue_create(); - - if (state->config.preview_parameters.wantPreview ) + if (state->config->preview_parameters.wantPreview ) { - if (state->config.verbose) + if (state->config->verbose) { fprintf(stderr, "Connecting camera preview port to preview input port\n"); fprintf(stderr, "Starting video preview\n"); @@ -1032,46 +1053,22 @@ raspi_capture_start(RASPIVID_CONFIG *config) status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection); if (status != MMAL_SUCCESS) { - mmal_status_to_int(status); vcos_log_error("%s: Failed to connect camera to preview", __func__); - goto error; + return FALSE; } } - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Connecting camera stills port to encoder input port\n"); // Now connect the camera to the encoder - status = connect_ports(camera_video_port, encoder_input_port, &state->encoder_connection); - + status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection); if (status != MMAL_SUCCESS) { - vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); - goto error; - } - - if (state->config.filename) - { - if (state->config.filename[0] == '-') - { - state->output_file = stdout; - - // Ensure we don't upset the output stream with diagnostics/info - state->config.verbose = 0; - } - else - { - if (state->config.verbose) - fprintf(stderr, "Opening output file \"%s\"\n", state->config.filename); - - state->output_file = fopen(state->config.filename, "wb"); - } - - if (!state->output_file) - { - // Notify user, carry on but discarding encoded output buffers - vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state->config.filename); - } + if (state->config->preview_parameters.wantPreview ) + mmal_connection_destroy(state->preview_connection); + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + return FALSE; } // Set up our userdata - this is passed though to the callback where we need the information. @@ -1080,93 +1077,75 @@ raspi_capture_start(RASPIVID_CONFIG *config) state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data; - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Enabling encoder output port\n"); // Enable the encoder output port and tell it its callback function status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback); - if (status != MMAL_SUCCESS) { vcos_log_error("Failed to setup encoder output"); goto error; } - if (state->config.demoMode) + if (state->config->demoMode) { // Run for the user specific time.. - int num_iterations = state->config.timeout / state->config.demoInterval; + int num_iterations = state->config->timeout / state->config->demoInterval; int i; - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Running in demo mode\n"); - for (i=0;state->config.timeout == 0 || iconfig->timeout == 0 || icamera_component); - vcos_sleep(state->config.demoInterval); + vcos_sleep(state->config->demoInterval); } } - else + + if (state->config->verbose) + fprintf(stderr, "Starting video capture\n"); + + if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) { - // Only encode stuff if we have a filename and it opened - if (state->output_file) + goto error; + } + + // Send all the buffers to the encoder output port + { + int num = mmal_queue_length(state->encoder_pool->queue); + int q; + for (q=0;qencoder_pool->queue); - if (state->config.verbose) - fprintf(stderr, "Starting video capture\n"); + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); - if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) - { - goto error; - } + if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); - // Send all the buffers to the encoder output port - { - int num = mmal_queue_length(state->encoder_pool->queue); - int q; - for (q=0;qencoder_pool->queue); - - if (!buffer) - vcos_log_error("Unable to get a required buffer %d from pool queue", q); - - if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS) - vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); - - } - } - - // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example - // out of storage space) - // Going to check every ABORT_INTERVAL milliseconds - -#if 0 - for (wait = 0; state->config.timeout == 0 || wait < state->config.timeout; wait+= ABORT_INTERVAL) - { - vcos_sleep(ABORT_INTERVAL); - if (state->callback_data.abort) - break; - } - - if (state->config.verbose) - fprintf(stderr, "Finished capture\n"); -#endif - } - else - { -#if 0 - if (state->config.timeout) - vcos_sleep(state->config.timeout); - else - for (;;) vcos_sleep(ABORT_INTERVAL); -#endif } } - return state; + // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example + // out of storage space) + // Going to check every ABORT_INTERVAL milliseconds + +#if 0 + for (wait = 0; state->config->timeout == 0 || wait < state->config->timeout; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (state->callback_data.abort) + break; + } + + if (state->config->verbose) + fprintf(stderr, "Finished capture\n"); +#endif + + return (status == MMAL_SUCCESS); error: raspi_capture_stop(state); @@ -1176,23 +1155,27 @@ error: raspicamcontrol_check_configuration(128); } - return NULL; + return FALSE; } void raspi_capture_stop(RASPIVID_STATE *state) { - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Closing down\n"); + if (state->config->preview_parameters.wantPreview ) + mmal_connection_destroy(state->preview_connection); + mmal_connection_destroy(state->encoder_connection); + // Disable all our ports that are not handled by connections check_disable_port(state->camera_still_port); check_disable_port(state->encoder_output_port); +} - if (state->config.preview_parameters.wantPreview ) - mmal_connection_destroy(state->preview_connection); - mmal_connection_destroy(state->encoder_connection); - +void +raspi_capture_free(RASPIVID_STATE *state) +{ // Can now close our file. Note disabling ports may flush buffers which causes // problems if we have already closed the file! if (state->output_file && state->output_file != stdout) @@ -1202,17 +1185,17 @@ raspi_capture_stop(RASPIVID_STATE *state) if (state->encoder_component) mmal_component_disable(state->encoder_component); - if (state->config.preview_parameters.preview_component) - mmal_component_disable(state->config.preview_parameters.preview_component); + if (state->config->preview_parameters.preview_component) + mmal_component_disable(state->config->preview_parameters.preview_component); if (state->camera_component) mmal_component_disable(state->camera_component); destroy_encoder_component(state); - raspipreview_destroy(&state->config.preview_parameters); + raspipreview_destroy(&state->config->preview_parameters); destroy_camera_component(state); - if (state->config.verbose) + if (state->config->verbose) fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); free(state); diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 4069ab3eb2..54ebdbaaa2 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -60,14 +60,14 @@ G_BEGIN_DECLS */ typedef struct { - char *filename; int verbose; /// !0 if want detailed run information int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds int width; /// Requested width of image int height; /// requested height of image int bitrate; /// Requested bitrate - int framerate; /// Requested frame rate (fps) + int fps_n; /// Requested frame rate (fps) numerator + int fps_d; /// Requested frame rate (fps) denominator int intraperiod; /// Intra-refresh period (key frame rate) int demoMode; /// Run app in demo mode int demoInterval; /// Interval between camera settings changes @@ -82,9 +82,11 @@ typedef struct RASPIVID_STATE_T RASPIVID_STATE; void raspicapture_init(); void raspicapture_default_config(RASPIVID_CONFIG *config); -RASPIVID_STATE *raspi_capture_start(RASPIVID_CONFIG *config); +RASPIVID_STATE *raspi_capture_setup(RASPIVID_CONFIG *config); +gboolean raspi_capture_start(RASPIVID_STATE *state); GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf); void raspi_capture_stop(RASPIVID_STATE *state); +void raspi_capture_free(RASPIVID_STATE *state); G_END_DECLS diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index b9753f3e40..0a8cfabe76 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -88,8 +88,41 @@ enum enum { PROP_0, + PROP_BITRATE, + PROP_SHARPNESS, + PROP_CONTRAST, + PROP_BRIGHTNESS, + PROP_SATURATION, + PROP_ISO, + PROP_VIDEO_STABILISATION, + PROP_EXPOSURE_COMPENSATION, }; +#define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ +#define BITRATE_HIGHEST 25000000 + +#define SHARPNESS_DEFAULT 0 +#define CONTRAST_DEFAULT 0 +#define BRIGHTNESS_DEFAULT 50 +#define SATURATION_DEFAULT 0 +#define ISO_DEFAULT 0 +#define VIDEO_STABILISATION_DEFAULT FALSE +#define EXPOSURE_COMPENSATION_DEFAULT FALSE + +/* + params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; + params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; + params->awbMode = MMAL_PARAM_AWBMODE_AUTO; + params->imageEffect = MMAL_PARAM_IMAGEFX_NONE; + params->colourEffects.enable = 0; + params->colourEffects.u = 128; + params->colourEffects.v = 128; + params->rotation = 0; + params->hflip = params->vflip = 0; + params->roi.x = params->roi.y = 0.0; + params->roi.w = params->roi.h = 1.0; +*/ + #define RAW_AND_JPEG_CAPS \ GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ";" \ "image/jpeg," \ @@ -125,6 +158,7 @@ static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc * src, GstQuery * query); static GstFlowReturn gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf); static GstCaps *gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter); +static gboolean gst_rpi_cam_src_set_caps (GstBaseSrc * src, GstCaps *caps); static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps); static void @@ -143,6 +177,27 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gobject_class->set_property = gst_rpi_cam_src_set_property; gobject_class->get_property = gst_rpi_cam_src_get_property; + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE, + g_param_spec_int ("bitrate", "Bitrate", "Bitrate for encoding", + 1, BITRATE_HIGHEST, BITRATE_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHARPNESS, + g_param_spec_int ("sharpness", "Sharpness", "Image capture sharpness", + -100, 100, SHARPNESS_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONTRAST, + g_param_spec_int ("contrast", "Contrast", "Image capture contrast", + -100, 100, CONTRAST_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BRIGHTNESS, + g_param_spec_int ("brightness", "Brightness", "Image capture brightness", + 0, 100, BRIGHTNESS_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SATURATION, + g_param_spec_int ("saturation", "Saturation", "Image capture saturation", + -100, 100, SATURATION_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", @@ -156,6 +211,7 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) basesrc_class->stop = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_stop); basesrc_class->decide_allocation = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_decide_allocation); basesrc_class->get_caps = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_get_caps); + basesrc_class->set_caps = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_set_caps); basesrc_class->fixate = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fixate); pushsrc_class->create = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_create); @@ -170,8 +226,6 @@ gst_rpi_cam_src_init (GstRpiCamSrc *src) raspicapture_default_config(&src->capture_config); src->capture_config.verbose = 1; - src->capture_config.filename = "test.out"; - } static void @@ -181,6 +235,21 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, GstRpiCamSrc *src = GST_RPICAMSRC (object); switch (prop_id) { + case PROP_BITRATE: + src->capture_config.bitrate = g_value_get_int(value); + break; + case PROP_SHARPNESS: + src->capture_config.camera_parameters.sharpness = g_value_get_int(value); + break; + case PROP_CONTRAST: + src->capture_config.camera_parameters.contrast = g_value_get_int(value); + break; + case PROP_BRIGHTNESS: + src->capture_config.camera_parameters.brightness = g_value_get_int(value); + break; + case PROP_SATURATION: + src->capture_config.camera_parameters.saturation = g_value_get_int(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -194,6 +263,21 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GstRpiCamSrc *src = GST_RPICAMSRC (object); switch (prop_id) { + case PROP_BITRATE: + g_value_set_int(value, src->capture_config.bitrate); + break; + case PROP_SHARPNESS: + g_value_set_int(value, src->capture_config.camera_parameters.sharpness); + break; + case PROP_CONTRAST: + g_value_set_int(value, src->capture_config.camera_parameters.contrast); + break; + case PROP_BRIGHTNESS: + g_value_set_int(value, src->capture_config.camera_parameters.brightness); + break; + case PROP_SATURATION: + g_value_set_int(value, src->capture_config.camera_parameters.saturation); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -205,7 +289,10 @@ gst_rpi_cam_src_start (GstBaseSrc *parent) { GstRpiCamSrc *src = GST_RPICAMSRC(parent); g_print ("In src_start()\n"); - src->capture_state = raspi_capture_start(&src->capture_config); + src->capture_state = raspi_capture_setup(&src->capture_config); + if (src->capture_state == NULL) + return FALSE; + return TRUE; } @@ -213,17 +300,52 @@ static gboolean gst_rpi_cam_src_stop (GstBaseSrc *parent) { GstRpiCamSrc *src = GST_RPICAMSRC(parent); - raspi_capture_stop(src->capture_state); + if (src->started) + raspi_capture_stop(src->capture_state); + raspi_capture_free(src->capture_state); + src->capture_state = NULL; return TRUE; } static GstCaps * -gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter) +gst_rpi_cam_src_get_caps (GstBaseSrc *bsrc, GstCaps * filter) { - g_print ("In get_caps\n"); - //if (src->state == NULL) - return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src)); - /* Retrieve limiting parameters from the camera module, max width/height fps-range */ + GstRpiCamSrc *src = GST_RPICAMSRC(bsrc); + GstCaps *caps; + + caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc)); + if (src->capture_state == NULL) + goto done; + + /* FIXME: Retrieve limiting parameters from the camera module, max width/height fps-range */ + caps = gst_caps_make_writable(caps); + gst_caps_set_simple (caps, + "width", GST_TYPE_INT_RANGE, 1, 1920, + "height", GST_TYPE_INT_RANGE, 1, 1080, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60, 1, + NULL); + +done: + GST_DEBUG_OBJECT(src, "get_caps returning %" GST_PTR_FORMAT, caps); + return caps; +} + +static gboolean +gst_rpi_cam_src_set_caps (GstBaseSrc *bsrc, GstCaps *caps) +{ + GstRpiCamSrc *src = GST_RPICAMSRC(bsrc); + GstVideoInfo info; + + GST_DEBUG_OBJECT (src, "In set_caps %" GST_PTR_FORMAT, caps); + if (!gst_video_info_from_caps (&info, caps)) + return FALSE; + + src->capture_config.width = info.width; + src->capture_config.height = info.height; + src->capture_config.fps_n = info.fps_n; + src->capture_config.fps_d = info.fps_d; + + return TRUE; } static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc *bsrc, @@ -245,11 +367,10 @@ static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) for (i = 0; i < gst_caps_get_size (caps); ++i) { structure = gst_caps_get_structure (caps, i); - /* Fixate to the camera's 1920x1080 resolution if possible */ + /* Fixate to 1920x1080 resolution if possible */ gst_structure_fixate_field_nearest_int (structure, "width", 1920); gst_structure_fixate_field_nearest_int (structure, "height", 1080); - gst_structure_fixate_field_nearest_fraction (structure, "framerate", - G_MAXINT, 1); + gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1); gst_structure_fixate_field (structure, "format"); } @@ -266,10 +387,16 @@ gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf) GstRpiCamSrc *src = GST_RPICAMSRC(parent); GstFlowReturn ret; + if (!src->started) { + if (!raspi_capture_start(src->capture_state)) + return GST_FLOW_ERROR; + src->started = TRUE; + } + /* FIXME: Use custom allocator */ ret = raspi_capture_fill_buffer(src->capture_state, buf); if (*buf) - g_print ("Made buffer of size %" G_GSIZE_FORMAT "\n", gst_buffer_get_size(*buf)); + GST_LOG_OBJECT(src, "Made buffer of size %" G_GSIZE_FORMAT "\n", gst_buffer_get_size(*buf)); return ret; } diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index e321df40b8..68f560ae0c 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -71,6 +71,7 @@ struct _GstRpiCamSrc RASPIVID_CONFIG capture_config; RASPIVID_STATE *capture_state; + gboolean started; }; struct _GstRpiCamSrcClass From 42729b92f0f7e990942920307e0ab0835e1b2e91 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 13 Oct 2013 01:29:08 +1100 Subject: [PATCH 06/77] rpicamsrc: Tell basesrc to timestamp buffers for us, for now. --- sys/rpicamsrc/gstrpicamsrc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 0a8cfabe76..7f61e5a294 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -226,6 +226,8 @@ gst_rpi_cam_src_init (GstRpiCamSrc *src) raspicapture_default_config(&src->capture_config); src->capture_config.verbose = 1; + /* do-timestamping by default for now. FIXME: Implement proper timestamping */ + gst_base_src_set_do_timestamp(GST_BASE_SRC(src), TRUE); } static void From 5f871311c11529dd39413519aa594d5e478a6416 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 13 Oct 2013 17:46:07 +1100 Subject: [PATCH 07/77] rpicamsrc: Implement a bunch of the raspivid command-line params Add properties for controlling various parts of the capture --- sys/rpicamsrc/gstrpicam-enums-template.c | 40 +++++ sys/rpicamsrc/gstrpicam-enums-template.h | 26 +++ sys/rpicamsrc/gstrpicam_types.h | 71 ++++++++ sys/rpicamsrc/gstrpicamsrc.c | 204 ++++++++++++++++++++++- 4 files changed, 336 insertions(+), 5 deletions(-) create mode 100644 sys/rpicamsrc/gstrpicam-enums-template.c create mode 100644 sys/rpicamsrc/gstrpicam-enums-template.h create mode 100644 sys/rpicamsrc/gstrpicam_types.h diff --git a/sys/rpicamsrc/gstrpicam-enums-template.c b/sys/rpicamsrc/gstrpicam-enums-template.c new file mode 100644 index 0000000000..b9dee13e11 --- /dev/null +++ b/sys/rpicamsrc/gstrpicam-enums-template.c @@ -0,0 +1,40 @@ +/*** BEGIN file-header ***/ +#include "gstrpicam-enum-types.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +#include "@filename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static GType the_type = 0; + + if (the_type == 0) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, + "@VALUENAME@", + "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + the_type = g_@type@_register_static ( + g_intern_static_string ("@EnumName@"), + values); + } + return the_type; +} + +/*** END value-tail ***/ + diff --git a/sys/rpicamsrc/gstrpicam-enums-template.h b/sys/rpicamsrc/gstrpicam-enums-template.h new file mode 100644 index 0000000000..62b49ca647 --- /dev/null +++ b/sys/rpicamsrc/gstrpicam-enums-template.h @@ -0,0 +1,26 @@ +/*** BEGIN file-header ***/ +#ifndef __GSTRPICAM_ENUM_TYPES_H__ +#define __GSTRPICAM_ENUM_TYPES_H__ + +#include + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* Enumerations from "@filename@" */ + +/*** END file-production ***/ + +/*** BEGIN enumeration-production ***/ +#define GST_RPI_CAM_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) +GType @enum_name@_get_type (void) G_GNUC_CONST; + +/*** END enumeration-production ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __GSTRPICAM_ENUM_TYPES_H__ */ +/*** END file-tail ***/ diff --git a/sys/rpicamsrc/gstrpicam_types.h b/sys/rpicamsrc/gstrpicam_types.h new file mode 100644 index 0000000000..7ebccb2dbf --- /dev/null +++ b/sys/rpicamsrc/gstrpicam_types.h @@ -0,0 +1,71 @@ +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/mmal_parameters_camera.h" + +typedef enum { + GST_RPI_CAM_SRC_EXPOSURE_MODE_OFF = MMAL_PARAM_EXPOSUREMODE_OFF, + GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO = MMAL_PARAM_EXPOSUREMODE_AUTO, + GST_RPI_CAM_SRC_EXPOSURE_MODE_NIGHT = MMAL_PARAM_EXPOSUREMODE_NIGHT, + GST_RPI_CAM_SRC_EXPOSURE_MODE_NIGHTPREVIEW = MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + GST_RPI_CAM_SRC_EXPOSURE_MODE_BACKLIGHT = MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + GST_RPI_CAM_SRC_EXPOSURE_MODE_SPOTLIGHT = MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + GST_RPI_CAM_SRC_EXPOSURE_MODE_SPORTS = MMAL_PARAM_EXPOSUREMODE_SPORTS, + GST_RPI_CAM_SRC_EXPOSURE_MODE_SNOW = MMAL_PARAM_EXPOSUREMODE_SNOW, + GST_RPI_CAM_SRC_EXPOSURE_MODE_BEACH = MMAL_PARAM_EXPOSUREMODE_BEACH, + GST_RPI_CAM_SRC_EXPOSURE_MODE_VERYLONG = MMAL_PARAM_EXPOSUREMODE_VERYLONG, + GST_RPI_CAM_SRC_EXPOSURE_MODE_FIXEDFPS = MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + GST_RPI_CAM_SRC_EXPOSURE_MODE_ANTISHAKE = MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + GST_RPI_CAM_SRC_EXPOSURE_MODE_FIREWORKS = MMAL_PARAM_EXPOSUREMODE_FIREWORKS +} GstRpiCamSrcExposureMode; + +typedef enum { + GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, + GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_SPOT = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, + GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_BACKLIST = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, + GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_MATRIX = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX +} GstRpiCamSrcExposureMeteringMode; + +typedef enum { + GST_RPI_CAM_SRC_AWB_MODE_OFF = MMAL_PARAM_AWBMODE_OFF, + GST_RPI_CAM_SRC_AWB_MODE_AUTO = MMAL_PARAM_AWBMODE_AUTO, + GST_RPI_CAM_SRC_AWB_MODE_SUNLIGHT = MMAL_PARAM_AWBMODE_SUNLIGHT, + GST_RPI_CAM_SRC_AWB_MODE_CLOUDY = MMAL_PARAM_AWBMODE_CLOUDY, + GST_RPI_CAM_SRC_AWB_MODE_SHADE = MMAL_PARAM_AWBMODE_SHADE, + GST_RPI_CAM_SRC_AWB_MODE_TUNGSTEN = MMAL_PARAM_AWBMODE_TUNGSTEN, + GST_RPI_CAM_SRC_AWB_MODE_FLUORESCENT = MMAL_PARAM_AWBMODE_FLUORESCENT, + GST_RPI_CAM_SRC_AWB_MODE_INCANDESCENT = MMAL_PARAM_AWBMODE_INCANDESCENT, + GST_RPI_CAM_SRC_AWB_MODE_FLASH = MMAL_PARAM_AWBMODE_FLASH, + GST_RPI_CAM_SRC_AWB_MODE_HORIZON = MMAL_PARAM_AWBMODE_HORIZON +} GstRpiCamSrcAWBMode; + +typedef enum { + GST_RPI_CAM_SRC_IMAGEFX_NONE = MMAL_PARAM_IMAGEFX_NONE, + GST_RPI_CAM_SRC_IMAGEFX_NEGATIVE = MMAL_PARAM_IMAGEFX_NEGATIVE, + GST_RPI_CAM_SRC_IMAGEFX_SOLARIZE = MMAL_PARAM_IMAGEFX_SOLARIZE, + GST_RPI_CAM_SRC_IMAGEFX_POSTERIZE = MMAL_PARAM_IMAGEFX_POSTERIZE, + GST_RPI_CAM_SRC_IMAGEFX_WHITEBOARD = MMAL_PARAM_IMAGEFX_WHITEBOARD, + GST_RPI_CAM_SRC_IMAGEFX_BLACKBOARD = MMAL_PARAM_IMAGEFX_BLACKBOARD, + GST_RPI_CAM_SRC_IMAGEFX_SKETCH = MMAL_PARAM_IMAGEFX_SKETCH, + GST_RPI_CAM_SRC_IMAGEFX_DENOISE = MMAL_PARAM_IMAGEFX_DENOISE, + GST_RPI_CAM_SRC_IMAGEFX_EMBOSS = MMAL_PARAM_IMAGEFX_EMBOSS, + GST_RPI_CAM_SRC_IMAGEFX_OILPAINT = MMAL_PARAM_IMAGEFX_OILPAINT, + GST_RPI_CAM_SRC_IMAGEFX_HATCH = MMAL_PARAM_IMAGEFX_HATCH, + GST_RPI_CAM_SRC_IMAGEFX_GPEN = MMAL_PARAM_IMAGEFX_GPEN, + GST_RPI_CAM_SRC_IMAGEFX_PASTEL = MMAL_PARAM_IMAGEFX_PASTEL, + GST_RPI_CAM_SRC_IMAGEFX_WATERCOLOUR = MMAL_PARAM_IMAGEFX_WATERCOLOUR, + GST_RPI_CAM_SRC_IMAGEFX_FILM = MMAL_PARAM_IMAGEFX_FILM, + GST_RPI_CAM_SRC_IMAGEFX_BLUR = MMAL_PARAM_IMAGEFX_BLUR, + GST_RPI_CAM_SRC_IMAGEFX_SATURATION = MMAL_PARAM_IMAGEFX_SATURATION, + GST_RPI_CAM_SRC_IMAGEFX_COLOURSWAP = MMAL_PARAM_IMAGEFX_COLOURSWAP, + GST_RPI_CAM_SRC_IMAGEFX_WASHEDOUT = MMAL_PARAM_IMAGEFX_WASHEDOUT, + GST_RPI_CAM_SRC_IMAGEFX_POSTERISE = MMAL_PARAM_IMAGEFX_POSTERISE, + GST_RPI_CAM_SRC_IMAGEFX_COLOURPOINT = MMAL_PARAM_IMAGEFX_COLOURPOINT, + GST_RPI_CAM_SRC_IMAGEFX_COLOURBALANCE = MMAL_PARAM_IMAGEFX_COLOURBALANCE, + GST_RPI_CAM_SRC_IMAGEFX_CARTOON = MMAL_PARAM_IMAGEFX_CARTOON +} GstRpiCamSrcImageEffect; + +typedef enum { + GST_RPI_CAM_SRC_FLICKERAVOID_OFF = MMAL_PARAM_FLICKERAVOID_OFF, + GST_RPI_CAM_SRC_FLICKERAVOID_AUTO = MMAL_PARAM_FLICKERAVOID_AUTO, + GST_RPI_CAM_SRC_FLICKERAVOID_50HZ = MMAL_PARAM_FLICKERAVOID_50HZ, + GST_RPI_CAM_SRC_FLICKERAVOID_60HZ = MMAL_PARAM_FLICKERAVOID_60HZ +} GstRpiCamSrcFlickerAvoidance; diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 7f61e5a294..43de4b2436 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -62,6 +62,8 @@ #include #include "gstrpicamsrc.h" +#include "gstrpicam_types.h" +#include "gstrpicam-enum-types.h" #include "RaspiCapture.h" #include "bcm_host.h" @@ -89,6 +91,10 @@ enum { PROP_0, PROP_BITRATE, + PROP_PREVIEW, + PROP_PREVIEW_ENCODED, + PROP_PREVIEW_OPACITY, + PROP_FULLSCREEN, PROP_SHARPNESS, PROP_CONTRAST, PROP_BRIGHTNESS, @@ -96,6 +102,19 @@ enum PROP_ISO, PROP_VIDEO_STABILISATION, PROP_EXPOSURE_COMPENSATION, + PROP_EXPOSURE_MODE, + PROP_EXPOSURE_METERING_MODE, + PROP_AWB_MODE, + PROP_IMAGE_EFFECT, + PROP_IMAGE_EFFECT_PARAMS, + PROP_COLOUR_EFFECTS, + PROP_ROTATION, + PROP_HFLIP, + PROP_VFLIP, + PROP_ROI_X, + PROP_ROI_Y, + PROP_ROI_W, + PROP_ROI_H, }; #define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ @@ -107,7 +126,10 @@ enum #define SATURATION_DEFAULT 0 #define ISO_DEFAULT 0 #define VIDEO_STABILISATION_DEFAULT FALSE -#define EXPOSURE_COMPENSATION_DEFAULT FALSE +#define EXPOSURE_COMPENSATION_DEFAULT 0 + +#define EXPOSURE_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO +#define EXPOSURE_METERING_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE /* params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; @@ -177,26 +199,96 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gobject_class->set_property = gst_rpi_cam_src_set_property; gobject_class->get_property = gst_rpi_cam_src_get_property; - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE, + g_object_class_install_property (gobject_class, PROP_BITRATE, g_param_spec_int ("bitrate", "Bitrate", "Bitrate for encoding", 1, BITRATE_HIGHEST, BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PREVIEW, + g_param_spec_boolean ("preview", + "Preview Window", "Display preview window overlay", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_FULLSCREEN, + g_param_spec_boolean ("fullscreen", + "Fullscreen Preview", "Display preview window full screen", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PREVIEW_ENCODED, + g_param_spec_boolean ("preview-encoded", + "Preview Encoded", "Display encoder output in the preview", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_OPACITY, + g_param_spec_int ("preview-opacity", "Preview Opacity", "Opacity to use for the preview window", + 0, 255, 255, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHARPNESS, g_param_spec_int ("sharpness", "Sharpness", "Image capture sharpness", -100, 100, SHARPNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONTRAST, + g_object_class_install_property (gobject_class, PROP_CONTRAST, g_param_spec_int ("contrast", "Contrast", "Image capture contrast", -100, 100, CONTRAST_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BRIGHTNESS, + g_object_class_install_property (gobject_class, PROP_BRIGHTNESS, g_param_spec_int ("brightness", "Brightness", "Image capture brightness", 0, 100, BRIGHTNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SATURATION, + g_object_class_install_property (gobject_class, PROP_SATURATION, g_param_spec_int ("saturation", "Saturation", "Image capture saturation", -100, 100, SATURATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ISO, + g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", + 0, 3200, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_VIDEO_STABILISATION, + g_param_spec_boolean ("video-stabilisation", + "Video Stabilisation", "Enable or disable video stabilisation", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_EXPOSURE_COMPENSATION, + g_param_spec_int ("exposure-compensation", "EV compensation", "Exposure Value compensation", + -10, 10, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_EXPOSURE_MODE, + g_param_spec_enum ("exposure-mode", "Exposure Mode", + "Camera exposure mode to use", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_MODE, EXPOSURE_MODE_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_EXPOSURE_METERING_MODE, + g_param_spec_enum ("metering-mode", "Exposure Metering Mode", + "Camera exposure metering mode to use", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, EXPOSURE_METERING_MODE_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_AWB_MODE, + g_param_spec_enum ("awb-mode", "Automatic White Balance Mode", + "White Balance mode", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, GST_RPI_CAM_SRC_AWB_MODE_AUTO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#if 0 + PROP_IMAGE_EFFECT, + PROP_IMAGE_EFFECT_PARAMS, + PROP_COLOUR_EFFECTS, +#endif + g_object_class_install_property (gobject_class, PROP_ROTATION, + g_param_spec_int ("rotation", "Rotation", "Rotate captured image (0, 90, 180, 270 degrees)", + 0, 270, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_HFLIP, + g_param_spec_boolean ("hflip", + "Horizontal Flip", "Flip capture horizontally", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_VFLIP, + g_param_spec_boolean ("vflip", + "Vertical Flip", "Flip capture vertically", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROI_X, + g_param_spec_float ("roi-x", "ROI X", "Normalised region-of-interest X coord", + 0, 1.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROI_Y, + g_param_spec_float ("roi-y", "ROI Y", "Normalised region-of-interest Y coord", + 0, 1.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROI_W, + g_param_spec_float ("roi-w", "ROI W", "Normalised region-of-interest W coord", + 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROI_H, + g_param_spec_float ("roi-h", "ROI H", "Normalised region-of-interest H coord", + 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", @@ -240,6 +332,18 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_BITRATE: src->capture_config.bitrate = g_value_get_int(value); break; + case PROP_PREVIEW: + src->capture_config.preview_parameters.wantPreview = g_value_get_boolean(value); + break; + case PROP_PREVIEW_ENCODED: + src->capture_config.immutableInput = g_value_get_boolean(value); + break; + case PROP_FULLSCREEN: + src->capture_config.preview_parameters.wantFullScreenPreview = g_value_get_boolean(value); + break; + case PROP_PREVIEW_OPACITY: + src->capture_config.preview_parameters.opacity = g_value_get_int(value); + break; case PROP_SHARPNESS: src->capture_config.camera_parameters.sharpness = g_value_get_int(value); break; @@ -252,6 +356,45 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_SATURATION: src->capture_config.camera_parameters.saturation = g_value_get_int(value); break; + case PROP_ISO: + src->capture_config.camera_parameters.ISO = g_value_get_int(value); + break; + case PROP_VIDEO_STABILISATION: + src->capture_config.camera_parameters.videoStabilisation = g_value_get_boolean(value); + break; + case PROP_EXPOSURE_COMPENSATION: + src->capture_config.camera_parameters.exposureCompensation = g_value_get_int(value); + break; + case PROP_EXPOSURE_MODE: + src->capture_config.camera_parameters.exposureMode = g_value_get_enum(value); + break; + case PROP_EXPOSURE_METERING_MODE: + src->capture_config.camera_parameters.exposureMeterMode = g_value_get_enum(value); + break; + case PROP_ROTATION: + src->capture_config.camera_parameters.rotation = g_value_get_int(value); + break; + case PROP_AWB_MODE: + src->capture_config.camera_parameters.awbMode = g_value_get_enum(value); + break; + case PROP_HFLIP: + src->capture_config.camera_parameters.hflip = g_value_get_boolean(value); + break; + case PROP_VFLIP: + src->capture_config.camera_parameters.vflip = g_value_get_boolean(value); + break; + case PROP_ROI_X: + src->capture_config.camera_parameters.roi.x = g_value_get_float(value); + break; + case PROP_ROI_Y: + src->capture_config.camera_parameters.roi.y = g_value_get_float(value); + break; + case PROP_ROI_W: + src->capture_config.camera_parameters.roi.w = g_value_get_float(value); + break; + case PROP_ROI_H: + src->capture_config.camera_parameters.roi.h = g_value_get_float(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -268,6 +411,18 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_BITRATE: g_value_set_int(value, src->capture_config.bitrate); break; + case PROP_PREVIEW: + g_value_set_boolean(value, src->capture_config.preview_parameters.wantPreview); + break; + case PROP_PREVIEW_ENCODED: + g_value_set_boolean(value, src->capture_config.immutableInput); + break; + case PROP_FULLSCREEN: + g_value_set_boolean(value, src->capture_config.preview_parameters.wantFullScreenPreview); + break; + case PROP_PREVIEW_OPACITY: + g_value_set_int(value, src->capture_config.preview_parameters.opacity); + break; case PROP_SHARPNESS: g_value_set_int(value, src->capture_config.camera_parameters.sharpness); break; @@ -280,6 +435,45 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_SATURATION: g_value_set_int(value, src->capture_config.camera_parameters.saturation); break; + case PROP_ISO: + g_value_set_int(value, src->capture_config.camera_parameters.ISO); + break; + case PROP_VIDEO_STABILISATION: + g_value_set_boolean(value, !!(src->capture_config.camera_parameters.videoStabilisation)); + break; + case PROP_EXPOSURE_COMPENSATION: + g_value_set_int(value, src->capture_config.camera_parameters.exposureCompensation); + break; + case PROP_EXPOSURE_MODE: + g_value_set_enum(value, src->capture_config.camera_parameters.exposureMode); + break; + case PROP_EXPOSURE_METERING_MODE: + g_value_set_enum(value, src->capture_config.camera_parameters.exposureMeterMode); + break; + case PROP_ROTATION: + g_value_set_int(value, src->capture_config.camera_parameters.rotation); + break; + case PROP_AWB_MODE: + g_value_set_enum(value, src->capture_config.camera_parameters.awbMode); + break; + case PROP_HFLIP: + g_value_set_boolean(value, !!(src->capture_config.camera_parameters.hflip)); + break; + case PROP_VFLIP: + g_value_set_boolean(value, !!(src->capture_config.camera_parameters.vflip)); + break; + case PROP_ROI_X: + g_value_set_float(value, src->capture_config.camera_parameters.roi.x); + break; + case PROP_ROI_Y: + g_value_set_float(value, src->capture_config.camera_parameters.roi.y); + break; + case PROP_ROI_W: + g_value_set_float(value, src->capture_config.camera_parameters.roi.w); + break; + case PROP_ROI_H: + g_value_set_float(value, src->capture_config.camera_parameters.roi.h); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; From 1b9a614272edb18492d0282f07c909e919650456 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 13 Oct 2013 18:01:00 +1100 Subject: [PATCH 08/77] rpicamsrc: Re-flow element source code with gst-indent --- sys/rpicamsrc/gstrpicamsrc.c | 281 ++++++++++++++++++----------------- 1 file changed, 147 insertions(+), 134 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 43de4b2436..010b97b242 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -117,7 +117,7 @@ enum PROP_ROI_H, }; -#define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ +#define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ #define BITRATE_HIGHEST 25000000 #define SHARPNESS_DEFAULT 0 @@ -160,11 +160,10 @@ enum "alignment = (string) { au }, " \ "profile = (string) { baseline, main, high }" -static GstStaticPadTemplate video_src_template = - GST_STATIC_PAD_TEMPLATE ("src", +static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (/*RAW_AND_JPEG_CAPS "; "*/ H264_CAPS) + GST_STATIC_CAPS ( /*RAW_AND_JPEG_CAPS "; " */ H264_CAPS) ); #define gst_rpi_cam_src_parent_class parent_class @@ -174,13 +173,14 @@ static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent); -static gboolean gst_rpi_cam_src_stop (GstBaseSrc *parent); +static gboolean gst_rpi_cam_src_start (GstBaseSrc * parent); +static gboolean gst_rpi_cam_src_stop (GstBaseSrc * parent); static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc * src, GstQuery * query); -static GstFlowReturn gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf); +static GstFlowReturn gst_rpi_cam_src_create (GstPushSrc * parent, + GstBuffer ** buf); static GstCaps *gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter); -static gboolean gst_rpi_cam_src_set_caps (GstBaseSrc * src, GstCaps *caps); +static gboolean gst_rpi_cam_src_set_caps (GstBaseSrc * src, GstCaps * caps); static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps); static void @@ -216,17 +216,16 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "Preview Encoded", "Display encoder output in the preview", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_OPACITY, - g_param_spec_int ("preview-opacity", "Preview Opacity", "Opacity to use for the preview window", - 0, 255, 255, + g_param_spec_int ("preview-opacity", "Preview Opacity", + "Opacity to use for the preview window", 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHARPNESS, g_param_spec_int ("sharpness", "Sharpness", "Image capture sharpness", -100, 100, SHARPNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CONTRAST, - g_param_spec_int ("contrast", "Contrast", "Image capture contrast", - -100, 100, CONTRAST_DEFAULT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("contrast", "Contrast", "Image capture contrast", -100, + 100, CONTRAST_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_BRIGHTNESS, g_param_spec_int ("brightness", "Brightness", "Image capture brightness", 0, 100, BRIGHTNESS_DEFAULT, @@ -236,16 +235,16 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) -100, 100, SATURATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ISO, - g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", - 0, 3200, 0, + g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", 0, 3200, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_VIDEO_STABILISATION, - g_param_spec_boolean ("video-stabilisation", - "Video Stabilisation", "Enable or disable video stabilisation", - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("video-stabilisation", "Video Stabilisation", + "Enable or disable video stabilisation", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EXPOSURE_COMPENSATION, - g_param_spec_int ("exposure-compensation", "EV compensation", "Exposure Value compensation", - -10, 10, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("exposure-compensation", "EV compensation", + "Exposure Value compensation", -10, 10, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EXPOSURE_MODE, g_param_spec_enum ("exposure-mode", "Exposure Mode", "Camera exposure mode to use", @@ -254,72 +253,75 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_object_class_install_property (gobject_class, PROP_EXPOSURE_METERING_MODE, g_param_spec_enum ("metering-mode", "Exposure Metering Mode", "Camera exposure metering mode to use", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, EXPOSURE_METERING_MODE_DEFAULT, + GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, + EXPOSURE_METERING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_MODE, g_param_spec_enum ("awb-mode", "Automatic White Balance Mode", - "White Balance mode", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, GST_RPI_CAM_SRC_AWB_MODE_AUTO, + "White Balance mode", GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, + GST_RPI_CAM_SRC_AWB_MODE_AUTO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); #if 0 - PROP_IMAGE_EFFECT, - PROP_IMAGE_EFFECT_PARAMS, - PROP_COLOUR_EFFECTS, + PROP_IMAGE_EFFECT, PROP_IMAGE_EFFECT_PARAMS, PROP_COLOUR_EFFECTS, #endif - g_object_class_install_property (gobject_class, PROP_ROTATION, - g_param_spec_int ("rotation", "Rotation", "Rotate captured image (0, 90, 180, 270 degrees)", - 0, 270, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROTATION, + g_param_spec_int ("rotation", "Rotation", + "Rotate captured image (0, 90, 180, 270 degrees)", 0, 270, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_HFLIP, - g_param_spec_boolean ("hflip", - "Horizontal Flip", "Flip capture horizontally", - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("hflip", "Horizontal Flip", + "Flip capture horizontally", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_VFLIP, - g_param_spec_boolean ("vflip", - "Vertical Flip", "Flip capture vertically", + g_param_spec_boolean ("vflip", "Vertical Flip", "Flip capture vertically", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_X, - g_param_spec_float ("roi-x", "ROI X", "Normalised region-of-interest X coord", - 0, 1.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_float ("roi-x", "ROI X", + "Normalised region-of-interest X coord", 0, 1.0, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_Y, - g_param_spec_float ("roi-y", "ROI Y", "Normalised region-of-interest Y coord", - 0, 1.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_float ("roi-y", "ROI Y", + "Normalised region-of-interest Y coord", 0, 1.0, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_W, - g_param_spec_float ("roi-w", "ROI W", "Normalised region-of-interest W coord", - 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_float ("roi-w", "ROI W", + "Normalised region-of-interest W coord", 0, 1.0, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_H, - g_param_spec_float ("roi-h", "ROI H", "Normalised region-of-interest H coord", - 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_float ("roi-h", "ROI H", + "Normalised region-of-interest H coord", 0, 1.0, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", - "Raspberry Pi camera module source", - "Jan Schmidt "); + "Raspberry Pi camera module source", "Jan Schmidt "); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&video_src_template)); - basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start); - basesrc_class->stop = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_stop); - basesrc_class->decide_allocation = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_decide_allocation); - basesrc_class->get_caps = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_get_caps); - basesrc_class->set_caps = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_set_caps); - basesrc_class->fixate = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fixate); - pushsrc_class->create = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_create); + basesrc_class->start = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_start); + basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_stop); + basesrc_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_rpi_cam_src_decide_allocation); + basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_get_caps); + basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_set_caps); + basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_fixate); + pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_create); - raspicapture_init(); + raspicapture_init (); } static void -gst_rpi_cam_src_init (GstRpiCamSrc *src) +gst_rpi_cam_src_init (GstRpiCamSrc * src) { gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); - gst_base_src_set_live (GST_BASE_SRC (src), TRUE); - raspicapture_default_config(&src->capture_config); + gst_base_src_set_live (GST_BASE_SRC (src), TRUE); + raspicapture_default_config (&src->capture_config); src->capture_config.verbose = 1; /* do-timestamping by default for now. FIXME: Implement proper timestamping */ - gst_base_src_set_do_timestamp(GST_BASE_SRC(src), TRUE); + gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE); } static void @@ -330,70 +332,78 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_BITRATE: - src->capture_config.bitrate = g_value_get_int(value); + src->capture_config.bitrate = g_value_get_int (value); break; case PROP_PREVIEW: - src->capture_config.preview_parameters.wantPreview = g_value_get_boolean(value); + src->capture_config.preview_parameters.wantPreview = + g_value_get_boolean (value); break; case PROP_PREVIEW_ENCODED: - src->capture_config.immutableInput = g_value_get_boolean(value); + src->capture_config.immutableInput = g_value_get_boolean (value); break; case PROP_FULLSCREEN: - src->capture_config.preview_parameters.wantFullScreenPreview = g_value_get_boolean(value); + src->capture_config.preview_parameters.wantFullScreenPreview = + g_value_get_boolean (value); break; case PROP_PREVIEW_OPACITY: - src->capture_config.preview_parameters.opacity = g_value_get_int(value); + src->capture_config.preview_parameters.opacity = g_value_get_int (value); break; case PROP_SHARPNESS: - src->capture_config.camera_parameters.sharpness = g_value_get_int(value); + src->capture_config.camera_parameters.sharpness = g_value_get_int (value); break; case PROP_CONTRAST: - src->capture_config.camera_parameters.contrast = g_value_get_int(value); + src->capture_config.camera_parameters.contrast = g_value_get_int (value); break; case PROP_BRIGHTNESS: - src->capture_config.camera_parameters.brightness = g_value_get_int(value); + src->capture_config.camera_parameters.brightness = + g_value_get_int (value); break; case PROP_SATURATION: - src->capture_config.camera_parameters.saturation = g_value_get_int(value); + src->capture_config.camera_parameters.saturation = + g_value_get_int (value); break; case PROP_ISO: - src->capture_config.camera_parameters.ISO = g_value_get_int(value); + src->capture_config.camera_parameters.ISO = g_value_get_int (value); break; case PROP_VIDEO_STABILISATION: - src->capture_config.camera_parameters.videoStabilisation = g_value_get_boolean(value); + src->capture_config.camera_parameters.videoStabilisation = + g_value_get_boolean (value); break; case PROP_EXPOSURE_COMPENSATION: - src->capture_config.camera_parameters.exposureCompensation = g_value_get_int(value); + src->capture_config.camera_parameters.exposureCompensation = + g_value_get_int (value); break; case PROP_EXPOSURE_MODE: - src->capture_config.camera_parameters.exposureMode = g_value_get_enum(value); + src->capture_config.camera_parameters.exposureMode = + g_value_get_enum (value); break; case PROP_EXPOSURE_METERING_MODE: - src->capture_config.camera_parameters.exposureMeterMode = g_value_get_enum(value); + src->capture_config.camera_parameters.exposureMeterMode = + g_value_get_enum (value); break; case PROP_ROTATION: - src->capture_config.camera_parameters.rotation = g_value_get_int(value); + src->capture_config.camera_parameters.rotation = g_value_get_int (value); break; case PROP_AWB_MODE: - src->capture_config.camera_parameters.awbMode = g_value_get_enum(value); + src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); break; case PROP_HFLIP: - src->capture_config.camera_parameters.hflip = g_value_get_boolean(value); + src->capture_config.camera_parameters.hflip = g_value_get_boolean (value); break; case PROP_VFLIP: - src->capture_config.camera_parameters.vflip = g_value_get_boolean(value); + src->capture_config.camera_parameters.vflip = g_value_get_boolean (value); break; case PROP_ROI_X: - src->capture_config.camera_parameters.roi.x = g_value_get_float(value); + src->capture_config.camera_parameters.roi.x = g_value_get_float (value); break; case PROP_ROI_Y: - src->capture_config.camera_parameters.roi.y = g_value_get_float(value); + src->capture_config.camera_parameters.roi.y = g_value_get_float (value); break; case PROP_ROI_W: - src->capture_config.camera_parameters.roi.w = g_value_get_float(value); + src->capture_config.camera_parameters.roi.w = g_value_get_float (value); break; case PROP_ROI_H: - src->capture_config.camera_parameters.roi.h = g_value_get_float(value); + src->capture_config.camera_parameters.roi.h = g_value_get_float (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -409,70 +419,78 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_BITRATE: - g_value_set_int(value, src->capture_config.bitrate); + g_value_set_int (value, src->capture_config.bitrate); break; case PROP_PREVIEW: - g_value_set_boolean(value, src->capture_config.preview_parameters.wantPreview); + g_value_set_boolean (value, + src->capture_config.preview_parameters.wantPreview); break; case PROP_PREVIEW_ENCODED: - g_value_set_boolean(value, src->capture_config.immutableInput); + g_value_set_boolean (value, src->capture_config.immutableInput); break; case PROP_FULLSCREEN: - g_value_set_boolean(value, src->capture_config.preview_parameters.wantFullScreenPreview); + g_value_set_boolean (value, + src->capture_config.preview_parameters.wantFullScreenPreview); break; case PROP_PREVIEW_OPACITY: - g_value_set_int(value, src->capture_config.preview_parameters.opacity); + g_value_set_int (value, src->capture_config.preview_parameters.opacity); break; case PROP_SHARPNESS: - g_value_set_int(value, src->capture_config.camera_parameters.sharpness); + g_value_set_int (value, src->capture_config.camera_parameters.sharpness); break; case PROP_CONTRAST: - g_value_set_int(value, src->capture_config.camera_parameters.contrast); + g_value_set_int (value, src->capture_config.camera_parameters.contrast); break; case PROP_BRIGHTNESS: - g_value_set_int(value, src->capture_config.camera_parameters.brightness); + g_value_set_int (value, src->capture_config.camera_parameters.brightness); break; case PROP_SATURATION: - g_value_set_int(value, src->capture_config.camera_parameters.saturation); + g_value_set_int (value, src->capture_config.camera_parameters.saturation); break; case PROP_ISO: - g_value_set_int(value, src->capture_config.camera_parameters.ISO); + g_value_set_int (value, src->capture_config.camera_parameters.ISO); break; case PROP_VIDEO_STABILISATION: - g_value_set_boolean(value, !!(src->capture_config.camera_parameters.videoStabilisation)); + g_value_set_boolean (value, + ! !(src->capture_config.camera_parameters.videoStabilisation)); break; case PROP_EXPOSURE_COMPENSATION: - g_value_set_int(value, src->capture_config.camera_parameters.exposureCompensation); + g_value_set_int (value, + src->capture_config.camera_parameters.exposureCompensation); break; case PROP_EXPOSURE_MODE: - g_value_set_enum(value, src->capture_config.camera_parameters.exposureMode); + g_value_set_enum (value, + src->capture_config.camera_parameters.exposureMode); break; case PROP_EXPOSURE_METERING_MODE: - g_value_set_enum(value, src->capture_config.camera_parameters.exposureMeterMode); + g_value_set_enum (value, + src->capture_config.camera_parameters.exposureMeterMode); break; case PROP_ROTATION: - g_value_set_int(value, src->capture_config.camera_parameters.rotation); + g_value_set_int (value, src->capture_config.camera_parameters.rotation); break; case PROP_AWB_MODE: - g_value_set_enum(value, src->capture_config.camera_parameters.awbMode); + g_value_set_enum (value, src->capture_config.camera_parameters.awbMode); break; case PROP_HFLIP: - g_value_set_boolean(value, !!(src->capture_config.camera_parameters.hflip)); + g_value_set_boolean (value, + ! !(src->capture_config.camera_parameters.hflip)); break; case PROP_VFLIP: - g_value_set_boolean(value, !!(src->capture_config.camera_parameters.vflip)); + g_value_set_boolean (value, + ! !(src->capture_config.camera_parameters.vflip)); break; case PROP_ROI_X: - g_value_set_float(value, src->capture_config.camera_parameters.roi.x); + g_value_set_float (value, src->capture_config.camera_parameters.roi.x); break; case PROP_ROI_Y: - g_value_set_float(value, src->capture_config.camera_parameters.roi.y); + g_value_set_float (value, src->capture_config.camera_parameters.roi.y); break; case PROP_ROI_W: - g_value_set_float(value, src->capture_config.camera_parameters.roi.w); + g_value_set_float (value, src->capture_config.camera_parameters.roi.w); break; case PROP_ROI_H: - g_value_set_float(value, src->capture_config.camera_parameters.roi.h); + g_value_set_float (value, src->capture_config.camera_parameters.roi.h); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -481,11 +499,11 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, } static gboolean -gst_rpi_cam_src_start (GstBaseSrc *parent) +gst_rpi_cam_src_start (GstBaseSrc * parent) { - GstRpiCamSrc *src = GST_RPICAMSRC(parent); + GstRpiCamSrc *src = GST_RPICAMSRC (parent); g_print ("In src_start()\n"); - src->capture_state = raspi_capture_setup(&src->capture_config); + src->capture_state = raspi_capture_setup (&src->capture_config); if (src->capture_state == NULL) return FALSE; @@ -493,65 +511,65 @@ gst_rpi_cam_src_start (GstBaseSrc *parent) } static gboolean -gst_rpi_cam_src_stop (GstBaseSrc *parent) +gst_rpi_cam_src_stop (GstBaseSrc * parent) { - GstRpiCamSrc *src = GST_RPICAMSRC(parent); + GstRpiCamSrc *src = GST_RPICAMSRC (parent); if (src->started) - raspi_capture_stop(src->capture_state); - raspi_capture_free(src->capture_state); + raspi_capture_stop (src->capture_state); + raspi_capture_free (src->capture_state); src->capture_state = NULL; return TRUE; } static GstCaps * -gst_rpi_cam_src_get_caps (GstBaseSrc *bsrc, GstCaps * filter) +gst_rpi_cam_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) { - GstRpiCamSrc *src = GST_RPICAMSRC(bsrc); + GstRpiCamSrc *src = GST_RPICAMSRC (bsrc); GstCaps *caps; - + caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc)); if (src->capture_state == NULL) goto done; /* FIXME: Retrieve limiting parameters from the camera module, max width/height fps-range */ - caps = gst_caps_make_writable(caps); + caps = gst_caps_make_writable (caps); gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 1920, "height", GST_TYPE_INT_RANGE, 1, 1080, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60, 1, - NULL); + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60, 1, NULL); done: - GST_DEBUG_OBJECT(src, "get_caps returning %" GST_PTR_FORMAT, caps); + GST_DEBUG_OBJECT (src, "get_caps returning %" GST_PTR_FORMAT, caps); return caps; } static gboolean -gst_rpi_cam_src_set_caps (GstBaseSrc *bsrc, GstCaps *caps) +gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) { - GstRpiCamSrc *src = GST_RPICAMSRC(bsrc); + GstRpiCamSrc *src = GST_RPICAMSRC (bsrc); GstVideoInfo info; GST_DEBUG_OBJECT (src, "In set_caps %" GST_PTR_FORMAT, caps); if (!gst_video_info_from_caps (&info, caps)) return FALSE; - + src->capture_config.width = info.width; src->capture_config.height = info.height; src->capture_config.fps_n = info.fps_n; src->capture_config.fps_d = info.fps_d; - + return TRUE; } -static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc *bsrc, - GstQuery * query) +static gboolean +gst_rpi_cam_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) { g_print ("In decide_allocation\n"); return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); } -static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) +static GstCaps * +gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) { GstStructure *structure; gint i; @@ -578,21 +596,22 @@ static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) } static GstFlowReturn -gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf) +gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) { - GstRpiCamSrc *src = GST_RPICAMSRC(parent); + GstRpiCamSrc *src = GST_RPICAMSRC (parent); GstFlowReturn ret; if (!src->started) { - if (!raspi_capture_start(src->capture_state)) + if (!raspi_capture_start (src->capture_state)) return GST_FLOW_ERROR; src->started = TRUE; } /* FIXME: Use custom allocator */ - ret = raspi_capture_fill_buffer(src->capture_state, buf); + ret = raspi_capture_fill_buffer (src->capture_state, buf); if (*buf) - GST_LOG_OBJECT(src, "Made buffer of size %" G_GSIZE_FORMAT "\n", gst_buffer_get_size(*buf)); + GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT "\n", + gst_buffer_get_size (*buf)); return ret; } @@ -611,14 +630,8 @@ plugin_init (GstPlugin * rpicamsrc) #define PACKAGE "gstrpicamsrc" #endif -GST_PLUGIN_DEFINE ( - GST_VERSION_MAJOR, +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, rpicamsrc, "Raspberry Pi Camera Source", - plugin_init, - VERSION, - "LGPL", - "GStreamer", - "http://gstreamer.net/" -) + plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/") From 1dfb41bdce07f3300f9688163222dfd7d65637fb Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 14 Oct 2013 02:39:00 +1100 Subject: [PATCH 09/77] rpicamsrc: Enable image effects --- sys/rpicamsrc/RaspiCamControl.c | 1 + sys/rpicamsrc/gstrpicamsrc.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c index c1b246510e..50d2ab2530 100644 --- a/sys/rpicamsrc/RaspiCamControl.c +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -581,6 +581,7 @@ void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params) fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); //fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); + fprintf(stderr, "Exposure Mode '%d', AWB Mode '%d', Image Effect '%d'\n", params->exposureMode, params->awbMode, params->imageEffect); //fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h); diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 010b97b242..c8a42d612b 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -261,8 +261,14 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "White Balance mode", GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, GST_RPI_CAM_SRC_AWB_MODE_AUTO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_IMAGE_EFFECT, + g_param_spec_enum ("image-effect", "Image effect", + "Visual FX to apply to the image", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_IMAGE_EFFECT, + GST_RPI_CAM_SRC_IMAGEFX_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); #if 0 - PROP_IMAGE_EFFECT, PROP_IMAGE_EFFECT_PARAMS, PROP_COLOUR_EFFECTS, + PROP_IMAGE_EFFECT_PARAMS, PROP_COLOUR_EFFECTS, #endif g_object_class_install_property (gobject_class, PROP_ROTATION, g_param_spec_int ("rotation", "Rotation", @@ -387,6 +393,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_AWB_MODE: src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); break; + case PROP_IMAGE_EFFECT: + src->capture_config.camera_parameters.imageEffect = g_value_get_enum (value); + break; case PROP_HFLIP: src->capture_config.camera_parameters.hflip = g_value_get_boolean (value); break; @@ -472,6 +481,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_AWB_MODE: g_value_set_enum (value, src->capture_config.camera_parameters.awbMode); break; + case PROP_IMAGE_EFFECT: + g_value_set_enum (value, src->capture_config.camera_parameters.imageEffect); + break; case PROP_HFLIP: g_value_set_boolean (value, ! !(src->capture_config.camera_parameters.hflip)); From df3ceb86f71c0882e8995da40b0af5099e04fd7d Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 19 Oct 2013 18:52:25 +1100 Subject: [PATCH 10/77] rpicamsrc: Update maximum framerate to 90 fps --- sys/rpicamsrc/gstrpicamsrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index c8a42d612b..874ee85368 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -548,7 +548,7 @@ gst_rpi_cam_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 1920, "height", GST_TYPE_INT_RANGE, 1, 1080, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60, 1, NULL); + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 90, 1, NULL); done: GST_DEBUG_OBJECT (src, "get_caps returning %" GST_PTR_FORMAT, caps); From 244091bb5e39b287b65942203f7f652bd825e64e Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 13 Mar 2014 00:16:18 +1100 Subject: [PATCH 11/77] rpicamsrc: Move all debug output to go via GStreamer logs Fixes https://github.com/thaytan/gst-rpicamsrc/issues/9 --- sys/rpicamsrc/RaspiCamControl.c | 3 +++ sys/rpicamsrc/RaspiCapture.c | 2 +- sys/rpicamsrc/RaspiCapture.h | 5 +++++ sys/rpicamsrc/RaspiPreview.c | 2 ++ sys/rpicamsrc/gstrpicamsrc.c | 9 ++++----- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c index 50d2ab2530..a93fdc4e96 100644 --- a/sys/rpicamsrc/RaspiCamControl.c +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -31,6 +31,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include + #include "interface/vcos/vcos.h" #include "interface/vmcs_host/vc_vchi_gencmd.h" @@ -40,6 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "interface/mmal/util/mmal_util_params.h" #include "interface/mmal/util/mmal_default_components.h" #include "RaspiCamControl.h" +#include "RaspiCapture.h" #if 0 /// Structure to cross reference exposure strings against the MMAL parameter equivalent diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 164ed6923c..797147fabb 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -798,7 +798,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (encoder_output->buffer_size < encoder_output->buffer_size_min) encoder_output->buffer_size = encoder_output->buffer_size_min; - g_print("encoder buffer size is %u\n", (guint)encoder_output->buffer_size); + GST_DEBUG ("encoder buffer size is %u", (guint)encoder_output->buffer_size); encoder_output->buffer_num = encoder_output->buffer_num_recommended; diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 54ebdbaaa2..3c6dcf5b1f 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -54,6 +54,11 @@ #include "RaspiCamControl.h" #include "RaspiPreview.h" +GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug); +#define GST_CAT_DEFAULT gst_rpi_cam_src_debug + +#undef fprintf +#define fprintf(f,...) GST_LOG(__VA_ARGS__) G_BEGIN_DECLS /** Structure containing all state information for the current run diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c index 08d2a74e1c..de11df3471 100644 --- a/sys/rpicamsrc/RaspiPreview.c +++ b/sys/rpicamsrc/RaspiPreview.c @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "interface/vcos/vcos.h" @@ -44,6 +45,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "interface/mmal/util/mmal_connection.h" #include "RaspiPreview.h" +#include "RaspiCapture.h" #if 0 #define CommandPreview 1 diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 874ee85368..2900bb2550 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -77,8 +77,7 @@ #include "interface/mmal/util/mmal_default_components.h" #include "interface/mmal/util/mmal_connection.h" -GST_DEBUG_CATEGORY_STATIC (gst_rpi_cam_src_debug); -#define GST_CAT_DEFAULT gst_rpi_cam_src_debug +GST_DEBUG_CATEGORY (gst_rpi_cam_src_debug); /* Filter signals and args */ enum @@ -514,7 +513,7 @@ static gboolean gst_rpi_cam_src_start (GstBaseSrc * parent) { GstRpiCamSrc *src = GST_RPICAMSRC (parent); - g_print ("In src_start()\n"); + GST_LOG_OBJECT (src, "In src_start()"); src->capture_state = raspi_capture_setup (&src->capture_config); if (src->capture_state == NULL) return FALSE; @@ -576,7 +575,7 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) { - g_print ("In decide_allocation\n"); + GST_LOG_OBJECT (bsrc, "In decide_allocation"); return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); } @@ -622,7 +621,7 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) /* FIXME: Use custom allocator */ ret = raspi_capture_fill_buffer (src->capture_state, buf); if (*buf) - GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT "\n", + GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (*buf)); return ret; From d226968275680750634cc644653dbaef0dabb391 Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Thu, 9 Oct 2014 20:38:41 +0000 Subject: [PATCH 12/77] rpicamsrc: Add force-key-unit event support --- sys/rpicamsrc/RaspiCapture.c | 15 ++++++++++ sys/rpicamsrc/RaspiCapture.h | 1 + sys/rpicamsrc/gstrpicamsrc.c | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 797147fabb..3688ae4152 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -752,6 +752,21 @@ static void destroy_camera_component(RASPIVID_STATE *state) } } +gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state) +{ + MMAL_PORT_T *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_PARAMETER_BOOLEAN_T param = {{ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, sizeof(param)}, 1}; + encoder_output = state->encoder_component->output[0]; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to request I-frame"); + return FALSE; + } + return TRUE; +} + /** * Create the encoder component, set up its ports * diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 3c6dcf5b1f..37a7ba7bef 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -92,6 +92,7 @@ gboolean raspi_capture_start(RASPIVID_STATE *state); GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf); void raspi_capture_stop(RASPIVID_STATE *state); void raspi_capture_free(RASPIVID_STATE *state); +gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state); G_END_DECLS diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 2900bb2550..5d2d0c3ea7 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -181,6 +181,8 @@ static GstFlowReturn gst_rpi_cam_src_create (GstPushSrc * parent, static GstCaps *gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter); static gboolean gst_rpi_cam_src_set_caps (GstBaseSrc * src, GstCaps * caps); static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps); +static gboolean gst_rpi_cam_src_event (GstBaseSrc * src, GstEvent * event); +static gboolean gst_rpi_cam_src_send_event (GstElement * element, GstEvent * event); static void gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) @@ -312,6 +314,8 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_get_caps); basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_set_caps); basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_fixate); + basesrc_class->event = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_event); + gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_send_event); pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_create); raspicapture_init (); @@ -532,6 +536,58 @@ gst_rpi_cam_src_stop (GstBaseSrc * parent) return TRUE; } +static gboolean +gst_rpi_cam_src_send_event (GstElement * parent, GstEvent * event) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (parent); + gboolean ret; + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM: + case GST_EVENT_CUSTOM_UPSTREAM: + if (gst_video_event_is_force_key_unit (event)) { + if (src->started) { + ret = raspi_capture_request_i_frame (src->capture_state); + } else { + ret = FALSE; + } + gst_event_unref (event); + } else { + ret = GST_ELEMENT_CLASS (parent_class)->send_event(parent, event); + } + break; + default: + ret = GST_ELEMENT_CLASS (parent_class)->send_event(parent, event); + break; + } + return ret; +} + +static gboolean +gst_rpi_cam_src_event (GstBaseSrc * parent, GstEvent * event) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (parent); + gboolean ret; + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM: + case GST_EVENT_CUSTOM_UPSTREAM: + if (gst_video_event_is_force_key_unit (event)) { + if (src->started) { + ret = raspi_capture_request_i_frame (src->capture_state); + } else { + ret = FALSE; + } + gst_event_unref (event); + } else { + ret = GST_BASE_SRC_CLASS (parent_class)->event(parent, event); + } + break; + default: + ret = GST_BASE_SRC_CLASS (parent_class)->event(parent, event); + break; + } + return ret; +} + static GstCaps * gst_rpi_cam_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) { From 4b15fa2abce9c6644e7d1ffc2fe528977125e377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 27 Sep 2014 14:31:10 +0100 Subject: [PATCH 13/77] rpicamsrc: avoid single-element lists in template caps --- sys/rpicamsrc/gstrpicamsrc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 5d2d0c3ea7..1a038d4b7d 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -155,8 +155,8 @@ enum "width = " GST_VIDEO_SIZE_RANGE ", " \ "height = " GST_VIDEO_SIZE_RANGE ", " \ "framerate = " GST_VIDEO_FPS_RANGE ", " \ - "stream-format = (string) { byte-stream }, " \ - "alignment = (string) { au }, " \ + "stream-format = (string) byte-stream, " \ + "alignment = (string) au, " \ "profile = (string) { baseline, main, high }" static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", From 8e9c8663f8a9bf09d1bda87ecb3268649bd05c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 29 Oct 2014 00:43:51 +0000 Subject: [PATCH 14/77] rpicamsrc: Add GstDeviceProvider for rpi camera module --- sys/rpicamsrc/gstrpicamsrc.c | 16 ++- sys/rpicamsrc/gstrpicamsrcdeviceprovider.c | 135 +++++++++++++++++++++ sys/rpicamsrc/gstrpicamsrcdeviceprovider.h | 79 ++++++++++++ 3 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 sys/rpicamsrc/gstrpicamsrcdeviceprovider.c create mode 100644 sys/rpicamsrc/gstrpicamsrcdeviceprovider.h diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 1a038d4b7d..42b0d9a3ae 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -64,6 +64,7 @@ #include "gstrpicamsrc.h" #include "gstrpicam_types.h" #include "gstrpicam-enum-types.h" +#include "gstrpicamsrcdeviceprovider.h" #include "RaspiCapture.h" #include "bcm_host.h" @@ -684,13 +685,24 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) } static gboolean -plugin_init (GstPlugin * rpicamsrc) +plugin_init (GstPlugin * plugin) { + gboolean ret; + GST_DEBUG_CATEGORY_INIT (gst_rpi_cam_src_debug, "rpicamsrc", 0, "rpicamsrc debug"); - return gst_element_register (rpicamsrc, "rpicamsrc", GST_RANK_NONE, + ret = gst_element_register (plugin, "rpicamsrc", GST_RANK_NONE, GST_TYPE_RPICAMSRC); + +#if GST_CHECK_VERSION (1,4,0) + + ret &= gst_device_provider_register (plugin, "rpicamsrcdeviceprovider", + GST_RANK_PRIMARY, GST_TYPE_RPICAMSRC_DEVICE_PROVIDER); + +#endif + + return ret; } #ifndef PACKAGE diff --git a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c new file mode 100644 index 0000000000..1a8b8229a4 --- /dev/null +++ b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c @@ -0,0 +1,135 @@ +/* GStreamer Raspberry Pi Camera Source Device Provider + * Copyright (C) 2014 Tim-Philipp Müller + * + * 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 "config.h" +#endif + +#include "gstrpicamsrcdeviceprovider.h" + +#include + +#if GST_CHECK_VERSION (1,4,0) + +/* FIXME: translations */ +#define _(s) s + +static GstRpiCamSrcDevice * gst_rpi_cam_src_device_new (void); + +G_DEFINE_TYPE (GstRpiCamSrcDeviceProvider, gst_rpi_cam_src_device_provider, + GST_TYPE_DEVICE_PROVIDER); + + +static GList *gst_rpi_cam_src_device_provider_probe (GstDeviceProvider * provider); + +static void +gst_rpi_cam_src_device_provider_class_init (GstRpiCamSrcDeviceProviderClass * klass) +{ + GstDeviceProviderClass *dprovider_class = GST_DEVICE_PROVIDER_CLASS (klass); + + dprovider_class->probe = gst_rpi_cam_src_device_provider_probe; + + gst_device_provider_class_set_static_metadata (dprovider_class, + "Raspberry Pi Camera Source Device Provider", "Source/Video", + "Lists Raspberry Pi camera devices", + "Tim-Philipp Müller "); +} + +static void +gst_rpi_cam_src_device_provider_init (GstRpiCamSrcDeviceProvider * provider) +{ + /* nothing to do here yet */ +} + +static GList * +gst_rpi_cam_src_device_provider_probe (GstDeviceProvider * provider) +{ + GstRpiCamSrcDevice *device; + + /* FIXME: check if camera module is usable and supported */ + device = gst_rpi_cam_src_device_new (); + + return g_list_append (NULL, device); +} + +G_DEFINE_TYPE (GstRpiCamSrcDevice, gst_rpi_cam_src_device, GST_TYPE_DEVICE); + +static GstElement *gst_rpi_cam_src_device_create_element (GstDevice * device, + const gchar * name); + +static void +gst_rpi_cam_src_device_class_init (GstRpiCamSrcDeviceClass * klass) +{ + GstDeviceClass *device_class = GST_DEVICE_CLASS (klass); + + device_class->create_element = gst_rpi_cam_src_device_create_element; +} + +static void +gst_rpi_cam_src_device_init (GstRpiCamSrcDevice * device) +{ + /* nothing to do here */ +} + +static GstElement * +gst_rpi_cam_src_device_create_element (GstDevice * device, const gchar * name) +{ + return gst_element_factory_make ("rpicamsrc", name); +} + +static GstRpiCamSrcDevice * +gst_rpi_cam_src_device_new (void) +{ + GstRpiCamSrcDevice *device; + GValue profiles = G_VALUE_INIT; + GValue val = G_VALUE_INIT; + GstStructure *s; + GstCaps *caps; + + /* FIXME: retrieve limits from the camera module, max width/height/fps etc. */ + s = gst_structure_new ("video/x-h264", + "width", GST_TYPE_INT_RANGE, 1, 1920, + "height", GST_TYPE_INT_RANGE, 1, 1080, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 90, 1, + "stream-format", G_TYPE_STRING, "byte-stream", + "alignment", G_TYPE_STRING, "au", + NULL); + + g_value_init (&profiles, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + g_value_set_static_string (&val, "high"); + gst_value_list_append_value (&profiles, &val); + g_value_set_static_string (&val, "main"); + gst_value_list_append_value (&profiles, &val); + g_value_set_static_string (&val, "baseline"); + gst_value_list_append_and_take_value (&profiles, &val); + gst_structure_take_value (s, "profiles", &profiles); + + caps = gst_caps_new_full (s, NULL); + + device = g_object_new (GST_TYPE_RPICAMSRC_DEVICE, + "display-name", _("Raspberry Pi Camera Module"), + "device-class", "Video/Source", "caps", caps, NULL); + + gst_caps_unref (caps); + + return device; +} + +#endif /* GST_CHECK_VERSION (1,4,0) */ diff --git a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.h b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.h new file mode 100644 index 0000000000..cd5e9aad38 --- /dev/null +++ b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.h @@ -0,0 +1,79 @@ +/* GStreamer Raspberry Pi Camera Source Device Provider + * Copyright (C) 2014 Tim-Philipp Müller + * + * 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_RPICAMSRC_DEVICE_PROVIDER_H__ +#define __GST_RPICAMSRC_DEVICE_PROVIDER_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#if GST_CHECK_VERSION (1,4,0) + +G_BEGIN_DECLS + +typedef struct _GstRpiCamSrcDeviceProvider GstRpiCamSrcDeviceProvider; +typedef struct _GstRpiCamSrcDeviceProviderClass GstRpiCamSrcDeviceProviderClass; + +#define GST_TYPE_RPICAMSRC_DEVICE_PROVIDER (gst_rpi_cam_src_device_provider_get_type()) +#define GST_IS_RPICAMSRC_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER)) +#define GST_IS_RPICAMSRC_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER)) +#define GST_RPICAMSRC_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER, GstRpiCamSrcDeviceProviderClass)) +#define GST_RPICAMSRC_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER, GstRpiCamSrcDeviceProvider)) +#define GST_RPICAMSRC_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstRpiCamSrcDeviceProviderClass)) +#define GST_RPICAMSRC_DEVICE_PROVIDER_CAST(obj) ((GstRpiCamSrcDeviceProvider *)(obj)) + +struct _GstRpiCamSrcDeviceProvider { + GstDeviceProvider parent; +}; + +struct _GstRpiCamSrcDeviceProviderClass { + GstDeviceProviderClass parent_class; +}; + +GType gst_rpi_cam_src_device_provider_get_type (void); + +typedef struct _GstRpiCamSrcDevice GstRpiCamSrcDevice; +typedef struct _GstRpiCamSrcDeviceClass GstRpiCamSrcDeviceClass; + +#define GST_TYPE_RPICAMSRC_DEVICE (gst_rpi_cam_src_device_get_type()) +#define GST_IS_RPICAMSRC_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RPICAMSRC_DEVICE)) +#define GST_IS_RPICAMSRC_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RPICAMSRC_DEVICE)) +#define GST_RPICAMSRC_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RPICAMSRC_DEVICE, GstRpiCamSrcDeviceClass)) +#define GST_RPICAMSRC_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RPICAMSRC_DEVICE, GstRpiCamSrcDevice)) +#define GST_RPICAMSRC_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstRpiCamSrcDeviceClass)) +#define GST_RPICAMSRC_DEVICE_CAST(obj) ((GstRpiCamSrcDevice *)(obj)) + +struct _GstRpiCamSrcDevice { + GstDevice parent; +}; + +struct _GstRpiCamSrcDeviceClass { + GstDeviceClass parent_class; +}; + +GType gst_rpi_cam_src_device_get_type (void); + +G_END_DECLS + +#endif /* GST_CHECK_VERSION (1,4,0) */ + +#endif /* __GST_RPICAMSRC_DEVICE_PROVIDER_H__ */ From 27c35a8ff39078de7694a9aabfd2a69f10add58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 30 Oct 2014 00:45:18 +0000 Subject: [PATCH 15/77] rpicamsrc: deviceprovider: check if camera is detected and supported --- sys/rpicamsrc/RaspiCamControl.c | 2 +- sys/rpicamsrc/RaspiCamControl.h | 1 + sys/rpicamsrc/gstrpicamsrcdeviceprovider.c | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c index a93fdc4e96..d03cb4e626 100644 --- a/sys/rpicamsrc/RaspiCamControl.c +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -1094,7 +1094,7 @@ static int raspicamcontrol_get_mem_gpu(void) * @param supported None-zero if software supports the camera * @param detected None-zero if a camera has been detected */ -static void raspicamcontrol_get_camera(int *supported, int *detected) +void raspicamcontrol_get_camera(int *supported, int *detected) { char response[80] = ""; if (vc_gencmd(response, sizeof response, "get_camera") == 0) diff --git a/sys/rpicamsrc/RaspiCamControl.h b/sys/rpicamsrc/RaspiCamControl.h index 847b4470c0..aac3e19cad 100644 --- a/sys/rpicamsrc/RaspiCamControl.h +++ b/sys/rpicamsrc/RaspiCamControl.h @@ -147,6 +147,7 @@ void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params); void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params); void raspicamcontrol_check_configuration(int min_gpu_mem); +void raspicamcontrol_get_camera(int *supported, int *detected); // Individual setting functions int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation); diff --git a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c index 1a8b8229a4..387ee61c27 100644 --- a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c +++ b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c @@ -27,6 +27,8 @@ #if GST_CHECK_VERSION (1,4,0) +#include "RaspiCapture.h" + /* FIXME: translations */ #define _(s) s @@ -54,15 +56,27 @@ gst_rpi_cam_src_device_provider_class_init (GstRpiCamSrcDeviceProviderClass * kl static void gst_rpi_cam_src_device_provider_init (GstRpiCamSrcDeviceProvider * provider) { - /* nothing to do here yet */ + raspicapture_init (); } static GList * gst_rpi_cam_src_device_provider_probe (GstDeviceProvider * provider) { GstRpiCamSrcDevice *device; + int supported = 0, detected = 0; + + raspicamcontrol_get_camera (&supported, &detected); + + if (!detected) { + GST_INFO ("No Raspberry Pi camera module detected."); + return NULL; + } else if (!supported) { + GST_WARNING ("Raspberry Pi camera module not supported, make sure to enable it."); + return NULL; + } + + GST_INFO ("Raspberry Pi camera module detected and supported."); - /* FIXME: check if camera module is usable and supported */ device = gst_rpi_cam_src_device_new (); return g_list_append (NULL, device); From eb345f032c5c6dfb46972414ae983896751ec2c5 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 5 Jan 2015 02:21:16 +1100 Subject: [PATCH 16/77] rpicamsrc: Add keyframe-interval property to the element --- sys/rpicamsrc/gstrpicamsrc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 42b0d9a3ae..8278ee05b0 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1,6 +1,6 @@ /* * GStreamer - * Copyright (C) 2013 Jan Schmidt + * Copyright (C) 2013-2014 Jan Schmidt * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -91,6 +91,7 @@ enum { PROP_0, PROP_BITRATE, + PROP_KEYFRAME_INTERVAL, PROP_PREVIEW, PROP_PREVIEW_ENCODED, PROP_PREVIEW_OPACITY, @@ -127,6 +128,7 @@ enum #define ISO_DEFAULT 0 #define VIDEO_STABILISATION_DEFAULT FALSE #define EXPOSURE_COMPENSATION_DEFAULT 0 +#define KEYFRAME_INTERVAL_DEFAULT 25 #define EXPOSURE_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO #define EXPOSURE_METERING_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE @@ -205,6 +207,11 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_int ("bitrate", "Bitrate", "Bitrate for encoding", 1, BITRATE_HIGHEST, BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_KEYFRAME_INTERVAL, + g_param_spec_int ("keyframe-interval", "Keyframe Interface", + "Interval (in frames) between I frames. 0 = single-keyframe", + 0, G_MAXINT, KEYFRAME_INTERVAL_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_PREVIEW, g_param_spec_boolean ("preview", "Preview Window", "Display preview window overlay", @@ -329,7 +336,9 @@ gst_rpi_cam_src_init (GstRpiCamSrc * src) gst_base_src_set_live (GST_BASE_SRC (src), TRUE); raspicapture_default_config (&src->capture_config); + src->capture_config.intraperiod = KEYFRAME_INTERVAL_DEFAULT; src->capture_config.verbose = 1; + /* do-timestamping by default for now. FIXME: Implement proper timestamping */ gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE); } @@ -344,6 +353,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_BITRATE: src->capture_config.bitrate = g_value_get_int (value); break; + case PROP_KEYFRAME_INTERVAL: + src->capture_config.intraperiod = g_value_get_int (value); + break; case PROP_PREVIEW: src->capture_config.preview_parameters.wantPreview = g_value_get_boolean (value); @@ -434,6 +446,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_BITRATE: g_value_set_int (value, src->capture_config.bitrate); break; + case PROP_KEYFRAME_INTERVAL: + g_value_set_int (value, src->capture_config.intraperiod); + break; case PROP_PREVIEW: g_value_set_boolean (value, src->capture_config.preview_parameters.wantPreview); From 6bd0347bf0fcda6db28d00745ef75431f11a0e4a Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 5 Mar 2015 17:01:33 +1100 Subject: [PATCH 17/77] rpicamsrc: Incorporate raspivid changes from upstream Merge all changes for new features from upstream raspberrypi userland, up to commit 0de0b2 --- sys/rpicamsrc/RaspiCLI.c | 155 ++++++++ sys/rpicamsrc/RaspiCLI.h | 56 +++ sys/rpicamsrc/RaspiCamControl.c | 228 ++++++++++- sys/rpicamsrc/RaspiCamControl.h | 32 +- sys/rpicamsrc/RaspiCapture.c | 654 +++++++++++++++++++++++++++++--- sys/rpicamsrc/RaspiCapture.h | 13 +- sys/rpicamsrc/RaspiPreview.c | 2 +- sys/rpicamsrc/RaspiPreview.h | 14 +- 8 files changed, 1094 insertions(+), 60 deletions(-) create mode 100644 sys/rpicamsrc/RaspiCLI.c create mode 100644 sys/rpicamsrc/RaspiCLI.h diff --git a/sys/rpicamsrc/RaspiCLI.c b/sys/rpicamsrc/RaspiCLI.c new file mode 100644 index 0000000000..2bbb3ce095 --- /dev/null +++ b/sys/rpicamsrc/RaspiCLI.c @@ -0,0 +1,155 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiCLI.c + * Code for handling command line parameters + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * Some functions/structures for command line parameter parsing + * + */ +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "RaspiCLI.h" + + +/** + * Convert a string from command line to a comand_id from the list + * + * @param commands Array of command to check + * @param num_command Number of commands in the array + * @param arg String to search for in the list + * @param num_parameters Returns the number of parameters used by the command + * + * @return command ID if found, -1 if not found + * + */ +int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters) +{ + int command_id = -1; + int j; + + vcos_assert(commands); + vcos_assert(num_parameters); + vcos_assert(arg); + + if (!commands || !num_parameters || !arg) + return -1; + + for (j = 0; j < num_commands; j++) + { + if (!strcmp(arg, commands[j].command) || + !strcmp(arg, commands[j].abbrev)) + { + // match + command_id = commands[j].id; + *num_parameters = commands[j].num_parameters; + break; + } + } + + return command_id; +} + + +/** + * Display the list of commands in help format + * + * @param commands Array of command to check + * @param num_command Number of commands in the arry + * + * + */ +void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands) +{ + int i; + + vcos_assert(commands); + + if (!commands) + return; + + for (i = 0; i < num_commands; i++) + { + fprintf(stderr, "-%s, -%s\t: %s\n", commands[i].abbrev, + commands[i].command, commands[i].help); + } +} + + +/** + * Function to take a string, a mapping, and return the int equivalent + * @param str Incoming string to match + * @param map Mapping data + * @param num_refs The number of items in the mapping data + * @return The integer match for the string, or -1 if no match + */ +int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs) +{ + int i; + + for (i=0;ishutter_speed); + used = 2; + break; + } + + case CommandAwbGains : + { + double r,b; + int args; + + args = sscanf(arg2, "%lf,%lf", &r,&b); + + if (args != 2 || r > 8.0 || b > 8.0) + { + return 0; + } + + params->awb_gains_r = r; + params->awb_gains_b = b; + + used = 2; + break; + } + + case CommandDRCLevel: + { + params->drc_level = drc_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStatsPass: + { + params->stats_pass = MMAL_TRUE; + used = 1; + break; + } + + case CommandAnnotate: + { + // If parameter is a number, assume its a bitmask, otherwise a string + if (isdigit(*arg2)) + { + sscanf(arg2, "%u", ¶ms->enable_annotate); + } + else + { + params->enable_annotate = ANNOTATE_USER_TEXT; + strncpy(params->annotate_string, arg2, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); + params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2-1] = '\0'; + } + used=2; + break; + } + } return used; @@ -565,6 +642,13 @@ void raspicamcontrol_display_help() fprintf(stderr, ",%s", metering_mode_map[i].mode); } + fprintf(stderr, "\n\nDynamic Range Compression (DRC) options :\n%s", drc_mode_map[0].mode ); + + for (i=1;ihflip = params->vflip = 0; params->roi.x = params->roi.y = 0.0; params->roi.w = params->roi.h = 1.0; + params->shutter_speed = 0; // 0 = auto + params->awb_gains_r = 0; // Only have any function if AWB OFF is used. + params->awb_gains_b = 0; + params->drc_level = MMAL_PARAMETER_DRC_STRENGTH_OFF; + params->stats_pass = MMAL_FALSE; + params->enable_annotate = 0; + params->annotate_string[0] = '\0'; } /** @@ -706,12 +797,17 @@ int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_ result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); result += raspicamcontrol_set_awb_mode(camera, params->awbMode); + result += raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b); result += raspicamcontrol_set_imageFX(camera, params->imageEffect); result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason result += raspicamcontrol_set_rotation(camera, params->rotation); result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); result += raspicamcontrol_set_ROI(camera, params->roi); + result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); + result += raspicamcontrol_set_DRC(camera, params->drc_level); + result += raspicamcontrol_set_stats_pass(camera, params->stats_pass); + result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string); return result; } @@ -944,6 +1040,22 @@ int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); } +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain) +{ + MMAL_PARAMETER_AWB_GAINS_T param = {{MMAL_PARAMETER_CUSTOM_AWB_GAINS,sizeof(param)}, {0,0}, {0,0}}; + + if (!camera) + return 1; + + if (!r_gain || !b_gain) + return 0; + + param.r_gain.num = (unsigned int)(r_gain * 65536); + param.b_gain.num = (unsigned int)(b_gain * 65536); + param.r_gain.den = param.b_gain.den = 65536; + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + /** * Set the image effect for the images * @param camera Pointer to camera component @@ -1074,6 +1186,116 @@ int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) return mmal_port_parameter_set(camera->control, &crop.hdr); } +/** + * Adjust the exposure time used for images + * @param camera Pointer to camera component + * @param shutter speed in microseconds + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_SHUTTER_SPEED, speed)); +} + +/** + * Adjust the Dynamic range compression level + * @param camera Pointer to camera component + * @param strength Strength of DRC to apply + * MMAL_PARAMETER_DRC_STRENGTH_OFF + * MMAL_PARAMETER_DRC_STRENGTH_LOW + * MMAL_PARAMETER_DRC_STRENGTH_MEDIUM + * MMAL_PARAMETER_DRC_STRENGTH_HIGH + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength) +{ + MMAL_PARAMETER_DRC_T drc = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, sizeof(MMAL_PARAMETER_DRC_T)}, strength}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &drc.hdr)); +} + +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_CAPTURE_STATS_PASS, stats_pass)); +} + + +/** + * Set the annotate data + * @param camera Pointer to camera component + * @param Bitmask of required annotation data. 0 for off. + * @param If set, a pointer to text string to use instead of bitmask, max length 32 characters + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string) +{ + MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T annotate = + {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T)}}; + + if (settings) + { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; + + annotate.enable = 1; + + if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT)) + { + strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); + annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2-1] = '\0'; + } + + if (settings & ANNOTATE_TIME_TEXT) + { + strftime(tmp, 32, "%X ", &tm ); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - strlen(annotate.text) - 1); + } + + if (settings & ANNOTATE_DATE_TEXT) + { + strftime(tmp, 32, "%x", &tm ); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - strlen(annotate.text) - 1); + } + + if (settings & ANNOTATE_SHUTTER_SETTINGS) + annotate.show_shutter = 1; + + if (settings & ANNOTATE_GAIN_SETTINGS) + annotate.show_analog_gain = 1; + + if (settings & ANNOTATE_LENS_SETTINGS) + annotate.show_lens = 1; + + if (settings & ANNOTATE_CAF_SETTINGS) + annotate.show_caf = 1; + + if (settings & ANNOTATE_MOTION_SETTINGS) + annotate.show_motion = 1; + + if (settings & ANNOTATE_FRAME_NUMBER) + annotate.show_frame_num = 1; + + if (settings & ANNOTATE_BLACK_BACKGROUND) + annotate.black_text_background = 1; + } + else + annotate.enable = 0; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &annotate.hdr)); +} + /** * Asked GPU how much memory it has allocated diff --git a/sys/rpicamsrc/RaspiCamControl.h b/sys/rpicamsrc/RaspiCamControl.h index aac3e19cad..3393947db7 100644 --- a/sys/rpicamsrc/RaspiCamControl.h +++ b/sys/rpicamsrc/RaspiCamControl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jan Schmidt + * Copyright (c) 2013-2015 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes @@ -87,6 +87,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/// Annotate bitmask options +/// Supplied by user on command line +#define ANNOTATE_USER_TEXT 1 +/// Supplied by app using this module +#define ANNOTATE_APP_TEXT 2 +/// Insert current date +#define ANNOTATE_DATE_TEXT 4 +// Insert current time +#define ANNOTATE_TIME_TEXT 8 + +#define ANNOTATE_SHUTTER_SETTINGS 16 +#define ANNOTATE_CAF_SETTINGS 32 +#define ANNOTATE_GAIN_SETTINGS 64 +#define ANNOTATE_LENS_SETTINGS 128 +#define ANNOTATE_MOTION_SETTINGS 256 +#define ANNOTATE_FRAME_NUMBER 512 +#define ANNOTATE_BLACK_BACKGROUND 1024 // There isn't actually a MMAL structure for the following, so make one @@ -131,6 +148,14 @@ typedef struct int hflip; /// 0 or 1 int vflip; /// 0 or 1 PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect + int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms + float awb_gains_r; /// AWB red gain + float awb_gains_b; /// AWB blue gain + MMAL_PARAMETER_DRC_STRENGTH_T drc_level; // Strength of Dynamic Range compression to apply + MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off + int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed + char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings + } RASPICAM_CAMERA_PARAMETERS; @@ -160,11 +185,16 @@ int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabi int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain); int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength); +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass); +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string); //Individual getting functions int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 3688ae4152..d5c9e15300 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jan Schmidt + * Copyright (c) 2013-2015 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Modification of the RaspiVid command line capture program for GStreamer * use. * - * \date 28th Feb 2013, 11 Oct 2013 + * \date 28th Feb 2013, 11 Oct 2013, 5 Mar 2015 * \Author: James Hughes, Jan Schmidt * * Description @@ -50,6 +50,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * We use the RaspiPreview code to handle the (generic) preview window */ +// We use some GNU extensions (basename, asprintf) +#define _GNU_SOURCE #include #include @@ -73,18 +75,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "RaspiCapture.h" #include "RaspiCamControl.h" #include "RaspiPreview.h" +#include "RaspiCLI.h" #include -/// Camera number to use - we only have one camera, indexed from 0. -#define CAMERA_NUMBER 0 - // Standard port setting for the camera component #define MMAL_CAMERA_PREVIEW_PORT 0 #define MMAL_CAMERA_VIDEO_PORT 1 #define MMAL_CAMERA_CAPTURE_PORT 2 // Video format information +// 0 implies variable #define VIDEO_FRAME_RATE_NUM 30 #define VIDEO_FRAME_RATE_DEN 1 @@ -92,7 +93,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define VIDEO_OUTPUT_BUFFERS_NUM 3 // Max bitrate we allow for recording -const int MAX_BITRATE = 30000000; // 30Mbits/s +const int MAX_BITRATE = 25000000; // 25Mbits/s /// Interval at which we check for an failure abort during capture const int ABORT_INTERVAL = 100; // ms @@ -128,9 +129,12 @@ struct RASPIVID_STATE_T PORT_USERDATA callback_data; MMAL_QUEUE_T *encoded_buffer_q; + + int64_t base_time; + int64_t last_second; }; -#if 0 + /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent static XREF_T profile_map[] = { @@ -142,6 +146,28 @@ static XREF_T profile_map[] = static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]); +#if 0 +static XREF_T initial_map[] = +{ + {"record", 0}, + {"pause", 1}, +}; + +static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]); +#endif + +static XREF_T intra_refresh_map[] = +{ + {"cyclic", MMAL_VIDEO_INTRA_REFRESH_CYCLIC}, + {"adaptive", MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE}, + {"both", MMAL_VIDEO_INTRA_REFRESH_BOTH}, + {"cyclicrows", MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS}, +// {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why. +}; + +static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]); + +#if 0 static void display_valid_parameters(char *app_name); @@ -158,20 +184,53 @@ static void display_valid_parameters(char *app_name); #define CommandPreviewEnc 9 #define CommandIntraPeriod 10 #define CommandProfile 11 +#define CommandTimed 12 +#define CommandSignal 13 +#define CommandKeypress 14 +#define CommandInitialState 15 +#define CommandQP 16 +#define CommandInlineHeaders 17 +#define CommandSegmentFile 18 +#define CommandSegmentWrap 19 +#define CommandSegmentStart 20 +#define CommandSplitWait 21 +#define CommandCircular 22 +#define CommandIMV 23 +#define CommandCamSelect 24 +#define CommandSettings 25 +#define CommandSensorMode 26 +#define CommandIntraRefreshType 27 static COMMAND_LIST cmdline_commands[] = { - { CommandHelp, "-help", "?", "This help information", 0 }, - { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, - { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, - { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, - { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, - { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, - { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, - { CommandFramerate,"-framerate", "fps","Specify the frames per second to record", 1}, - { CommandPreviewEnc,"-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, - { CommandIntraPeriod,"-intra", "g", "Specify the intra refresh period (key frame rate/GoP size)", 1}, - { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -')", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1}, + { CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, + { CommandIntraPeriod, "-intra", "g", "Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames.", 1}, + { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, + { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0}, + { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0}, + { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0}, + { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1}, + { CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1}, + { CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0}, + { CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval ", 1}, + { CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1}, + { CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1}, + { CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0}, + { CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0}, + { CommandIMV, "-vectors", "x", "Output filename for inline motion vectors", 1 }, + { CommandCamSelect, "-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1}, }; static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); @@ -186,6 +245,14 @@ static void dump_state(RASPIVID_STATE *state); */ void raspicapture_default_config(RASPIVID_CONFIG *config) { + if (!config) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(config, 0, sizeof(RASPIVID_CONFIG)); // Now set anything non-zero config->timeout = 5000; // 5s delay before take image @@ -194,12 +261,23 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) config->bitrate = 17000000; // This is a decent default bitrate for 1080p config->fps_n = VIDEO_FRAME_RATE_NUM; config->fps_d = VIDEO_FRAME_RATE_DEN; - config->intraperiod = 0; // Not set + config->intraperiod = -1; // Not set + config->quantisationParameter = 0; config->demoMode = 0; config->demoInterval = 250; // ms config->immutableInput = 1; config->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + config->bInlineHeaders = 0; + + config->inlineMotionVectors = 0; + + config->cameraNum = 0; + config->settings = 0; + config->sensor_mode = 0; + + config->intra_refresh_type = -1; + // Setup preview window defaults raspipreview_set_defaults(&config->preview_parameters); @@ -208,6 +286,7 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) } + /** * Dump image state parameters to printf. Used for debugging * @@ -311,7 +390,7 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) state->filename = malloc(len + 1); vcos_assert(state->filename); if (state->filename) - strncpy(state->filename, argv[i + 1], len); + strncpy(state->filename, argv[i + 1], len+1); i++; } else @@ -327,7 +406,10 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) { if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) { - // TODO : What limits do we need for timeout? + // Ensure that if previously selected a waitMethod we dont overwrite it + if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE) + state->waitMethod = WAIT_METHOD_FOREVER; + i++; } else @@ -386,6 +468,15 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) break; } + case CommandQP: // quantisation parameter + { + if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1) + i++; + else + valid = 0; + break; + } + case CommandProfile: // H264 profile { state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size); @@ -397,6 +488,146 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) break; } + case CommandInlineHeaders: // H264 inline headers + { + state->bInlineHeaders = 1; + break; + } + + case CommandTimed: + { + if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2) + { + i++; + + if (state->onTime < 1000) + state->onTime = 1000; + + if (state->offTime < 1000) + state->offTime = 1000; + + state->waitMethod = WAIT_METHOD_TIMED; + } + else + valid = 0; + break; + } + + case CommandKeypress: + state->waitMethod = WAIT_METHOD_KEYPRESS; + break; + + case CommandSignal: + state->waitMethod = WAIT_METHOD_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandInitialState: + { + state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size); + + if( state->bCapturing == -1) + state->bCapturing = 0; + + i++; + break; + } + + case CommandSegmentFile: // Segment file in to chunks of specified time + { + if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1) + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + i++; + } + else + valid = 0; + break; + } + + case CommandSegmentWrap: // segment wrap value + { + if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1) + i++; + else + valid = 0; + break; + } + + case CommandSegmentStart: // initial segment number + { + if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap))) + i++; + else + valid = 0; + break; + } + + case CommandSplitWait: // split files on restart + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + state->splitWait = 1; + break; + } + + case CommandCircular: + { + state->bCircularBuffer = 1; + break; + } + + case CommandIMV: // output filename + { + state->inlineMotionVectors = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->imv_filename = malloc(len + 1); + vcos_assert(state->imv_filename); + if (state->imv_filename) + strncpy(state->imv_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandSettings: + state->settings = 1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandIntraRefreshType: + { + state->config->intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size); + i++; + break; + } + default: { // Try parsing for any image specific parameters @@ -423,10 +654,16 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) if (!valid) { - fprintf(stderr, "Invalid command line option (%s)\n", argv[i]); + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); return 1; } + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + return 0; } @@ -454,6 +691,18 @@ static void display_valid_parameters(char *app_name) fprintf(stderr, ",%s", profile_map[i].mode); } + fprintf(stderr, "\n"); + + // Intra refresh options + fprintf(stderr, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode ); + + for (i=1;icmd == MMAL_EVENT_PARAMETER_CHANGED) { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } } else { @@ -487,6 +752,123 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf mmal_buffer_header_release(buffer); } +#if 0 +/** + * Open a file based on the settings in state + * + * @param state Pointer to state + */ +static FILE *open_filename(RASPIVID_STATE *pState) +{ + FILE *new_handle = NULL; + char *tempname = NULL, *filename = NULL; + + if (pState->segmentSize || pState->splitWait) + { + // Create a new filename string + asprintf(&tempname, pState->filename, pState->segmentNumber); + filename = tempname; + } + else + { + filename = pState->filename; + } + + if (filename) + new_handle = fopen(filename, "wb"); + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new file \"%s\"\n", filename); + } + + if (tempname) + free(tempname); + + return new_handle; +} + +/** + * Open a file based on the settings in state + * + * This time for the imv output file + * + * @param state Pointer to state + */ +static FILE *open_imv_filename(RASPIVID_STATE *pState) +{ + FILE *new_handle = NULL; + char *tempname = NULL, *filename = NULL; + + if (pState->segmentSize || pState->splitWait) + { + // Create a new filename string + asprintf(&tempname, pState->imv_filename, pState->segmentNumber); + filename = tempname; + } + else + { + filename = pState->imv_filename; + } + + if (filename) + new_handle = fopen(filename, "wb"); + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening imv output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new imv file \"%s\"\n", filename); + } + + if (tempname) + free(tempname); + + return new_handle; +} +#endif + +/** + * Update any annotation data specific to the video. + * This simply passes on the setting from cli, or + * if application defined annotate requested, updates + * with the H264 parameters + * + * @param state Pointer to state control struct + * + */ +static void update_annotation_data(RASPIVID_STATE *state) +{ + RASPIVID_CONFIG *config = state->config; + + // So, if we have asked for a application supplied string, set it to the H264 parameters + if (config->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) + { + char *text; + const char *refresh = raspicli_unmap_xref(config->intra_refresh_type, intra_refresh_map, intra_refresh_map_size); + + asprintf(&text, "%dk,%ff,%s,%d,%s", + config->bitrate / 1000, ((float)(config->fps_n) / config->fps_d), + refresh ? refresh : "(none)", + config->intraperiod, + raspicli_unmap_xref(config->profile, profile_map, profile_map_size)); + + raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text); + + free(text); + } + else + { + raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, config->camera_parameters.annotate_string); + } +} + + + /** * buffer header callback function for encoder * @@ -499,6 +881,11 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf { PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; RASPIVID_STATE *state = pData->state; + int64_t current_time; + + // All our segment times based on the receipt of the first encoder callback + if (state->base_time == -1) + state->base_time = vcos_getmicrosecs64()/1000; if (pData == NULL) { @@ -508,6 +895,17 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf return; } + current_time = vcos_getmicrosecs64()/1000; + if (state->base_time == -1) + state->base_time = current_time; + + // See if the second count has changed and we need to update any annotation + if (current_time/1000 != state->last_second) + { + update_annotation_data(state); + state->last_second = current_time/1000; + } + /* Send buffer to GStreamer element for pushing to the pipeline */ mmal_queue_put(state->encoded_buffer_q, buffer); } @@ -565,6 +963,7 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) { MMAL_COMPONENT_T *camera = NULL; MMAL_STATUS_T status; + RASPIVID_CONFIG *config = state->config; /* Create the component */ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); @@ -574,7 +973,18 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) vcos_log_error("Failed to create camera component"); goto error; } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum}; + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + if (!camera->output_num) { status = MMAL_ENOSYS; @@ -582,6 +992,26 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) goto error; } + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + if (config->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } // Enable the camera, and tell it its control callback function status = mmal_port_enable(camera->control, camera_control_callback); @@ -610,18 +1040,19 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) MMAL_STATUS_T status; MMAL_ES_FORMAT_T *format; MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + RASPIVID_CONFIG *config = state->config; // set up the camera configuration MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = { { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, - .max_stills_w = state->config->width, - .max_stills_h = state->config->height, + .max_stills_w = config->width, + .max_stills_h = config->height, .stills_yuv422 = 0, .one_shot_stills = 0, - .max_preview_video_w = state->config->width, - .max_preview_video_h = state->config->height, + .max_preview_video_w = config->width, + .max_preview_video_h = config->height, .num_preview_video_frames = 3, .stills_capture_circular_buffer_height = 0, .fast_preview_resume = 0, @@ -645,15 +1076,40 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; + if(config->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(config->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + + //enable dynamic framerate if necessary + if (config->camera_parameters.shutter_speed) + { + if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed) + { + config->fps_n = 0; + config->fps_d = 1; + if (config->verbose) + fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + } + } + format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->config->width; - format->es->video.height = state->config->height; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); + format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config->width; - format->es->video.crop.height = state->config->height; - format->es->video.frame_rate.num = state->config->fps_n; - format->es->video.frame_rate.den = state->config->fps_d; + format->es->video.crop.width = config->width; + format->es->video.crop.height = config->height; + format->es->video.frame_rate.num = config->fps_n; + format->es->video.frame_rate.den = config->fps_d; status = mmal_port_format_commit(preview_port); @@ -668,15 +1124,28 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format = video_port->format; format->encoding_variant = MMAL_ENCODING_I420; + if(config->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + else if(config->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->config->width; - format->es->video.height = state->config->height; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); + format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config->width; - format->es->video.crop.height = state->config->height; - format->es->video.frame_rate.num = state->config->fps_n; - format->es->video.frame_rate.den = state->config->fps_d; + format->es->video.crop.width = config->width; + format->es->video.crop.height = config->height; + format->es->video.frame_rate.num = config->fps_n; + format->es->video.frame_rate.den = config->fps_d; status = mmal_port_format_commit(video_port); @@ -698,13 +1167,13 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; - format->es->video.width = state->config->width; - format->es->video.height = state->config->height; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); + format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config->width; - format->es->video.crop.height = state->config->height; - format->es->video.frame_rate.num = 1; + format->es->video.crop.width = config->width; + format->es->video.crop.height = config->height; + format->es->video.frame_rate.num = 0; format->es->video.frame_rate.den = 1; status = mmal_port_format_commit(still_port); @@ -728,12 +1197,20 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) goto error; } - raspicamcontrol_set_all_parameters(camera, &state->config->camera_parameters); + raspicamcontrol_set_all_parameters(camera, &config->camera_parameters); - if (state->config->verbose) + update_annotation_data(state); + + if (config->verbose) fprintf(stderr, "Camera component done\n"); + return status; + error: + + if (camera) + mmal_component_destroy(camera); + return status; } @@ -781,6 +1258,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; MMAL_STATUS_T status; MMAL_POOL_T *pool; + RASPIVID_CONFIG *config = state->config; status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); @@ -806,7 +1284,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) // Only supporting H264 at the moment encoder_output->format->encoding = MMAL_ENCODING_H264; - encoder_output->format->bitrate = state->config->bitrate; + encoder_output->format->bitrate = config->bitrate; encoder_output->buffer_size = encoder_output->buffer_size_recommended; @@ -820,6 +1298,11 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (encoder_output->buffer_num < encoder_output->buffer_num_min) encoder_output->buffer_num = encoder_output->buffer_num_min; + // We need to set the frame rate on output to 0, to ensure it gets + // updated correctly from the input framerate when port connected + encoder_output->format->es->video.frame_rate.num = 0; + encoder_output->format->es->video.frame_rate.den = 1; + // Commit the port changes to the output port status = mmal_port_format_commit(encoder_output); @@ -829,7 +1312,6 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) goto error; } - // Set the rate control parameter if (0) { @@ -843,7 +1325,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (state->config->intraperiod) + if (config->intraperiod != -1) { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config->intraperiod}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -852,6 +1334,33 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) vcos_log_error("Unable to set intraperiod"); goto error; } + } + + if (config->quantisationParameter) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set initial QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m2.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set min QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m3.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set max QP"); + goto error; + } } @@ -860,7 +1369,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) param.hdr.id = MMAL_PARAMETER_PROFILE; param.hdr.size = sizeof(param); - param.profile[0].profile = state->config->profile; + param.profile[0].profile = config->profile; param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -872,12 +1381,49 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->config->immutableInput) != MMAL_SUCCESS) + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS) { vcos_log_error("Unable to set immutable input flag"); // Continue rather than abort.. } + //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE HEADER FLAG parameters"); + // Continue rather than abort.. + } + + //set INLINE VECTORS flag to request motion vector estimates + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE VECTORS parameters"); + // Continue rather than abort.. + } + + // Adaptive intra refresh settings + if (config->intra_refresh_type != -1) + { + MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; + param.hdr.size = sizeof(param); + + // Get first so we don't overwrite anything unexpectedly + status = mmal_port_parameter_get(encoder_output, ¶m.hdr); + + param.refresh_mode = config->intra_refresh_type; + + //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS) + // param.cir_mbs = 10; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 intra-refresh values"); + goto error; + } + } + // Enable component status = mmal_component_enable(encoder); @@ -889,6 +1435,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) /* Create pool of buffer headers for the output port to consume */ pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + if (!pool) { vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); @@ -897,7 +1444,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) state->encoder_pool = pool; state->encoder_component = encoder; - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Encoder component done\n"); return status; @@ -906,6 +1453,8 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (encoder) mmal_component_destroy(encoder); + state->encoder_component = NULL; + return status; } @@ -996,6 +1545,9 @@ raspi_capture_setup(RASPIVID_CONFIG *config) /* Apply passed in config */ state->config = config; + /* Initialize timestamping */ + state->base_time = state->last_second = -1; + /* So far, all we can do is create the camera component. Actual * config and connection of encoders etc happens in _start() */ diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 37a7ba7bef..84ad8c023b 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -1,7 +1,7 @@ /* * GStreamer * Copyright (C) 2013 Jan Schmidt - * + * * 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 @@ -66,7 +66,7 @@ G_BEGIN_DECLS typedef struct { int verbose; /// !0 if want detailed run information - + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds int width; /// Requested width of image int height; /// requested height of image @@ -74,6 +74,8 @@ typedef struct int fps_n; /// Requested frame rate (fps) numerator int fps_d; /// Requested frame rate (fps) denominator int intraperiod; /// Intra-refresh period (key frame rate) + int quantisationParameter; /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate + int bInlineHeaders; /// Insert inline headers to stream (SPS, PPS) int demoMode; /// Run app in demo mode int demoInterval; /// Interval between camera settings changes int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either @@ -81,6 +83,13 @@ typedef struct int profile; /// H264 profile to use for encoding RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + int inlineMotionVectors; /// Encoder outputs inline Motion Vectors + + int cameraNum; /// Camera number + int settings; /// Request settings from the camera + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + int intra_refresh_type; /// What intra refresh type to use. -1 to not set. } RASPIVID_CONFIG; typedef struct RASPIVID_STATE_T RASPIVID_STATE; diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c index de11df3471..1632ae09cd 100644 --- a/sys/rpicamsrc/RaspiPreview.c +++ b/sys/rpicamsrc/RaspiPreview.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jan Schmidt + * Copyright (c) 2013-2015 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes diff --git a/sys/rpicamsrc/RaspiPreview.h b/sys/rpicamsrc/RaspiPreview.h index c986d2d862..409dbffb1a 100644 --- a/sys/rpicamsrc/RaspiPreview.h +++ b/sys/rpicamsrc/RaspiPreview.h @@ -31,12 +31,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// Layer that preview window should be displayed on #define PREVIEW_LAYER 2 -#define PREVIEW_FRAME_RATE_NUM 30 + +// Frames rates of 0 implies variable, but denominator needs to be 1 to prevent div by 0 +#define PREVIEW_FRAME_RATE_NUM 0 #define PREVIEW_FRAME_RATE_DEN 1 -#define FULL_RES_PREVIEW_FRAME_RATE_NUM 15 +#define FULL_RES_PREVIEW_FRAME_RATE_NUM 0 #define FULL_RES_PREVIEW_FRAME_RATE_DEN 1 +#define FULL_FOV_PREVIEW_16x9_X 1280 +#define FULL_FOV_PREVIEW_16x9_Y 720 + +#define FULL_FOV_PREVIEW_4x3_X 1296 +#define FULL_FOV_PREVIEW_4x3_Y 972 + +#define FULL_FOV_PREVIEW_FRAME_RATE_NUM 0 +#define FULL_FOV_PREVIEW_FRAME_RATE_DEN 1 typedef struct { From 861c3538736c5c944bf9d7b4bd5b90c4b9200d83 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 00:21:31 +1100 Subject: [PATCH 18/77] rpicamsrc: Add quantisation-parameter property, support variable bitrate Allow birate=0 and implement the quantisation-parameter property Fixes https://github.com/thaytan/gst-rpicamsrc/issues/21 --- sys/rpicamsrc/gstrpicamsrc.c | 66 +++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 8278ee05b0..1211797c14 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1,7 +1,7 @@ /* * GStreamer * Copyright (C) 2013-2014 Jan Schmidt - * + * * 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 @@ -116,11 +116,14 @@ enum PROP_ROI_Y, PROP_ROI_W, PROP_ROI_H, + PROP_QUANTISATION_PARAMETER }; #define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ #define BITRATE_HIGHEST 25000000 +#define QUANTISATION_DEFAULT 0 + #define SHARPNESS_DEFAULT 0 #define CONTRAST_DEFAULT 0 #define BRIGHTNESS_DEFAULT 50 @@ -153,7 +156,7 @@ enum "width = " GST_VIDEO_SIZE_RANGE "," \ "height = " GST_VIDEO_SIZE_RANGE "," \ "framerate = " GST_VIDEO_FPS_RANGE -#define H264_CAPS \ +#define H264_CAPS \ "video/x-h264, " \ "width = " GST_VIDEO_SIZE_RANGE ", " \ "height = " GST_VIDEO_SIZE_RANGE ", " \ @@ -185,7 +188,8 @@ static GstCaps *gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter); static gboolean gst_rpi_cam_src_set_caps (GstBaseSrc * src, GstCaps * caps); static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps); static gboolean gst_rpi_cam_src_event (GstBaseSrc * src, GstEvent * event); -static gboolean gst_rpi_cam_src_send_event (GstElement * element, GstEvent * event); +static gboolean gst_rpi_cam_src_send_event (GstElement * element, + GstEvent * event); static void gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) @@ -204,26 +208,27 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gobject_class->get_property = gst_rpi_cam_src_get_property; g_object_class_install_property (gobject_class, PROP_BITRATE, - g_param_spec_int ("bitrate", "Bitrate", "Bitrate for encoding", - 1, BITRATE_HIGHEST, BITRATE_DEFAULT, + g_param_spec_int ("bitrate", "Bitrate", + "Bitrate for encoding. 0 for VBR using quantisation-parameter", 0, + BITRATE_HIGHEST, BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_KEYFRAME_INTERVAL, g_param_spec_int ("keyframe-interval", "Keyframe Interface", - "Interval (in frames) between I frames. 0 = single-keyframe", - 0, G_MAXINT, KEYFRAME_INTERVAL_DEFAULT, + "Interval (in frames) between I frames. 0 = single-keyframe", 0, + G_MAXINT, KEYFRAME_INTERVAL_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_PREVIEW, - g_param_spec_boolean ("preview", - "Preview Window", "Display preview window overlay", - TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("preview", "Preview Window", + "Display preview window overlay", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_FULLSCREEN, - g_param_spec_boolean ("fullscreen", - "Fullscreen Preview", "Display preview window full screen", - TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("fullscreen", "Fullscreen Preview", + "Display preview window full screen", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_PREVIEW_ENCODED, - g_param_spec_boolean ("preview-encoded", - "Preview Encoded", "Display encoder output in the preview", - TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("preview-encoded", "Preview Encoded", + "Display encoder output in the preview", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_OPACITY, g_param_spec_int ("preview-opacity", "Preview Opacity", "Opacity to use for the preview window", 0, 255, 255, @@ -306,6 +311,11 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_float ("roi-h", "ROI H", "Normalised region-of-interest H coord", 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_QUANTISATION_PARAMETER, + g_param_spec_int ("quantisation-parameter", "Quantisation Parameter", + "Set a Quantisation Parameter approx 10-40 with bitrate=0 for VBR encoding. 0 = off", + 0, G_MAXINT, QUANTISATION_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", @@ -410,7 +420,8 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); break; case PROP_IMAGE_EFFECT: - src->capture_config.camera_parameters.imageEffect = g_value_get_enum (value); + src->capture_config.camera_parameters.imageEffect = + g_value_get_enum (value); break; case PROP_HFLIP: src->capture_config.camera_parameters.hflip = g_value_get_boolean (value); @@ -430,6 +441,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_ROI_H: src->capture_config.camera_parameters.roi.h = g_value_get_float (value); break; + case PROP_QUANTISATION_PARAMETER: + src->capture_config.quantisationParameter = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -501,7 +515,8 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, g_value_set_enum (value, src->capture_config.camera_parameters.awbMode); break; case PROP_IMAGE_EFFECT: - g_value_set_enum (value, src->capture_config.camera_parameters.imageEffect); + g_value_set_enum (value, + src->capture_config.camera_parameters.imageEffect); break; case PROP_HFLIP: g_value_set_boolean (value, @@ -523,6 +538,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_ROI_H: g_value_set_float (value, src->capture_config.camera_parameters.roi.h); break; + case PROP_QUANTISATION_PARAMETER: + g_value_set_int (value, src->capture_config.quantisationParameter); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -568,12 +586,12 @@ gst_rpi_cam_src_send_event (GstElement * parent, GstEvent * event) } gst_event_unref (event); } else { - ret = GST_ELEMENT_CLASS (parent_class)->send_event(parent, event); + ret = GST_ELEMENT_CLASS (parent_class)->send_event (parent, event); } break; default: - ret = GST_ELEMENT_CLASS (parent_class)->send_event(parent, event); - break; + ret = GST_ELEMENT_CLASS (parent_class)->send_event (parent, event); + break; } return ret; } @@ -594,12 +612,12 @@ gst_rpi_cam_src_event (GstBaseSrc * parent, GstEvent * event) } gst_event_unref (event); } else { - ret = GST_BASE_SRC_CLASS (parent_class)->event(parent, event); + ret = GST_BASE_SRC_CLASS (parent_class)->event (parent, event); } break; default: - ret = GST_BASE_SRC_CLASS (parent_class)->event(parent, event); - break; + ret = GST_BASE_SRC_CLASS (parent_class)->event (parent, event); + break; } return ret; } From 1ea517a692afe4466afba5bf9bb9ad4c5452cea0 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 00:45:05 +1100 Subject: [PATCH 19/77] rpicamsrc: add inline-headers and shutter-speed properties --- sys/rpicamsrc/gstrpicamsrc.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 1211797c14..dd1671c485 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -106,6 +106,8 @@ enum PROP_EXPOSURE_MODE, PROP_EXPOSURE_METERING_MODE, PROP_AWB_MODE, + PROP_AWB_GAIN_B, + PROP_AWB_GAIN_G, PROP_IMAGE_EFFECT, PROP_IMAGE_EFFECT_PARAMS, PROP_COLOUR_EFFECTS, @@ -116,7 +118,13 @@ enum PROP_ROI_Y, PROP_ROI_W, PROP_ROI_H, - PROP_QUANTISATION_PARAMETER + PROP_QUANTISATION_PARAMETER, + PROP_INLINE_HEADERS, + PROP_SHUTTER_SPEED, + PROP_SENSOR_MODE, + PROP_DRC, + PROP_ANNOTATION_MODE, + PROP_ANNOTATION_STRING }; #define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ @@ -317,6 +325,16 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) 0, G_MAXINT, QUANTISATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_INLINE_HEADERS, + g_param_spec_boolean ("inline-headers", "Inline Headers", + "Set to TRUE to insert SPS/PPS before each IDR packet", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_SHUTTER_SPEED, + g_param_spec_int ("shutter-speed", "Shutter Speed", + "Set a fixed shutter speed, in microseconds. (0 = Auto)", + 0, 6000000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", @@ -444,6 +462,13 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_QUANTISATION_PARAMETER: src->capture_config.quantisationParameter = g_value_get_int (value); break; + case PROP_INLINE_HEADERS: + src->capture_config.bInlineHeaders = g_value_get_boolean (value); + break; + case PROP_SHUTTER_SPEED: + src->capture_config.camera_parameters.shutter_speed = + g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -541,6 +566,13 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_QUANTISATION_PARAMETER: g_value_set_int (value, src->capture_config.quantisationParameter); break; + case PROP_INLINE_HEADERS: + g_value_set_boolean (value, src->capture_config.bInlineHeaders); + break; + case PROP_SHUTTER_SPEED: + g_value_set_int (value, + src->capture_config.camera_parameters.shutter_speed); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; From f6451323b5df286920c95aec968559916269cead Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 00:52:37 +1100 Subject: [PATCH 20/77] rpicamsrc: Add camera-number property --- sys/rpicamsrc/gstrpicamsrc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index dd1671c485..fcb5ae963e 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -90,6 +90,7 @@ enum enum { PROP_0, + PROP_CAMERA_NUMBER, PROP_BITRATE, PROP_KEYFRAME_INTERVAL, PROP_PREVIEW, @@ -127,6 +128,8 @@ enum PROP_ANNOTATION_STRING }; +#define CAMERA_DEFAULT 0 + #define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ #define BITRATE_HIGHEST 25000000 @@ -215,6 +218,11 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gobject_class->set_property = gst_rpi_cam_src_set_property; gobject_class->get_property = gst_rpi_cam_src_get_property; + g_object_class_install_property (gobject_class, PROP_CAMERA_NUMBER, + g_param_spec_int ("camera-number", "Camera Number", + "Which camera to use on a multi-camera system - 0 or 1", 0, + 1, CAMERA_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BITRATE, g_param_spec_int ("bitrate", "Bitrate", "Bitrate for encoding. 0 for VBR using quantisation-parameter", 0, @@ -378,6 +386,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, GstRpiCamSrc *src = GST_RPICAMSRC (object); switch (prop_id) { + case PROP_CAMERA_NUMBER: + src->capture_config.cameraNum = g_value_get_int (value); + break; case PROP_BITRATE: src->capture_config.bitrate = g_value_get_int (value); break; @@ -482,6 +493,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GstRpiCamSrc *src = GST_RPICAMSRC (object); switch (prop_id) { + case PROP_CAMERA_NUMBER: + g_value_set_int (value, src->capture_config.cameraNum); + break; case PROP_BITRATE: g_value_set_int (value, src->capture_config.bitrate); break; From 78b36bbb6bd86d92c36deacef499dfdb37ada298 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 01:09:16 +1100 Subject: [PATCH 21/77] rpicamsrc: add awb-gain-red and awb-gain-blue properties --- sys/rpicamsrc/gstrpicamsrc.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index fcb5ae963e..71100ed0ab 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -107,8 +107,8 @@ enum PROP_EXPOSURE_MODE, PROP_EXPOSURE_METERING_MODE, PROP_AWB_MODE, - PROP_AWB_GAIN_B, - PROP_AWB_GAIN_G, + PROP_AWB_GAIN_RED, + PROP_AWB_GAIN_BLUE, PROP_IMAGE_EFFECT, PROP_IMAGE_EFFECT_PARAMS, PROP_COLOUR_EFFECTS, @@ -291,6 +291,14 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "White Balance mode", GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, GST_RPI_CAM_SRC_AWB_MODE_AUTO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, + g_param_spec_float ("awb-gain-red", "AWB Red Gain", + "Manual AWB Gain for red channel when awb-mode=OFF", + 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, + g_param_spec_float ("awb-gain-blue", "AWB Blue Gain", + "Manual AWB Gain for blue channel when awb-mode=OFF", + 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_IMAGE_EFFECT, g_param_spec_enum ("image-effect", "Image effect", "Visual FX to apply to the image", @@ -448,6 +456,14 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_AWB_MODE: src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); break; + case PROP_AWB_GAIN_RED: + src->capture_config.camera_parameters.awb_gains_r = + g_value_get_float (value); + break; + case PROP_AWB_GAIN_BLUE: + src->capture_config.camera_parameters.awb_gains_b = + g_value_get_float (value); + break; case PROP_IMAGE_EFFECT: src->capture_config.camera_parameters.imageEffect = g_value_get_enum (value); @@ -553,6 +569,14 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_AWB_MODE: g_value_set_enum (value, src->capture_config.camera_parameters.awbMode); break; + case PROP_AWB_GAIN_RED: + g_value_set_float (value, + src->capture_config.camera_parameters.awb_gains_r); + break; + case PROP_AWB_GAIN_BLUE: + g_value_set_float (value, + src->capture_config.camera_parameters.awb_gains_b); + break; case PROP_IMAGE_EFFECT: g_value_set_enum (value, src->capture_config.camera_parameters.imageEffect); From 37028010f1f82c827cfa4ac35a04850d80ea2fbc Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 01:15:48 +1100 Subject: [PATCH 22/77] rpicamsrc: Implement drc property --- sys/rpicamsrc/gstrpicam_types.h | 8 ++++++++ sys/rpicamsrc/gstrpicamsrc.c | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/sys/rpicamsrc/gstrpicam_types.h b/sys/rpicamsrc/gstrpicam_types.h index 7ebccb2dbf..acc47423d8 100644 --- a/sys/rpicamsrc/gstrpicam_types.h +++ b/sys/rpicamsrc/gstrpicam_types.h @@ -69,3 +69,11 @@ typedef enum { GST_RPI_CAM_SRC_FLICKERAVOID_50HZ = MMAL_PARAM_FLICKERAVOID_50HZ, GST_RPI_CAM_SRC_FLICKERAVOID_60HZ = MMAL_PARAM_FLICKERAVOID_60HZ } GstRpiCamSrcFlickerAvoidance; + +typedef enum { + GST_RPI_CAM_SRC_DRC_LEVEL_OFF = MMAL_PARAMETER_DRC_STRENGTH_OFF, + GST_RPI_CAM_SRC_DRC_LEVEL_LOW = MMAL_PARAMETER_DRC_STRENGTH_LOW, + GST_RPI_CAM_SRC_DRC_LEVEL_MEDIUM = MMAL_PARAMETER_DRC_STRENGTH_MEDIUM, + GST_RPI_CAM_SRC_DRC_LEVEL_HIGH = MMAL_PARAMETER_DRC_STRENGTH_HIGH +} GstRpiCamSrcDRCLevel; + diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 71100ed0ab..d0e3049c5c 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -286,6 +286,12 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, EXPOSURE_METERING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DRC, + g_param_spec_enum ("drc", "DRC level", + "Dynamic Range Control level", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_DRC_LEVEL, + GST_RPI_CAM_SRC_DRC_LEVEL_OFF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_MODE, g_param_spec_enum ("awb-mode", "Automatic White Balance Mode", "White Balance mode", GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, @@ -496,6 +502,11 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.camera_parameters.shutter_speed = g_value_get_int (value); break; + case PROP_DRC: + src->capture_config.camera_parameters.drc_level = + g_value_get_enum (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -611,6 +622,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, g_value_set_int (value, src->capture_config.camera_parameters.shutter_speed); break; + case PROP_DRC: + g_value_set_enum (value, src->capture_config.camera_parameters.drc_level); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; From 52ae74f9ce4568b6c9842b54ddcc0b1629c5a7d8 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 01:27:44 +1100 Subject: [PATCH 23/77] rpicamsrc: More conversion to GStreamer logging --- sys/rpicamsrc/RaspiCapture.c | 23 +++++++++++------------ sys/rpicamsrc/RaspiCapture.h | 3 +++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index d5c9e15300..7412d19e37 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -271,7 +271,7 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) config->bInlineHeaders = 0; config->inlineMotionVectors = 0; - + config->cameraNum = 0; config->settings = 0; config->sensor_mode = 0; @@ -565,7 +565,7 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) break; } - case CommandSplitWait: // split files on restart + case CommandSplitWait: // split files on restart { // Must enable inline headers for this to work state->bInlineHeaders = 1; @@ -733,7 +733,7 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf { MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", - settings->exposure, + settings->exposure, settings->analog_gain.num, settings->analog_gain.den, settings->digital_gain.num, settings->digital_gain.den); vcos_log_error("AWB R=%u/%u, B=%u/%u", @@ -898,7 +898,7 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf current_time = vcos_getmicrosecs64()/1000; if (state->base_time == -1) state->base_time = current_time; - + // See if the second count has changed and we need to update any annotation if (current_time/1000 != state->last_second) { @@ -973,18 +973,18 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) vcos_log_error("Failed to create camera component"); goto error; } - + MMAL_PARAMETER_INT32_T camera_num = {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum}; status = mmal_port_parameter_set(camera->control, &camera_num.hdr); - + if (status != MMAL_SUCCESS) { vcos_log_error("Could not select camera : error %d", status); goto error; } - + if (!camera->output_num) { status = MMAL_ENOSYS; @@ -1091,15 +1091,14 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) //enable dynamic framerate if necessary if (config->camera_parameters.shutter_speed) - { + { if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed) { config->fps_n = 0; config->fps_d = 1; - if (config->verbose) - fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement"); } - } + } format->encoding = MMAL_ENCODING_OPAQUE; format->es->video.width = VCOS_ALIGN_UP(config->width, 32); @@ -1393,7 +1392,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) vcos_log_error("failed to set INLINE HEADER FLAG parameters"); // Continue rather than abort.. } - + //set INLINE VECTORS flag to request motion vector estimates if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) { diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 84ad8c023b..f4f0bf9048 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -59,6 +59,9 @@ GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug); #undef fprintf #define fprintf(f,...) GST_LOG(__VA_ARGS__) +#undef vcos_log_error +#define vcos_log_error GST_ERROR + G_BEGIN_DECLS /** Structure containing all state information for the current run From 39d212df574fabdbc447ad035a75a8b29e73714c Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 02:42:00 +1100 Subject: [PATCH 24/77] rpicamsrc: implement sensor-mode property --- sys/rpicamsrc/gstrpicamsrc.c | 156 +++++++++++++++++++---------------- sys/rpicamsrc/gstrpicamsrc.h | 11 +++ 2 files changed, 95 insertions(+), 72 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index d0e3049c5c..4e40597c68 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -202,6 +202,41 @@ static gboolean gst_rpi_cam_src_event (GstBaseSrc * src, GstEvent * event); static gboolean gst_rpi_cam_src_send_event (GstElement * element, GstEvent * event); +#define C_ENUM(v) ((gint) v) + +GType +gst_rpi_cam_src_sensor_mode_get_type (void) +{ + static const GEnumValue values[] = { + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_AUTOMATIC), "Automatic", "automatic"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_1920x1080), "1920x1080 16:9 1-30fps", + "1920x1080"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_FAST), + "2592x1944 4:3 1-15fps", + "2592x1944-fast"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_SLOW), + "2592x1944 4:3 0.1666-1fps", "2592x1944-slow"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_1296x972), "1296x972 4:3 1-42fps", + "1296x972"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_1296x730), "1296x730 16:9 1-49fps", + "1296x730"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_640x480_SLOW), + "640x480 4:3 42.1-60fps", "640x480-slow"}, + {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_640x480_FAST), + "640x480 4:3 60.1-90fps", "640x480-fast"}, + {0, NULL, NULL} + }; + static volatile GType id = 0; + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + _id = g_enum_register_static ("GstRpiCamSrcSensorMode", values); + g_once_init_leave ((gsize *) & id, _id); + } + + return id; +} + + static void gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) { @@ -209,20 +244,16 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) GstElementClass *gstelement_class; GstBaseSrcClass *basesrc_class; GstPushSrcClass *pushsrc_class; - gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; basesrc_class = (GstBaseSrcClass *) klass; pushsrc_class = (GstPushSrcClass *) klass; - gobject_class->set_property = gst_rpi_cam_src_set_property; gobject_class->get_property = gst_rpi_cam_src_get_property; - g_object_class_install_property (gobject_class, PROP_CAMERA_NUMBER, g_param_spec_int ("camera-number", "Camera Number", "Which camera to use on a multi-camera system - 0 or 1", 0, 1, CAMERA_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_BITRATE, g_param_spec_int ("bitrate", "Bitrate", "Bitrate for encoding. 0 for VBR using quantisation-parameter", 0, @@ -245,50 +276,50 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_boolean ("preview-encoded", "Preview Encoded", "Display encoder output in the preview", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_OPACITY, - g_param_spec_int ("preview-opacity", "Preview Opacity", - "Opacity to use for the preview window", 0, 255, 255, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_PREVIEW_OPACITY, g_param_spec_int ("preview-opacity", + "Preview Opacity", "Opacity to use for the preview window", 0, + 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHARPNESS, g_param_spec_int ("sharpness", "Sharpness", "Image capture sharpness", -100, 100, SHARPNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CONTRAST, - g_param_spec_int ("contrast", "Contrast", "Image capture contrast", -100, - 100, CONTRAST_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("contrast", "Contrast", "Image capture contrast", + -100, 100, CONTRAST_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_BRIGHTNESS, - g_param_spec_int ("brightness", "Brightness", "Image capture brightness", - 0, 100, BRIGHTNESS_DEFAULT, + g_param_spec_int ("brightness", "Brightness", + "Image capture brightness", 0, 100, BRIGHTNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_SATURATION, - g_param_spec_int ("saturation", "Saturation", "Image capture saturation", - -100, 100, SATURATION_DEFAULT, + g_param_spec_int ("saturation", "Saturation", + "Image capture saturation", -100, 100, SATURATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ISO, - g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", 0, 3200, 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", 0, + 3200, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_VIDEO_STABILISATION, g_param_spec_boolean ("video-stabilisation", "Video Stabilisation", "Enable or disable video stabilisation", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_EXPOSURE_COMPENSATION, - g_param_spec_int ("exposure-compensation", "EV compensation", - "Exposure Value compensation", -10, 10, 0, + g_object_class_install_property (gobject_class, + PROP_EXPOSURE_COMPENSATION, g_param_spec_int ("exposure-compensation", + "EV compensation", "Exposure Value compensation", -10, 10, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EXPOSURE_MODE, g_param_spec_enum ("exposure-mode", "Exposure Mode", "Camera exposure mode to use", GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_MODE, EXPOSURE_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_EXPOSURE_METERING_MODE, - g_param_spec_enum ("metering-mode", "Exposure Metering Mode", - "Camera exposure metering mode to use", + g_object_class_install_property (gobject_class, + PROP_EXPOSURE_METERING_MODE, g_param_spec_enum ("metering-mode", + "Exposure Metering Mode", "Camera exposure metering mode to use", GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, EXPOSURE_METERING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_DRC, - g_param_spec_enum ("drc", "DRC level", - "Dynamic Range Control level", + g_param_spec_enum ("drc", "DRC level", "Dynamic Range Control level", GST_RPI_CAM_TYPE_RPI_CAM_SRC_DRC_LEVEL, GST_RPI_CAM_SRC_DRC_LEVEL_OFF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -299,12 +330,12 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, g_param_spec_float ("awb-gain-red", "AWB Red Gain", - "Manual AWB Gain for red channel when awb-mode=OFF", - 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Manual AWB Gain for red channel when awb-mode=OFF", 0, 8.0, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, g_param_spec_float ("awb-gain-blue", "AWB Blue Gain", - "Manual AWB Gain for blue channel when awb-mode=OFF", - 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Manual AWB Gain for blue channel when awb-mode=OFF", 0, 8.0, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_IMAGE_EFFECT, g_param_spec_enum ("image-effect", "Image effect", "Visual FX to apply to the image", @@ -323,8 +354,9 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "Flip capture horizontally", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_VFLIP, - g_param_spec_boolean ("vflip", "Vertical Flip", "Flip capture vertically", - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("vflip", "Vertical Flip", + "Flip capture vertically", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_X, g_param_spec_float ("roi-x", "ROI X", "Normalised region-of-interest X coord", 0, 1.0, 0, @@ -341,30 +373,32 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_float ("roi-h", "ROI H", "Normalised region-of-interest H coord", 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_QUANTISATION_PARAMETER, - g_param_spec_int ("quantisation-parameter", "Quantisation Parameter", + g_object_class_install_property (gobject_class, + PROP_QUANTISATION_PARAMETER, + g_param_spec_int ("quantisation-parameter", + "Quantisation Parameter", "Set a Quantisation Parameter approx 10-40 with bitrate=0 for VBR encoding. 0 = off", 0, G_MAXINT, QUANTISATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_INLINE_HEADERS, g_param_spec_boolean ("inline-headers", "Inline Headers", "Set to TRUE to insert SPS/PPS before each IDR packet", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_SHUTTER_SPEED, g_param_spec_int ("shutter-speed", "Shutter Speed", - "Set a fixed shutter speed, in microseconds. (0 = Auto)", - 0, 6000000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - + "Set a fixed shutter speed, in microseconds. (0 = Auto)", 0, + 6000000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SENSOR_MODE, + g_param_spec_enum ("sensor-mode", "Camera Sensor Mode", + "Manually set the camera sensor mode", + gst_rpi_cam_src_sensor_mode_get_type (), + GST_RPI_CAM_SRC_SENSOR_MODE_AUTOMATIC, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, - "Raspberry Pi Camera Source", - "Source/Video", + "Raspberry Pi Camera Source", "Source/Video", "Raspberry Pi camera module source", "Jan Schmidt "); - gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&video_src_template)); - basesrc_class->start = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_start); basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_stop); basesrc_class->decide_allocation = @@ -375,7 +409,6 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) basesrc_class->event = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_event); gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_send_event); pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_rpi_cam_src_create); - raspicapture_init (); } @@ -385,10 +418,8 @@ gst_rpi_cam_src_init (GstRpiCamSrc * src) gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (src), TRUE); raspicapture_default_config (&src->capture_config); - src->capture_config.intraperiod = KEYFRAME_INTERVAL_DEFAULT; src->capture_config.verbose = 1; - /* do-timestamping by default for now. FIXME: Implement proper timestamping */ gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE); } @@ -398,7 +429,6 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstRpiCamSrc *src = GST_RPICAMSRC (object); - switch (prop_id) { case PROP_CAMERA_NUMBER: src->capture_config.cameraNum = g_value_get_int (value); @@ -506,7 +536,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.camera_parameters.drc_level = g_value_get_enum (value); break; - + case PROP_SENSOR_MODE: + src->capture_config.sensor_mode = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -518,7 +550,6 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstRpiCamSrc *src = GST_RPICAMSRC (object); - switch (prop_id) { case PROP_CAMERA_NUMBER: g_value_set_int (value, src->capture_config.cameraNum); @@ -625,6 +656,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_DRC: g_value_set_enum (value, src->capture_config.camera_parameters.drc_level); break; + case PROP_SENSOR_MODE: + g_value_set_enum (value, src->capture_config.sensor_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -639,7 +673,6 @@ gst_rpi_cam_src_start (GstBaseSrc * parent) src->capture_state = raspi_capture_setup (&src->capture_config); if (src->capture_state == NULL) return FALSE; - return TRUE; } @@ -711,18 +744,14 @@ gst_rpi_cam_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) { GstRpiCamSrc *src = GST_RPICAMSRC (bsrc); GstCaps *caps; - caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc)); if (src->capture_state == NULL) goto done; - /* FIXME: Retrieve limiting parameters from the camera module, max width/height fps-range */ caps = gst_caps_make_writable (caps); - gst_caps_set_simple (caps, - "width", GST_TYPE_INT_RANGE, 1, 1920, - "height", GST_TYPE_INT_RANGE, 1, 1080, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 90, 1, NULL); - + gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 1920, "height", + GST_TYPE_INT_RANGE, 1, 1080, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, + 90, 1, NULL); done: GST_DEBUG_OBJECT (src, "get_caps returning %" GST_PTR_FORMAT, caps); return caps; @@ -733,16 +762,13 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) { GstRpiCamSrc *src = GST_RPICAMSRC (bsrc); GstVideoInfo info; - GST_DEBUG_OBJECT (src, "In set_caps %" GST_PTR_FORMAT, caps); if (!gst_video_info_from_caps (&info, caps)) return FALSE; - src->capture_config.width = info.width; src->capture_config.height = info.height; src->capture_config.fps_n = info.fps_n; src->capture_config.fps_d = info.fps_d; - return TRUE; } @@ -758,14 +784,10 @@ gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) { GstStructure *structure; gint i; - GST_DEBUG_OBJECT (basesrc, "fixating caps %" GST_PTR_FORMAT, caps); - caps = gst_caps_make_writable (caps); - for (i = 0; i < gst_caps_get_size (caps); ++i) { structure = gst_caps_get_structure (caps, i); - /* Fixate to 1920x1080 resolution if possible */ gst_structure_fixate_field_nearest_int (structure, "width", 1920); gst_structure_fixate_field_nearest_int (structure, "height", 1080); @@ -774,9 +796,7 @@ gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps) } GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, caps); - caps = GST_BASE_SRC_CLASS (parent_class)->fixate (basesrc, caps); - return caps; } @@ -785,7 +805,6 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) { GstRpiCamSrc *src = GST_RPICAMSRC (parent); GstFlowReturn ret; - if (!src->started) { if (!raspi_capture_start (src->capture_state)) return GST_FLOW_ERROR; @@ -797,7 +816,6 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) if (*buf) GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (*buf)); - return ret; } @@ -805,20 +823,14 @@ static gboolean plugin_init (GstPlugin * plugin) { gboolean ret; - GST_DEBUG_CATEGORY_INIT (gst_rpi_cam_src_debug, "rpicamsrc", 0, "rpicamsrc debug"); - ret = gst_element_register (plugin, "rpicamsrc", GST_RANK_NONE, GST_TYPE_RPICAMSRC); - #if GST_CHECK_VERSION (1,4,0) - ret &= gst_device_provider_register (plugin, "rpicamsrcdeviceprovider", GST_RANK_PRIMARY, GST_TYPE_RPICAMSRC_DEVICE_PROVIDER); - #endif - return ret; } diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index 68f560ae0c..592362453f 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -81,6 +81,17 @@ struct _GstRpiCamSrcClass GType gst_rpi_cam_src_get_type (void); +typedef enum { + GST_RPI_CAM_SRC_SENSOR_MODE_AUTOMATIC = 0, + GST_RPI_CAM_SRC_SENSOR_MODE_1920x1080 = 1, + GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_FAST = 2, + GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_SLOW = 3, + GST_RPI_CAM_SRC_SENSOR_MODE_1296x972 = 4, + GST_RPI_CAM_SRC_SENSOR_MODE_1296x730 = 5, + GST_RPI_CAM_SRC_SENSOR_MODE_640x480_SLOW = 6, + GST_RPI_CAM_SRC_SENSOR_MODE_640x480_FAST = 7 +} GstRpiCamSrcSensorMode; + G_END_DECLS #endif /* __GST_RPICAMSRC_H__ */ From ed22d5cc4ee01905b7a2648d94d75079752df31f Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 03:05:24 +1100 Subject: [PATCH 25/77] rpicamsrc: Add annotation-mode and annotation-text properties --- sys/rpicamsrc/gstrpicam_types.h | 14 ++++++++++++++ sys/rpicamsrc/gstrpicamsrc.c | 26 +++++++++++++++++++++++++- sys/rpicamsrc/gstrpicamsrc.h | 4 ++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/sys/rpicamsrc/gstrpicam_types.h b/sys/rpicamsrc/gstrpicam_types.h index acc47423d8..a2ef412ac5 100644 --- a/sys/rpicamsrc/gstrpicam_types.h +++ b/sys/rpicamsrc/gstrpicam_types.h @@ -1,5 +1,6 @@ #include "interface/mmal/util/mmal_util_params.h" #include "interface/mmal/mmal_parameters_camera.h" +#include "RaspiCamControl.h" typedef enum { GST_RPI_CAM_SRC_EXPOSURE_MODE_OFF = MMAL_PARAM_EXPOSUREMODE_OFF, @@ -77,3 +78,16 @@ typedef enum { GST_RPI_CAM_SRC_DRC_LEVEL_HIGH = MMAL_PARAMETER_DRC_STRENGTH_HIGH } GstRpiCamSrcDRCLevel; +typedef enum /*< flags >*/ { + GST_RPI_CAM_SRC_ANNOTATION_MODE_CUSTOM_TEXT = ANNOTATE_USER_TEXT, + GST_RPI_CAM_SRC_ANNOTATION_MODE_TEXT = ANNOTATE_APP_TEXT, + GST_RPI_CAM_SRC_ANNOTATION_MODE_DATE = ANNOTATE_DATE_TEXT, + GST_RPI_CAM_SRC_ANNOTATION_MODE_TIME = ANNOTATE_TIME_TEXT, + GST_RPI_CAM_SRC_ANNOTATION_MODE_SHUTTER_SETTINGS = ANNOTATE_SHUTTER_SETTINGS, + GST_RPI_CAM_SRC_ANNOTATION_MODE_CAF_SETTINGS = ANNOTATE_CAF_SETTINGS, + GST_RPI_CAM_SRC_ANNOTATION_MODE_GAIN_SETTINGS = ANNOTATE_GAIN_SETTINGS, + GST_RPI_CAM_SRC_ANNOTATION_MODE_LENS_SETTINGS = ANNOTATE_LENS_SETTINGS, + GST_RPI_CAM_SRC_ANNOTATION_MODE_MOTION_SETTINGS = ANNOTATE_MOTION_SETTINGS, + GST_RPI_CAM_SRC_ANNOTATION_MODE_FRAME_NUMBER = ANNOTATE_FRAME_NUMBER, + GST_RPI_CAM_SRC_ANNOTATION_MODE_BLACK_BACKGROUND = ANNOTATE_BLACK_BACKGROUND +} GstRpiCamSrcAnnotationMode; diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 4e40597c68..d3763aa9ef 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -125,7 +125,7 @@ enum PROP_SENSOR_MODE, PROP_DRC, PROP_ANNOTATION_MODE, - PROP_ANNOTATION_STRING + PROP_ANNOTATION_TEXT }; #define CAMERA_DEFAULT 0 @@ -394,6 +394,16 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gst_rpi_cam_src_sensor_mode_get_type (), GST_RPI_CAM_SRC_SENSOR_MODE_AUTOMATIC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ANNOTATION_MODE, + g_param_spec_flags ("annotation-mode", "Annotation Mode", + "Flags to control annotation of the output video", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_ANNOTATION_MODE, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT, + g_param_spec_string ("annotation-text", "Annotation Text", + "Text string to annotate onto video when annotation-mode flags include 'custom-text'", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", "Raspberry Pi camera module source", "Jan Schmidt "); @@ -539,6 +549,14 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_SENSOR_MODE: src->capture_config.sensor_mode = g_value_get_enum (value); break; + case PROP_ANNOTATION_MODE: + src->capture_config.camera_parameters.enable_annotate = g_value_get_flags (value); + break; + case PROP_ANNOTATION_TEXT: + strncpy (src->capture_config.camera_parameters.annotate_string, + g_value_get_string (value), MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); + src->capture_config.camera_parameters.annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - 1] = '\0'; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -659,6 +677,12 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_SENSOR_MODE: g_value_set_enum (value, src->capture_config.sensor_mode); break; + case PROP_ANNOTATION_MODE: + g_value_set_flags (value, src->capture_config.camera_parameters.enable_annotate); + break; + case PROP_ANNOTATION_TEXT: + g_value_set_string (value, src->capture_config.camera_parameters.annotate_string); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index 592362453f..86f5d24ab4 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -1,7 +1,7 @@ /* * GStreamer * Copyright (C) 2013 Jan Schmidt - * + * * 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 @@ -74,7 +74,7 @@ struct _GstRpiCamSrc gboolean started; }; -struct _GstRpiCamSrcClass +struct _GstRpiCamSrcClass { GstPushSrcClass parent_class; }; From 2087b1e44e4b63c16cde506f1fe7daf39d40de85 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 6 Mar 2015 03:43:07 +1100 Subject: [PATCH 26/77] rpicamsrc: Add intra-refresh-type property, and set default keyframe spacing to -1 (auto) This plus other recent commits mostly fix bug https://github.com/thaytan/gst-rpicamsrc/issues/16 --- sys/rpicamsrc/gstrpicam_types.h | 8 ++++ sys/rpicamsrc/gstrpicamsrc.c | 76 ++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/sys/rpicamsrc/gstrpicam_types.h b/sys/rpicamsrc/gstrpicam_types.h index a2ef412ac5..375bf573ce 100644 --- a/sys/rpicamsrc/gstrpicam_types.h +++ b/sys/rpicamsrc/gstrpicam_types.h @@ -91,3 +91,11 @@ typedef enum /*< flags >*/ { GST_RPI_CAM_SRC_ANNOTATION_MODE_FRAME_NUMBER = ANNOTATE_FRAME_NUMBER, GST_RPI_CAM_SRC_ANNOTATION_MODE_BLACK_BACKGROUND = ANNOTATE_BLACK_BACKGROUND } GstRpiCamSrcAnnotationMode; + +typedef enum { + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE = -1, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_ADAPTIVE, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_BOTH, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC_ROWS +} GstRpiCamSrcIntraRefreshType; diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index d3763aa9ef..4605b34afc 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -125,7 +125,8 @@ enum PROP_SENSOR_MODE, PROP_DRC, PROP_ANNOTATION_MODE, - PROP_ANNOTATION_TEXT + PROP_ANNOTATION_TEXT, + PROP_INTRA_REFRESH_TYPE }; #define CAMERA_DEFAULT 0 @@ -142,7 +143,7 @@ enum #define ISO_DEFAULT 0 #define VIDEO_STABILISATION_DEFAULT FALSE #define EXPOSURE_COMPENSATION_DEFAULT 0 -#define KEYFRAME_INTERVAL_DEFAULT 25 +#define KEYFRAME_INTERVAL_DEFAULT -1 #define EXPOSURE_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO #define EXPOSURE_METERING_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE @@ -261,8 +262,8 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_KEYFRAME_INTERVAL, g_param_spec_int ("keyframe-interval", "Keyframe Interface", - "Interval (in frames) between I frames. 0 = single-keyframe", 0, - G_MAXINT, KEYFRAME_INTERVAL_DEFAULT, + "Interval (in frames) between I frames. -1 = automatic, 0 = single-keyframe", + -1, G_MAXINT, KEYFRAME_INTERVAL_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_PREVIEW, g_param_spec_boolean ("preview", "Preview Window", @@ -276,52 +277,50 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_boolean ("preview-encoded", "Preview Encoded", "Display encoder output in the preview", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), - PROP_PREVIEW_OPACITY, g_param_spec_int ("preview-opacity", - "Preview Opacity", "Opacity to use for the preview window", 0, - 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_OPACITY, + g_param_spec_int ("preview-opacity", "Preview Opacity", + "Opacity to use for the preview window", 0, 255, 255, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHARPNESS, g_param_spec_int ("sharpness", "Sharpness", "Image capture sharpness", -100, 100, SHARPNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CONTRAST, - g_param_spec_int ("contrast", "Contrast", "Image capture contrast", - -100, 100, CONTRAST_DEFAULT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("contrast", "Contrast", "Image capture contrast", -100, + 100, CONTRAST_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_BRIGHTNESS, - g_param_spec_int ("brightness", "Brightness", - "Image capture brightness", 0, 100, BRIGHTNESS_DEFAULT, + g_param_spec_int ("brightness", "Brightness", "Image capture brightness", + 0, 100, BRIGHTNESS_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_SATURATION, - g_param_spec_int ("saturation", "Saturation", - "Image capture saturation", -100, 100, SATURATION_DEFAULT, + g_param_spec_int ("saturation", "Saturation", "Image capture saturation", + -100, 100, SATURATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ISO, - g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", 0, - 3200, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("iso", "ISO", "ISO value to use (0 = Auto)", 0, 3200, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_VIDEO_STABILISATION, g_param_spec_boolean ("video-stabilisation", "Video Stabilisation", "Enable or disable video stabilisation", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, - PROP_EXPOSURE_COMPENSATION, g_param_spec_int ("exposure-compensation", - "EV compensation", "Exposure Value compensation", -10, 10, 0, + g_object_class_install_property (gobject_class, PROP_EXPOSURE_COMPENSATION, + g_param_spec_int ("exposure-compensation", "EV compensation", + "Exposure Value compensation", -10, 10, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EXPOSURE_MODE, g_param_spec_enum ("exposure-mode", "Exposure Mode", "Camera exposure mode to use", GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_MODE, EXPOSURE_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, - PROP_EXPOSURE_METERING_MODE, g_param_spec_enum ("metering-mode", - "Exposure Metering Mode", "Camera exposure metering mode to use", + g_object_class_install_property (gobject_class, PROP_EXPOSURE_METERING_MODE, + g_param_spec_enum ("metering-mode", "Exposure Metering Mode", + "Camera exposure metering mode to use", GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, EXPOSURE_METERING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_DRC, g_param_spec_enum ("drc", "DRC level", "Dynamic Range Control level", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_DRC_LEVEL, - GST_RPI_CAM_SRC_DRC_LEVEL_OFF, + GST_RPI_CAM_TYPE_RPI_CAM_SRC_DRC_LEVEL, GST_RPI_CAM_SRC_DRC_LEVEL_OFF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_MODE, g_param_spec_enum ("awb-mode", "Automatic White Balance Mode", @@ -403,6 +402,12 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_string ("annotation-text", "Annotation Text", "Text string to annotate onto video when annotation-mode flags include 'custom-text'", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_INTRA_REFRESH_TYPE, + g_param_spec_enum ("intra-refresh-type", "Intra Refresh Type", + "Type of Intra Refresh to use, -1 to disable intra refresh", + GST_RPI_CAM_TYPE_RPI_CAM_SRC_INTRA_REFRESH_TYPE, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", @@ -550,12 +555,19 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.sensor_mode = g_value_get_enum (value); break; case PROP_ANNOTATION_MODE: - src->capture_config.camera_parameters.enable_annotate = g_value_get_flags (value); + src->capture_config.camera_parameters.enable_annotate = + g_value_get_flags (value); break; case PROP_ANNOTATION_TEXT: strncpy (src->capture_config.camera_parameters.annotate_string, g_value_get_string (value), MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); - src->capture_config.camera_parameters.annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - 1] = '\0'; + src->capture_config. + camera_parameters.annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 + - 1] = '\0'; + break; + case PROP_INTRA_REFRESH_TYPE: + src->capture_config.intra_refresh_type = + g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -678,10 +690,16 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, g_value_set_enum (value, src->capture_config.sensor_mode); break; case PROP_ANNOTATION_MODE: - g_value_set_flags (value, src->capture_config.camera_parameters.enable_annotate); + g_value_set_flags (value, + src->capture_config.camera_parameters.enable_annotate); break; case PROP_ANNOTATION_TEXT: - g_value_set_string (value, src->capture_config.camera_parameters.annotate_string); + g_value_set_string (value, + src->capture_config.camera_parameters.annotate_string); + break; + case PROP_INTRA_REFRESH_TYPE: + g_value_set_enum (value, + src->capture_config.intra_refresh_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); From 3abe941e74c424a67d7e5415ce2c69e8b93738a3 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 7 Mar 2015 01:17:30 +1100 Subject: [PATCH 27/77] rpicamsrc: Read and set H.264 profile from negotiated caps --- sys/rpicamsrc/gstrpicamsrc.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 4605b34afc..cf5c6bdc05 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -566,8 +566,7 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, - 1] = '\0'; break; case PROP_INTRA_REFRESH_TYPE: - src->capture_config.intra_refresh_type = - g_value_get_enum (value); + src->capture_config.intra_refresh_type = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -698,8 +697,7 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, src->capture_config.camera_parameters.annotate_string); break; case PROP_INTRA_REFRESH_TYPE: - g_value_set_enum (value, - src->capture_config.intra_refresh_type); + g_value_set_enum (value, src->capture_config.intra_refresh_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -804,9 +802,26 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) { GstRpiCamSrc *src = GST_RPICAMSRC (bsrc); GstVideoInfo info; + GstStructure *structure; + const gchar *profile_str = NULL; + GST_DEBUG_OBJECT (src, "In set_caps %" GST_PTR_FORMAT, caps); if (!gst_video_info_from_caps (&info, caps)) return FALSE; + + structure = gst_caps_get_structure (caps, 0); + profile_str = gst_structure_get_string (structure, "profile"); + if (profile_str) { + if (g_str_equal (profile_str, "baseline")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; + else if (g_str_equal (profile_str, "main")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_MAIN; + else if (g_str_equal (profile_str, "high")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_HIGH; + else + g_warning ("Unknown profile string in rpicamsrc caps: %s", profile_str); + } + src->capture_config.width = info.width; src->capture_config.height = info.height; src->capture_config.fps_n = info.fps_n; From c3ad9c99a61957e98570bf4f67b69f19deccecf1 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 7 Mar 2015 02:11:25 +1100 Subject: [PATCH 28/77] rpicamsrc: Defer encoder creation until after caps are negotiated This ensures the encoder is created with the profile negotiated with downstream --- sys/rpicamsrc/RaspiCapture.c | 49 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 7412d19e37..f8b357643d 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1466,20 +1466,21 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) static void destroy_encoder_component(RASPIVID_STATE *state) { /* Empty the buffer header q */ - while (mmal_queue_length(state->encoded_buffer_q)) { - MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q); - mmal_buffer_header_release(buffer); - } - mmal_queue_destroy(state->encoded_buffer_q); - - // Get rid of any port buffers first - if (state->encoder_pool) - { - mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + if (state->encoded_buffer_q) { + while (mmal_queue_length(state->encoded_buffer_q)) { + MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q); + mmal_buffer_header_release(buffer); + } } - if (state->encoder_component) - { + if (state->encoder_component) { + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + state->encoder_pool = NULL; + } + mmal_component_destroy(state->encoder_component); state->encoder_component = NULL; } @@ -1566,14 +1567,6 @@ raspi_capture_setup(RASPIVID_CONFIG *config) return NULL; } - if ((status = create_encoder_component(state)) != MMAL_SUCCESS) - { - vcos_log_error("%s: Failed to create encode component", __func__); - raspipreview_destroy(&state->config->preview_parameters); - destroy_camera_component(state); - return NULL; - } - state->encoded_buffer_q = mmal_queue_create(); return state; @@ -1587,6 +1580,12 @@ raspi_capture_start(RASPIVID_STATE *state) MMAL_PORT_T *preview_input_port = NULL; MMAL_PORT_T *encoder_input_port = NULL; + if ((status = create_encoder_component(state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + return FALSE; + } + if (state->config->verbose) { dump_state(state); @@ -1737,6 +1736,11 @@ raspi_capture_stop(RASPIVID_STATE *state) // Disable all our ports that are not handled by connections check_disable_port(state->camera_still_port); check_disable_port(state->encoder_output_port); + + if (state->encoder_component) { + mmal_component_disable(state->encoder_component); + destroy_encoder_component(state); + } } void @@ -1761,6 +1765,11 @@ raspi_capture_free(RASPIVID_STATE *state) raspipreview_destroy(&state->config->preview_parameters); destroy_camera_component(state); + if (state->encoded_buffer_q) { + mmal_queue_destroy(state->encoded_buffer_q); + state->encoded_buffer_q = NULL; + } + if (state->config->verbose) fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); From 8fe0590c293b31f67096faacf26658137e473da7 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 10 Mar 2015 00:22:40 +1100 Subject: [PATCH 29/77] rpicamsrc: Use MMAL PTS and STC to calculate GStreamer timestamps Don't apply timestamps based on output time from the encoder, but use the MMAL STC and capture PTS to generate a GStreamer timestamp that more accurately resembles the input (and would preserve reordering should the encoder ever add B-frames). Fixes https://github.com/thaytan/gst-rpicamsrc/issues/16 --- sys/rpicamsrc/RaspiCapture.c | 34 ++++++++++++++++++++++++++++++++-- sys/rpicamsrc/RaspiCapture.h | 3 ++- sys/rpicamsrc/gstrpicamsrc.c | 19 ++++++++++++++++--- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index f8b357643d..489fcd970c 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -911,18 +911,48 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf } GstFlowReturn -raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp) +raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, + GstClock *clock, GstClockTime base_time) { GstBuffer *buf; MMAL_BUFFER_HEADER_T *buffer; GstFlowReturn ret = GST_FLOW_ERROR; + /* No timestamps if no clockm or invalid PTS */ + GstClockTime gst_pts = GST_CLOCK_TIME_NONE; /* FIXME: Use our own interruptible cond wait: */ buffer = mmal_queue_wait(state->encoded_buffer_q); + + if (G_LIKELY (clock)) { + MMAL_PARAMETER_INT64_T param; + GstClockTime runtime; + + runtime = gst_clock_get_time (clock) - base_time; + + param.hdr.id = MMAL_PARAMETER_SYSTEM_TIME; + param.hdr.size = sizeof(param); + param.value = -1; + + mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr); + + if (param.value != -1 && param.value >= buffer->pts) { + GstClockTime offset = param.value - buffer->pts; + if (runtime >= offset) + gst_pts = runtime - offset; + } + GST_LOG ("Buf PTS %" G_GINT64_FORMAT " DTS %" G_GINT64_FORMAT + " STC %" G_GINT64_FORMAT " TS %" GST_TIME_FORMAT, + buffer->pts, buffer->dts, param.value, + GST_TIME_ARGS (gst_pts)); + } + + mmal_buffer_header_mem_lock(buffer); buf = gst_buffer_new_allocate(NULL, buffer->length, NULL); if (buf) { + /* FIXME: Can we avoid copies and give MMAL our own buffers to fill? */ + GST_BUFFER_PTS(buf) = gst_pts; gst_buffer_fill(buf, 0, buffer->data, buffer->length); ret = GST_FLOW_OK; } @@ -1056,7 +1086,7 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) .num_preview_video_frames = 3, .stills_capture_circular_buffer_height = 0, .fast_preview_resume = 0, - .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC }; camera = state->camera_component; diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index f4f0bf9048..5a72195a2e 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -101,7 +101,8 @@ void raspicapture_init(); void raspicapture_default_config(RASPIVID_CONFIG *config); RASPIVID_STATE *raspi_capture_setup(RASPIVID_CONFIG *config); gboolean raspi_capture_start(RASPIVID_STATE *state); -GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf); +GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf, + GstClock *clock, GstClockTime base_time); void raspi_capture_stop(RASPIVID_STATE *state); void raspi_capture_free(RASPIVID_STATE *state); gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state); diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index cf5c6bdc05..4a6afe08f8 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -435,8 +435,9 @@ gst_rpi_cam_src_init (GstRpiCamSrc * src) raspicapture_default_config (&src->capture_config); src->capture_config.intraperiod = KEYFRAME_INTERVAL_DEFAULT; src->capture_config.verbose = 1; - /* do-timestamping by default for now. FIXME: Implement proper timestamping */ - gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE); + /* Don't let basesrc set timestamps, we'll do it using + * buffer PTS and system times */ + gst_base_src_set_do_timestamp (GST_BASE_SRC (src), FALSE); } static void @@ -862,17 +863,29 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) { GstRpiCamSrc *src = GST_RPICAMSRC (parent); GstFlowReturn ret; + GstClock *clock = NULL; + GstClockTime base_time; + if (!src->started) { if (!raspi_capture_start (src->capture_state)) return GST_FLOW_ERROR; src->started = TRUE; } + GST_OBJECT_LOCK (src); + if ((clock = GST_ELEMENT_CLOCK (src)) != NULL) + gst_object_ref (clock); + base_time = GST_ELEMENT_CAST (src)->base_time; + GST_OBJECT_UNLOCK (src); + /* FIXME: Use custom allocator */ - ret = raspi_capture_fill_buffer (src->capture_state, buf); + ret = raspi_capture_fill_buffer (src->capture_state, buf, clock, base_time); if (*buf) GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (*buf)); + + if (clock) + gst_object_unref (clock); return ret; } From edf4927704a48536248ed84fa31c954566bde944 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 21 Apr 2015 00:02:27 +1000 Subject: [PATCH 30/77] rpicamsrc: Map intra-refresh cyclic-rows to the correct MMAL param. --- sys/rpicamsrc/gstrpicam_types.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/rpicamsrc/gstrpicam_types.h b/sys/rpicamsrc/gstrpicam_types.h index 375bf573ce..5baa491e7e 100644 --- a/sys/rpicamsrc/gstrpicam_types.h +++ b/sys/rpicamsrc/gstrpicam_types.h @@ -94,8 +94,8 @@ typedef enum /*< flags >*/ { typedef enum { GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE = -1, - GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC, - GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_ADAPTIVE, - GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_BOTH, - GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC_ROWS + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC = MMAL_VIDEO_INTRA_REFRESH_CYCLIC, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_ADAPTIVE = MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_BOTH = MMAL_VIDEO_INTRA_REFRESH_BOTH, + GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC_ROWS = MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS } GstRpiCamSrcIntraRefreshType; From 4d70e1d8eea8673557ea26bb662f70b3abb8fe9b Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 21 Apr 2015 01:17:55 +1000 Subject: [PATCH 31/77] rpicamsrc: Merge changes from userland repo Current to b69f807ce59189457662c2144a8e7e12dc776988 No integration of stereoscopic support as yet --- sys/rpicamsrc/RaspiCamControl.c | 155 ++++++++++++++++++++++++++++---- sys/rpicamsrc/RaspiCamControl.h | 9 +- sys/rpicamsrc/RaspiCapture.c | 11 ++- 3 files changed, 152 insertions(+), 23 deletions(-) diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c index 5167523610..1b3d059647 100644 --- a/sys/rpicamsrc/RaspiCamControl.c +++ b/sys/rpicamsrc/RaspiCamControl.c @@ -128,6 +128,15 @@ static XREF_T drc_mode_map[] = static const int drc_mode_map_size = sizeof(drc_mode_map)/sizeof(drc_mode_map[0]); +static XREF_T stereo_mode_map[] = +{ + {"off", MMAL_STEREOSCOPIC_MODE_NONE}, + {"sbs", MMAL_STEREOSCOPIC_MODE_SIDE_BY_SIDE}, + {"tb", MMAL_STEREOSCOPIC_MODE_TOP_BOTTOM}, +}; + +static const int stereo_mode_map_size = sizeof(stereo_mode_map)/sizeof(stereo_mode_map[0]); + #define CommandSharpness 0 #define CommandContrast 1 @@ -150,6 +159,10 @@ static const int drc_mode_map_size = sizeof(drc_mode_map)/sizeof(drc_mode_map[0] #define CommandDRCLevel 18 #define CommandStatsPass 19 #define CommandAnnotate 20 +#define CommandStereoMode 21 +#define CommandStereoDecimate 22 +#define CommandStereoSwap 23 +#define CommandAnnotateExtras 24 static COMMAND_LIST cmdline_commands[] = { @@ -174,6 +187,10 @@ static COMMAND_LIST cmdline_commands[] = {CommandDRCLevel, "-drc", "drc", "Set DRC Level", 1}, {CommandStatsPass, "-stats", "st", "Force recomputation of statistics on stills capture pass"}, {CommandAnnotate, "-annotate", "a", "Enable/Set annotate flags or text", 1}, + {CommandStereoMode, "-stereo", "3d", "Select stereoscopic mode", 1}, + {CommandStereoDecimate,"-decimate","dec", "Half width/height of stereo image"}, + {CommandStereoSwap, "-3dswap", "3dswap", "Swap camera order for stereoscopic"}, + {CommandAnnotateExtras,"-annotateex","ae", "Set extra annotation parameters (text size, text colour(hex YUV), bg colour(hex YUV))", 2}, }; static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); @@ -591,13 +608,69 @@ int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char else { params->enable_annotate = ANNOTATE_USER_TEXT; - strncpy(params->annotate_string, arg2, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); - params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2-1] = '\0'; + //copy string char by char and replace "\n" with newline character + unsigned char c; + char const *s = arg2; + char *t = ¶ms->annotate_string[0]; + int n=0; + while ((c = *s++) && n < MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1) + { + if (c == '\\' && *s) + { + switch (c = *s++) + { + case 'n': + c = '\n'; + break; + + default: + c = '\\'; + s--; + break; + } + } + *(t++) = c; + n++; + } + *t='\0'; + + //params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; } used=2; break; } + case CommandAnnotateExtras: + { + // 3 parameters - text size (6-80), text colour (Hex VVUUYY) and background colour (Hex VVUUYY) + sscanf(arg2, "%u,%X,%X", ¶ms->annotate_text_size, + ¶ms->annotate_text_colour, + ¶ms->annotate_bg_colour); + used=2; + break; + } + + case CommandStereoMode: + { + params->stereo_mode.mode = stereo_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStereoDecimate: + { + params->stereo_mode.decimate = MMAL_TRUE; + used = 1; + break; + } + + case CommandStereoSwap: + { + params->stereo_mode.swap_eyes = MMAL_TRUE; + used = 1; + break; + } + } return used; @@ -744,6 +817,12 @@ void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params) params->stats_pass = MMAL_FALSE; params->enable_annotate = 0; params->annotate_string[0] = '\0'; + params->annotate_text_size = 0; //Use firmware default + params->annotate_text_colour = -1; //Use firmware default + params->annotate_bg_colour = -1; //Use firmware default + params->stereo_mode.mode = MMAL_STEREOSCOPIC_MODE_NONE; + params->stereo_mode.decimate = MMAL_FALSE; + params->stereo_mode.swap_eyes = MMAL_FALSE; } /** @@ -807,7 +886,10 @@ int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_ result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); result += raspicamcontrol_set_DRC(camera, params->drc_level); result += raspicamcontrol_set_stats_pass(camera, params->stats_pass); - result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string); + result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string, + params->annotate_text_size, + params->annotate_text_colour, + params->annotate_bg_colour); return result; } @@ -1238,57 +1320,80 @@ int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass) * * @return 0 if successful, non-zero if any parameters out of range */ -int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string) +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string, + const int text_size, const int text_colour, const int bg_colour) { - MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T annotate = - {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T)}}; + MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T annotate = + {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T)}}; if (settings) { time_t t = time(NULL); struct tm tm = *localtime(&t); - char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; + char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3]; - annotate.enable = 1; + annotate.enable = 1; if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT)) { - strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); - annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2-1] = '\0'; + strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3); + annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; } if (settings & ANNOTATE_TIME_TEXT) { strftime(tmp, 32, "%X ", &tm ); - strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - strlen(annotate.text) - 1); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); } if (settings & ANNOTATE_DATE_TEXT) { strftime(tmp, 32, "%x", &tm ); - strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - strlen(annotate.text) - 1); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); } if (settings & ANNOTATE_SHUTTER_SETTINGS) - annotate.show_shutter = 1; + annotate.show_shutter = MMAL_TRUE; if (settings & ANNOTATE_GAIN_SETTINGS) - annotate.show_analog_gain = 1; + annotate.show_analog_gain = MMAL_TRUE; if (settings & ANNOTATE_LENS_SETTINGS) - annotate.show_lens = 1; + annotate.show_lens = MMAL_TRUE; if (settings & ANNOTATE_CAF_SETTINGS) - annotate.show_caf = 1; + annotate.show_caf = MMAL_TRUE; if (settings & ANNOTATE_MOTION_SETTINGS) - annotate.show_motion = 1; + annotate.show_motion = MMAL_TRUE; if (settings & ANNOTATE_FRAME_NUMBER) - annotate.show_frame_num = 1; + annotate.show_frame_num = MMAL_TRUE; if (settings & ANNOTATE_BLACK_BACKGROUND) - annotate.black_text_background = 1; + annotate.enable_text_background = MMAL_TRUE; + + annotate.text_size = text_size; + + if (text_colour != -1) + { + annotate.custom_text_colour = MMAL_TRUE; + annotate.custom_text_Y = text_colour&0xff; + annotate.custom_text_U = (text_colour>>8)&0xff; + annotate.custom_text_V = (text_colour>>16)&0xff; + } + else + annotate.custom_text_colour = MMAL_FALSE; + + if (bg_colour != -1) + { + annotate.custom_background_colour = MMAL_TRUE; + annotate.custom_background_Y = bg_colour&0xff; + annotate.custom_background_U = (bg_colour>>8)&0xff; + annotate.custom_background_V = (bg_colour>>16)&0xff; + } + else + annotate.custom_background_colour = MMAL_FALSE; } else annotate.enable = 0; @@ -1296,6 +1401,18 @@ int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, c return mmal_status_to_int(mmal_port_parameter_set(camera->control, &annotate.hdr)); } +int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode) +{ + MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo = { {MMAL_PARAMETER_STEREOSCOPIC_MODE, sizeof(stereo)}, + MMAL_STEREOSCOPIC_MODE_NONE, MMAL_FALSE, MMAL_FALSE }; + if (stereo_mode->mode != MMAL_STEREOSCOPIC_MODE_NONE) + { + stereo.mode = stereo_mode->mode; + stereo.decimate = stereo_mode->decimate; + stereo.swap_eyes = stereo_mode->swap_eyes; + } + return mmal_status_to_int(mmal_port_parameter_set(port, &stereo.hdr)); +} /** * Asked GPU how much memory it has allocated diff --git a/sys/rpicamsrc/RaspiCamControl.h b/sys/rpicamsrc/RaspiCamControl.h index 3393947db7..83e9eddf15 100644 --- a/sys/rpicamsrc/RaspiCamControl.h +++ b/sys/rpicamsrc/RaspiCamControl.h @@ -155,7 +155,10 @@ typedef struct MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings - + int annotate_text_size; // Text size for annotation + int annotate_text_colour; // Text colour for annotation + int annotate_bg_colour; // Background colour for annotation + MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo_mode; } RASPICAM_CAMERA_PARAMETERS; @@ -194,7 +197,9 @@ int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength); int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass); -int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string); +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string, + const int text_size, const int text_colour, const int bg_colour); +int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode); //Individual getting functions int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 489fcd970c..b08b91fbf7 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -857,13 +857,20 @@ static void update_annotation_data(RASPIVID_STATE *state) config->intraperiod, raspicli_unmap_xref(config->profile, profile_map, profile_map_size)); - raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text); + raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text, + config->camera_parameters.annotate_text_size, + config->camera_parameters.annotate_text_colour, + config->camera_parameters.annotate_bg_colour); free(text); } else { - raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, config->camera_parameters.annotate_string); + raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, + config->camera_parameters.annotate_string, + config->camera_parameters.annotate_text_size, + config->camera_parameters.annotate_text_colour, + config->camera_parameters.annotate_bg_colour); } } From 3771777f7f04cbe9fce43b46a3db87204b51df58 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 21 Apr 2015 02:45:59 +1000 Subject: [PATCH 32/77] rpicamsrc: Clear intra-refresh MMAL param struct. Use memset on the stack allocated MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T struct. Apparently mmal_port_parameter_get() doesn't retrieve all parameters, causing random failures when we set the intra-refresh param on the encoder. Fixes https://github.com/thaytan/gst-rpicamsrc/issues/22 for me. --- sys/rpicamsrc/RaspiCapture.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index b08b91fbf7..34194fbae2 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1441,6 +1441,13 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (config->intra_refresh_type != -1) { MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + + /* Need to memset, apparently mmal_port_parameter_get() + * doesn't retrieve all parameters, causing random failures + * when we set it + */ + memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T)); + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; param.hdr.size = sizeof(param); From 1610030b40ff1a51b708ced82477f1e06f373ca4 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 27 Apr 2015 00:40:23 +1000 Subject: [PATCH 33/77] rpicamsrc: split preview config and state --- sys/rpicamsrc/RaspiPreview.c | 121 ++++++++++++++++++++--------------- sys/rpicamsrc/RaspiPreview.h | 20 ++++-- 2 files changed, 84 insertions(+), 57 deletions(-) diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c index 1632ae09cd..032f0f16a9 100644 --- a/sys/rpicamsrc/RaspiPreview.c +++ b/sys/rpicamsrc/RaspiPreview.c @@ -72,13 +72,15 @@ static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_com * @return MMAL_SUCCESS if all OK, something else otherwise * */ -MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_STATE *state, + RASPIPREVIEW_PARAMETERS *config) { MMAL_COMPONENT_T *preview = 0; - MMAL_PORT_T *preview_port = NULL; MMAL_STATUS_T status; - if (!state->wantPreview) + state->havePreview = config->wantPreview; + + if (!config->wantPreview) { // No preview required, so create a null sink component to take its place status = mmal_component_create("vc.null_sink", &preview); @@ -88,12 +90,13 @@ MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) vcos_log_error("Unable to create null sink component"); goto error; } + + state->preview_component = preview; } else { status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &preview); - if (status != MMAL_SUCCESS) { vcos_log_error("Unable to create preview component"); @@ -107,32 +110,9 @@ MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) goto error; } - preview_port = preview->input[0]; - - MMAL_DISPLAYREGION_T param; - param.hdr.id = MMAL_PARAMETER_DISPLAYREGION; - param.hdr.size = sizeof(MMAL_DISPLAYREGION_T); - - param.set = MMAL_DISPLAY_SET_LAYER; - param.layer = PREVIEW_LAYER; - - param.set |= MMAL_DISPLAY_SET_ALPHA; - param.alpha = state->opacity; - - if (state->wantFullScreenPreview) - { - param.set |= MMAL_DISPLAY_SET_FULLSCREEN; - param.fullscreen = 1; - } - else - { - param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN); - param.fullscreen = 0; - param.dest_rect = state->previewWindow; - } - - status = mmal_port_parameter_set(preview_port, ¶m.hdr); + state->preview_component = preview; + raspipreview_update_config (state, config); if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) { vcos_log_error("unable to set preview port parameters (%u)", status); @@ -142,25 +122,65 @@ MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) /* Enable component */ status = mmal_component_enable(preview); - if (status != MMAL_SUCCESS) { vcos_log_error("Unable to enable preview/null sink component (%u)", status); goto error; } - state->preview_component = preview; - return status; error: - - if (preview) - mmal_component_destroy(preview); + if (preview) { + mmal_component_destroy(preview); + state->preview_component = NULL; + } return status; } +MMAL_STATUS_T +raspipreview_update_config (RASPIPREVIEW_STATE *state, + RASPIPREVIEW_PARAMETERS *config) +{ + MMAL_COMPONENT_T *preview = state->preview_component; + MMAL_PORT_T *preview_port = NULL; + MMAL_DISPLAYREGION_T param; + MMAL_STATUS_T status; + + /* Can't update props on the null preview component */ + if (state->havePreview == 0) + return MMAL_SUCCESS; + + preview_port = preview->input[0]; + + param.hdr.id = MMAL_PARAMETER_DISPLAYREGION; + param.hdr.size = sizeof(MMAL_DISPLAYREGION_T); + + param.set = MMAL_DISPLAY_SET_LAYER; + param.layer = PREVIEW_LAYER; + + param.set |= MMAL_DISPLAY_SET_ALPHA; + param.alpha = config->opacity; + + if (config->wantFullScreenPreview) + { + param.set |= MMAL_DISPLAY_SET_FULLSCREEN; + param.fullscreen = 1; + } + else + { + param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN); + param.fullscreen = 0; + param.dest_rect = config->previewWindow; + } + + status = mmal_port_parameter_set(preview_port, ¶m.hdr); + if (status == MMAL_ENOSYS) + status = MMAL_SUCCESS; + + return status; +} /** * Destroy the preview component @@ -168,7 +188,7 @@ error: * @param state Pointer to state control struct * */ -void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state) +void raspipreview_destroy(RASPIPREVIEW_STATE *state) { if (state->preview_component) { @@ -183,16 +203,15 @@ void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state) * @param state Pointer to parameter block * */ -void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state) +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *config) { - state->wantPreview = 1; - state->wantFullScreenPreview = 1; - state->opacity = 255; - state->previewWindow.x = 0; - state->previewWindow.y = 0; - state->previewWindow.width = 1024; - state->previewWindow.height = 768; - state->preview_component = NULL; + config->wantPreview = 1; + config->wantFullScreenPreview = 1; + config->opacity = 255; + config->previewWindow.x = 0; + config->previewWindow.y = 0; + config->previewWindow.width = 1024; + config->previewWindow.height = 768; } /** @@ -201,14 +220,14 @@ void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state) * @param state Pointer to parameter block * */ -void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state) +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *config) { - fprintf(stderr, "Preview %s, Full screen %s\n", state->wantPreview ? "Yes" : "No", - state->wantFullScreenPreview ? "Yes" : "No"); + fprintf(stderr, "Preview %s, Full screen %s\n", config->wantPreview ? "Yes" : "No", + config->wantFullScreenPreview ? "Yes" : "No"); - fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", state->previewWindow.x, - state->previewWindow.y, state->previewWindow.width, - state->previewWindow.height, state->opacity); + fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", config->previewWindow.x, + config->previewWindow.y, config->previewWindow.width, + config->previewWindow.height, config->opacity); }; #if 0 diff --git a/sys/rpicamsrc/RaspiPreview.h b/sys/rpicamsrc/RaspiPreview.h index 409dbffb1a..f5e14daf4e 100644 --- a/sys/rpicamsrc/RaspiPreview.h +++ b/sys/rpicamsrc/RaspiPreview.h @@ -48,20 +48,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define FULL_FOV_PREVIEW_FRAME_RATE_NUM 0 #define FULL_FOV_PREVIEW_FRAME_RATE_DEN 1 +typedef struct +{ + MMAL_COMPONENT_T *preview_component; /// Pointer to the created preview display component + int havePreview; /// component is preview, else null sink +} RASPIPREVIEW_STATE; + typedef struct { int wantPreview; /// Display a preview int wantFullScreenPreview; /// 0 is use previewRect, non-zero to use full screen int opacity; /// Opacity of window - 0 = transparent, 255 = opaque MMAL_RECT_T previewWindow; /// Destination rectangle for the preview window. - MMAL_COMPONENT_T *preview_component; /// Pointer to the created preview display component } RASPIPREVIEW_PARAMETERS; -MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state); -void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state); -void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state); -void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state); -int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2); +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_STATE *state, + RASPIPREVIEW_PARAMETERS *config); +void raspipreview_destroy(RASPIPREVIEW_STATE *state); +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *config); +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *config); +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *config, const char *arg1, const char *arg2); void raspipreview_display_help(); +MMAL_STATUS_T raspipreview_update_config (RASPIPREVIEW_STATE *state, + RASPIPREVIEW_PARAMETERS *config); #endif /* RASPIPREVIEW_H_ */ From 3b85ddd90eaf950ac5fe4f24c18d186125256262 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 27 Apr 2015 00:54:54 +1000 Subject: [PATCH 34/77] rpicamsrc: Update properties dynamically where possible Update camera and encoder properties at runtime where possible Fixes https://github.com/thaytan/gst-rpicamsrc/issues/19 and https://github.com/thaytan/gst-rpicamsrc/issues/23 --- sys/rpicamsrc/RaspiCapture.c | 205 ++++++++++++++++++++++++++++------- sys/rpicamsrc/RaspiCapture.h | 19 +++- sys/rpicamsrc/gstrpicamsrc.c | 67 +++++++++++- sys/rpicamsrc/gstrpicamsrc.h | 2 + 4 files changed, 253 insertions(+), 40 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 34194fbae2..a81b750b7d 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -111,7 +111,7 @@ typedef struct struct RASPIVID_STATE_T { - RASPIVID_CONFIG *config; + RASPIVID_CONFIG config; FILE *output_file; @@ -132,6 +132,8 @@ struct RASPIVID_STATE_T int64_t base_time; int64_t last_second; + + RASPIPREVIEW_STATE preview_state; }; @@ -294,18 +296,23 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) */ static void dump_state(RASPIVID_STATE *state) { + RASPIVID_CONFIG *config; + if (!state) { vcos_assert(0); return; } - fprintf(stderr, "Width %d, Height %d\n", state->config->width, state->config->height); - fprintf(stderr, "bitrate %d, framerate %d/%d, time delay %d\n", state->config->bitrate, state->config->fps_n, state->config->fps_d, state->config->timeout); - //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->config->profile, profile_map, profile_map_size)); + config = &state->config; - raspipreview_dump_parameters(&state->config->preview_parameters); - raspicamcontrol_dump_parameters(&state->config->camera_parameters); + fprintf(stderr, "Width %d, Height %d\n", config->width, config->height); + fprintf(stderr, "bitrate %d, framerate %d/%d, time delay %d\n", + config->bitrate, config->fps_n, config->fps_d, config->timeout); + //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(config->profile, profile_map, profile_map_size)); + + raspipreview_dump_parameters(&config->preview_parameters); + raspicamcontrol_dump_parameters(&config->camera_parameters); } #if 0 @@ -623,7 +630,7 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) case CommandIntraRefreshType: { - state->config->intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size); + state->config.intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size); i++; break; } @@ -843,7 +850,7 @@ static FILE *open_imv_filename(RASPIVID_STATE *pState) */ static void update_annotation_data(RASPIVID_STATE *state) { - RASPIVID_CONFIG *config = state->config; + RASPIVID_CONFIG *config = &state->config; // So, if we have asked for a application supplied string, set it to the H264 parameters if (config->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) @@ -930,7 +937,7 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, /* FIXME: Use our own interruptible cond wait: */ buffer = mmal_queue_wait(state->encoded_buffer_q); - + if (G_LIKELY (clock)) { MMAL_PARAMETER_INT64_T param; GstClockTime runtime; @@ -1000,7 +1007,7 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) { MMAL_COMPONENT_T *camera = NULL; MMAL_STATUS_T status; - RASPIVID_CONFIG *config = state->config; + RASPIVID_CONFIG *config = &state->config; /* Create the component */ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); @@ -1077,7 +1084,7 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) MMAL_STATUS_T status; MMAL_ES_FORMAT_T *format; MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; - RASPIVID_CONFIG *config = state->config; + RASPIVID_CONFIG *config = &state->config; // set up the camera configuration @@ -1294,7 +1301,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; MMAL_STATUS_T status; MMAL_POOL_T *pool; - RASPIVID_CONFIG *config = state->config; + RASPIVID_CONFIG *config = &state->config; status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); @@ -1363,7 +1370,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (config->intraperiod != -1) { - MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config->intraperiod}; + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); if (status != MMAL_SUCCESS) { @@ -1374,7 +1381,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (config->quantisationParameter) { - MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->config->quantisationParameter}; + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); if (status != MMAL_SUCCESS) { @@ -1587,7 +1594,7 @@ raspi_capture_setup(RASPIVID_CONFIG *config) state = calloc(1, sizeof(RASPIVID_STATE)); /* Apply passed in config */ - state->config = config; + state->config = *config; /* Initialize timestamping */ state->base_time = state->last_second = -1; @@ -1604,7 +1611,7 @@ raspi_capture_setup(RASPIVID_CONFIG *config) return NULL; } - if ((status = raspipreview_create(&state->config->preview_parameters)) != MMAL_SUCCESS) + if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS) { vcos_log_error("%s: Failed to create preview component", __func__); destroy_camera_component(state); @@ -1620,6 +1627,8 @@ gboolean raspi_capture_start(RASPIVID_STATE *state) { MMAL_STATUS_T status = MMAL_SUCCESS; + RASPIVID_CONFIG *config = &state->config; + MMAL_PORT_T *camera_preview_port = NULL; MMAL_PORT_T *preview_input_port = NULL; MMAL_PORT_T *encoder_input_port = NULL; @@ -1630,7 +1639,7 @@ raspi_capture_start(RASPIVID_STATE *state) return FALSE; } - if (state->config->verbose) + if (config->verbose) { dump_state(state); } @@ -1639,20 +1648,20 @@ raspi_capture_start(RASPIVID_STATE *state) return FALSE; } - if (state->config->verbose) + if (state->config.verbose) fprintf(stderr, "Starting component connection stage\n"); camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; - preview_input_port = state->config->preview_parameters.preview_component->input[0]; + preview_input_port = state->preview_state.preview_component->input[0]; encoder_input_port = state->encoder_component->input[0]; state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT]; state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; state->encoder_output_port = state->encoder_component->output[0]; - if (state->config->preview_parameters.wantPreview ) + if (config->preview_parameters.wantPreview ) { - if (state->config->verbose) + if (config->verbose) { fprintf(stderr, "Connecting camera preview port to preview input port\n"); fprintf(stderr, "Starting video preview\n"); @@ -1667,14 +1676,14 @@ raspi_capture_start(RASPIVID_STATE *state) } } - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Connecting camera stills port to encoder input port\n"); // Now connect the camera to the encoder status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection); if (status != MMAL_SUCCESS) { - if (state->config->preview_parameters.wantPreview ) + if (config->preview_parameters.wantPreview ) mmal_connection_destroy(state->preview_connection); vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); return FALSE; @@ -1686,7 +1695,7 @@ raspi_capture_start(RASPIVID_STATE *state) state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data; - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Enabling encoder output port\n"); // Enable the encoder output port and tell it its callback function @@ -1697,23 +1706,23 @@ raspi_capture_start(RASPIVID_STATE *state) goto error; } - if (state->config->demoMode) + if (config->demoMode) { // Run for the user specific time.. - int num_iterations = state->config->timeout / state->config->demoInterval; + int num_iterations = config->timeout / config->demoInterval; int i; - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Running in demo mode\n"); - for (i=0;state->config->timeout == 0 || itimeout == 0 || icamera_component); - vcos_sleep(state->config->demoInterval); + vcos_sleep(state->config.demoInterval); } } - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Starting video capture\n"); if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) @@ -1743,14 +1752,14 @@ raspi_capture_start(RASPIVID_STATE *state) // Going to check every ABORT_INTERVAL milliseconds #if 0 - for (wait = 0; state->config->timeout == 0 || wait < state->config->timeout; wait+= ABORT_INTERVAL) + for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL) { vcos_sleep(ABORT_INTERVAL); if (state->callback_data.abort) break; } - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Finished capture\n"); #endif @@ -1770,10 +1779,12 @@ error: void raspi_capture_stop(RASPIVID_STATE *state) { - if (state->config->verbose) + RASPIVID_CONFIG *config = &state->config; + + if (config->verbose) fprintf(stderr, "Closing down\n"); - if (state->config->preview_parameters.wantPreview ) + if (config->preview_parameters.wantPreview ) mmal_connection_destroy(state->preview_connection); mmal_connection_destroy(state->encoder_connection); @@ -1790,6 +1801,8 @@ raspi_capture_stop(RASPIVID_STATE *state) void raspi_capture_free(RASPIVID_STATE *state) { + RASPIVID_CONFIG *config = &state->config; + // Can now close our file. Note disabling ports may flush buffers which causes // problems if we have already closed the file! if (state->output_file && state->output_file != stdout) @@ -1799,14 +1812,14 @@ raspi_capture_free(RASPIVID_STATE *state) if (state->encoder_component) mmal_component_disable(state->encoder_component); - if (state->config->preview_parameters.preview_component) - mmal_component_disable(state->config->preview_parameters.preview_component); + if (state->preview_state.preview_component) + mmal_component_disable(state->preview_state.preview_component); if (state->camera_component) mmal_component_disable(state->camera_component); destroy_encoder_component(state); - raspipreview_destroy(&state->config->preview_parameters); + raspipreview_destroy(&state->preview_state); destroy_camera_component(state); if (state->encoded_buffer_q) { @@ -1814,8 +1827,124 @@ raspi_capture_free(RASPIVID_STATE *state) state->encoded_buffer_q = NULL; } - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); free(state); } + +void +raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config) +{ + MMAL_STATUS_T status; + RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters; + MMAL_COMPONENT_T *camera = state->camera_component; + + /* Store the new config */ + state->config = *config; + + if (config->change_flags & PROP_CHANGE_ENCODING) { + /* BITRATE or QUANT or KEY Interval, intra refresh */ + MMAL_COMPONENT_T *encoder = state->encoder_component; + MMAL_PORT_T *encoder_output = encoder->output[0]; + + encoder_output->format->bitrate = config->bitrate; + status = mmal_port_format_commit(encoder_output); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change bitrate dynamically"); + + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change intraperiod dynamically"); + } + + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change Initial Quantisation Parameter dynamically"); + + MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m2.hdr); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change Minimum Quantisation Parameter dynamically"); + + MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m3.hdr); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change Maximum Quantisation Parameter dynamically"); + } + + { + // Adaptive intra refresh settings + MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; + param.hdr.size = sizeof(param); + + // Get first so we don't overwrite anything unexpectedly + status = mmal_port_parameter_get(encoder_output, ¶m.hdr); + if (state != MMAL_SUCCESS) { + /* Need to memset, apparently mmal_port_parameter_get() + * doesn't retrieve all parameters, causing random failures + * when we set it. On older firmware the get fails. + */ + memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T)); + } + param.refresh_mode = config->intra_refresh_type; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to set H264 intra-refresh values dynamically"); + } + } + if (config->change_flags & PROP_CHANGE_PREVIEW) { + /* Preview settings or fullscreen */ + status = raspipreview_update_config (&state->preview_state, + &config->preview_parameters); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change preview config dynamically"); + } + if (config->change_flags & PROP_CHANGE_COLOURBALANCE) { + raspicamcontrol_set_saturation(camera, params->saturation); + raspicamcontrol_set_sharpness(camera, params->sharpness); + raspicamcontrol_set_contrast(camera, params->contrast); + raspicamcontrol_set_brightness(camera, params->brightness); + } + if (config->change_flags & PROP_CHANGE_SENSOR_SETTINGS) { + /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */ + raspicamcontrol_set_ISO(camera, params->ISO); + raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation); + raspicamcontrol_set_exposure_mode(camera, params->exposureMode); + raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); + raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); + raspicamcontrol_set_DRC(camera, params->drc_level); + + /* Can we change sensor mode on the fly? Disable if not */ + status = mmal_port_parameter_set_uint32(camera->control, + MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change sensor mode dynamically"); + } + if (config->change_flags & PROP_CHANGE_VIDEO_STABILISATION) { + raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation); + } + if (config->change_flags & PROP_CHANGE_AWB) { + raspicamcontrol_set_awb_mode(camera, params->awbMode); + raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b); + } + if (config->change_flags & PROP_CHANGE_IMAGE_COLOUR_EFFECT) { + raspicamcontrol_set_imageFX(camera, params->imageEffect); + raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); + } + if (config->change_flags & PROP_CHANGE_ORIENTATION) { + raspicamcontrol_set_rotation(camera, params->rotation); + raspicamcontrol_set_flips(camera, params->hflip, params->vflip); + } + if (config->change_flags & PROP_CHANGE_ROI) { + raspicamcontrol_set_ROI(camera, params->roi); + } + if (config->change_flags & PROP_CHANGE_ANNOTATION) + update_annotation_data(state); +} diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 5a72195a2e..940bff87d9 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -1,6 +1,6 @@ /* * GStreamer - * Copyright (C) 2013 Jan Schmidt + * Copyright (C) 2013-2015 Jan Schmidt * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -64,10 +64,26 @@ GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug); G_BEGIN_DECLS +typedef enum +{ + PROP_CHANGE_ENCODING = (1 << 0), /* BITRATE or QUANT or KEY Interval, intra refresh */ + PROP_CHANGE_PREVIEW = (1 << 1), /* Preview opacity or fullscreen */ + PROP_CHANGE_COLOURBALANCE = (1 << 2), + PROP_CHANGE_SENSOR_SETTINGS = (1 << 3), /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */ + PROP_CHANGE_VIDEO_STABILISATION = (1 << 4), + PROP_CHANGE_AWB = (1 << 5), + PROP_CHANGE_IMAGE_COLOUR_EFFECT = (1 << 6), + PROP_CHANGE_ORIENTATION = (1 << 7), + PROP_CHANGE_ROI = (1 << 8), + PROP_CHANGE_ANNOTATION = (1 << 9) +} RpiPropChangeFlags; + /** Structure containing all state information for the current run */ typedef struct { + RpiPropChangeFlags change_flags; + int verbose; /// !0 if want detailed run information int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds @@ -101,6 +117,7 @@ void raspicapture_init(); void raspicapture_default_config(RASPIVID_CONFIG *config); RASPIVID_STATE *raspi_capture_setup(RASPIVID_CONFIG *config); gboolean raspi_capture_start(RASPIVID_STATE *state); +void raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config); GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf, GstClock *clock, GstClockTime base_time); void raspi_capture_stop(RASPIVID_STATE *state); diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 4a6afe08f8..cf9c19dc11 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1,6 +1,6 @@ /* * GStreamer - * Copyright (C) 2013-2014 Jan Schmidt + * Copyright (C) 2013-2015 Jan Schmidt * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -186,6 +186,8 @@ static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", #define gst_rpi_cam_src_parent_class parent_class G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_PUSH_SRC); +static void gst_rpi_cam_src_finalize (GObject *object); + static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, @@ -249,6 +251,8 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gstelement_class = (GstElementClass *) klass; basesrc_class = (GstBaseSrcClass *) klass; pushsrc_class = (GstPushSrcClass *) klass; + + gobject_class->finalize = gst_rpi_cam_src_finalize; gobject_class->set_property = gst_rpi_cam_src_set_property; gobject_class->get_property = gst_rpi_cam_src_get_property; g_object_class_install_property (gobject_class, PROP_CAMERA_NUMBER, @@ -435,111 +439,152 @@ gst_rpi_cam_src_init (GstRpiCamSrc * src) raspicapture_default_config (&src->capture_config); src->capture_config.intraperiod = KEYFRAME_INTERVAL_DEFAULT; src->capture_config.verbose = 1; + + g_mutex_init (&src->config_lock); + /* Don't let basesrc set timestamps, we'll do it using * buffer PTS and system times */ gst_base_src_set_do_timestamp (GST_BASE_SRC (src), FALSE); } +static void +gst_rpi_cam_src_finalize (GObject *object) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (object); + g_mutex_clear (&src->config_lock); + + G_OBJECT_CLASS (gst_rpi_cam_src_parent_class)->finalize (object); +} + static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstRpiCamSrc *src = GST_RPICAMSRC (object); + + g_mutex_lock (&src->config_lock); + switch (prop_id) { case PROP_CAMERA_NUMBER: src->capture_config.cameraNum = g_value_get_int (value); break; case PROP_BITRATE: src->capture_config.bitrate = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ENCODING; break; case PROP_KEYFRAME_INTERVAL: src->capture_config.intraperiod = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ENCODING; break; case PROP_PREVIEW: src->capture_config.preview_parameters.wantPreview = g_value_get_boolean (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; break; case PROP_PREVIEW_ENCODED: src->capture_config.immutableInput = g_value_get_boolean (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; break; case PROP_FULLSCREEN: src->capture_config.preview_parameters.wantFullScreenPreview = g_value_get_boolean (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; break; case PROP_PREVIEW_OPACITY: src->capture_config.preview_parameters.opacity = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; break; case PROP_SHARPNESS: src->capture_config.camera_parameters.sharpness = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; break; case PROP_CONTRAST: src->capture_config.camera_parameters.contrast = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; break; case PROP_BRIGHTNESS: src->capture_config.camera_parameters.brightness = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; break; case PROP_SATURATION: src->capture_config.camera_parameters.saturation = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; break; case PROP_ISO: src->capture_config.camera_parameters.ISO = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_VIDEO_STABILISATION: src->capture_config.camera_parameters.videoStabilisation = g_value_get_boolean (value); + src->capture_config.change_flags |= PROP_CHANGE_VIDEO_STABILISATION; break; case PROP_EXPOSURE_COMPENSATION: src->capture_config.camera_parameters.exposureCompensation = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_EXPOSURE_MODE: src->capture_config.camera_parameters.exposureMode = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_EXPOSURE_METERING_MODE: src->capture_config.camera_parameters.exposureMeterMode = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_ROTATION: src->capture_config.camera_parameters.rotation = g_value_get_int (value); break; case PROP_AWB_MODE: src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_AWB; break; case PROP_AWB_GAIN_RED: src->capture_config.camera_parameters.awb_gains_r = g_value_get_float (value); + src->capture_config.change_flags |= PROP_CHANGE_AWB; break; case PROP_AWB_GAIN_BLUE: src->capture_config.camera_parameters.awb_gains_b = g_value_get_float (value); + src->capture_config.change_flags |= PROP_CHANGE_AWB; break; case PROP_IMAGE_EFFECT: src->capture_config.camera_parameters.imageEffect = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_IMAGE_COLOUR_EFFECT; break; case PROP_HFLIP: src->capture_config.camera_parameters.hflip = g_value_get_boolean (value); + src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; break; case PROP_VFLIP: src->capture_config.camera_parameters.vflip = g_value_get_boolean (value); + src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; break; case PROP_ROI_X: src->capture_config.camera_parameters.roi.x = g_value_get_float (value); + src->capture_config.change_flags |= PROP_CHANGE_ROI; break; case PROP_ROI_Y: src->capture_config.camera_parameters.roi.y = g_value_get_float (value); + src->capture_config.change_flags |= PROP_CHANGE_ROI; break; case PROP_ROI_W: src->capture_config.camera_parameters.roi.w = g_value_get_float (value); + src->capture_config.change_flags |= PROP_CHANGE_ROI; break; case PROP_ROI_H: src->capture_config.camera_parameters.roi.h = g_value_get_float (value); + src->capture_config.change_flags |= PROP_CHANGE_ROI; break; case PROP_QUANTISATION_PARAMETER: src->capture_config.quantisationParameter = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ENCODING; break; case PROP_INLINE_HEADERS: src->capture_config.bInlineHeaders = g_value_get_boolean (value); @@ -547,17 +592,21 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, case PROP_SHUTTER_SPEED: src->capture_config.camera_parameters.shutter_speed = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_DRC: src->capture_config.camera_parameters.drc_level = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_SENSOR_MODE: src->capture_config.sensor_mode = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_ANNOTATION_MODE: src->capture_config.camera_parameters.enable_annotate = g_value_get_flags (value); + src->capture_config.change_flags |= PROP_CHANGE_ANNOTATION; break; case PROP_ANNOTATION_TEXT: strncpy (src->capture_config.camera_parameters.annotate_string, @@ -565,14 +614,18 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config. camera_parameters.annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - 1] = '\0'; + src->capture_config.change_flags |= PROP_CHANGE_ANNOTATION; break; case PROP_INTRA_REFRESH_TYPE: src->capture_config.intra_refresh_type = g_value_get_enum (value); + src->capture_config.change_flags |= PROP_CHANGE_ENCODING; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + + g_mutex_unlock (&src->config_lock); } static void @@ -580,6 +633,8 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstRpiCamSrc *src = GST_RPICAMSRC (object); + + g_mutex_lock (&src->config_lock); switch (prop_id) { case PROP_CAMERA_NUMBER: g_value_set_int (value, src->capture_config.cameraNum); @@ -704,6 +759,7 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + g_mutex_unlock (&src->config_lock); } static gboolean @@ -711,7 +767,9 @@ gst_rpi_cam_src_start (GstBaseSrc * parent) { GstRpiCamSrc *src = GST_RPICAMSRC (parent); GST_LOG_OBJECT (src, "In src_start()"); + g_mutex_lock (&src->config_lock); src->capture_state = raspi_capture_setup (&src->capture_config); + g_mutex_unlock (&src->config_lock); if (src->capture_state == NULL) return FALSE; return TRUE; @@ -878,6 +936,13 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) base_time = GST_ELEMENT_CAST (src)->base_time; GST_OBJECT_UNLOCK (src); + g_mutex_lock (&src->config_lock); + if (src->capture_config.change_flags) { + raspi_capture_update_config (src->capture_state, &src->capture_config); + src->capture_config.change_flags = 0; + } + g_mutex_unlock (&src->config_lock); + /* FIXME: Use custom allocator */ ret = raspi_capture_fill_buffer (src->capture_state, buf, clock, base_time); if (*buf) diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index 86f5d24ab4..0bfc2b1d15 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -72,6 +72,8 @@ struct _GstRpiCamSrc RASPIVID_CONFIG capture_config; RASPIVID_STATE *capture_state; gboolean started; + + GMutex config_lock; }; struct _GstRpiCamSrcClass From acc7449d282544e2d32a910471bc7923f111f3f1 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 27 Apr 2015 02:43:14 +1000 Subject: [PATCH 35/77] rpicamsrc: Add dynamic properties example Python example of adjusting saturation on the fly --- tests/examples/rpicamsrc/dynamicprops.py | 68 ++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 tests/examples/rpicamsrc/dynamicprops.py diff --git a/tests/examples/rpicamsrc/dynamicprops.py b/tests/examples/rpicamsrc/dynamicprops.py new file mode 100755 index 0000000000..6c1816fbe2 --- /dev/null +++ b/tests/examples/rpicamsrc/dynamicprops.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gst', '1.0') +from gi.repository import GObject, Gst + +def bus_call(bus, msg, *args): + # print("BUSCALL", msg, msg.type, *args) + if msg.type == Gst.MessageType.EOS: + print("End-of-stream") + loop.quit() + return + elif msg.type == Gst.MessageType.ERROR: + print("GST ERROR", msg.parse_error()) + loop.quit() + return + return True + +saturation = -100 +def set_saturation(pipeline): + global saturation + if saturation <= 100: + print("Setting saturation to {0}".format(saturation)) + videosrc.set_property("saturation", saturation) + videosrc.set_property("annotation-text", "Saturation %d" % (saturation)) + else: + pipeline.send_event (Gst.Event.new_eos()) + return False + saturation += 10 + return True + + +if __name__ == "__main__": + GObject.threads_init() + # initialization + loop = GObject.MainLoop() + Gst.init(None) + + pipeline = Gst.parse_launch ("rpicamsrc name=src ! video/x-h264,width=320,height=240 ! h264parse ! mp4mux ! filesink name=s") + if pipeline == None: + print ("Failed to create pipeline") + sys.exit(0) + + # watch for messages on the pipeline's bus (note that this will only + # work like this when a GLib main loop is running) + bus = pipeline.get_bus() + bus.add_watch(0, bus_call, loop) + + videosrc = pipeline.get_by_name ("src") + videosrc.set_property("saturation", saturation) + videosrc.set_property("annotation-mode", 1) + + sink = pipeline.get_by_name ("s") + sink.set_property ("location", "test.mp4") + + # this will call set_saturation every 1s + GObject.timeout_add(1000, set_saturation, pipeline) + + # run + pipeline.set_state(Gst.State.PLAYING) + try: + loop.run() + except Exception as e: + print(e) + # cleanup + pipeline.set_state(Gst.State.NULL) + From 92aa566c43767211dbee726c9264e1152a52b658 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 27 Apr 2015 04:05:04 +1000 Subject: [PATCH 36/77] rpicamsrc: Send vcos_log_warn via GStreamer debug messages --- sys/rpicamsrc/RaspiCapture.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 940bff87d9..d24c3a34ee 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -61,6 +61,8 @@ GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug); #define fprintf(f,...) GST_LOG(__VA_ARGS__) #undef vcos_log_error #define vcos_log_error GST_ERROR +#undef vcos_log_warn +#define vcos_log_warn GST_WARNING G_BEGIN_DECLS From e58951cfb8fd259fbbf389e862de6e43e25464dd Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 27 Apr 2015 04:05:42 +1000 Subject: [PATCH 37/77] rpicamsrc: Disable bitrate, quantisation and intra-refresh dynamic changes The firmware rejects dynamic changes of those encoder params. --- sys/rpicamsrc/RaspiCapture.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index a81b750b7d..1c70760215 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1848,10 +1848,13 @@ raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config) MMAL_COMPONENT_T *encoder = state->encoder_component; MMAL_PORT_T *encoder_output = encoder->output[0]; +#if 0 /* not dynamically change-able */ encoder_output->format->bitrate = config->bitrate; status = mmal_port_format_commit(encoder_output); - if (status != MMAL_SUCCESS) - vcos_log_warn("Unable to change bitrate dynamically"); + if (status != MMAL_SUCCESS) { + vcos_log_warn("Cannot change bitrate dynamically"); + } +#endif { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod}; @@ -1860,6 +1863,7 @@ raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config) vcos_log_warn("Unable to change intraperiod dynamically"); } +#if 0 /* not dynamically change-able */ { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -1898,6 +1902,7 @@ raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config) if (status != MMAL_SUCCESS) vcos_log_warn("Unable to set H264 intra-refresh values dynamically"); } +#endif } if (config->change_flags & PROP_CHANGE_PREVIEW) { /* Preview settings or fullscreen */ From 4e827949efa4fa28e26e50c2e8f62e80d7e265e5 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 27 Apr 2015 22:56:32 +1000 Subject: [PATCH 38/77] rpicamsrc: Fix initial config setting. Make sure to update the captsure config before starting capture. Since the capture component now keeps a local copy of the config, it's not updated automatically. --- sys/rpicamsrc/RaspiCapture.c | 4 +++- sys/rpicamsrc/RaspiCapture.h | 3 ++- sys/rpicamsrc/gstrpicamsrc.c | 9 ++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 1c70760215..2c18a82425 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1834,7 +1834,7 @@ raspi_capture_free(RASPIVID_STATE *state) } void -raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config) +raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic) { MMAL_STATUS_T status; RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters; @@ -1842,6 +1842,8 @@ raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config) /* Store the new config */ state->config = *config; + if (!dynamic) + return; if (config->change_flags & PROP_CHANGE_ENCODING) { /* BITRATE or QUANT or KEY Interval, intra refresh */ diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index d24c3a34ee..2462caac0e 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -119,7 +119,8 @@ void raspicapture_init(); void raspicapture_default_config(RASPIVID_CONFIG *config); RASPIVID_STATE *raspi_capture_setup(RASPIVID_CONFIG *config); gboolean raspi_capture_start(RASPIVID_STATE *state); -void raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config); +void raspi_capture_update_config (RASPIVID_STATE *state, + RASPIVID_CONFIG *config, gboolean dynamic); GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf, GstClock *clock, GstClockTime base_time); void raspi_capture_stop(RASPIVID_STATE *state); diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index cf9c19dc11..1b2c59c689 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -769,6 +769,8 @@ gst_rpi_cam_src_start (GstBaseSrc * parent) GST_LOG_OBJECT (src, "In src_start()"); g_mutex_lock (&src->config_lock); src->capture_state = raspi_capture_setup (&src->capture_config); + /* Clear all capture flags */ + src->capture_config.change_flags = 0; g_mutex_unlock (&src->config_lock); if (src->capture_state == NULL) return FALSE; @@ -925,6 +927,11 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) GstClockTime base_time; if (!src->started) { + g_mutex_lock (&src->config_lock); + raspi_capture_update_config (src->capture_state, &src->capture_config, FALSE); + src->capture_config.change_flags = 0; + g_mutex_unlock (&src->config_lock); + if (!raspi_capture_start (src->capture_state)) return GST_FLOW_ERROR; src->started = TRUE; @@ -938,7 +945,7 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) g_mutex_lock (&src->config_lock); if (src->capture_config.change_flags) { - raspi_capture_update_config (src->capture_state, &src->capture_config); + raspi_capture_update_config (src->capture_state, &src->capture_config, TRUE); src->capture_config.change_flags = 0; } g_mutex_unlock (&src->config_lock); From 48a735ff2115d997ecdb628972c828fa0273ec49 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Wed, 29 Apr 2015 16:36:18 +0200 Subject: [PATCH 39/77] rpicamsrc: Implement GstColorBalance interface Fixes https://github.com/thaytan/gst-rpicamsrc/issues/24 --- sys/rpicamsrc/gstrpicamsrc.c | 127 ++++++++++++++++++++++++++++++++++- sys/rpicamsrc/gstrpicamsrc.h | 3 + 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 1b2c59c689..570eeb301c 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -183,11 +183,12 @@ static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS ( /*RAW_AND_JPEG_CAPS "; " */ H264_CAPS) ); -#define gst_rpi_cam_src_parent_class parent_class -G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_PUSH_SRC); static void gst_rpi_cam_src_finalize (GObject *object); +static void gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * + iface); + static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id, @@ -205,6 +206,12 @@ static gboolean gst_rpi_cam_src_event (GstBaseSrc * src, GstEvent * event); static gboolean gst_rpi_cam_src_send_event (GstElement * element, GstEvent * event); +#define gst_rpi_cam_src_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstRpiCamSrc, gst_rpi_cam_src, + GST_TYPE_PUSH_SRC, + G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE, + gst_rpi_cam_src_colorbalance_init)); + #define C_ENUM(v) ((gint) v) GType @@ -434,6 +441,8 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) static void gst_rpi_cam_src_init (GstRpiCamSrc * src) { + GstColorBalanceChannel *channel; + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (src), TRUE); raspicapture_default_config (&src->capture_config); @@ -445,17 +454,131 @@ gst_rpi_cam_src_init (GstRpiCamSrc * src) /* Don't let basesrc set timestamps, we'll do it using * buffer PTS and system times */ gst_base_src_set_do_timestamp (GST_BASE_SRC (src), FALSE); + + /* Generate the channels list */ + channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL); + channel->label = g_strdup ("CONTRAST"); + channel->min_value = -100; + channel->max_value = 100; + src->channels = g_list_append (src->channels, channel); + + channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL); + channel->label = g_strdup ("BRIGHTNESS"); + channel->min_value = 0; + channel->max_value = 100; + src->channels = g_list_append (src->channels, channel); + + channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL); + channel->label = g_strdup ("SATURATION"); + channel->min_value = -100; + channel->max_value = 100; + src->channels = g_list_append (src->channels, channel); } static void gst_rpi_cam_src_finalize (GObject *object) { GstRpiCamSrc *src = GST_RPICAMSRC (object); + GList *channels = NULL; g_mutex_clear (&src->config_lock); + channels = src->channels; + while (channels) { + GstColorBalanceChannel *channel = channels->data; + + g_object_unref (channel); + channels->data = NULL; + channels = g_list_next (channels); + } + + if (src->channels) + g_list_free (src->channels); + G_OBJECT_CLASS (gst_rpi_cam_src_parent_class)->finalize (object); } +static const GList * +gst_rpi_cam_src_colorbalance_list_channels (GstColorBalance * balance) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (balance); + + g_return_val_if_fail (src != NULL, NULL); + g_return_val_if_fail (GST_IS_RPICAMSRC (src), NULL); + + return src->channels; +} + +static void +gst_rpi_cam_src_colorbalance_set_value (GstColorBalance * balance, + GstColorBalanceChannel * channel, gint value) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (balance); + gboolean changed = FALSE; + + g_return_if_fail (src != NULL); + g_return_if_fail (GST_IS_RPICAMSRC (src)); + g_return_if_fail (channel->label != NULL); + + GST_OBJECT_LOCK (src); + if (!g_ascii_strcasecmp (channel->label, "SATURATION")) { + changed = value != src->capture_config.camera_parameters.saturation; + src->capture_config.camera_parameters.saturation = value; + } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) { + changed = value != src->capture_config.camera_parameters.brightness; + src->capture_config.camera_parameters.brightness = value; + } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) { + changed = value != src->capture_config.camera_parameters.contrast; + src->capture_config.camera_parameters.contrast = value; + } + + if (changed) + src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; + + GST_OBJECT_UNLOCK (src); + + if (changed) { + gst_color_balance_value_changed (balance, channel, + gst_color_balance_get_value (balance, channel)); + } +} + +static gint +gst_rpi_cam_src_colorbalance_get_value (GstColorBalance * balance, + GstColorBalanceChannel * channel) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (balance); + gint value = 0; + + g_return_val_if_fail (src != NULL, 0); + g_return_val_if_fail (GST_IS_RPICAMSRC (src), 0); + g_return_val_if_fail (channel->label != NULL, 0); + + if (!g_ascii_strcasecmp (channel->label, "SATURATION")) { + value = src->capture_config.camera_parameters.saturation; + } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) { + value = src->capture_config.camera_parameters.brightness; + } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) { + value = src->capture_config.camera_parameters.contrast; + } + + return value; +} + +static GstColorBalanceType +gst_rpi_cam_src_colorbalance_get_balance_type (GstColorBalance * balance) +{ + return GST_COLOR_BALANCE_HARDWARE; +} + +static void +gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * iface) +{ + iface->list_channels = gst_rpi_cam_src_colorbalance_list_channels; + iface->set_value = gst_rpi_cam_src_colorbalance_set_value; + iface->get_value = gst_rpi_cam_src_colorbalance_get_value; + iface->get_balance_type = gst_rpi_cam_src_colorbalance_get_balance_type; +} + static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index 0bfc2b1d15..dbfc5bb609 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -74,6 +74,9 @@ struct _GstRpiCamSrc gboolean started; GMutex config_lock; + + /* channels for interface */ + GList *channels; }; struct _GstRpiCamSrcClass From c51503fc417c199a88fa5ca825284bd0551ad4bf Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Tue, 5 May 2015 19:03:43 +0200 Subject: [PATCH 40/77] rpicamsrc: add test-color-balance example This small test will display a live video preview of the rpicam with the balance controls being updated once a second. The controls to update can be disabled in the source by setting the CONTROL_* macros values to 0. --- tests/examples/rpicamsrc/test_color_balance.c | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/examples/rpicamsrc/test_color_balance.c diff --git a/tests/examples/rpicamsrc/test_color_balance.c b/tests/examples/rpicamsrc/test_color_balance.c new file mode 100644 index 0000000000..b5bac083d5 --- /dev/null +++ b/tests/examples/rpicamsrc/test_color_balance.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Igalia S.L + * Author: Philippe Normand + * Licence: LGPL. (See COPYING.LGPL) + */ + +#include +#include +#include + +#define CONTROL_SATURATION 1 +#define CONTROL_BRIGHTNESS 1 +#define CONTROL_CONTRAST 1 + +#define PIPELINE "rpicamsrc name=src preview=0 fullscreen=0 ! h264parse ! omxh264dec ! glimagesink sync=0" + +#define declare_value(name, value) \ + static gint current_##name = value; \ + static gboolean incrementing_##name = TRUE; + +#if CONTROL_SATURATION +declare_value(SATURATION, 50); +#endif +#if CONTROL_BRIGHTNESS +declare_value(BRIGHTNESS, 50); +#endif +#if CONTROL_CONTRAST +declare_value(CONTRAST, 0); +#endif + +#define update(name, channel, current_value) \ + if (!g_strcmp0(channel->label, #name)) { \ + if (current_value >= channel->max_value) \ + incrementing_##name = FALSE; \ + else if (current_value <= channel->min_value) \ + incrementing_##name = TRUE; \ + current_##name += incrementing_##name ? 10 : -10; \ + g_print("new " #name ": %d\n", current_##name); \ + return current_##name; \ + } + +gint compute_value(GstColorBalanceChannel* channel, gint current_value) +{ +#if CONTROL_SATURATION + update(SATURATION, channel, current_value); +#endif +#if CONTROL_BRIGHTNESS + update(BRIGHTNESS, channel, current_value); +#endif +#if CONTROL_CONTRAST + update(CONTRAST, channel, current_value); +#endif + return current_value; +} + +static gboolean process(gpointer data) +{ + GstColorBalance* balance = (GstColorBalance*) data; + const GList* controls; + GstColorBalanceChannel* channel; + const GList* item; + gint index, new_value, current_value; + + controls = gst_color_balance_list_channels(balance); + + if (controls == NULL) { + g_printerr("There is no list of colorbalance controls\n"); + return G_SOURCE_REMOVE; + } + + for (item = controls, index = 0; item != NULL; + item = item->next, ++index) { + channel = item->data; + current_value = gst_color_balance_get_value(balance, channel); + new_value = compute_value(channel, current_value); + gst_color_balance_set_value(balance, channel, new_value); + } + + return G_SOURCE_CONTINUE; +} + +int main(int argc, char** argv) +{ + GMainLoop* loop; + GstElement* pipeline; + GError* error = NULL; + GstElement* src; + GstColorBalance* balance; + + gst_init(&argc, &argv); + loop = g_main_loop_new(NULL, FALSE); + + pipeline = gst_parse_launch(PIPELINE, &error); + if (error != NULL) { + g_printerr("Error parsing '%s': %s", PIPELINE, error->message); + g_error_free(error); + return -1; + } + + gst_element_set_state(pipeline, GST_STATE_PLAYING); + src = gst_bin_get_by_name(GST_BIN(pipeline), "src"); + if (!src) { + g_printerr("Source element not found\n"); + return -2; + } + + balance = GST_COLOR_BALANCE(src); + g_timeout_add_seconds(1, process, balance); + g_main_loop_run(loop); + + gst_object_unref(src); + gst_element_set_state(pipeline, GST_STATE_NULL); + return 0; +} From 961cf17c97a2e302a19e24277874a93dc30bd553 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Mon, 11 May 2015 10:17:18 +0200 Subject: [PATCH 41/77] rpicamsrc: colorbalance: protect with config_lock mutex --- sys/rpicamsrc/gstrpicamsrc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 570eeb301c..edc9d842a3 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -519,7 +519,7 @@ gst_rpi_cam_src_colorbalance_set_value (GstColorBalance * balance, g_return_if_fail (GST_IS_RPICAMSRC (src)); g_return_if_fail (channel->label != NULL); - GST_OBJECT_LOCK (src); + g_mutex_lock (&src->config_lock); if (!g_ascii_strcasecmp (channel->label, "SATURATION")) { changed = value != src->capture_config.camera_parameters.saturation; src->capture_config.camera_parameters.saturation = value; @@ -534,7 +534,7 @@ gst_rpi_cam_src_colorbalance_set_value (GstColorBalance * balance, if (changed) src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; - GST_OBJECT_UNLOCK (src); + g_mutex_unlock (&src->config_lock); if (changed) { gst_color_balance_value_changed (balance, channel, @@ -553,6 +553,8 @@ gst_rpi_cam_src_colorbalance_get_value (GstColorBalance * balance, g_return_val_if_fail (GST_IS_RPICAMSRC (src), 0); g_return_val_if_fail (channel->label != NULL, 0); + g_mutex_lock (&src->config_lock); + if (!g_ascii_strcasecmp (channel->label, "SATURATION")) { value = src->capture_config.camera_parameters.saturation; } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) { @@ -561,6 +563,8 @@ gst_rpi_cam_src_colorbalance_get_value (GstColorBalance * balance, value = src->capture_config.camera_parameters.contrast; } + g_mutex_unlock (&src->config_lock); + return value; } From 1a4870d13ddaf557fe62952ba34a56484d8418fe Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 11 May 2015 21:29:58 +1000 Subject: [PATCH 42/77] rpicamsrc: Describe awb-mode=off in lowercase Change the descriptions for the awb-gain-blue and awb-gain-red properties to say 'awb-mode=off' instead of 'awb-mode=OFF' See https://github.com/thaytan/gst-rpicamsrc/issues/26 --- sys/rpicamsrc/gstrpicamsrc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index edc9d842a3..0e831e5bb1 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -340,11 +340,11 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, g_param_spec_float ("awb-gain-red", "AWB Red Gain", - "Manual AWB Gain for red channel when awb-mode=OFF", 0, 8.0, 0, + "Manual AWB Gain for red channel when awb-mode=off", 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, g_param_spec_float ("awb-gain-blue", "AWB Blue Gain", - "Manual AWB Gain for blue channel when awb-mode=OFF", 0, 8.0, 0, + "Manual AWB Gain for blue channel when awb-mode=off", 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_IMAGE_EFFECT, g_param_spec_enum ("image-effect", "Image effect", From cda483cb3c18b6666efbdfaf5a4800e30b888606 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Mon, 11 May 2015 11:16:52 +0200 Subject: [PATCH 43/77] rpicamsrc: Basic orientation interface support The (h,v)flip attributes are now supported through this interface. It should also be possible to support (h,v)center attributes using the ROI properties. --- sys/rpicamsrc/gstrpicamsrc.c | 78 ++++++++++++++++++++- tests/examples/rpicamsrc/test_orientation.c | 70 ++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 tests/examples/rpicamsrc/test_orientation.c diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 0e831e5bb1..8272188e0d 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -188,6 +188,7 @@ static void gst_rpi_cam_src_finalize (GObject *object); static void gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * iface); +static void gst_rpi_cam_src_orientation_init (GstVideoOrientationInterface * iface); static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -210,7 +211,9 @@ static gboolean gst_rpi_cam_src_send_event (GstElement * element, G_DEFINE_TYPE_WITH_CODE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_PUSH_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE, - gst_rpi_cam_src_colorbalance_init)); + gst_rpi_cam_src_colorbalance_init); + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_ORIENTATION, + gst_rpi_cam_src_orientation_init)); #define C_ENUM(v) ((gint) v) @@ -583,6 +586,79 @@ gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * iface) iface->get_balance_type = gst_rpi_cam_src_colorbalance_get_balance_type; } +static gboolean +gst_rpi_cam_src_orientation_get_hflip (GstVideoOrientation * orientation, gboolean * flip) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (orientation); + + g_return_val_if_fail (src != NULL, FALSE); + g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); + + g_mutex_lock (&src->config_lock); + *flip = src->capture_config.camera_parameters.hflip; + g_mutex_unlock (&src->config_lock); + + return TRUE; +} + +static gboolean +gst_rpi_cam_src_orientation_get_vflip (GstVideoOrientation * orientation, gboolean * flip) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (orientation); + + g_return_val_if_fail (src != NULL, FALSE); + g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); + + g_mutex_lock (&src->config_lock); + *flip = src->capture_config.camera_parameters.vflip; + g_mutex_unlock (&src->config_lock); + + return TRUE; +} + +static gboolean +gst_rpi_cam_src_orientation_set_hflip (GstVideoOrientation * orientation, gboolean flip) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (orientation); + + g_return_val_if_fail (src != NULL, FALSE); + g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); + + g_mutex_lock (&src->config_lock); + src->capture_config.camera_parameters.hflip = flip; + src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; + g_mutex_unlock (&src->config_lock); + + return TRUE; +} + +static gboolean +gst_rpi_cam_src_orientation_set_vflip (GstVideoOrientation * orientation, gboolean flip) +{ + GstRpiCamSrc *src = GST_RPICAMSRC (orientation); + + g_return_val_if_fail (src != NULL, FALSE); + g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); + + g_mutex_lock (&src->config_lock); + src->capture_config.camera_parameters.vflip = flip; + src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; + g_mutex_unlock (&src->config_lock); + + return TRUE; +} + +static void +gst_rpi_cam_src_orientation_init (GstVideoOrientationInterface * iface) +{ + iface->get_hflip = gst_rpi_cam_src_orientation_get_hflip; + iface->set_hflip = gst_rpi_cam_src_orientation_set_hflip; + iface->get_vflip = gst_rpi_cam_src_orientation_get_vflip; + iface->set_vflip = gst_rpi_cam_src_orientation_set_vflip; + + /* TODO: hcenter / vcenter support */ +} + static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) diff --git a/tests/examples/rpicamsrc/test_orientation.c b/tests/examples/rpicamsrc/test_orientation.c new file mode 100644 index 0000000000..7702537d3a --- /dev/null +++ b/tests/examples/rpicamsrc/test_orientation.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, Igalia S.L + * Author: Philippe Normand + * Licence: LGPL. (See COPYING.LGPL) + */ + +#include +#include +#include + +#define PIPELINE "rpicamsrc name=src preview=0 fullscreen=0 ! h264parse ! omxh264dec ! glimagesink sync=0" + +void configure_orientation(GstVideoOrientation* orientation) +{ + gboolean flip; + + if (gst_video_orientation_get_hflip(orientation, &flip)) { + g_print("current hflip: %s\n", flip ? "enabled" : "disabled"); + + if (g_getenv("HFLIP")) + gst_video_orientation_set_hflip(orientation, TRUE); + + gst_video_orientation_get_hflip(orientation, &flip); + g_print("new hflip: %s\n", flip ? "enabled" : "disabled"); + } + + if (gst_video_orientation_get_vflip(orientation, &flip)) { + g_print("current vflip: %s\n", flip ? "enabled" : "disabled"); + + if (g_getenv("VFLIP")) + gst_video_orientation_set_vflip(orientation, TRUE); + + gst_video_orientation_get_vflip(orientation, &flip); + g_print("new vflip: %s\n", flip ? "enabled" : "disabled"); + } +} + +int main(int argc, char** argv) +{ + GMainLoop* loop; + GstElement* pipeline; + GError* error = NULL; + GstElement* src; + GstVideoOrientation* orientation; + + gst_init(&argc, &argv); + loop = g_main_loop_new(NULL, FALSE); + + pipeline = gst_parse_launch(PIPELINE, &error); + if (error != NULL) { + g_printerr("Error parsing '%s': %s", PIPELINE, error->message); + g_error_free(error); + return -1; + } + + gst_element_set_state(pipeline, GST_STATE_PLAYING); + src = gst_bin_get_by_name(GST_BIN(pipeline), "src"); + if (!src) { + g_printerr("Source element not found\n"); + return -2; + } + + orientation = GST_VIDEO_ORIENTATION(src); + configure_orientation(orientation); + g_main_loop_run(loop); + + gst_object_unref(src); + gst_element_set_state(pipeline, GST_STATE_NULL); + return 0; +} From e97b50af51b9e607ef48882bc1c813b9fd2cc3bc Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 19 Jul 2015 01:48:35 +1000 Subject: [PATCH 44/77] rpicamsrc: Fix buffer PTS calculation Timestamps from MMAL are in microseconds, so make sure to convert to nanoseconds before using them to adjust the GStreamer buffer time --- sys/rpicamsrc/RaspiCapture.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 2c18a82425..abe5346cd7 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -951,13 +951,15 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr); if (param.value != -1 && param.value >= buffer->pts) { - GstClockTime offset = param.value - buffer->pts; + /* Convert microsecond RPi TS to GStreamer clock: */ + GstClockTime offset = (param.value - buffer->pts) * 1000; if (runtime >= offset) gst_pts = runtime - offset; } - GST_LOG ("Buf PTS %" G_GINT64_FORMAT " DTS %" G_GINT64_FORMAT - " STC %" G_GINT64_FORMAT " TS %" GST_TIME_FORMAT, - buffer->pts, buffer->dts, param.value, + GST_LOG ("Buf (uS) PTS %" G_GINT64_FORMAT " DTS %" G_GINT64_FORMAT + " STC %" G_GINT64_FORMAT " (latency %" G_GINT64_FORMAT + "uS) TS %" GST_TIME_FORMAT, + buffer->pts, buffer->dts, param.value, param.value - buffer->pts, GST_TIME_ARGS (gst_pts)); } From a3de294cb5ebe3aec26c7a6611c258273b7a6915 Mon Sep 17 00:00:00 2001 From: ibauer Date: Thu, 8 Oct 2015 10:32:32 +0200 Subject: [PATCH 45/77] rpicamsrc: Changed awb-gain-blue use the correct enum PROP_AWB_GAIN_BLUE and not PROP_AWB_GAIN_RED --- sys/rpicamsrc/gstrpicamsrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 8272188e0d..1afd6e0b8c 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -345,7 +345,7 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_float ("awb-gain-red", "AWB Red Gain", "Manual AWB Gain for red channel when awb-mode=off", 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, + g_object_class_install_property (gobject_class, PROP_AWB_GAIN_BLUE, g_param_spec_float ("awb-gain-blue", "AWB Blue Gain", "Manual AWB Gain for blue channel when awb-mode=off", 0, 8.0, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); From 63f60f7e7e734d092225eede183169259ed42af5 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 21 Oct 2015 21:11:36 +1100 Subject: [PATCH 46/77] rpicamsrc: Add properties for configuring annotation text size and colour. Map the raspivid setting for annotation text size and colours to properties. --- sys/rpicamsrc/gstrpicamsrc.c | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 1afd6e0b8c..3a74b812aa 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -126,6 +126,9 @@ enum PROP_DRC, PROP_ANNOTATION_MODE, PROP_ANNOTATION_TEXT, + PROP_ANNOTATION_TEXT_SIZE, + PROP_ANNOTATION_TEXT_COLOUR, + PROP_ANNOTATION_TEXT_BG_COLOUR, PROP_INTRA_REFRESH_TYPE }; @@ -422,6 +425,18 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) GST_RPI_CAM_TYPE_RPI_CAM_SRC_INTRA_REFRESH_TYPE, GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT_SIZE, + g_param_spec_int ("annotation-text-size", "Annotation text size", + "Set the size of annotation text (in pixels) (0 = Auto)", 0, + G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT_COLOUR, + g_param_spec_int ("annotation-text-colour", "Annotation text colour (VUY)", + "Set the annotation text colour, as a VUY hex value eg #8080FF, -1 for default", -1, + G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT_BG_COLOUR, + g_param_spec_int ("annotation-text-bg-colour", "Annotation text background colour (VUY)", + "Set the annotation text background colour, as a VUY hex value eg #8080FF, -1 for default", -1, + G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", @@ -819,6 +834,21 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, - 1] = '\0'; src->capture_config.change_flags |= PROP_CHANGE_ANNOTATION; break; + case PROP_ANNOTATION_TEXT_SIZE: + src->capture_config. + camera_parameters.annotate_text_size = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ANNOTATION; + break; + case PROP_ANNOTATION_TEXT_COLOUR: + src->capture_config. + camera_parameters.annotate_text_colour = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ANNOTATION; + break; + case PROP_ANNOTATION_TEXT_BG_COLOUR: + src->capture_config. + camera_parameters.annotate_bg_colour = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ANNOTATION; + break; case PROP_INTRA_REFRESH_TYPE: src->capture_config.intra_refresh_type = g_value_get_enum (value); src->capture_config.change_flags |= PROP_CHANGE_ENCODING; @@ -955,6 +985,15 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, g_value_set_string (value, src->capture_config.camera_parameters.annotate_string); break; + case PROP_ANNOTATION_TEXT_SIZE: + g_value_set_int (value, src->capture_config.camera_parameters.annotate_text_size); + break; + case PROP_ANNOTATION_TEXT_COLOUR: + g_value_set_int (value, src->capture_config.camera_parameters.annotate_text_colour); + break; + case PROP_ANNOTATION_TEXT_BG_COLOUR: + g_value_set_int (value, src->capture_config.camera_parameters.annotate_bg_colour); + break; case PROP_INTRA_REFRESH_TYPE: g_value_set_enum (value, src->capture_config.intra_refresh_type); break; From 48b3c2bc8e785895266c9ba0f260f2d9a7f0e3a7 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 2 Dec 2015 01:20:10 +1100 Subject: [PATCH 47/77] rpicamsrc: Add preview-x/y/w/h properties Expose properties for setting the position of the preview window on the screen --- sys/rpicamsrc/gstrpicamsrc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 3a74b812aa..b6a5edd4ae 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -96,6 +96,10 @@ enum PROP_PREVIEW, PROP_PREVIEW_ENCODED, PROP_PREVIEW_OPACITY, + PROP_PREVIEW_X, + PROP_PREVIEW_Y, + PROP_PREVIEW_W, + PROP_PREVIEW_H, PROP_FULLSCREEN, PROP_SHARPNESS, PROP_CONTRAST, @@ -298,6 +302,22 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_int ("preview-opacity", "Preview Opacity", "Opacity to use for the preview window", 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_X, + g_param_spec_int ("preview-x", "Preview window X position", + "Start X coordinate of the preview window (in pixels)", 0, 2048, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_Y, + g_param_spec_int ("preview-y", "Preview window Y position", + "Start Y coordinate of the preview window (in pixels)", 0, 2048, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_W, + g_param_spec_int ("preview-w", "Preview window width", + "Width of the preview window (in pixels)", 0, 2048, 1024, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PREVIEW_H, + g_param_spec_int ("preview-h", "Preview window height", + "Height of the preview window (in pixels)", 0, 2048, 768, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHARPNESS, g_param_spec_int ("sharpness", "Sharpness", "Image capture sharpness", -100, 100, SHARPNESS_DEFAULT, @@ -712,6 +732,22 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.preview_parameters.opacity = g_value_get_int (value); src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; break; + case PROP_PREVIEW_X: + src->capture_config.preview_parameters.previewWindow.x = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; + break; + case PROP_PREVIEW_Y: + src->capture_config.preview_parameters.previewWindow.y = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; + break; + case PROP_PREVIEW_W: + src->capture_config.preview_parameters.previewWindow.width = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; + break; + case PROP_PREVIEW_H: + src->capture_config.preview_parameters.previewWindow.height = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_PREVIEW; + break; case PROP_SHARPNESS: src->capture_config.camera_parameters.sharpness = g_value_get_int (value); src->capture_config.change_flags |= PROP_CHANGE_COLOURBALANCE; From 8fa4acacc0a7989a114b18edf79a2447bd58bd5c Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 17 Dec 2015 14:16:10 +1100 Subject: [PATCH 48/77] rpicamsrc: Add property getters for preview window position. Add the lines in get_property() for the preview-x/y/w/h properties so the values can be retrieved without causing critical warnings. Fixes https://github.com/thaytan/gst-rpicamsrc/issues/42 --- sys/rpicamsrc/gstrpicamsrc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index b6a5edd4ae..4e21b87b31 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -928,6 +928,18 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_PREVIEW_OPACITY: g_value_set_int (value, src->capture_config.preview_parameters.opacity); break; + case PROP_PREVIEW_X: + g_value_set_int (value, src->capture_config.preview_parameters.previewWindow.x); + break; + case PROP_PREVIEW_Y: + g_value_set_int (value, src->capture_config.preview_parameters.previewWindow.y); + break; + case PROP_PREVIEW_W: + g_value_set_int (value, src->capture_config.preview_parameters.previewWindow.width); + break; + case PROP_PREVIEW_H: + g_value_set_int (value, src->capture_config.preview_parameters.previewWindow.height); + break; case PROP_SHARPNESS: g_value_set_int (value, src->capture_config.camera_parameters.sharpness); break; From 8e8ac0504cd6e1543805bdc3998947af550b3376 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 3 Jan 2016 08:26:23 +1100 Subject: [PATCH 49/77] rpicamsrc: basesrc event handlers should not unref Don't unref the passed event when handling events via the GstBaseSrc src pad event handler - basesrc does the unref. That breaks handling of upstream force-key-unit events by unreffing twice. Fixes https://github.com/thaytan/gst-rpicamsrc/issues/43 --- sys/rpicamsrc/gstrpicamsrc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 4e21b87b31..b264ac0692 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1118,7 +1118,6 @@ gst_rpi_cam_src_event (GstBaseSrc * parent, GstEvent * event) } else { ret = FALSE; } - gst_event_unref (event); } else { ret = GST_BASE_SRC_CLASS (parent_class)->event (parent, event); } From c785d4f5db182d306c7a425d25b4cb326e7d0c2e Mon Sep 17 00:00:00 2001 From: Xabier Rodriguez Calvar Date: Fri, 8 Jul 2016 15:32:21 +0200 Subject: [PATCH 50/77] rpicamsrc: Create orientation property Its behavior and choices are analog to the ones present in [gl]videoflip for the method property. --- sys/rpicamsrc/gstrpicamsrc.c | 114 +++++++++++++++++++++++++++++++++++ sys/rpicamsrc/gstrpicamsrc.h | 28 +++++++++ 2 files changed, 142 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index b264ac0692..e8e3b7a21a 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -119,6 +119,7 @@ enum PROP_ROTATION, PROP_HFLIP, PROP_VFLIP, + PROP_ORIENTATION, PROP_ROI_X, PROP_ROI_Y, PROP_ROI_W, @@ -136,6 +137,8 @@ enum PROP_INTRA_REFRESH_TYPE }; +#define PROP_ORIENTATION_DEFAULT GST_RPI_CAM_ORIENTATION_IDENTITY + #define CAMERA_DEFAULT 0 #define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ @@ -224,6 +227,37 @@ G_DEFINE_TYPE_WITH_CODE (GstRpiCamSrc, gst_rpi_cam_src, #define C_ENUM(v) ((gint) v) +#define GST_TYPE_RPI_CAM_ORIENTATION (gst_rpi_cam_orientation_get_type()) + +static const GEnumValue video_orientation_methods[] = { + {GST_RPI_CAM_ORIENTATION_IDENTITY, "Identity (no rotation)", "none"}, + {GST_RPI_CAM_ORIENTATION_90R, "Rotate clockwise 90 degrees", "clockwise"}, + {GST_RPI_CAM_ORIENTATION_180, "Rotate 180 degrees", "rotate-180"}, + {GST_RPI_CAM_ORIENTATION_90L, "Rotate counter-clockwise 90 degrees", + "counterclockwise"}, + {GST_RPI_CAM_ORIENTATION_FLIP_HORIZ, "Flip horizontally", "horizontal-flip"}, + {GST_RPI_CAM_ORIENTATION_FLIP_VERT, "Flip vertically", "vertical-flip"}, + {GST_RPI_CAM_ORIENTATION_FLIP_TRANS, + "Flip across upper left/lower right diagonal", "upper-left-diagonal"}, + {GST_RPI_CAM_ORIENTATION_FLIP_OTHER, + "Flip across upper right/lower left diagonal", "upper-right-diagonal"}, + {GST_RPI_CAM_ORIENTATION_CUSTOM, + "Set through custom properties", "custom"}, + {0, NULL, NULL}, +}; + +static GType +gst_rpi_cam_orientation_get_type (void) +{ + static GType rpi_cam_orientation_type = 0; + + if (!rpi_cam_orientation_type) { + rpi_cam_orientation_type = g_enum_register_static ("GstRpiCamOrientation", + video_orientation_methods); + } + return rpi_cam_orientation_type; +} + GType gst_rpi_cam_src_sensor_mode_get_type (void) { @@ -393,6 +427,11 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_boolean ("vflip", "Vertical Flip", "Flip capture vertically", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", "Orientation", "Orientation", + GST_TYPE_RPI_CAM_ORIENTATION, PROP_ORIENTATION_DEFAULT, + GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_X, g_param_spec_float ("roi-x", "ROI X", "Normalised region-of-interest X coord", 0, 1.0, 0, @@ -612,6 +651,70 @@ gst_rpi_cam_src_colorbalance_get_balance_type (GstColorBalance * balance) return GST_COLOR_BALANCE_HARDWARE; } +static void gst_rpi_cam_src_set_orientation (GstRpiCamSrc * src, GstRpiCamOrientation orientation) +{ + switch (orientation) { + case GST_RPI_CAM_ORIENTATION_IDENTITY: + src->capture_config.camera_parameters.rotation = 0; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = FALSE; + GST_DEBUG_OBJECT (src, "set orientation identity"); + break; + case GST_RPI_CAM_ORIENTATION_90R: + src->capture_config.camera_parameters.rotation = 90; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = FALSE; + GST_DEBUG_OBJECT (src, "set orientation 90R"); + break; + case GST_RPI_CAM_ORIENTATION_180: + src->capture_config.camera_parameters.rotation = 180; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = FALSE; + GST_DEBUG_OBJECT (src, "set orientation 180"); + break; + case GST_RPI_CAM_ORIENTATION_90L: + src->capture_config.camera_parameters.rotation = 270; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = FALSE; + GST_DEBUG_OBJECT (src, "set orientation 90L"); + break; + case GST_RPI_CAM_ORIENTATION_FLIP_HORIZ: + src->capture_config.camera_parameters.rotation = 0; + src->capture_config.camera_parameters.hflip = TRUE; + src->capture_config.camera_parameters.vflip = FALSE; + GST_DEBUG_OBJECT (src, "set orientation hflip"); + break; + case GST_RPI_CAM_ORIENTATION_FLIP_VERT: + src->capture_config.camera_parameters.rotation = 0; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = TRUE; + GST_DEBUG_OBJECT (src, "set orientation vflip"); + break; + case GST_RPI_CAM_ORIENTATION_FLIP_TRANS: + src->capture_config.camera_parameters.rotation = 90; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = TRUE; + GST_DEBUG_OBJECT (src, "set orientation trans"); + break; + case GST_RPI_CAM_ORIENTATION_FLIP_OTHER: + src->capture_config.camera_parameters.rotation = 270; + src->capture_config.camera_parameters.hflip = FALSE; + src->capture_config.camera_parameters.vflip = TRUE; + GST_DEBUG_OBJECT (src, "set orientation trans"); + break; + case GST_RPI_CAM_ORIENTATION_CUSTOM: + break; + default: + GST_WARNING_OBJECT (src, "unsupported orientation %d", orientation); + break; + } + src->orientation = + orientation >= GST_RPI_CAM_ORIENTATION_IDENTITY && + orientation <= GST_RPI_CAM_ORIENTATION_CUSTOM ? + orientation : GST_RPI_CAM_ORIENTATION_CUSTOM; + src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; +} + static void gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * iface) { @@ -660,6 +763,7 @@ gst_rpi_cam_src_orientation_set_hflip (GstVideoOrientation * orientation, gboole g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); g_mutex_lock (&src->config_lock); + src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; src->capture_config.camera_parameters.hflip = flip; src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; g_mutex_unlock (&src->config_lock); @@ -676,6 +780,7 @@ gst_rpi_cam_src_orientation_set_vflip (GstVideoOrientation * orientation, gboole g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); g_mutex_lock (&src->config_lock); + src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; src->capture_config.camera_parameters.vflip = flip; src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; g_mutex_unlock (&src->config_lock); @@ -791,8 +896,12 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_ROTATION: + src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; src->capture_config.camera_parameters.rotation = g_value_get_int (value); break; + case PROP_ORIENTATION: + gst_rpi_cam_src_set_orientation (src, g_value_get_enum (value)); + break; case PROP_AWB_MODE: src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); src->capture_config.change_flags |= PROP_CHANGE_AWB; @@ -813,10 +922,12 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.change_flags |= PROP_CHANGE_IMAGE_COLOUR_EFFECT; break; case PROP_HFLIP: + src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; src->capture_config.camera_parameters.hflip = g_value_get_boolean (value); src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; break; case PROP_VFLIP: + src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; src->capture_config.camera_parameters.vflip = g_value_get_boolean (value); src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; break; @@ -974,6 +1085,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_ROTATION: g_value_set_int (value, src->capture_config.camera_parameters.rotation); break; + case PROP_ORIENTATION: + g_value_set_enum (value, src->orientation); + break; case PROP_AWB_MODE: g_value_set_enum (value, src->capture_config.camera_parameters.awbMode); break; diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index dbfc5bb609..d116ee1a37 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -50,6 +50,32 @@ G_BEGIN_DECLS +/** + * GstRpiCamOrientation: + * @GST_RPI_CAM_ORIENTATION_IDENTITY: Identity (no rotation) + * @GST_RPI_CAM_ORIENTATION_90R: Rotate clockwise 90 degrees + * @GST_RPI_CAM_ORIENTATION_180: Rotate 180 degrees + * @GST_RPI_CAM_ORIENTATION_90L: Rotate counter-clockwise 90 degrees + * @GST_RPI_CAM_ORIENTATION_FLIP_HORIZ: Flip horizontally + * @GST_RPI_CAM_ORIENTATION_FLIP_VERT: Flip vertically + * @GST_RPI_CAM_ORIENTATION_FLIP_TRANS: Flip across upper left/lower right diagonal + * @GST_RPI_CAM_ORIENTATION_FLIP_OTHER: Flip across upper right/lower left diagonal + * @GST_RPI_CAM_ORIENTATION_CUSTOM: Orientation is set through rotation, hflip or vflip properties. + * + * The different orientation methods. + */ +typedef enum { + GST_RPI_CAM_ORIENTATION_IDENTITY, + GST_RPI_CAM_ORIENTATION_90R, + GST_RPI_CAM_ORIENTATION_180, + GST_RPI_CAM_ORIENTATION_90L, + GST_RPI_CAM_ORIENTATION_FLIP_HORIZ, + GST_RPI_CAM_ORIENTATION_FLIP_VERT, + GST_RPI_CAM_ORIENTATION_FLIP_TRANS, + GST_RPI_CAM_ORIENTATION_FLIP_OTHER, + GST_RPI_CAM_ORIENTATION_CUSTOM +} GstRpiCamOrientation; + #define GST_TYPE_RPICAMSRC (gst_rpi_cam_src_get_type()) #define GST_RPICAMSRC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RPICAMSRC,GstRpiCamSrc)) @@ -77,6 +103,8 @@ struct _GstRpiCamSrc /* channels for interface */ GList *channels; + + GstRpiCamOrientation orientation; }; struct _GstRpiCamSrcClass From 2496d0e859829e3970d8a1ac2a695e1561f61f2e Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 21 Jul 2016 02:29:57 +1000 Subject: [PATCH 51/77] rpicamsrc: MMAL gives buffers with nal alignment, not AU Fix the output caps, our buffers are not AU aligned, since the SPS / PPS are given in separate packets at the start. --- sys/rpicamsrc/gstrpicamsrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index e8e3b7a21a..e5cb5afb21 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -184,7 +184,7 @@ enum "height = " GST_VIDEO_SIZE_RANGE ", " \ "framerate = " GST_VIDEO_FPS_RANGE ", " \ "stream-format = (string) byte-stream, " \ - "alignment = (string) au, " \ + "alignment = (string) nal, " \ "profile = (string) { baseline, main, high }" static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", From 39afecac324d48af210327d0ad47ca8a96871ddb Mon Sep 17 00:00:00 2001 From: Xabier Rodriguez Calvar Date: Tue, 30 Aug 2016 17:00:41 +0200 Subject: [PATCH 52/77] rpicamsrc: Implement GstVideoDirection interface Instead of implementing a custom property, we implement that interface. --- sys/rpicamsrc/gstrpicamsrc.c | 121 +++++++++++++++++------------------ sys/rpicamsrc/gstrpicamsrc.h | 35 +++------- 2 files changed, 65 insertions(+), 91 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index e5cb5afb21..879d0f232e 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -119,7 +119,6 @@ enum PROP_ROTATION, PROP_HFLIP, PROP_VFLIP, - PROP_ORIENTATION, PROP_ROI_X, PROP_ROI_Y, PROP_ROI_W, @@ -134,11 +133,12 @@ enum PROP_ANNOTATION_TEXT_SIZE, PROP_ANNOTATION_TEXT_COLOUR, PROP_ANNOTATION_TEXT_BG_COLOUR, - PROP_INTRA_REFRESH_TYPE + PROP_INTRA_REFRESH_TYPE, +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION + PROP_VIDEO_DIRECTION +#endif }; -#define PROP_ORIENTATION_DEFAULT GST_RPI_CAM_ORIENTATION_IDENTITY - #define CAMERA_DEFAULT 0 #define BITRATE_DEFAULT 17000000 /* 17Mbit/s default for 1080p */ @@ -187,6 +187,12 @@ enum "alignment = (string) nal, " \ "profile = (string) { baseline, main, high }" +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION +#define gst_rpi_cam_src_reset_custom_orientation(src) { src->orientation = GST_VIDEO_ORIENTATION_CUSTOM; } +#else +#define gst_rpi_cam_src_reset_custom_orientation(src) { } +#endif + static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, @@ -199,6 +205,9 @@ static void gst_rpi_cam_src_finalize (GObject *object); static void gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * iface); static void gst_rpi_cam_src_orientation_init (GstVideoOrientationInterface * iface); +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION +static void gst_rpi_cam_src_direction_init (GstVideoDirectionInterface * iface); +#endif static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -222,42 +231,15 @@ G_DEFINE_TYPE_WITH_CODE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_PUSH_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE, gst_rpi_cam_src_colorbalance_init); +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_DIRECTION, + gst_rpi_cam_src_direction_init); +#endif G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_ORIENTATION, gst_rpi_cam_src_orientation_init)); #define C_ENUM(v) ((gint) v) -#define GST_TYPE_RPI_CAM_ORIENTATION (gst_rpi_cam_orientation_get_type()) - -static const GEnumValue video_orientation_methods[] = { - {GST_RPI_CAM_ORIENTATION_IDENTITY, "Identity (no rotation)", "none"}, - {GST_RPI_CAM_ORIENTATION_90R, "Rotate clockwise 90 degrees", "clockwise"}, - {GST_RPI_CAM_ORIENTATION_180, "Rotate 180 degrees", "rotate-180"}, - {GST_RPI_CAM_ORIENTATION_90L, "Rotate counter-clockwise 90 degrees", - "counterclockwise"}, - {GST_RPI_CAM_ORIENTATION_FLIP_HORIZ, "Flip horizontally", "horizontal-flip"}, - {GST_RPI_CAM_ORIENTATION_FLIP_VERT, "Flip vertically", "vertical-flip"}, - {GST_RPI_CAM_ORIENTATION_FLIP_TRANS, - "Flip across upper left/lower right diagonal", "upper-left-diagonal"}, - {GST_RPI_CAM_ORIENTATION_FLIP_OTHER, - "Flip across upper right/lower left diagonal", "upper-right-diagonal"}, - {GST_RPI_CAM_ORIENTATION_CUSTOM, - "Set through custom properties", "custom"}, - {0, NULL, NULL}, -}; - -static GType -gst_rpi_cam_orientation_get_type (void) -{ - static GType rpi_cam_orientation_type = 0; - - if (!rpi_cam_orientation_type) { - rpi_cam_orientation_type = g_enum_register_static ("GstRpiCamOrientation", - video_orientation_methods); - } - return rpi_cam_orientation_type; -} - GType gst_rpi_cam_src_sensor_mode_get_type (void) { @@ -427,11 +409,6 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_boolean ("vflip", "Vertical Flip", "Flip capture vertically", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_ORIENTATION, - g_param_spec_enum ("orientation", "Orientation", "Orientation", - GST_TYPE_RPI_CAM_ORIENTATION, PROP_ORIENTATION_DEFAULT, - GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ROI_X, g_param_spec_float ("roi-x", "ROI X", "Normalised region-of-interest X coord", 0, 1.0, 0, @@ -496,6 +473,10 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_param_spec_int ("annotation-text-bg-colour", "Annotation text background colour (VUY)", "Set the annotation text background colour, as a VUY hex value eg #8080FF, -1 for default", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION + g_object_class_override_property (gobject_class, PROP_VIDEO_DIRECTION, + "video-direction"); +#endif gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", @@ -651,70 +632,78 @@ gst_rpi_cam_src_colorbalance_get_balance_type (GstColorBalance * balance) return GST_COLOR_BALANCE_HARDWARE; } -static void gst_rpi_cam_src_set_orientation (GstRpiCamSrc * src, GstRpiCamOrientation orientation) +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION +static void gst_rpi_cam_src_set_orientation (GstRpiCamSrc * src, GstVideoOrientationMethod orientation) { switch (orientation) { - case GST_RPI_CAM_ORIENTATION_IDENTITY: + case GST_VIDEO_ORIENTATION_IDENTITY: src->capture_config.camera_parameters.rotation = 0; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = FALSE; GST_DEBUG_OBJECT (src, "set orientation identity"); break; - case GST_RPI_CAM_ORIENTATION_90R: + case GST_VIDEO_ORIENTATION_90R: src->capture_config.camera_parameters.rotation = 90; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = FALSE; GST_DEBUG_OBJECT (src, "set orientation 90R"); break; - case GST_RPI_CAM_ORIENTATION_180: + case GST_VIDEO_ORIENTATION_180: src->capture_config.camera_parameters.rotation = 180; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = FALSE; GST_DEBUG_OBJECT (src, "set orientation 180"); break; - case GST_RPI_CAM_ORIENTATION_90L: + case GST_VIDEO_ORIENTATION_90L: src->capture_config.camera_parameters.rotation = 270; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = FALSE; GST_DEBUG_OBJECT (src, "set orientation 90L"); break; - case GST_RPI_CAM_ORIENTATION_FLIP_HORIZ: + case GST_VIDEO_ORIENTATION_HORIZ: src->capture_config.camera_parameters.rotation = 0; src->capture_config.camera_parameters.hflip = TRUE; src->capture_config.camera_parameters.vflip = FALSE; GST_DEBUG_OBJECT (src, "set orientation hflip"); break; - case GST_RPI_CAM_ORIENTATION_FLIP_VERT: + case GST_VIDEO_ORIENTATION_VERT: src->capture_config.camera_parameters.rotation = 0; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = TRUE; GST_DEBUG_OBJECT (src, "set orientation vflip"); break; - case GST_RPI_CAM_ORIENTATION_FLIP_TRANS: + case GST_VIDEO_ORIENTATION_UL_LR: src->capture_config.camera_parameters.rotation = 90; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = TRUE; GST_DEBUG_OBJECT (src, "set orientation trans"); break; - case GST_RPI_CAM_ORIENTATION_FLIP_OTHER: + case GST_VIDEO_ORIENTATION_UR_LL: src->capture_config.camera_parameters.rotation = 270; src->capture_config.camera_parameters.hflip = FALSE; src->capture_config.camera_parameters.vflip = TRUE; GST_DEBUG_OBJECT (src, "set orientation trans"); break; - case GST_RPI_CAM_ORIENTATION_CUSTOM: + case GST_VIDEO_ORIENTATION_CUSTOM: break; default: GST_WARNING_OBJECT (src, "unsupported orientation %d", orientation); break; } src->orientation = - orientation >= GST_RPI_CAM_ORIENTATION_IDENTITY && - orientation <= GST_RPI_CAM_ORIENTATION_CUSTOM ? - orientation : GST_RPI_CAM_ORIENTATION_CUSTOM; + orientation >= GST_VIDEO_ORIENTATION_IDENTITY && + orientation <= GST_VIDEO_ORIENTATION_CUSTOM ? + orientation : GST_VIDEO_ORIENTATION_CUSTOM; src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; } +static void +gst_rpi_cam_src_direction_init (GstVideoDirectionInterface * iface) +{ + /* We implement the video-direction property */ +} +#endif + static void gst_rpi_cam_src_colorbalance_init (GstColorBalanceInterface * iface) { @@ -763,7 +752,7 @@ gst_rpi_cam_src_orientation_set_hflip (GstVideoOrientation * orientation, gboole g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); g_mutex_lock (&src->config_lock); - src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; + gst_rpi_cam_src_reset_custom_orientation(src); src->capture_config.camera_parameters.hflip = flip; src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; g_mutex_unlock (&src->config_lock); @@ -780,7 +769,7 @@ gst_rpi_cam_src_orientation_set_vflip (GstVideoOrientation * orientation, gboole g_return_val_if_fail (GST_IS_RPICAMSRC (src), FALSE); g_mutex_lock (&src->config_lock); - src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; + gst_rpi_cam_src_reset_custom_orientation(src); src->capture_config.camera_parameters.vflip = flip; src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; g_mutex_unlock (&src->config_lock); @@ -896,12 +885,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.change_flags |= PROP_CHANGE_SENSOR_SETTINGS; break; case PROP_ROTATION: - src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; + gst_rpi_cam_src_reset_custom_orientation(src); src->capture_config.camera_parameters.rotation = g_value_get_int (value); break; - case PROP_ORIENTATION: - gst_rpi_cam_src_set_orientation (src, g_value_get_enum (value)); - break; case PROP_AWB_MODE: src->capture_config.camera_parameters.awbMode = g_value_get_enum (value); src->capture_config.change_flags |= PROP_CHANGE_AWB; @@ -922,12 +908,12 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.change_flags |= PROP_CHANGE_IMAGE_COLOUR_EFFECT; break; case PROP_HFLIP: - src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; + gst_rpi_cam_src_reset_custom_orientation(src); src->capture_config.camera_parameters.hflip = g_value_get_boolean (value); src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; break; case PROP_VFLIP: - src->orientation = GST_RPI_CAM_ORIENTATION_CUSTOM; + gst_rpi_cam_src_reset_custom_orientation(src); src->capture_config.camera_parameters.vflip = g_value_get_boolean (value); src->capture_config.change_flags |= PROP_CHANGE_ORIENTATION; break; @@ -1000,6 +986,11 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.intra_refresh_type = g_value_get_enum (value); src->capture_config.change_flags |= PROP_CHANGE_ENCODING; break; +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION + case PROP_VIDEO_DIRECTION: + gst_rpi_cam_src_set_orientation (src, g_value_get_enum (value)); + break; +#endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1085,9 +1076,6 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_ROTATION: g_value_set_int (value, src->capture_config.camera_parameters.rotation); break; - case PROP_ORIENTATION: - g_value_set_enum (value, src->orientation); - break; case PROP_AWB_MODE: g_value_set_enum (value, src->capture_config.camera_parameters.awbMode); break; @@ -1159,6 +1147,11 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_INTRA_REFRESH_TYPE: g_value_set_enum (value, src->capture_config.intra_refresh_type); break; +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION + case PROP_VIDEO_DIRECTION: + g_value_set_enum (value, src->orientation); + break; +#endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index d116ee1a37..6675f88049 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -46,36 +46,11 @@ #include #include +#include /* only used for GST_PLUGINS_BASE_VERSION_* */ #include "RaspiCapture.h" G_BEGIN_DECLS -/** - * GstRpiCamOrientation: - * @GST_RPI_CAM_ORIENTATION_IDENTITY: Identity (no rotation) - * @GST_RPI_CAM_ORIENTATION_90R: Rotate clockwise 90 degrees - * @GST_RPI_CAM_ORIENTATION_180: Rotate 180 degrees - * @GST_RPI_CAM_ORIENTATION_90L: Rotate counter-clockwise 90 degrees - * @GST_RPI_CAM_ORIENTATION_FLIP_HORIZ: Flip horizontally - * @GST_RPI_CAM_ORIENTATION_FLIP_VERT: Flip vertically - * @GST_RPI_CAM_ORIENTATION_FLIP_TRANS: Flip across upper left/lower right diagonal - * @GST_RPI_CAM_ORIENTATION_FLIP_OTHER: Flip across upper right/lower left diagonal - * @GST_RPI_CAM_ORIENTATION_CUSTOM: Orientation is set through rotation, hflip or vflip properties. - * - * The different orientation methods. - */ -typedef enum { - GST_RPI_CAM_ORIENTATION_IDENTITY, - GST_RPI_CAM_ORIENTATION_90R, - GST_RPI_CAM_ORIENTATION_180, - GST_RPI_CAM_ORIENTATION_90L, - GST_RPI_CAM_ORIENTATION_FLIP_HORIZ, - GST_RPI_CAM_ORIENTATION_FLIP_VERT, - GST_RPI_CAM_ORIENTATION_FLIP_TRANS, - GST_RPI_CAM_ORIENTATION_FLIP_OTHER, - GST_RPI_CAM_ORIENTATION_CUSTOM -} GstRpiCamOrientation; - #define GST_TYPE_RPICAMSRC (gst_rpi_cam_src_get_type()) #define GST_RPICAMSRC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RPICAMSRC,GstRpiCamSrc)) @@ -86,6 +61,10 @@ typedef enum { #define GST_IS_RPICAMSRC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RPICAMSRC)) +#if GST_CHECK_PLUGINS_BASE_VERSION(1, 9, 2) +#define GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION +#endif + typedef struct _GstRpiCamSrc GstRpiCamSrc; typedef struct _GstRpiCamSrcClass GstRpiCamSrcClass; @@ -104,7 +83,9 @@ struct _GstRpiCamSrc /* channels for interface */ GList *channels; - GstRpiCamOrientation orientation; +#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION + GstVideoOrientationMethod orientation; +#endif }; struct _GstRpiCamSrcClass From 0a3864221426663f1c3be8e07e632c7a62d1d2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 19 Sep 2016 12:06:05 +0000 Subject: [PATCH 53/77] rpicamsrc: Add experimental build using the Meson build system Builds in about 10 seconds vs. 77 seconds with autotools. --- sys/rpicamsrc/gstplugin.map | 3 +++ sys/rpicamsrc/meson.build | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 sys/rpicamsrc/gstplugin.map create mode 100644 sys/rpicamsrc/meson.build diff --git a/sys/rpicamsrc/gstplugin.map b/sys/rpicamsrc/gstplugin.map new file mode 100644 index 0000000000..08d854102a --- /dev/null +++ b/sys/rpicamsrc/gstplugin.map @@ -0,0 +1,3 @@ +{ global: +gst_plugin_desc; +local: *; }; diff --git a/sys/rpicamsrc/meson.build b/sys/rpicamsrc/meson.build new file mode 100644 index 0000000000..62c21451cc --- /dev/null +++ b/sys/rpicamsrc/meson.build @@ -0,0 +1,36 @@ +rpicamsrc_sources = [ + 'gstrpicamsrc.c', + 'gstrpicamsrcdeviceprovider.c', + 'RaspiCapture.c', + 'RaspiCamControl.c', + 'RaspiPreview.c', + 'RaspiCLI.c', +] + +# This can be simplified once meson gets native support for glib-mkenums (soon) +glib_mkenums = find_program('glib-mkenums') + +gstrpicam_enum_types_h = custom_target('gstrpicam-enum-types.h', + output : 'gstrpicam-enum-types.h', + input : files('gstrpicam_types.h'), + command : [glib_mkenums, '--template', meson.current_source_dir() + '/gstrpicam-enums-template.h', '@INPUT@'], + capture : true) + +gstrpicam_enum_types_c = custom_target('gstrpicam-enum-types.c', + output : 'gstrpicam-enum-types.c', + input : files('gstrpicam_types.h'), + depends : [gstrpicam_enum_types_h], + command : [glib_mkenums, '--template', meson.current_source_dir() + '/gstrpicam-enums-template.c', '@INPUT@'], + capture : true) + +mapfile = 'gstplugin.map' + +library('gstrpicamsrc', + rpicamsrc_sources, gstrpicam_enum_types_h, gstrpicam_enum_types_c, + c_args : gst_rpicamsrc_args, + include_directories : config_inc, + link_args : '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile), + link_depends : mapfile, + dependencies : [gst_dep, gstbase_dep, gstvideo_dep] + mmal_deps, + install : true, + install_dir : plugins_install_dir) From da86cec40e375d4196b5ea73b7421075b6d2ad98 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 3 Oct 2016 02:34:50 +1100 Subject: [PATCH 54/77] rpicamsrc: First attempt at implementing MJPEG and raw video support --- sys/rpicamsrc/RaspiCapture.c | 169 +++++++++++++++++++++++------------ sys/rpicamsrc/RaspiCapture.h | 5 ++ sys/rpicamsrc/gstrpicamsrc.c | 70 +++++++++++---- 3 files changed, 170 insertions(+), 74 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index abe5346cd7..ad4798f7f2 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Jan Schmidt + * Copyright (c) 2013-2016 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes @@ -269,6 +269,7 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) config->demoInterval = 250; // ms config->immutableInput = 1; config->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + config->encoding = MMAL_ENCODING_H264; config->bInlineHeaders = 0; @@ -1119,9 +1120,6 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format = preview_port->format; - format->encoding = MMAL_ENCODING_OPAQUE; - format->encoding_variant = MMAL_ENCODING_I420; - if(config->camera_parameters.shutter_speed > 6000000) { MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, @@ -1147,6 +1145,8 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) } format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; @@ -1165,9 +1165,7 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) } // Set the encode format on the video port - format = video_port->format; - format->encoding_variant = MMAL_ENCODING_I420; if(config->camera_parameters.shutter_speed > 6000000) { @@ -1182,7 +1180,16 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) mmal_port_parameter_set(video_port, &fps_range.hdr); } - format->encoding = MMAL_ENCODING_OPAQUE; + /* If encoding, set opaque tunneling format */ + if (state->encoder_component) { + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + } + else { + format->encoding = config->encoding; + format->encoding_variant = config->encoding; + } + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; @@ -1279,6 +1286,10 @@ gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state) MMAL_PORT_T *encoder_output = NULL; MMAL_STATUS_T status; MMAL_PARAMETER_BOOLEAN_T param = {{ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, sizeof(param)}, 1}; + + if (state->encoder_component) + return TRUE; + encoder_output = state->encoder_component->output[0]; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); if (status != MMAL_SUCCESS) @@ -1302,15 +1313,24 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) MMAL_COMPONENT_T *encoder = 0; MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; MMAL_STATUS_T status; - MMAL_POOL_T *pool; RASPIVID_CONFIG *config = &state->config; - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); + gboolean encoded_format = + (config->encoding == MMAL_ENCODING_H264 || + config->encoding == MMAL_ENCODING_MJPEG || + config->encoding == MMAL_ENCODING_JPEG); - if (status != MMAL_SUCCESS) - { - vcos_log_error("Unable to create video encoder component"); - goto error; + if (!encoded_format) + return MMAL_SUCCESS; + + if (config->encoding == MMAL_ENCODING_JPEG) + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder); + else + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) { + vcos_log_error("Unable to create video encoder component"); + goto error; } if (!encoder->input_num || !encoder->output_num) @@ -1326,23 +1346,27 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) // We want same format on input and output mmal_format_copy(encoder_output->format, encoder_input->format); - // Only supporting H264 at the moment - encoder_output->format->encoding = MMAL_ENCODING_H264; + // Configure desired encoding + encoder_output->format->encoding = config->encoding; encoder_output->format->bitrate = config->bitrate; - encoder_output->buffer_size = encoder_output->buffer_size_recommended; + if (config->encoding == MMAL_ENCODING_H264) + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + else + encoder_output->buffer_size = 256<<10; if (encoder_output->buffer_size < encoder_output->buffer_size_min) encoder_output->buffer_size = encoder_output->buffer_size_min; - GST_DEBUG ("encoder buffer size is %u", (guint)encoder_output->buffer_size); - encoder_output->buffer_num = encoder_output->buffer_num_recommended; if (encoder_output->buffer_num < encoder_output->buffer_num_min) encoder_output->buffer_num = encoder_output->buffer_num_min; + GST_DEBUG ("encoder wants %d buffers of size %u", + (guint)encoder_output->buffer_num, (guint)encoder_output->buffer_size); + // We need to set the frame rate on output to 0, to ensure it gets // updated correctly from the input framerate when port connected encoder_output->format->es->video.frame_rate.num = 0; @@ -1350,9 +1374,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) // Commit the port changes to the output port status = mmal_port_format_commit(encoder_output); - - if (status != MMAL_SUCCESS) - { + if (status != MMAL_SUCCESS) { vcos_log_error("Unable to set format on video encoder output port"); goto error; } @@ -1370,7 +1392,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (config->intraperiod != -1) + if (config->encoding == MMAL_ENCODING_H264 && config->intraperiod != -1) { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -1381,7 +1403,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } } - if (config->quantisationParameter) + if (config->encoding == MMAL_ENCODING_H264 && config->quantisationParameter) { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -1409,6 +1431,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } + if (config->encoding == MMAL_ENCODING_H264) { MMAL_PARAMETER_VIDEO_PROFILE_T param; param.hdr.id = MMAL_PARAMETER_PROFILE; @@ -1440,14 +1463,16 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } //set INLINE VECTORS flag to request motion vector estimates - if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) + if (config->encoding == MMAL_ENCODING_H264 && + mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) { vcos_log_error("failed to set INLINE VECTORS parameters"); // Continue rather than abort.. } // Adaptive intra refresh settings - if (config->intra_refresh_type != -1) + if (config->encoding == MMAL_ENCODING_H264 && + config->intra_refresh_type != -1) { MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; @@ -1476,6 +1501,23 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } } + if (config->encoding == MMAL_ENCODING_JPEG) + { + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, config->jpegQuality); + if (status != MMAL_SUCCESS) { + vcos_log_error("Unable to set JPEG quality"); + goto error; + } + +#ifdef MMAL_PARAMETER_JPEG_RESTART_INTERVAL + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, config->jpegRestartInterval); + if (status != MMAL_SUCCESS) { + vcos_log_error("Unable to set JPEG restart interval"); + goto error; + } +#endif + } + // Enable component status = mmal_component_enable(encoder); @@ -1485,15 +1527,6 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) goto error; } - /* Create pool of buffer headers for the output port to consume */ - pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); - - if (!pool) - { - vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); - } - - state->encoder_pool = pool; state->encoder_component = encoder; if (config->verbose) @@ -1635,10 +1668,11 @@ raspi_capture_start(RASPIVID_STATE *state) MMAL_PORT_T *preview_input_port = NULL; MMAL_PORT_T *encoder_input_port = NULL; - if ((status = create_encoder_component(state)) != MMAL_SUCCESS) - { - vcos_log_error("%s: Failed to create encode component", __func__); - return FALSE; + MMAL_POOL_T *pool; + + if ((status = create_encoder_component(state)) != MMAL_SUCCESS) { + vcos_log_error("%s: Failed to create encode component", __func__); + return FALSE; } if (config->verbose) @@ -1646,21 +1680,38 @@ raspi_capture_start(RASPIVID_STATE *state) dump_state(state); } + state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + preview_input_port = state->preview_state.preview_component->input[0]; + + if (state->encoder_component) { + encoder_input_port = state->encoder_component->input[0]; + state->encoder_output_port = state->encoder_component->output[0]; + } else { + state->encoder_output_port = state->camera_video_port; + } + if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) { return FALSE; } + GST_DEBUG ("Creating pool of %d buffers of size %d", + state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size); + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(state->encoder_output_port, + state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size); + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", + state->encoder_output_port->name); + return FALSE; + } + state->encoder_pool = pool; + if (state->config.verbose) fprintf(stderr, "Starting component connection stage\n"); - camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; - preview_input_port = state->preview_state.preview_component->input[0]; - encoder_input_port = state->encoder_component->input[0]; - - state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT]; - state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; - state->encoder_output_port = state->encoder_component->output[0]; - if (config->preview_parameters.wantPreview ) { if (config->verbose) @@ -1678,17 +1729,19 @@ raspi_capture_start(RASPIVID_STATE *state) } } - if (config->verbose) - fprintf(stderr, "Connecting camera stills port to encoder input port\n"); + if (state->encoder_component) { + if (config->verbose) + fprintf(stderr, "Connecting camera video port to encoder input port\n"); - // Now connect the camera to the encoder - status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection); - if (status != MMAL_SUCCESS) - { - if (config->preview_parameters.wantPreview ) - mmal_connection_destroy(state->preview_connection); - vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); - return FALSE; + // Now connect the camera to the encoder + status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection); + if (status != MMAL_SUCCESS) + { + if (config->preview_parameters.wantPreview ) + mmal_connection_destroy(state->preview_connection); + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + return FALSE; + } } // Set up our userdata - this is passed though to the callback where we need the information. @@ -1788,13 +1841,13 @@ raspi_capture_stop(RASPIVID_STATE *state) if (config->preview_parameters.wantPreview ) mmal_connection_destroy(state->preview_connection); - mmal_connection_destroy(state->encoder_connection); // Disable all our ports that are not handled by connections check_disable_port(state->camera_still_port); check_disable_port(state->encoder_output_port); if (state->encoder_component) { + mmal_connection_destroy(state->encoder_connection); mmal_component_disable(state->encoder_component); destroy_encoder_component(state); } @@ -1847,7 +1900,7 @@ raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gbo if (!dynamic) return; - if (config->change_flags & PROP_CHANGE_ENCODING) { + if (state->encoder_component && config->change_flags & PROP_CHANGE_ENCODING) { /* BITRATE or QUANT or KEY Interval, intra refresh */ MMAL_COMPONENT_T *encoder = state->encoder_component; MMAL_PORT_T *encoder_output = encoder->output[0]; diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 2462caac0e..6a79ac5240 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -111,6 +111,11 @@ typedef struct int settings; /// Request settings from the camera int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. int intra_refresh_type; /// What intra refresh type to use. -1 to not set. + + MMAL_FOURCC_T encoding; // Which encoding to use + + int jpegQuality; + int jpegRestartInterval; } RASPIVID_CONFIG; typedef struct RASPIVID_STATE_T RASPIVID_STATE; diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 879d0f232e..3bf9ab6fcc 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -57,8 +57,6 @@ #ifdef HAVE_CONFIG_H # include #endif - -#include #include #include "gstrpicamsrc.h" @@ -135,8 +133,9 @@ enum PROP_ANNOTATION_TEXT_BG_COLOUR, PROP_INTRA_REFRESH_TYPE, #ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION - PROP_VIDEO_DIRECTION + PROP_VIDEO_DIRECTION, #endif + PROP_JPEG_QUALITY }; #define CAMERA_DEFAULT 0 @@ -158,6 +157,8 @@ enum #define EXPOSURE_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO #define EXPOSURE_METERING_MODE_DEFAULT GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE +#define DEFAULT_JPEG_QUALITY 50 + /* params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; @@ -172,8 +173,7 @@ enum params->roi.w = params->roi.h = 1.0; */ -#define RAW_AND_JPEG_CAPS \ - GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ";" \ +#define JPEG_CAPS \ "image/jpeg," \ "width = " GST_VIDEO_SIZE_RANGE "," \ "height = " GST_VIDEO_SIZE_RANGE "," \ @@ -186,6 +186,8 @@ enum "stream-format = (string) byte-stream, " \ "alignment = (string) nal, " \ "profile = (string) { baseline, main, high }" +#define RAW_CAPS \ + GST_VIDEO_CAPS_MAKE ("{ I420, RGB, BGR, RGBA }") /* FIXME: Map more raw formats */ #ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION #define gst_rpi_cam_src_reset_custom_orientation(src) { src->orientation = GST_VIDEO_ORIENTATION_CUSTOM; } @@ -196,7 +198,7 @@ enum static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ( /*RAW_AND_JPEG_CAPS "; " */ H264_CAPS) + GST_STATIC_CAPS ( H264_CAPS "; " JPEG_CAPS "; " RAW_CAPS) ); @@ -297,6 +299,10 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "Bitrate for encoding. 0 for VBR using quantisation-parameter", 0, BITRATE_HIGHEST, BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_JPEG_QUALITY, + g_param_spec_int ("jpeg-quality", "JPEG Quality", + "Quality setting for JPEG encode", 1, 100, DEFAULT_JPEG_QUALITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_KEYFRAME_INTERVAL, g_param_spec_int ("keyframe-interval", "Keyframe Interface", "Interval (in frames) between I frames. -1 = automatic, 0 = single-keyframe", @@ -804,6 +810,10 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, src->capture_config.bitrate = g_value_get_int (value); src->capture_config.change_flags |= PROP_CHANGE_ENCODING; break; + case PROP_JPEG_QUALITY: + src->capture_config.jpegQuality = g_value_get_int (value); + src->capture_config.change_flags |= PROP_CHANGE_ENCODING; + break; case PROP_KEYFRAME_INTERVAL: src->capture_config.intraperiod = g_value_get_int (value); src->capture_config.change_flags |= PROP_CHANGE_ENCODING; @@ -1013,6 +1023,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, case PROP_BITRATE: g_value_set_int (value, src->capture_config.bitrate); break; + case PROP_JPEG_QUALITY: + g_value_set_int (value, src->capture_config.jpegQuality); + break; case PROP_KEYFRAME_INTERVAL: g_value_set_int (value, src->capture_config.intraperiod); break; @@ -1267,16 +1280,41 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) return FALSE; structure = gst_caps_get_structure (caps, 0); - profile_str = gst_structure_get_string (structure, "profile"); - if (profile_str) { - if (g_str_equal (profile_str, "baseline")) - src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; - else if (g_str_equal (profile_str, "main")) - src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_MAIN; - else if (g_str_equal (profile_str, "high")) - src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_HIGH; - else - g_warning ("Unknown profile string in rpicamsrc caps: %s", profile_str); + if (gst_structure_has_name (structure, "video/x-h264")) { + src->capture_config.encoding = MMAL_ENCODING_H264; + profile_str = gst_structure_get_string (structure, "profile"); + if (profile_str) { + if (g_str_equal (profile_str, "baseline")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; + else if (g_str_equal (profile_str, "main")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_MAIN; + else if (g_str_equal (profile_str, "high")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_HIGH; + else + g_warning ("Unknown profile string in rpicamsrc caps: %s", profile_str); + } + } + else if (gst_structure_has_name (structure, "image/jpeg")) { + src->capture_config.encoding = MMAL_ENCODING_MJPEG; + } + else { + /* Raw caps */ + switch (GST_VIDEO_INFO_FORMAT(&info)) { + case GST_VIDEO_FORMAT_I420: + src->capture_config.encoding = MMAL_ENCODING_I420; + break; + case GST_VIDEO_FORMAT_RGB: + src->capture_config.encoding = MMAL_ENCODING_RGB24; + break; + case GST_VIDEO_FORMAT_BGR: + src->capture_config.encoding = MMAL_ENCODING_BGR24; + break; + case GST_VIDEO_FORMAT_RGBA: + src->capture_config.encoding = MMAL_ENCODING_RGBA; + break; + default: + return FALSE; + } } src->capture_config.width = info.width; From 38ef8c2411fec2c8319d36a76b58b9c716f0b2a9 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 3 Oct 2016 14:00:54 +0000 Subject: [PATCH 55/77] rpicamsrc: Don't try and set H264 params with JPEG codec --- sys/rpicamsrc/RaspiCapture.c | 88 ++++++++++++++++++------------------ sys/rpicamsrc/gstrpicamsrc.c | 24 +++++++--- 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index ad4798f7f2..b3608ca78e 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1428,7 +1428,6 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) vcos_log_error("Unable to set max QP"); goto error; } - } if (config->encoding == MMAL_ENCODING_H264) @@ -1448,57 +1447,60 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } } - - if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS) + if (config->encoding != MMAL_ENCODING_JPEG) { - vcos_log_error("Unable to set immutable input flag"); - // Continue rather than abort.. - } + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS) + { + vcos_log_error("Unable to set immutable input flag"); + // Continue rather than abort.. + } - //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested - if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS) - { - vcos_log_error("failed to set INLINE HEADER FLAG parameters"); - // Continue rather than abort.. - } + //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE HEADER FLAG parameters"); + // Continue rather than abort.. + } + } - //set INLINE VECTORS flag to request motion vector estimates - if (config->encoding == MMAL_ENCODING_H264 && - mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) - { - vcos_log_error("failed to set INLINE VECTORS parameters"); - // Continue rather than abort.. - } + if (config->encoding == MMAL_ENCODING_H264) + { + //set INLINE VECTORS flag to request motion vector estimates + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE VECTORS parameters"); + // Continue rather than abort.. + } - // Adaptive intra refresh settings - if (config->encoding == MMAL_ENCODING_H264 && - config->intra_refresh_type != -1) - { - MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + // Adaptive intra refresh settings + if (config->intra_refresh_type != -1) + { + MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; - /* Need to memset, apparently mmal_port_parameter_get() - * doesn't retrieve all parameters, causing random failures - * when we set it - */ - memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T)); + /* Need to memset, apparently mmal_port_parameter_get() + * doesn't retrieve all parameters, causing random failures + * when we set it + */ + memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T)); - param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; - param.hdr.size = sizeof(param); + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; + param.hdr.size = sizeof(param); - // Get first so we don't overwrite anything unexpectedly - status = mmal_port_parameter_get(encoder_output, ¶m.hdr); + // Get first so we don't overwrite anything unexpectedly + status = mmal_port_parameter_get(encoder_output, ¶m.hdr); - param.refresh_mode = config->intra_refresh_type; + param.refresh_mode = config->intra_refresh_type; - //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS) - // param.cir_mbs = 10; + //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS) + // param.cir_mbs = 10; - status = mmal_port_parameter_set(encoder_output, ¶m.hdr); - if (status != MMAL_SUCCESS) - { + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { vcos_log_error("Unable to set H264 intra-refresh values"); - goto error; - } + goto error; + } + } } if (config->encoding == MMAL_ENCODING_JPEG) @@ -1506,14 +1508,14 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, config->jpegQuality); if (status != MMAL_SUCCESS) { vcos_log_error("Unable to set JPEG quality"); - goto error; + // Continue after warning } #ifdef MMAL_PARAMETER_JPEG_RESTART_INTERVAL status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, config->jpegRestartInterval); if (status != MMAL_SUCCESS) { vcos_log_error("Unable to set JPEG restart interval"); - goto error; + // Continue after warning } #endif } diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 3bf9ab6fcc..542c3bb246 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -250,10 +250,10 @@ gst_rpi_cam_src_sensor_mode_get_type (void) {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_1920x1080), "1920x1080 16:9 1-30fps", "1920x1080"}, {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_FAST), - "2592x1944 4:3 1-15fps", + "2592x1944 4:3 1-15fps / 3240x2464 15fps w/ v.2 board", "2592x1944-fast"}, {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_SLOW), - "2592x1944 4:3 0.1666-1fps", "2592x1944-slow"}, + "2592x1944 4:3 0.1666-1fps / 3240x2464 15fps w/ v.2 board", "2592x1944-slow"}, {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_1296x972), "1296x972 4:3 1-42fps", "1296x972"}, {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_1296x730), "1296x730 16:9 1-49fps", @@ -1254,14 +1254,26 @@ gst_rpi_cam_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) { GstRpiCamSrc *src = GST_RPICAMSRC (bsrc); GstCaps *caps; + gint i; + caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc)); if (src->capture_state == NULL) goto done; /* FIXME: Retrieve limiting parameters from the camera module, max width/height fps-range */ caps = gst_caps_make_writable (caps); - gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 1920, "height", - GST_TYPE_INT_RANGE, 1, 1080, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, - 90, 1, NULL); + for (i = 0; i < gst_caps_get_size (caps); i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + if (gst_structure_has_name (s, "video/x-h264")) { + gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 1920, "height", + GST_TYPE_INT_RANGE, 1, 1080, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, + 90, 1, NULL); + } + else { + gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 3240, "height", + GST_TYPE_INT_RANGE, 1, 2464, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, + 90, 1, NULL); + } + } done: GST_DEBUG_OBJECT (src, "get_caps returning %" GST_PTR_FORMAT, caps); return caps; @@ -1295,7 +1307,7 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) } } else if (gst_structure_has_name (structure, "image/jpeg")) { - src->capture_config.encoding = MMAL_ENCODING_MJPEG; + src->capture_config.encoding = MMAL_ENCODING_JPEG; } else { /* Raw caps */ From f42afec4712501b25294cbcd2a8768a89db66d51 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 3 Oct 2016 15:29:49 +0000 Subject: [PATCH 56/77] rpicamsrc: Switch back to MJPEG codec for image/jpeg The JPEG codec hangs, not sure why yet. The MJPEG codec doesn't provide a quality setting, and sometimes freezes on shutdown, but otherwise seems more reliable --- sys/rpicamsrc/gstrpicamsrc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 542c3bb246..a9cf7cd89d 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -78,6 +78,9 @@ GST_DEBUG_CATEGORY (gst_rpi_cam_src_debug); +/* comment out to use JPEG codec instead of MJPEG */ +// #define USE_JPEG_CODEC + /* Filter signals and args */ enum { @@ -299,10 +302,12 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) "Bitrate for encoding. 0 for VBR using quantisation-parameter", 0, BITRATE_HIGHEST, BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#ifdef USE_JPEG_CODEC g_object_class_install_property (gobject_class, PROP_JPEG_QUALITY, g_param_spec_int ("jpeg-quality", "JPEG Quality", "Quality setting for JPEG encode", 1, 100, DEFAULT_JPEG_QUALITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif g_object_class_install_property (gobject_class, PROP_KEYFRAME_INTERVAL, g_param_spec_int ("keyframe-interval", "Keyframe Interface", "Interval (in frames) between I frames. -1 = automatic, 0 = single-keyframe", @@ -1307,7 +1312,11 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) } } else if (gst_structure_has_name (structure, "image/jpeg")) { +#ifdef USE_JPEG_CODEC src->capture_config.encoding = MMAL_ENCODING_JPEG; +#else + src->capture_config.encoding = MMAL_ENCODING_MJPEG; +#endif } else { /* Raw caps */ From dd9d7341b0f8120d9bb3862d195a95b5b0834ae2 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 8 Oct 2016 11:10:30 +0000 Subject: [PATCH 57/77] rpicamsrc: Destroy mmal pool on shutdown always. Avoid hangs on the next run because we didn't clean up the mmal pool last time we shutdown. --- sys/rpicamsrc/RaspiCapture.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index b3608ca78e..8065e7044d 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1561,13 +1561,14 @@ static void destroy_encoder_component(RASPIVID_STATE *state) } } + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_output_port, state->encoder_pool); + state->encoder_pool = NULL; + } + if (state->encoder_component) { - // Get rid of any port buffers first - if (state->encoder_pool) - { - mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); - state->encoder_pool = NULL; - } mmal_component_destroy(state->encoder_component); state->encoder_component = NULL; From 16707125004d50761d4afa41a513b3d0b2ef83f2 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 8 Oct 2016 11:12:09 +0000 Subject: [PATCH 58/77] rpicamsrc: Set outgoing buffer durations based on negotiated framerate. make sure outgoing buffers have at least some duration set, otherwise it leads to strange situations, like qtmux writing out a file that doesn't include the final frame inside the playable segment, because no-duration = 0 duration there. --- sys/rpicamsrc/gstrpicamsrc.c | 11 ++++++++++- sys/rpicamsrc/gstrpicamsrc.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index a9cf7cd89d..8712c21c24 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1342,6 +1342,13 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) src->capture_config.height = info.height; src->capture_config.fps_n = info.fps_n; src->capture_config.fps_d = info.fps_d; + + if (info.fps_n != 0 && info.fps_d != 0) + src->duration = gst_util_uint64_scale_int (GST_SECOND, info.fps_d, + info.fps_n); + else + src->duration = GST_CLOCK_TIME_NONE; + return TRUE; } @@ -1407,9 +1414,11 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) /* FIXME: Use custom allocator */ ret = raspi_capture_fill_buffer (src->capture_state, buf, clock, base_time); - if (*buf) + if (*buf) { GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (*buf)); + GST_BUFFER_DURATION (*buf) = src->duration; + } if (clock) gst_object_unref (clock); diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h index 6675f88049..68fdc1829b 100644 --- a/sys/rpicamsrc/gstrpicamsrc.h +++ b/sys/rpicamsrc/gstrpicamsrc.h @@ -86,6 +86,8 @@ struct _GstRpiCamSrc #ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION GstVideoOrientationMethod orientation; #endif + + GstClockTime duration; }; struct _GstRpiCamSrcClass From 2c458819f510a4c84fd871d892f503f7371413f6 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 27 Jan 2017 12:58:29 +1100 Subject: [PATCH 59/77] rpicamsrc: Implement dynamic bitrate update Use mmal_port_set_parameter_uint32 to update the encoder bitrate. Fixes https://github.com/thaytan/gst-rpicamsrc/issues/60 --- sys/rpicamsrc/RaspiCapture.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 8065e7044d..c50ef9957e 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1908,13 +1908,9 @@ raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gbo MMAL_COMPONENT_T *encoder = state->encoder_component; MMAL_PORT_T *encoder_output = encoder->output[0]; -#if 0 /* not dynamically change-able */ - encoder_output->format->bitrate = config->bitrate; - status = mmal_port_format_commit(encoder_output); - if (status != MMAL_SUCCESS) { - vcos_log_warn("Cannot change bitrate dynamically"); - } -#endif + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_VIDEO_BIT_RATE, config->bitrate); + if (status != MMAL_SUCCESS) + vcos_log_warn("Unable to change bitrate dynamically"); { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod}; From f093e3f5fb168cde45be71d8646ed7ac042a1389 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 19 May 2017 20:55:35 +1000 Subject: [PATCH 60/77] rpicamsrc: Fix the descriptions of text annotation colour properties The text annotation colour properties take an integer value corresponding to a VUY colour, not a text string like the copy-pasted description from raspivid suggests. Fixes https://github.com/thaytan/gst-rpicamsrc/issues/59 --- sys/rpicamsrc/gstrpicamsrc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 8712c21c24..f2a993c0bf 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -478,11 +478,11 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT_COLOUR, g_param_spec_int ("annotation-text-colour", "Annotation text colour (VUY)", - "Set the annotation text colour, as a VUY hex value eg #8080FF, -1 for default", -1, + "Set the annotation text colour, as the integer corresponding to a VUY value eg 0x8080FF = 8421631, -1 for default", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT_BG_COLOUR, g_param_spec_int ("annotation-text-bg-colour", "Annotation text background colour (VUY)", - "Set the annotation text background colour, as a VUY hex value eg #8080FF, -1 for default", -1, + "Set the annotation text background colour, as the integer corresponding to a VUY value eg 0x8080FF = 8421631, -1 for default", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); #ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION g_object_class_override_property (gobject_class, PROP_VIDEO_DIRECTION, From ed893592eaa5493fa3b3b92e7255b209da1672ab Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 1 Jul 2017 00:51:13 +1000 Subject: [PATCH 61/77] rpicamsrc: Implement use-stc property to disable STC timestamps If use-stc=false, then rpicamsrc won't apply the camera timestamping to outgoing buffers, instead relying on real-time timestamping by the GStreamer clock. It means slightly less accuracy and more jitter in timestamps, but might help on some CSI inputs with broken timestamping. --- sys/rpicamsrc/RaspiCapture.c | 12 ++++++++---- sys/rpicamsrc/RaspiCapture.h | 2 ++ sys/rpicamsrc/gstrpicamsrc.c | 27 ++++++++++++++++++++++----- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index c50ef9957e..d0616c5f82 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -929,6 +929,7 @@ GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, GstClock *clock, GstClockTime base_time) { + RASPIVID_CONFIG *config = &state->config; GstBuffer *buf; MMAL_BUFFER_HEADER_T *buffer; GstFlowReturn ret = GST_FLOW_ERROR; @@ -939,7 +940,7 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, buffer = mmal_queue_wait(state->encoded_buffer_q); - if (G_LIKELY (clock)) { + if (G_LIKELY (config->useSTC && clock)) { MMAL_PARAMETER_INT64_T param; GstClockTime runtime; @@ -951,7 +952,7 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr); - if (param.value != -1 && param.value >= buffer->pts) { + if (buffer->pts != -1 && param.value != -1 && param.value >= buffer->pts) { /* Convert microsecond RPi TS to GStreamer clock: */ GstClockTime offset = (param.value - buffer->pts) * 1000; if (runtime >= offset) @@ -963,13 +964,16 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, buffer->pts, buffer->dts, param.value, param.value - buffer->pts, GST_TIME_ARGS (gst_pts)); } - + else { + GST_LOG ("use-stc=false. Not applying STC to buffer"); + } mmal_buffer_header_mem_lock(buffer); buf = gst_buffer_new_allocate(NULL, buffer->length, NULL); if (buf) { + if (config->useSTC) + GST_BUFFER_DTS(buf) = GST_BUFFER_PTS(buf) = gst_pts; /* FIXME: Can we avoid copies and give MMAL our own buffers to fill? */ - GST_BUFFER_PTS(buf) = gst_pts; gst_buffer_fill(buf, 0, buffer->data, buffer->length); ret = GST_FLOW_OK; } diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 6a79ac5240..053ae1c03a 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -116,6 +116,8 @@ typedef struct int jpegQuality; int jpegRestartInterval; + + int useSTC; } RASPIVID_CONFIG; typedef struct RASPIVID_STATE_T RASPIVID_STATE; diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index f2a993c0bf..7cd369e80c 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -138,7 +138,8 @@ enum #ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION PROP_VIDEO_DIRECTION, #endif - PROP_JPEG_QUALITY + PROP_JPEG_QUALITY, + PROP_USE_STC }; #define CAMERA_DEFAULT 0 @@ -488,6 +489,10 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_object_class_override_property (gobject_class, PROP_VIDEO_DIRECTION, "video-direction"); #endif + g_object_class_install_property (gobject_class, PROP_USE_STC, + g_param_spec_boolean ("use-stc", "Use System Time Clock", + "Use the camera STC for timestamping buffers", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_static_metadata (gstelement_class, "Raspberry Pi Camera Source", "Source/Video", @@ -517,12 +522,12 @@ gst_rpi_cam_src_init (GstRpiCamSrc * src) raspicapture_default_config (&src->capture_config); src->capture_config.intraperiod = KEYFRAME_INTERVAL_DEFAULT; src->capture_config.verbose = 1; + src->capture_config.useSTC = TRUE; g_mutex_init (&src->config_lock); - /* Don't let basesrc set timestamps, we'll do it using - * buffer PTS and system times */ - gst_base_src_set_do_timestamp (GST_BASE_SRC (src), FALSE); + /* basesrc will generate timestamps if use-stc = false */ + gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE); /* Generate the channels list */ channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL); @@ -1006,6 +1011,9 @@ gst_rpi_cam_src_set_property (GObject * object, guint prop_id, gst_rpi_cam_src_set_orientation (src, g_value_get_enum (value)); break; #endif + case PROP_USE_STC: + src->capture_config.useSTC = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1170,6 +1178,9 @@ gst_rpi_cam_src_get_property (GObject * object, guint prop_id, g_value_set_enum (value, src->orientation); break; #endif + case PROP_USE_STC: + g_value_set_boolean (value, src->capture_config.useSTC); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1182,6 +1193,9 @@ gst_rpi_cam_src_start (GstBaseSrc * parent) { GstRpiCamSrc *src = GST_RPICAMSRC (parent); GST_LOG_OBJECT (src, "In src_start()"); + /* Ensure basesrc timestamping is off is use-stc is on */ + if (src->capture_config.useSTC) + gst_base_src_set_do_timestamp (GST_BASE_SRC (src), FALSE); g_mutex_lock (&src->config_lock); src->capture_state = raspi_capture_setup (&src->capture_config); /* Clear all capture flags */ @@ -1417,7 +1431,10 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) if (*buf) { GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (*buf)); - GST_BUFFER_DURATION (*buf) = src->duration; + /* Only set the duration when we have a PTS update from the rpi encoder. + * not every buffer is a frame */ + if (GST_BUFFER_PTS_IS_VALID (*buf)) + GST_BUFFER_DURATION (*buf) = src->duration; } if (clock) From 149fdee7fb243e50f02f1c444c921d920a088724 Mon Sep 17 00:00:00 2001 From: Georgii Staroselskii Date: Tue, 7 Nov 2017 15:14:06 +0000 Subject: [PATCH 62/77] rpicamsrc: RaspiCapture: handle MMAL_EVENT_ERROR --- sys/rpicamsrc/RaspiCapture.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index d0616c5f82..cdc6ce21aa 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -752,8 +752,9 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf break; } } - else - { + else if (buffer->cmd == MMAL_EVENT_ERROR) { + vcos_log_error("Camera control callback got an error"); + } else { vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); } From b45e23912810617d3f755d640acf475ab7f254d4 Mon Sep 17 00:00:00 2001 From: Georgii Staroselskii Date: Wed, 8 Nov 2017 09:14:35 +0000 Subject: [PATCH 63/77] rpicamsrc: RaspiCapture: use mmal_queue_timedwait() for buffer queueing If an external camera was disconnected, there were no feedback in an application. It seems reasonable to wait on mmal_queue no longer than 100ms. If it's stuck we just return a FLOW_ERROR and let the application decide what to do later. --- sys/rpicamsrc/RaspiCapture.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index cdc6ce21aa..1d899cf091 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -938,8 +938,12 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, GstClockTime gst_pts = GST_CLOCK_TIME_NONE; /* FIXME: Use our own interruptible cond wait: */ - buffer = mmal_queue_wait(state->encoded_buffer_q); + buffer = mmal_queue_timedwait(state->encoded_buffer_q, 100); + + if (G_UNLIKELY(buffer == NULL)) { + return GST_FLOW_ERROR; + } if (G_LIKELY (config->useSTC && clock)) { MMAL_PARAMETER_INT64_T param; From cab6585378ca0ade3729f5dc1b1cffbd31cd817d Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 14 Nov 2017 15:01:21 +1100 Subject: [PATCH 64/77] rpicamsrc: Expand frame timeout from 100ms to 500ms rpicamsrc on a normal rpi camera doesn't start up fast enough, and always fails the new 100ms timeout. A better solution might be to have a longer timeout for the first frame, but shorter once frames are running - but this quick fix will at least make rpicamsrc work again. --- sys/rpicamsrc/RaspiCapture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 1d899cf091..cae5cf61ce 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -939,7 +939,7 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, /* FIXME: Use our own interruptible cond wait: */ - buffer = mmal_queue_timedwait(state->encoded_buffer_q, 100); + buffer = mmal_queue_timedwait(state->encoded_buffer_q, 500); if (G_UNLIKELY(buffer == NULL)) { return GST_FLOW_ERROR; From 93bd8c34edd7d9e7f6314ca2ae7e0a1bc1040505 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 28 Mar 2018 22:00:10 +1100 Subject: [PATCH 65/77] rpicamsrc: Add define and increase reported maximum FPS from 90 to 1000 --- sys/rpicamsrc/RaspiCapture.h | 2 ++ sys/rpicamsrc/gstrpicamsrc.c | 4 ++-- sys/rpicamsrc/gstrpicamsrcdeviceprovider.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 053ae1c03a..571d3b7fc1 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -54,6 +54,8 @@ #include "RaspiCamControl.h" #include "RaspiPreview.h" +#define RPICAMSRC_MAX_FPS 1000 + GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug); #define GST_CAT_DEFAULT gst_rpi_cam_src_debug diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 7cd369e80c..5b2532d609 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1285,12 +1285,12 @@ gst_rpi_cam_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) if (gst_structure_has_name (s, "video/x-h264")) { gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 1920, "height", GST_TYPE_INT_RANGE, 1, 1080, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, - 90, 1, NULL); + RPICAMSRC_MAX_FPS, 1, NULL); } else { gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, 3240, "height", GST_TYPE_INT_RANGE, 1, 2464, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, - 90, 1, NULL); + RPICAMSRC_MAX_FPS, 1, NULL); } } done: diff --git a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c index 387ee61c27..ff80832275 100644 --- a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c +++ b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c @@ -120,7 +120,7 @@ gst_rpi_cam_src_device_new (void) s = gst_structure_new ("video/x-h264", "width", GST_TYPE_INT_RANGE, 1, 1920, "height", GST_TYPE_INT_RANGE, 1, 1080, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 90, 1, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, RPICAMSRC_MAX_FPS, 1, "stream-format", G_TYPE_STRING, "byte-stream", "alignment", G_TYPE_STRING, "au", NULL); From d5cd0c030183b64579c4e563e25ab34346279dce Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 12 May 2018 19:57:43 +0000 Subject: [PATCH 66/77] rpicamsrc: Expose constrained-baseline profile constrained-baseline is a useful profile for streaming to iOS devices, and seems to work in the firmware, so let's publish it --- sys/rpicamsrc/gstrpicamsrc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index 5b2532d609..da8446023a 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -189,7 +189,7 @@ enum "framerate = " GST_VIDEO_FPS_RANGE ", " \ "stream-format = (string) byte-stream, " \ "alignment = (string) nal, " \ - "profile = (string) { baseline, main, high }" + "profile = (string) { constrained-baseline, baseline, main, high }" #define RAW_CAPS \ GST_VIDEO_CAPS_MAKE ("{ I420, RGB, BGR, RGBA }") /* FIXME: Map more raw formats */ @@ -1317,6 +1317,8 @@ gst_rpi_cam_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps) if (profile_str) { if (g_str_equal (profile_str, "baseline")) src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; + else if (g_str_equal (profile_str, "constrained-baseline")) + src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE; else if (g_str_equal (profile_str, "main")) src->capture_config.profile = MMAL_VIDEO_PROFILE_H264_MAIN; else if (g_str_equal (profile_str, "high")) From 39a026760d26050f2f9060c0fc083241b932d9c4 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 12 May 2018 21:13:52 +0000 Subject: [PATCH 67/77] rpicamsrc: Add webrtc streaming example Add an example for testing webrtc streaming from the rpi camera, based on the code from https://bugzilla.gnome.org/show_bug.cgi?id=795404 Requires GStreamer 1.14.1 or git master --- .../rpicamsrc/webrtc-unidirectional-h264.c | 663 ++++++++++++++++++ 1 file changed, 663 insertions(+) create mode 100644 tests/examples/rpicamsrc/webrtc-unidirectional-h264.c diff --git a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c new file mode 100644 index 0000000000..290e46da30 --- /dev/null +++ b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c @@ -0,0 +1,663 @@ +#include +#include +#include +#include + +#define GST_USE_UNSTABLE_API +#include + +#include +#include +#include + + + +#define RTP_PAYLOAD_TYPE "96" +#define SOUP_HTTP_PORT 57778 + + + +typedef struct _ReceiverEntry ReceiverEntry; + +ReceiverEntry *create_receiver_entry (SoupWebsocketConnection * connection); +void destroy_receiver_entry (gpointer receiver_entry_ptr); + +GstPadProbeReturn payloader_caps_event_probe_cb (GstPad * pad, + GstPadProbeInfo * info, gpointer user_data); + +void on_offer_created_cb (GstPromise * promise, gpointer user_data); +void on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data); +void on_ice_candidate_cb (GstElement * webrtcbin, guint mline_index, + gchar * candidate, gpointer user_data); + +void soup_websocket_message_cb (SoupWebsocketConnection * connection, + SoupWebsocketDataType data_type, GBytes * message, gpointer user_data); +void soup_websocket_closed_cb (SoupWebsocketConnection * connection, + gpointer user_data); + +void soup_http_handler (SoupServer * soup_server, SoupMessage * message, + const char *path, GHashTable * query, SoupClientContext * client_context, + gpointer user_data); +void soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, + SoupWebsocketConnection * connection, const char *path, + SoupClientContext * client_context, gpointer user_data); + +static gchar *get_string_from_json_object (JsonObject * object); + +gboolean exit_sighandler (gpointer user_data); + + + + +struct _ReceiverEntry +{ + SoupWebsocketConnection *connection; + + GstElement *pipeline; + GstElement *webrtcbin; + GstElement *payloader; + + GCond profile_level_id_cond; + GMutex profile_level_id_mutex; + gchar *profile_level_id; + + gboolean shutting_down; +}; + + + +const gchar *html_source = " \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ +
\n \ + \n \ +
\n \ + \n \ + \n \ +"; + + + + +ReceiverEntry * +create_receiver_entry (SoupWebsocketConnection * connection) +{ + GError *error; + ReceiverEntry *receiver_entry; + GstPad *payloader_srcpad; + + receiver_entry = g_slice_alloc0 (sizeof (ReceiverEntry)); + receiver_entry->connection = connection; + + g_cond_init (&receiver_entry->profile_level_id_cond); + g_mutex_init (&receiver_entry->profile_level_id_mutex); + + g_object_ref (G_OBJECT (connection)); + + g_signal_connect (G_OBJECT (connection), "message", + G_CALLBACK (soup_websocket_message_cb), (gpointer) receiver_entry); + + error = NULL; + receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin " + "rpicamsrc bitrate=300000 annotation-mode=12 ! video/x-h264,profile=baseline,width=640,height=480 ! queue max-size-time=100000000 ! h264parse ! " + "rtph264pay config-interval=-1 name=payloader ! " + "application/x-rtp,media=video,encoding-name=H264,payload=" + RTP_PAYLOAD_TYPE " ! webrtcbin. ", &error); + if (error != NULL) { + g_error ("Could not create WebRTC pipeline: %s\n", error->message); + g_error_free (error); + goto cleanup; + } + + receiver_entry->webrtcbin = + gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "webrtcbin"); + receiver_entry->payloader = + gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "payloader"); + g_assert (receiver_entry->webrtcbin != NULL); + g_assert (receiver_entry->payloader != NULL); + + payloader_srcpad = + gst_element_get_static_pad (receiver_entry->payloader, "src"); + gst_pad_add_probe (payloader_srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + payloader_caps_event_probe_cb, (gpointer) receiver_entry, NULL); + gst_object_unref (GST_OBJECT (payloader_srcpad)); + + g_signal_connect (receiver_entry->webrtcbin, "on-negotiation-needed", + G_CALLBACK (on_negotiation_needed_cb), (gpointer) receiver_entry); + + g_signal_connect (receiver_entry->webrtcbin, "on-ice-candidate", + G_CALLBACK (on_ice_candidate_cb), (gpointer) receiver_entry); + + gst_element_set_state (receiver_entry->pipeline, GST_STATE_PLAYING); + + return receiver_entry; + +cleanup: + destroy_receiver_entry ((gpointer) receiver_entry); + return NULL; +} + +void +destroy_receiver_entry (gpointer receiver_entry_ptr) +{ + ReceiverEntry *receiver_entry = (ReceiverEntry *) receiver_entry_ptr; + + g_assert (receiver_entry != NULL); + + g_mutex_lock (&receiver_entry->profile_level_id_mutex); + receiver_entry->shutting_down = TRUE; + g_cond_signal (&receiver_entry->profile_level_id_cond); + g_mutex_unlock (&receiver_entry->profile_level_id_mutex); + + if (receiver_entry->pipeline != NULL) { + gst_element_set_state (GST_ELEMENT (receiver_entry->pipeline), + GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (receiver_entry->webrtcbin)); + gst_object_unref (GST_OBJECT (receiver_entry->payloader)); + gst_object_unref (GST_OBJECT (receiver_entry->pipeline)); + } + + g_cond_clear (&receiver_entry->profile_level_id_cond); + g_mutex_clear (&receiver_entry->profile_level_id_mutex); + g_free (receiver_entry->profile_level_id); + + if (receiver_entry->connection != NULL) + g_object_unref (G_OBJECT (receiver_entry->connection)); + + g_slice_free1 (sizeof (ReceiverEntry), receiver_entry); +} + + +GstPadProbeReturn +payloader_caps_event_probe_cb (G_GNUC_UNUSED GstPad * pad, + GstPadProbeInfo * info, gpointer user_data) +{ + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); + + if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) { + gchar const *profile_level_id; + GstStructure *s; + GstCaps *caps; + + caps = NULL; + gst_event_parse_caps (event, &caps); + + s = gst_caps_get_structure (caps, 0); + profile_level_id = gst_structure_get_string (s, "profile-level-id"); + g_assert (profile_level_id != NULL); + + g_mutex_lock (&receiver_entry->profile_level_id_mutex); + + g_free (receiver_entry->profile_level_id); + receiver_entry->profile_level_id = g_strdup (profile_level_id); + + g_cond_signal (&receiver_entry->profile_level_id_cond); + + g_mutex_unlock (&receiver_entry->profile_level_id_mutex); + } + + return GST_PAD_PROBE_OK; +} + + +void +on_offer_created_cb (GstPromise * promise, gpointer user_data) +{ + gchar *fmtp_value; + gchar *sdp_string; + gchar *json_string; + JsonObject *sdp_json; + JsonObject *sdp_data_json; + GstSDPMedia *sdp_media; + GstStructure const *reply; + GstPromise *local_desc_promise; + GstWebRTCSessionDescription *offer = NULL; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + reply = gst_promise_get_reply (promise); + gst_structure_get (reply, "offer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + &offer, NULL); + gst_promise_unref (promise); + + fmtp_value = g_strdup_printf (RTP_PAYLOAD_TYPE " profile-level-id=%s", + receiver_entry->profile_level_id); + sdp_media = + (GstSDPMedia *) & g_array_index (offer->sdp->medias, GstSDPMedia, 0); + gst_sdp_media_add_attribute (sdp_media, "fmtp", fmtp_value); + g_free (fmtp_value); + + local_desc_promise = gst_promise_new (); + g_signal_emit_by_name (receiver_entry->webrtcbin, "set-local-description", + offer, local_desc_promise); + gst_promise_interrupt (local_desc_promise); + gst_promise_unref (local_desc_promise); + + sdp_string = gst_sdp_message_as_text (offer->sdp); + g_print ("Negotiation offer created:\n%s\n", sdp_string); + + sdp_json = json_object_new (); + json_object_set_string_member (sdp_json, "type", "sdp"); + + sdp_data_json = json_object_new (); + json_object_set_string_member (sdp_data_json, "type", "offer"); + json_object_set_string_member (sdp_data_json, "sdp", sdp_string); + json_object_set_object_member (sdp_json, "data", sdp_data_json); + + json_string = get_string_from_json_object (sdp_json); + json_object_unref (sdp_json); + + soup_websocket_connection_send_text (receiver_entry->connection, json_string); + g_free (json_string); + + gst_webrtc_session_description_free (offer); +} + + +void +on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data) +{ + gboolean exit_early; + GstPromise *promise; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + g_mutex_lock (&receiver_entry->profile_level_id_mutex); + + while ((receiver_entry->profile_level_id == NULL) + && !receiver_entry->shutting_down) + g_cond_wait (&receiver_entry->profile_level_id_cond, + &receiver_entry->profile_level_id_mutex); + + exit_early = receiver_entry->shutting_down; + + g_mutex_unlock (&receiver_entry->profile_level_id_mutex); + + if (exit_early) + return; + + g_print ("Creating negotiation offer\n"); + + promise = gst_promise_new_with_change_func (on_offer_created_cb, + (gpointer) receiver_entry, NULL); + g_signal_emit_by_name (G_OBJECT (webrtcbin), "create-offer", NULL, promise); +} + + +void +on_ice_candidate_cb (G_GNUC_UNUSED GstElement * webrtcbin, guint mline_index, + gchar * candidate, gpointer user_data) +{ + JsonObject *ice_json; + JsonObject *ice_data_json; + gchar *json_string; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + ice_json = json_object_new (); + json_object_set_string_member (ice_json, "type", "ice"); + + ice_data_json = json_object_new (); + json_object_set_int_member (ice_data_json, "sdpMLineIndex", mline_index); + json_object_set_string_member (ice_data_json, "candidate", candidate); + json_object_set_object_member (ice_json, "data", ice_data_json); + + json_string = get_string_from_json_object (ice_json); + json_object_unref (ice_json); + + soup_websocket_connection_send_text (receiver_entry->connection, json_string); + g_free (json_string); +} + + +void +soup_websocket_message_cb (G_GNUC_UNUSED SoupWebsocketConnection * connection, + SoupWebsocketDataType data_type, GBytes * message, gpointer user_data) +{ + gsize size; + gchar *data; + gchar *data_string; + const gchar *type_string; + JsonNode *root_json; + JsonObject *root_json_object; + JsonObject *data_json_object; + JsonParser *json_parser = NULL; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + switch (data_type) { + case SOUP_WEBSOCKET_DATA_BINARY: + g_error ("Received unknown binary message, ignoring\n"); + g_bytes_unref (message); + return; + + case SOUP_WEBSOCKET_DATA_TEXT: + data = g_bytes_unref_to_data (message, &size); + /* Convert to NULL-terminated string */ + data_string = g_strndup (data, size); + g_free (data); + break; + + default: + g_assert_not_reached (); + } + + json_parser = json_parser_new (); + if (!json_parser_load_from_data (json_parser, data_string, -1, NULL)) + goto unknown_message; + + root_json = json_parser_get_root (json_parser); + if (!JSON_NODE_HOLDS_OBJECT (root_json)) + goto unknown_message; + + root_json_object = json_node_get_object (root_json); + + if (!json_object_has_member (root_json_object, "type")) { + g_error ("Received message without type field\n"); + goto cleanup; + } + type_string = json_object_get_string_member (root_json_object, "type"); + + if (!json_object_has_member (root_json_object, "data")) { + g_error ("Received message without data field\n"); + goto cleanup; + } + data_json_object = json_object_get_object_member (root_json_object, "data"); + + if (g_strcmp0 (type_string, "sdp") == 0) { + const gchar *sdp_type_string; + const gchar *sdp_string; + GstPromise *promise; + GstSDPMessage *sdp; + GstWebRTCSessionDescription *answer; + int ret; + + if (!json_object_has_member (data_json_object, "type")) { + g_error ("Received SDP message without type field\n"); + goto cleanup; + } + sdp_type_string = json_object_get_string_member (data_json_object, "type"); + + if (g_strcmp0 (sdp_type_string, "answer") != 0) { + g_error ("Expected SDP message type \"answer\", got \"%s\"\n", + sdp_type_string); + goto cleanup; + } + + if (!json_object_has_member (data_json_object, "sdp")) { + g_error ("Received SDP message without SDP string\n"); + goto cleanup; + } + sdp_string = json_object_get_string_member (data_json_object, "sdp"); + + g_print ("Received SDP:\n%s\n", sdp_string); + + ret = gst_sdp_message_new (&sdp); + g_assert_cmphex (ret, ==, GST_SDP_OK); + + ret = + gst_sdp_message_parse_buffer ((guint8 *) sdp_string, + strlen (sdp_string), sdp); + if (ret != GST_SDP_OK) { + g_error ("Could not parse SDP string\n"); + goto cleanup; + } + + answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, + sdp); + g_assert_nonnull (answer); + + promise = gst_promise_new (); + g_signal_emit_by_name (receiver_entry->webrtcbin, "set-remote-description", + answer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + } else if (g_strcmp0 (type_string, "ice") == 0) { + guint mline_index; + const gchar *candidate_string; + + if (!json_object_has_member (data_json_object, "sdpMLineIndex")) { + g_error ("Received ICE message without mline index\n"); + goto cleanup; + } + mline_index = + json_object_get_int_member (data_json_object, "sdpMLineIndex"); + + if (!json_object_has_member (data_json_object, "candidate")) { + g_error ("Received ICE message without ICE candidate string\n"); + goto cleanup; + } + candidate_string = json_object_get_string_member (data_json_object, + "candidate"); + + g_print ("Received ICE candidate with mline index %u; candidate: %s\n", + mline_index, candidate_string); + + g_signal_emit_by_name (receiver_entry->webrtcbin, "add-ice-candidate", + mline_index, candidate_string); + } else + goto unknown_message; + +cleanup: + if (json_parser != NULL) + g_object_unref (G_OBJECT (json_parser)); + g_free (data_string); + return; + +unknown_message: + g_error ("Unknown message \"%s\", ignoring", data_string); + goto cleanup; +} + + +void +soup_websocket_closed_cb (SoupWebsocketConnection * connection, + gpointer user_data) +{ + GHashTable *receiver_entry_table = (GHashTable *) user_data; + g_hash_table_remove (receiver_entry_table, connection); + g_print ("Closed websocket connection %p\n", (gpointer) connection); +} + + +void +soup_http_handler (G_GNUC_UNUSED SoupServer * soup_server, + SoupMessage * message, const char *path, G_GNUC_UNUSED GHashTable * query, + G_GNUC_UNUSED SoupClientContext * client_context, + G_GNUC_UNUSED gpointer user_data) +{ + SoupBuffer *soup_buffer; + + if ((g_strcmp0 (path, "/") != 0) && (g_strcmp0 (path, "/index.html") != 0)) { + soup_message_set_status (message, SOUP_STATUS_NOT_FOUND); + return; + } + + soup_buffer = + soup_buffer_new (SOUP_MEMORY_STATIC, html_source, strlen (html_source)); + + soup_message_headers_set_content_type (message->response_headers, "text/html", + NULL); + soup_message_body_append_buffer (message->response_body, soup_buffer); + soup_buffer_free (soup_buffer); + + soup_message_set_status (message, SOUP_STATUS_OK); +} + + +void +soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, + SoupWebsocketConnection * connection, G_GNUC_UNUSED const char *path, + G_GNUC_UNUSED SoupClientContext * client_context, gpointer user_data) +{ + ReceiverEntry *receiver_entry; + GHashTable *receiver_entry_table = (GHashTable *) user_data; + + g_print ("Processing new websocket connection %p", (gpointer) connection); + + g_signal_connect (G_OBJECT (connection), "closed", + G_CALLBACK (soup_websocket_closed_cb), (gpointer) receiver_entry_table); + + receiver_entry = create_receiver_entry (connection); + g_hash_table_replace (receiver_entry_table, connection, receiver_entry); +} + + +static gchar * +get_string_from_json_object (JsonObject * object) +{ + JsonNode *root; + JsonGenerator *generator; + gchar *text; + + /* Make it the root node */ + root = json_node_init_object (json_node_alloc (), object); + generator = json_generator_new (); + json_generator_set_root (generator, root); + text = json_generator_to_data (generator, NULL); + + /* Release everything */ + g_object_unref (generator); + json_node_free (root); + return text; +} + + +gboolean +exit_sighandler (gpointer user_data) +{ + g_print ("Caught signal, stopping mainloop\n"); + GMainLoop *mainloop = (GMainLoop *) user_data; + g_main_loop_quit (mainloop); + return TRUE; +} + + +int +main (int argc, char *argv[]) +{ + GMainLoop *mainloop; + SoupServer *soup_server; + GHashTable *receiver_entry_table; + + gst_init (&argc, &argv); + + receiver_entry_table = + g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, + destroy_receiver_entry); + + mainloop = g_main_loop_new (NULL, FALSE); + g_assert (mainloop != NULL); + + g_unix_signal_add (SIGINT, exit_sighandler, mainloop); + g_unix_signal_add (SIGTERM, exit_sighandler, mainloop); + + soup_server = + soup_server_new (SOUP_SERVER_SERVER_HEADER, "webrtc-soup-server", NULL); + soup_server_add_handler (soup_server, "/", soup_http_handler, NULL, NULL); + soup_server_add_websocket_handler (soup_server, "/ws", NULL, NULL, + soup_websocket_handler, (gpointer) receiver_entry_table, NULL); + soup_server_listen_all (soup_server, SOUP_HTTP_PORT, + (SoupServerListenOptions) 0, NULL); + + g_print ("WebRTC page link: http://127.0.0.1:%d/\n", (gint) SOUP_HTTP_PORT); + + g_main_loop_run (mainloop); + + g_object_unref (G_OBJECT (soup_server)); + g_hash_table_destroy (receiver_entry_table); + g_main_loop_unref (mainloop); + + gst_deinit (); + + return 0; +} From 09d9ac2d2ead25a83f00d1bc21d686ffb64949b0 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 20 Jun 2018 15:36:42 +0000 Subject: [PATCH 68/77] rpicamsrc: Don't destroy the camera component on startup error Just disable the camera component when it fails to start. The most common reason is that the camera device is already in use, and if we just disable the mmal component correct cleanup will happen later --- sys/rpicamsrc/RaspiCapture.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index cae5cf61ce..a7d72a0e1b 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1268,9 +1268,8 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) return status; error: - if (camera) - mmal_component_destroy(camera); + mmal_component_disable(camera); return status; } From fa840da60692ab6afc2678e20f5b4462bdb5cadc Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 21 Jun 2018 20:33:03 +1000 Subject: [PATCH 69/77] rpicamsrc: webrtc example: Set the locale Make the date format in the overlay respect the current locale --- tests/examples/rpicamsrc/webrtc-unidirectional-h264.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c index 290e46da30..1f960b02ef 100644 --- a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c +++ b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -629,6 +630,7 @@ main (int argc, char *argv[]) SoupServer *soup_server; GHashTable *receiver_entry_table; + setlocale (LC_ALL, ""); gst_init (&argc, &argv); receiver_entry_table = From d9115ef1ebe6980781ccbd9697859ec1dbbc14b4 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 21 Jun 2018 21:45:32 +1000 Subject: [PATCH 70/77] rpicamsrc: webrtc example: Remove external fmtp insertion GStreamer 1.14.2 should contain the backport of gst-plugins-bad commit 5c450c5 adding FEC and RTX support, and incidentally the fmtp field in the SDP --- .../rpicamsrc/webrtc-unidirectional-h264.c | 87 ------------------- 1 file changed, 87 deletions(-) diff --git a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c index 1f960b02ef..775db19e21 100644 --- a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c +++ b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c @@ -56,13 +56,6 @@ struct _ReceiverEntry GstElement *pipeline; GstElement *webrtcbin; - GstElement *payloader; - - GCond profile_level_id_cond; - GMutex profile_level_id_mutex; - gchar *profile_level_id; - - gboolean shutting_down; }; @@ -176,14 +169,10 @@ create_receiver_entry (SoupWebsocketConnection * connection) { GError *error; ReceiverEntry *receiver_entry; - GstPad *payloader_srcpad; receiver_entry = g_slice_alloc0 (sizeof (ReceiverEntry)); receiver_entry->connection = connection; - g_cond_init (&receiver_entry->profile_level_id_cond); - g_mutex_init (&receiver_entry->profile_level_id_mutex); - g_object_ref (G_OBJECT (connection)); g_signal_connect (G_OBJECT (connection), "message", @@ -203,16 +192,7 @@ create_receiver_entry (SoupWebsocketConnection * connection) receiver_entry->webrtcbin = gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "webrtcbin"); - receiver_entry->payloader = - gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "payloader"); g_assert (receiver_entry->webrtcbin != NULL); - g_assert (receiver_entry->payloader != NULL); - - payloader_srcpad = - gst_element_get_static_pad (receiver_entry->payloader, "src"); - gst_pad_add_probe (payloader_srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, - payloader_caps_event_probe_cb, (gpointer) receiver_entry, NULL); - gst_object_unref (GST_OBJECT (payloader_srcpad)); g_signal_connect (receiver_entry->webrtcbin, "on-negotiation-needed", G_CALLBACK (on_negotiation_needed_cb), (gpointer) receiver_entry); @@ -236,24 +216,14 @@ destroy_receiver_entry (gpointer receiver_entry_ptr) g_assert (receiver_entry != NULL); - g_mutex_lock (&receiver_entry->profile_level_id_mutex); - receiver_entry->shutting_down = TRUE; - g_cond_signal (&receiver_entry->profile_level_id_cond); - g_mutex_unlock (&receiver_entry->profile_level_id_mutex); - if (receiver_entry->pipeline != NULL) { gst_element_set_state (GST_ELEMENT (receiver_entry->pipeline), GST_STATE_NULL); gst_object_unref (GST_OBJECT (receiver_entry->webrtcbin)); - gst_object_unref (GST_OBJECT (receiver_entry->payloader)); gst_object_unref (GST_OBJECT (receiver_entry->pipeline)); } - g_cond_clear (&receiver_entry->profile_level_id_cond); - g_mutex_clear (&receiver_entry->profile_level_id_mutex); - g_free (receiver_entry->profile_level_id); - if (receiver_entry->connection != NULL) g_object_unref (G_OBJECT (receiver_entry->connection)); @@ -261,48 +231,13 @@ destroy_receiver_entry (gpointer receiver_entry_ptr) } -GstPadProbeReturn -payloader_caps_event_probe_cb (G_GNUC_UNUSED GstPad * pad, - GstPadProbeInfo * info, gpointer user_data) -{ - ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; - GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); - - if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) { - gchar const *profile_level_id; - GstStructure *s; - GstCaps *caps; - - caps = NULL; - gst_event_parse_caps (event, &caps); - - s = gst_caps_get_structure (caps, 0); - profile_level_id = gst_structure_get_string (s, "profile-level-id"); - g_assert (profile_level_id != NULL); - - g_mutex_lock (&receiver_entry->profile_level_id_mutex); - - g_free (receiver_entry->profile_level_id); - receiver_entry->profile_level_id = g_strdup (profile_level_id); - - g_cond_signal (&receiver_entry->profile_level_id_cond); - - g_mutex_unlock (&receiver_entry->profile_level_id_mutex); - } - - return GST_PAD_PROBE_OK; -} - - void on_offer_created_cb (GstPromise * promise, gpointer user_data) { - gchar *fmtp_value; gchar *sdp_string; gchar *json_string; JsonObject *sdp_json; JsonObject *sdp_data_json; - GstSDPMedia *sdp_media; GstStructure const *reply; GstPromise *local_desc_promise; GstWebRTCSessionDescription *offer = NULL; @@ -313,13 +248,6 @@ on_offer_created_cb (GstPromise * promise, gpointer user_data) &offer, NULL); gst_promise_unref (promise); - fmtp_value = g_strdup_printf (RTP_PAYLOAD_TYPE " profile-level-id=%s", - receiver_entry->profile_level_id); - sdp_media = - (GstSDPMedia *) & g_array_index (offer->sdp->medias, GstSDPMedia, 0); - gst_sdp_media_add_attribute (sdp_media, "fmtp", fmtp_value); - g_free (fmtp_value); - local_desc_promise = gst_promise_new (); g_signal_emit_by_name (receiver_entry->webrtcbin, "set-local-description", offer, local_desc_promise); @@ -350,24 +278,9 @@ on_offer_created_cb (GstPromise * promise, gpointer user_data) void on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data) { - gboolean exit_early; GstPromise *promise; ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; - g_mutex_lock (&receiver_entry->profile_level_id_mutex); - - while ((receiver_entry->profile_level_id == NULL) - && !receiver_entry->shutting_down) - g_cond_wait (&receiver_entry->profile_level_id_cond, - &receiver_entry->profile_level_id_mutex); - - exit_early = receiver_entry->shutting_down; - - g_mutex_unlock (&receiver_entry->profile_level_id_mutex); - - if (exit_early) - return; - g_print ("Creating negotiation offer\n"); promise = gst_promise_new_with_change_func (on_offer_created_cb, From b333e32e18daf8a17417bd999592781ffbf07fbe Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 21 Jun 2018 22:44:25 +1000 Subject: [PATCH 71/77] rpicamsrc: webrtc example: Modify HTML to support other ports than 57778 --- tests/examples/rpicamsrc/webrtc-unidirectional-h264.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c index 775db19e21..9538ede272 100644 --- a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c +++ b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c @@ -132,10 +132,13 @@ const gchar *html_source = " \n \ \n \ \n \ function playStream(videoElement, hostname, port, path, configuration, reportErrorCB) { \n \ - var wsHost = (hostname != undefined) ? hostname : window.location.hostname; \n \ - var wsPort = (port != undefined) ? port : 57778; \n \ + var l = window.location;\n \ + var wsHost = (hostname != undefined) ? hostname : l.hostname; \n \ + var wsPort = (port != undefined) ? port : l.port; \n \ var wsPath = (path != undefined) ? path : \"ws\"; \n \ - var wsUrl = \"ws://\" + wsHost + \":\" + wsPort + \"/\" + wsPath; \n \ + if (wsPort) \n\ + wsPort = \":\" + wsPort; \n\ + var wsUrl = \"ws://\" + wsHost + wsPort + \"/\" + wsPath; \n \ \n \ html5VideoElement = videoElement; \n \ webrtcConfiguration = configuration; \n \ From 41f41f1fdd368a5ee2fe8c4ed4f2b4a45bfa6357 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 21 Jun 2018 22:50:28 +1000 Subject: [PATCH 72/77] rpicamsrc: webrtc example: Add a STUN server to the configuration To let the webrtc example work through NAT firewalls --- tests/examples/rpicamsrc/webrtc-unidirectional-h264.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c index 9538ede272..b8a716d1d2 100644 --- a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c +++ b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c @@ -15,6 +15,7 @@ #define RTP_PAYLOAD_TYPE "96" #define SOUP_HTTP_PORT 57778 +#define STUN_SERVER "stun.l.google.com:19302" @@ -150,7 +151,8 @@ const gchar *html_source = " \n \ \n \ window.onload = function() { \n \ var vidstream = document.getElementById(\"stream\"); \n \ - playStream(vidstream, null, null, null, null, function (errmsg) { console.error(errmsg); }); \n \ + var config = { 'iceServers': [{ 'urls': 'stun:" STUN_SERVER "' }] }; \n\ + playStream(vidstream, null, null, null, config, function (errmsg) { console.error(errmsg); }); \n \ }; \n \ \n \ \n \ @@ -182,8 +184,8 @@ create_receiver_entry (SoupWebsocketConnection * connection) G_CALLBACK (soup_websocket_message_cb), (gpointer) receiver_entry); error = NULL; - receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin " - "rpicamsrc bitrate=300000 annotation-mode=12 ! video/x-h264,profile=baseline,width=640,height=480 ! queue max-size-time=100000000 ! h264parse ! " + receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin stun-server=stun://" STUN_SERVER " " + "rpicamsrc bitrate=600000 annotation-mode=12 preview=false ! video/x-h264,profile=constrained-baseline,width=640,height=360,level=3.0 ! queue max-size-time=100000000 ! h264parse ! " "rtph264pay config-interval=-1 name=payloader ! " "application/x-rtp,media=video,encoding-name=H264,payload=" RTP_PAYLOAD_TYPE " ! webrtcbin. ", &error); From c907deb15fda4fe07eadc93f4a83611f4581e2f2 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 16 Jul 2018 19:30:26 +1000 Subject: [PATCH 73/77] rpicamsrc: Improve timeout error Propagate timeout errors so they're not reported generically --- sys/rpicamsrc/RaspiCapture.c | 2 +- sys/rpicamsrc/RaspiCapture.h | 2 ++ sys/rpicamsrc/gstrpicamsrc.c | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index a7d72a0e1b..96c4692b07 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -942,7 +942,7 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, buffer = mmal_queue_timedwait(state->encoded_buffer_q, 500); if (G_UNLIKELY(buffer == NULL)) { - return GST_FLOW_ERROR; + return GST_FLOW_ERROR_TIMEOUT; } if (G_LIKELY (config->useSTC && clock)) { diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 571d3b7fc1..71e79b26e8 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -66,6 +66,8 @@ GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug); #undef vcos_log_warn #define vcos_log_warn GST_WARNING +#define GST_FLOW_ERROR_TIMEOUT GST_FLOW_CUSTOM_ERROR + G_BEGIN_DECLS typedef enum diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index da8446023a..c2de7619ff 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -1439,6 +1439,13 @@ gst_rpi_cam_src_create (GstPushSrc * parent, GstBuffer ** buf) GST_BUFFER_DURATION (*buf) = src->duration; } + if (ret == GST_FLOW_ERROR_TIMEOUT) { + GST_ELEMENT_ERROR (src, STREAM, FAILED, + ("Camera capture timed out."), + ("Waiting for a buffer from the camera took too long.")); + ret = GST_FLOW_ERROR; + } + if (clock) gst_object_unref (clock); return ret; From 3c5327ae6412ba51df8013b6b1db22e14d196c9e Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 16 Jul 2018 19:49:21 +1000 Subject: [PATCH 74/77] rpicamsrc: Attempt to workaround MMAL timeout bug mmal_queue_timedwait() might spuriously return immediately if called at exactly the wrong instant due to an internal off-by-one bug. Attempt to work around that and just retry. --- sys/rpicamsrc/RaspiCapture.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 96c4692b07..975078f290 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -937,9 +937,16 @@ raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp, /* No timestamps if no clockm or invalid PTS */ GstClockTime gst_pts = GST_CLOCK_TIME_NONE; - /* FIXME: Use our own interruptible cond wait: */ - - buffer = mmal_queue_timedwait(state->encoded_buffer_q, 500); + do { + buffer = mmal_queue_timedwait(state->encoded_buffer_q, 500); + // Work around a bug where mmal_queue_timedwait() might return + // immediately if the internal timeout time aligns exactly + // with a 1 second rollover boundary by checking errno. + if (errno == EINVAL) { + GST_WARNING ("Retrying mmal_queue_timedwait() due to spurious failure."); + continue; + } + } while (0); if (G_UNLIKELY(buffer == NULL)) { return GST_FLOW_ERROR_TIMEOUT; From 912ea0dceb32dcbc2d593fd9255dc4970826688b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 3 May 2020 11:09:47 +0000 Subject: [PATCH 75/77] rpicamsrc: meson: drop map file and fix plugin symbol export with newer gstreamer versions Use -fvisibility instead of a map file for symbol export, so that the right symbols get exported with newer gstreamer versions. Older GStreamer versions also still work of course. Fixes blacklisting/plugin-loading issues with GStreamer >= 1.14 Fixes https://github.com/thaytan/gst-rpicamsrc/issues/984, closes https://github.com/thaytan/gst-rpicamsrc/issues/94 and https://github.com/thaytan/gst-rpicamsrc/issues/67 --- sys/rpicamsrc/gstplugin.map | 3 --- sys/rpicamsrc/meson.build | 4 ---- 2 files changed, 7 deletions(-) delete mode 100644 sys/rpicamsrc/gstplugin.map diff --git a/sys/rpicamsrc/gstplugin.map b/sys/rpicamsrc/gstplugin.map deleted file mode 100644 index 08d854102a..0000000000 --- a/sys/rpicamsrc/gstplugin.map +++ /dev/null @@ -1,3 +0,0 @@ -{ global: -gst_plugin_desc; -local: *; }; diff --git a/sys/rpicamsrc/meson.build b/sys/rpicamsrc/meson.build index 62c21451cc..46e0becbbc 100644 --- a/sys/rpicamsrc/meson.build +++ b/sys/rpicamsrc/meson.build @@ -23,14 +23,10 @@ gstrpicam_enum_types_c = custom_target('gstrpicam-enum-types.c', command : [glib_mkenums, '--template', meson.current_source_dir() + '/gstrpicam-enums-template.c', '@INPUT@'], capture : true) -mapfile = 'gstplugin.map' - library('gstrpicamsrc', rpicamsrc_sources, gstrpicam_enum_types_h, gstrpicam_enum_types_c, c_args : gst_rpicamsrc_args, include_directories : config_inc, - link_args : '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile), - link_depends : mapfile, dependencies : [gst_dep, gstbase_dep, gstvideo_dep] + mmal_deps, install : true, install_dir : plugins_install_dir) From 1c25fe131dc155d08ab88f50a9a74d28cecde450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 2 May 2020 18:28:10 +0000 Subject: [PATCH 76/77] rpicamsrc: meson: use gnome.glib_mkenums_simple() and fix build as Meson subproject While at it also fix up the type defines, e.g. GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_MODE -> GST_RPI_CAM_SRC_TYPE_EXPOSURE_MODE --- sys/rpicamsrc/gstrpicamsrc.c | 14 +++++++------- sys/rpicamsrc/meson.build | 22 +++++++--------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c index c2de7619ff..544efe346a 100644 --- a/sys/rpicamsrc/gstrpicamsrc.c +++ b/sys/rpicamsrc/gstrpicamsrc.c @@ -375,21 +375,21 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_object_class_install_property (gobject_class, PROP_EXPOSURE_MODE, g_param_spec_enum ("exposure-mode", "Exposure Mode", "Camera exposure mode to use", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_MODE, EXPOSURE_MODE_DEFAULT, + GST_RPI_CAM_SRC_TYPE_EXPOSURE_MODE, EXPOSURE_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EXPOSURE_METERING_MODE, g_param_spec_enum ("metering-mode", "Exposure Metering Mode", "Camera exposure metering mode to use", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_EXPOSURE_METERING_MODE, + GST_RPI_CAM_SRC_TYPE_EXPOSURE_METERING_MODE, EXPOSURE_METERING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_DRC, g_param_spec_enum ("drc", "DRC level", "Dynamic Range Control level", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_DRC_LEVEL, GST_RPI_CAM_SRC_DRC_LEVEL_OFF, + GST_RPI_CAM_SRC_TYPE_DRC_LEVEL, GST_RPI_CAM_SRC_DRC_LEVEL_OFF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_MODE, g_param_spec_enum ("awb-mode", "Automatic White Balance Mode", - "White Balance mode", GST_RPI_CAM_TYPE_RPI_CAM_SRC_AWB_MODE, + "White Balance mode", GST_RPI_CAM_SRC_TYPE_AWB_MODE, GST_RPI_CAM_SRC_AWB_MODE_AUTO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_AWB_GAIN_RED, @@ -403,7 +403,7 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_object_class_install_property (gobject_class, PROP_IMAGE_EFFECT, g_param_spec_enum ("image-effect", "Image effect", "Visual FX to apply to the image", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_IMAGE_EFFECT, + GST_RPI_CAM_SRC_TYPE_IMAGE_EFFECT, GST_RPI_CAM_SRC_IMAGEFX_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); #if 0 @@ -461,7 +461,7 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_object_class_install_property (gobject_class, PROP_ANNOTATION_MODE, g_param_spec_flags ("annotation-mode", "Annotation Mode", "Flags to control annotation of the output video", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_ANNOTATION_MODE, 0, + GST_RPI_CAM_SRC_TYPE_ANNOTATION_MODE, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT, g_param_spec_string ("annotation-text", "Annotation Text", @@ -470,7 +470,7 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) g_object_class_install_property (gobject_class, PROP_INTRA_REFRESH_TYPE, g_param_spec_enum ("intra-refresh-type", "Intra Refresh Type", "Type of Intra Refresh to use, -1 to disable intra refresh", - GST_RPI_CAM_TYPE_RPI_CAM_SRC_INTRA_REFRESH_TYPE, + GST_RPI_CAM_SRC_TYPE_INTRA_REFRESH_TYPE, GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ANNOTATION_TEXT_SIZE, diff --git a/sys/rpicamsrc/meson.build b/sys/rpicamsrc/meson.build index 46e0becbbc..c3a1651ab3 100644 --- a/sys/rpicamsrc/meson.build +++ b/sys/rpicamsrc/meson.build @@ -7,24 +7,16 @@ rpicamsrc_sources = [ 'RaspiCLI.c', ] -# This can be simplified once meson gets native support for glib-mkenums (soon) -glib_mkenums = find_program('glib-mkenums') +# glib-mkenums +gnome = import('gnome') -gstrpicam_enum_types_h = custom_target('gstrpicam-enum-types.h', - output : 'gstrpicam-enum-types.h', - input : files('gstrpicam_types.h'), - command : [glib_mkenums, '--template', meson.current_source_dir() + '/gstrpicam-enums-template.h', '@INPUT@'], - capture : true) - -gstrpicam_enum_types_c = custom_target('gstrpicam-enum-types.c', - output : 'gstrpicam-enum-types.c', - input : files('gstrpicam_types.h'), - depends : [gstrpicam_enum_types_h], - command : [glib_mkenums, '--template', meson.current_source_dir() + '/gstrpicam-enums-template.c', '@INPUT@'], - capture : true) +enums = gnome.mkenums_simple('gstrpicam-enum-types', + sources: 'gstrpicam_types.h', + identifier_prefix: 'GstRpiCamSrc', + symbol_prefix: 'gst_rpi_cam_src') library('gstrpicamsrc', - rpicamsrc_sources, gstrpicam_enum_types_h, gstrpicam_enum_types_c, + rpicamsrc_sources, enums, c_args : gst_rpicamsrc_args, include_directories : config_inc, dependencies : [gst_dep, gstbase_dep, gstvideo_dep] + mmal_deps, From e5593a4c9c35a56749d68d5974b868968ac29e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 2 May 2020 19:27:20 +0000 Subject: [PATCH 77/77] rpicamsrc: sync autotools with glib-mkenum usage in meson build --- sys/rpicamsrc/gstrpicam-enums-template.c | 32 ++++++++++++------------ sys/rpicamsrc/gstrpicam-enums-template.h | 10 +++----- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/sys/rpicamsrc/gstrpicam-enums-template.c b/sys/rpicamsrc/gstrpicam-enums-template.c index b9dee13e11..8631751a5a 100644 --- a/sys/rpicamsrc/gstrpicam-enums-template.c +++ b/sys/rpicamsrc/gstrpicam-enums-template.c @@ -4,36 +4,36 @@ /*** END file-header ***/ /*** BEGIN file-production ***/ -/* enumerations from "@filename@" */ +/* enumerations from "@basename@" */ #include "@filename@" +#define C_ENUM(v) ((gint) v) +#define C_FLAGS(v) ((guint) v) + /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { - static GType the_type = 0; - - if (the_type == 0) - { - static const G@Type@Value values[] = { + static volatile gsize gtype_id = 0; + + if (g_once_init_enter (>ype_id)) { + static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ - { @VALUENAME@, - "@VALUENAME@", - "@valuenick@" }, + { C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ - { 0, NULL, NULL } - }; - the_type = g_@type@_register_static ( - g_intern_static_string ("@EnumName@"), - values); - } - return the_type; + { 0, NULL, NULL } + }; + + GType new_type = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (>ype_id, new_type); + } + return (GType) gtype_id; } /*** END value-tail ***/ diff --git a/sys/rpicamsrc/gstrpicam-enums-template.h b/sys/rpicamsrc/gstrpicam-enums-template.h index 62b49ca647..eb25042ba5 100644 --- a/sys/rpicamsrc/gstrpicam-enums-template.h +++ b/sys/rpicamsrc/gstrpicam-enums-template.h @@ -1,6 +1,5 @@ /*** BEGIN file-header ***/ -#ifndef __GSTRPICAM_ENUM_TYPES_H__ -#define __GSTRPICAM_ENUM_TYPES_H__ +#pragma once #include @@ -9,18 +8,17 @@ G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ -/* Enumerations from "@filename@" */ +/* Enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN enumeration-production ***/ -#define GST_RPI_CAM_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) -GType @enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) +GType @enum_name@_get_type (void); /*** END enumeration-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS -#endif /* __GSTRPICAM_ENUM_TYPES_H__ */ /*** END file-tail ***/