Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/GStreamer/gst-plugins-good.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorTim-Philipp Müller <tim@centricular.com>2020-07-09 14:46:30 +0300
committerTim-Philipp Müller <tim@centricular.com>2020-07-10 19:36:14 +0300
commit84dbf94313936b3c7a9bceddd956517e7f147f6c (patch)
tree3f7fffe52c8ec48b6729bffe75b1351cff611206 /sys
parent83beb9211bb9c475998d6e2a6ce8fb339d63f109 (diff)
parente5593a4c9c35a56749d68d5974b868968ac29e98 (diff)
Merge branch 'plugin-move-rpicamsrc'
Move rpicamsrc from https://github.com/thaytan/gst-rpicamsrc/ It's a useful little element and works well, so might as well make sure it's widely available so people can stop piping raspivid output into fdsrc. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/667>
Diffstat (limited to 'sys')
-rw-r--r--sys/rpicamsrc/RaspiCLI.c155
-rw-r--r--sys/rpicamsrc/RaspiCLI.h56
-rw-r--r--sys/rpicamsrc/RaspiCamControl.c1467
-rw-r--r--sys/rpicamsrc/RaspiCamControl.h220
-rw-r--r--sys/rpicamsrc/RaspiCapture.c2026
-rw-r--r--sys/rpicamsrc/RaspiCapture.h143
-rw-r--r--sys/rpicamsrc/RaspiPreview.c307
-rw-r--r--sys/rpicamsrc/RaspiPreview.h75
-rw-r--r--sys/rpicamsrc/RaspiStill.c1516
-rw-r--r--sys/rpicamsrc/RaspiStillYUV.c957
-rw-r--r--sys/rpicamsrc/gstrpicam-enums-template.c40
-rw-r--r--sys/rpicamsrc/gstrpicam-enums-template.h24
-rw-r--r--sys/rpicamsrc/gstrpicam_types.h101
-rw-r--r--sys/rpicamsrc/gstrpicamsrc.c1477
-rw-r--r--sys/rpicamsrc/gstrpicamsrc.h113
-rw-r--r--sys/rpicamsrc/gstrpicamsrcdeviceprovider.c149
-rw-r--r--sys/rpicamsrc/gstrpicamsrcdeviceprovider.h79
-rw-r--r--sys/rpicamsrc/meson.build24
18 files changed, 8929 insertions, 0 deletions
diff --git a/sys/rpicamsrc/RaspiCLI.c b/sys/rpicamsrc/RaspiCLI.c
new file mode 100644
index 000000000..2bbb3ce09
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+#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;i<num_refs;i++)
+ {
+ if (!strcasecmp(str, map[i].mode))
+ {
+ return map[i].mmal_mode;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Function to take a mmal enum (as int) and return the string equivalent
+ * @param en Incoming int to match
+ * @param map Mapping data
+ * @param num_refs The number of items in the mapping data
+ * @return const pointer to string, or NULL if no match
+ */
+const char *raspicli_unmap_xref(const int en, const XREF_T *map, int num_refs)
+{
+ int i;
+
+ for (i=0;i<num_refs;i++)
+ {
+ if (en == map[i].mmal_mode)
+ {
+ return map[i].mode;
+ }
+ }
+ return NULL;
+}
diff --git a/sys/rpicamsrc/RaspiCLI.h b/sys/rpicamsrc/RaspiCLI.h
new file mode 100644
index 000000000..38f252498
--- /dev/null
+++ b/sys/rpicamsrc/RaspiCLI.h
@@ -0,0 +1,56 @@
+/*
+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 RASPICLI_H_
+#define RASPICLI_H_
+
+typedef struct
+{
+ int id;
+ char *command;
+ char *abbrev;
+ char *help;
+ int num_parameters;
+} COMMAND_LIST;
+
+/// Cross reference structure, mode string against mode id
+typedef struct xref_t
+{
+ char *mode;
+ int mmal_mode;
+} XREF_T;
+
+
+void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands);
+int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters);
+
+int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs);
+const char *raspicli_unmap_xref(const int en, const XREF_T *map, int num_refs);
+
+
+#endif
diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c
new file mode 100644
index 000000000..1b3d05964
--- /dev/null
+++ b/sys/rpicamsrc/RaspiCamControl.c
@@ -0,0 +1,1467 @@
+/*
+ * Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
+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 <stdio.h>
+#include <memory.h>
+
+#include <gst/gst.h>
+
+#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"
+#include "RaspiCapture.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]);
+
+static XREF_T drc_mode_map[] =
+{
+ {"off", MMAL_PARAMETER_DRC_STRENGTH_OFF},
+ {"low", MMAL_PARAMETER_DRC_STRENGTH_LOW},
+ {"med", MMAL_PARAMETER_DRC_STRENGTH_MEDIUM},
+ {"high", MMAL_PARAMETER_DRC_STRENGTH_HIGH}
+};
+
+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
+#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
+#define CommandShutterSpeed 16
+#define CommandAwbGains 17
+#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[] =
+{
+ {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 stabilisation", 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},
+ {CommandShutterSpeed,"-shutter", "ss", "Set shutter speed in microseconds", 1},
+ {CommandAwbGains, "-awbgains", "awbg", "Set AWB gains - AWB mode must be off", 1},
+ {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]);
+
+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;
+}
+#endif
+
+
+/**
+ * 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)
+{
+ return 1;
+}
+#if 0
+ 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(&parameter_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(&parameter_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(&parameter_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(&parameter_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(&parameter_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(&parameter_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(&parameter_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(&parameter_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", &params->sharpness);
+ used = 2;
+ break;
+
+ case CommandContrast : // contrast - needs single number parameter
+ sscanf(arg2, "%d", &params->contrast);
+ used = 2;
+ break;
+
+ case CommandBrightness : // brightness - needs single number parameter
+ sscanf(arg2, "%d", &params->brightness);
+ used = 2;
+ break;
+
+ case CommandSaturation : // saturation - needs single number parameter
+ sscanf(arg2, "%d", &params->saturation);
+ used = 2;
+ break;
+
+ case CommandISO : // ISO - needs single number parameter
+ sscanf(arg2, "%d", &params->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", &params->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", &params->colourEffects.u, &params->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", &params->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;
+ }
+
+ case CommandShutterSpeed : // Shutter speed needs single number parameter
+ {
+ sscanf(arg2, "%d", &params->shutter_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", &params->enable_annotate);
+ }
+ else
+ {
+ params->enable_annotate = ANNOTATE_USER_TEXT;
+ //copy string char by char and replace "\n" with newline character
+ unsigned char c;
+ char const *s = arg2;
+ char *t = &params->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", &params->annotate_text_size,
+ &params->annotate_text_colour,
+ &params->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;
+}
+
+/**
+ * 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;i<exposure_map_size;i++)
+ {
+ fprintf(stderr, ",%s", exposure_map[i].mode);
+ }
+
+ fprintf(stderr, "\n\nAWB mode options :\n%s", awb_map[0].mode );
+
+ for (i=1;i<awb_map_size;i++)
+ {
+ fprintf(stderr, ",%s", awb_map[i].mode);
+ }
+
+ fprintf(stderr, "\n\nImage Effect mode options :\n%s", imagefx_map[0].mode );
+
+ for (i=1;i<imagefx_map_size;i++)
+ {
+ fprintf(stderr, ",%s", imagefx_map[i].mode);
+ }
+
+ fprintf(stderr, "\n\nMetering Mode options :\n%s", metering_mode_map[0].mode );
+
+ for (i=1;i<metering_mode_map_size;i++)
+ {
+ 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;i<drc_mode_map_size;i++)
+ {
+ fprintf(stderr, ",%s", drc_mode_map[i].mode);
+ }
+
+ fprintf(stderr, "\n");
+}
+
+#endif
+/**
+ * Dump contents of camera parameter structure to stdout for debugging/verbose logging
+ *
+ * @param params Const pointer to parameters structure to dump
+ */
+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);
+
+ 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);
+}
+
+/**
+ * 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;
+ 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';
+ 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;
+}
+
+/**
+ * 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_awb_gains(camera, params->awb_gains_r, params->awb_gains_b);
+ result += raspicamcontrol_set_imageFX(camera, params->imageEffect);
+ result += raspicamcontrol_set_colourFX(camera, &params->colourEffects);
+ //result += raspicamcontrol_set_thumbnail_parameters(camera, &params->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,
+ params->annotate_text_size,
+ params->annotate_text_colour,
+ params->annotate_bg_colour);
+
+ 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, &param.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, &param.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);
+}
+
+/**
+ * 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,
+ const int text_size, const int text_colour, const int bg_colour)
+{
+ 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_V3];
+
+ annotate.enable = 1;
+
+ if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT))
+ {
+ 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_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_V3 - strlen(annotate.text) - 1);
+ }
+
+ if (settings & ANNOTATE_SHUTTER_SETTINGS)
+ annotate.show_shutter = MMAL_TRUE;
+
+ if (settings & ANNOTATE_GAIN_SETTINGS)
+ annotate.show_analog_gain = MMAL_TRUE;
+
+ if (settings & ANNOTATE_LENS_SETTINGS)
+ annotate.show_lens = MMAL_TRUE;
+
+ if (settings & ANNOTATE_CAF_SETTINGS)
+ annotate.show_caf = MMAL_TRUE;
+
+ if (settings & ANNOTATE_MOTION_SETTINGS)
+ annotate.show_motion = MMAL_TRUE;
+
+ if (settings & ANNOTATE_FRAME_NUMBER)
+ annotate.show_frame_num = MMAL_TRUE;
+
+ if (settings & ANNOTATE_BLACK_BACKGROUND)
+ 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;
+
+ 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
+ *
+ * @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
+ */
+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 000000000..83e9eddf1
--- /dev/null
+++ b/sys/rpicamsrc/RaspiCamControl.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
+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,
+
+ */
+
+/// 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
+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
+ 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
+ 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;
+
+
+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);
+void raspicamcontrol_get_camera(int *supported, int *detected);
+
+// 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_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,
+ 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);
+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 000000000..975078f29
--- /dev/null
+++ b/sys/rpicamsrc/RaspiCapture.c
@@ -0,0 +1,2026 @@
+/*
+ * Copyright (c) 2013-2016 Jan Schmidt <jan@centricular.com>
+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, 5 Mar 2015
+ * \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
+ */
+
+// We use some GNU extensions (basename, asprintf)
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <sysexits.h>
+
+#include <gst/gst.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"
+
+#include "RaspiCapture.h"
+#include "RaspiCamControl.h"
+#include "RaspiPreview.h"
+#include "RaspiCLI.h"
+
+#include <semaphore.h>
+
+// 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
+
+/// Video render needs at least 2 buffers.
+#define VIDEO_OUTPUT_BUFFERS_NUM 3
+
+// Max bitrate we allow for recording
+const int MAX_BITRATE = 25000000; // 25Mbits/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);
+
+/** Struct used to pass information in encoder port userdata to callback
+ */
+typedef struct
+{
+ 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_PORT_T *camera_video_port;
+ MMAL_PORT_T *camera_still_port;
+ MMAL_PORT_T *encoder_output_port;
+
+ 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;
+
+ int64_t base_time;
+ int64_t last_second;
+
+ RASPIPREVIEW_STATE preview_state;
+};
+
+
+/// 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]);
+
+#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);
+
+/// 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
+#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 <size>. Default 1920", 1 },
+ { CommandHeight, "-height", "h", "Set image height <size>. 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 <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 <ms>", 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 <filename> for inline motion vectors", 1 },
+ { CommandCamSelect, "-camselect", "cs", "Select camera <number>. 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]);
+#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 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
+ config->width = 1920; // Default to 1080p
+ config->height = 1080;
+ 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 = -1; // Not set
+ config->quantisationParameter = 0;
+ config->demoMode = 0;
+ config->demoInterval = 250; // ms
+ config->immutableInput = 1;
+ config->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
+ config->encoding = MMAL_ENCODING_H264;
+
+ 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);
+
+ // Set up the camera_parameters to default
+ raspicamcontrol_set_defaults(&config->camera_parameters);
+
+}
+
+
+/**
+ * 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)
+{
+ RASPIVID_CONFIG *config;
+
+ if (!state)
+ {
+ vcos_assert(0);
+ return;
+ }
+
+ config = &state->config;
+
+ 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
+/**
+ * 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 --<something> or -<abreviation of something>
+
+ 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+1);
+ 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)
+ {
+ // 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
+ 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 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);
+
+ if( state->profile == -1)
+ state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
+
+ i++;
+ 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
+ // 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-1]);
+ 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;i<profile_map_size;i++)
+ {
+ 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;i<intra_refresh_map_size;i++)
+ {
+ fprintf(stderr, ",%s", intra_refresh_map[i].mode);
+ }
+
+ fprintf(stderr, "\n");
+
+ // Help for preview options
+ raspipreview_display_help();
+
+ // Now display any help information from the camcontrol code
+ raspicamcontrol_display_help();
+
+ fprintf(stderr, "\n");
+
+ return;
+}
+#endif
+
+/**
+ * buffer header callback function for camera control
+ *
+ * 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_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ if (buffer->cmd == 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 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);
+ }
+
+ 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,
+ 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,
+ config->camera_parameters.annotate_text_size,
+ config->camera_parameters.annotate_text_colour,
+ config->camera_parameters.annotate_bg_colour);
+ }
+}
+
+
+
+/**
+ * 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)
+{
+ 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)
+ {
+ vcos_log_error("Received a encoder buffer callback with no state");
+ // release buffer back to the pool
+ mmal_buffer_header_release(buffer);
+ 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);
+}
+
+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;
+ /* No timestamps if no clockm or invalid PTS */
+ GstClockTime gst_pts = GST_CLOCK_TIME_NONE;
+
+ 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;
+ }
+
+ if (G_LIKELY (config->useSTC && 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, &param.hdr);
+
+ 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)
+ gst_pts = runtime - offset;
+ }
+ 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));
+ }
+ 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_fill(buf, 0, buffer->data, 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
+ *
+ * @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 = NULL;
+ MMAL_STATUS_T status;
+ RASPIVID_CONFIG *config = &state->config;
+
+ /* 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;
+ }
+
+ 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;
+ vcos_log_error("Camera doesn't have output ports");
+ 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);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to enable control port : error %d", status);
+ 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;
+ 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 = config->width,
+ .max_stills_h = config->height,
+ .stills_yuv422 = 0,
+ .one_shot_stills = 0,
+ .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,
+ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_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
+
+ // 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;
+
+ 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;
+ GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement");
+ }
+ }
+
+ 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;
+ format->es->video.crop.y = 0;
+ 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);
+
+ 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;
+
+ 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);
+ }
+
+ /* 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;
+ format->es->video.crop.y = 0;
+ 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);
+
+ 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 = 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 = 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);
+
+ 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, &config->camera_parameters);
+
+ update_annotation_data(state);
+
+ if (config->verbose)
+ fprintf(stderr, "Camera component done\n");
+
+ return status;
+
+error:
+ if (camera)
+ mmal_component_disable(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;
+ }
+}
+
+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, &param.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
+ *
+ * @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;
+ RASPIVID_CONFIG *config = &state->config;
+
+ gboolean encoded_format =
+ (config->encoding == MMAL_ENCODING_H264 ||
+ config->encoding == MMAL_ENCODING_MJPEG ||
+ config->encoding == MMAL_ENCODING_JPEG);
+
+ 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)
+ {
+ 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);
+
+ // Configure desired encoding
+ encoder_output->format->encoding = config->encoding;
+
+ encoder_output->format->bitrate = config->bitrate;
+
+ 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;
+
+ 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;
+ encoder_output->format->es->video.frame_rate.den = 1;
+
+ // 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, &param.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set ratecontrol");
+ goto error;
+ }
+
+ }
+
+ 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, &param.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set intraperiod");
+ goto error;
+ }
+ }
+
+ 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, &param.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, &param2.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, &param3.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set max QP");
+ goto error;
+ }
+ }
+
+ if (config->encoding == MMAL_ENCODING_H264)
+ {
+ MMAL_PARAMETER_VIDEO_PROFILE_T param;
+ param.hdr.id = MMAL_PARAMETER_PROFILE;
+ param.hdr.size = sizeof(param);
+
+ 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, &param.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set H264 profile");
+ goto error;
+ }
+ }
+
+ if (config->encoding != MMAL_ENCODING_JPEG)
+ {
+ 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..
+ }
+ }
+
+ 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->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 (&param, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
+
+ 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, &param.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, &param.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set H264 intra-refresh values");
+ goto error;
+ }
+ }
+ }
+
+ 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");
+ // 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");
+ // Continue after warning
+ }
+#endif
+ }
+
+ // Enable component
+ status = mmal_component_enable(encoder);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to enable video encoder component");
+ goto error;
+ }
+
+ state->encoder_component = encoder;
+
+ if (config->verbose)
+ fprintf(stderr, "Encoder component done\n");
+
+ return status;
+
+ error:
+ if (encoder)
+ mmal_component_destroy(encoder);
+
+ state->encoder_component = NULL;
+
+ return status;
+}
+
+/**
+ * Destroy the encoder component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void destroy_encoder_component(RASPIVID_STATE *state)
+{
+ /* Empty the buffer header q */
+ 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);
+ }
+ }
+
+ // 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) {
+
+ 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);
+}
+
+void raspicapture_init()
+{
+ bcm_host_init();
+
+ // Register our application with the logging system
+ vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
+}
+
+RASPIVID_STATE *
+raspi_capture_setup(RASPIVID_CONFIG *config)
+{
+ // Our main data storage vessel..
+ RASPIVID_STATE *state;
+
+ MMAL_STATUS_T status = MMAL_SUCCESS;
+
+ /* Default everything to zero */
+ state = calloc(1, sizeof(RASPIVID_STATE));
+
+ /* 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()
+ */
+ // 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__);
+ return NULL;
+ }
+
+ 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);
+ return NULL;
+ }
+
+ state->encoded_buffer_q = mmal_queue_create();
+
+ return state;
+}
+
+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;
+
+ 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)
+ {
+ 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");
+
+ if (config->preview_parameters.wantPreview )
+ {
+ if (config->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);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("%s: Failed to connect camera to preview", __func__);
+ return FALSE;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ // 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;
+
+ state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
+
+ if (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 (config->demoMode)
+ {
+ // Run for the user specific time..
+ int num_iterations = config->timeout / config->demoInterval;
+ int i;
+
+ if (config->verbose)
+ fprintf(stderr, "Running in demo mode\n");
+
+ for (i=0;config->timeout == 0 || i<num_iterations;i++)
+ {
+ raspicamcontrol_cycle_test(state->camera_component);
+ vcos_sleep(state->config.demoInterval);
+ }
+ }
+
+ 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)
+ {
+ 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;q<num;q++)
+ {
+ MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_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; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
+ {
+ vcos_sleep(ABORT_INTERVAL);
+ if (state->callback_data.abort)
+ break;
+ }
+
+ if (config->verbose)
+ fprintf(stderr, "Finished capture\n");
+#endif
+
+ return (status == MMAL_SUCCESS);
+
+error:
+ raspi_capture_stop(state);
+
+ if (status != MMAL_SUCCESS) {
+ mmal_status_to_int(status);
+ raspicamcontrol_check_configuration(128);
+ }
+
+ return FALSE;
+}
+
+void
+raspi_capture_stop(RASPIVID_STATE *state)
+{
+ RASPIVID_CONFIG *config = &state->config;
+
+ if (config->verbose)
+ fprintf(stderr, "Closing down\n");
+
+ if (config->preview_parameters.wantPreview )
+ mmal_connection_destroy(state->preview_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);
+ }
+}
+
+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)
+ fclose(state->output_file);
+
+ /* Disable components */
+ if (state->encoder_component)
+ mmal_component_disable(state->encoder_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->preview_state);
+ destroy_camera_component(state);
+
+ if (state->encoded_buffer_q) {
+ mmal_queue_destroy(state->encoded_buffer_q);
+ state->encoded_buffer_q = NULL;
+ }
+
+ 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, gboolean dynamic)
+{
+ 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 (!dynamic)
+ return;
+
+ 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];
+
+ 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};
+ status = mmal_port_parameter_set(encoder_output, &param.hdr);
+ if (status != MMAL_SUCCESS)
+ 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, &param.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, &param2.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, &param3.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, &param.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 (&param, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
+ }
+ param.refresh_mode = config->intra_refresh_type;
+
+ status = mmal_port_parameter_set(encoder_output, &param.hdr);
+ 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 */
+ 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, &params->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
new file mode 100644
index 000000000..71e79b26e
--- /dev/null
+++ b/sys/rpicamsrc/RaspiCapture.h
@@ -0,0 +1,143 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013-2015 Jan Schmidt <jan@centricular.com>
+ *
+ * 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 <glib.h>
+#include <inttypes.h>
+
+#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"
+
+#define RPICAMSRC_MAX_FPS 1000
+
+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__)
+#undef vcos_log_error
+#define vcos_log_error GST_ERROR
+#undef vcos_log_warn
+#define vcos_log_warn GST_WARNING
+
+#define GST_FLOW_ERROR_TIMEOUT GST_FLOW_CUSTOM_ERROR
+
+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
+ int width; /// Requested width of image
+ int height; /// requested height of image
+ int bitrate; /// Requested bitrate
+ 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
+ /// 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
+
+ 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.
+
+ MMAL_FOURCC_T encoding; // Which encoding to use
+
+ int jpegQuality;
+ int jpegRestartInterval;
+
+ int useSTC;
+} RASPIVID_CONFIG;
+
+typedef struct RASPIVID_STATE_T RASPIVID_STATE;
+
+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, gboolean dynamic);
+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);
+
+G_END_DECLS
+
+#endif
diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c
new file mode 100644
index 000000000..032f0f16a
--- /dev/null
+++ b/sys/rpicamsrc/RaspiPreview.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
+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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <gst/gst.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 "RaspiPreview.h"
+#include "RaspiCapture.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_STATE *state,
+ RASPIPREVIEW_PARAMETERS *config)
+{
+ MMAL_COMPONENT_T *preview = 0;
+ MMAL_STATUS_T status;
+
+ 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);
+
+ if (status != MMAL_SUCCESS)
+ {
+ 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");
+ goto error;
+ }
+
+ if (!preview->input_num)
+ {
+ status = MMAL_ENOSYS;
+ vcos_log_error("No input ports found on component");
+ goto error;
+ }
+
+ 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);
+ 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;
+ }
+
+ return status;
+
+error:
+ 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, &param.hdr);
+ if (status == MMAL_ENOSYS)
+ status = MMAL_SUCCESS;
+
+ return status;
+}
+
+/**
+ * Destroy the preview component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+void raspipreview_destroy(RASPIPREVIEW_STATE *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 *config)
+{
+ 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;
+}
+
+/**
+ * Dump parameters as human readable to stdout
+ *
+ * @param state Pointer to parameter block
+ *
+ */
+void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *config)
+{
+ 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", config->previewWindow.x,
+ config->previewWindow.y, config->previewWindow.width,
+ config->previewWindow.height, config->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",
+ &params->previewWindow.x, &params->previewWindow.y,
+ &params->previewWindow.width, &params->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", &params->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 000000000..f5e14daf4
--- /dev/null
+++ b/sys/rpicamsrc/RaspiPreview.h
@@ -0,0 +1,75 @@
+/*
+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
+
+// 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 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
+{
+ 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.
+} RASPIPREVIEW_PARAMETERS;
+
+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_ */
diff --git a/sys/rpicamsrc/RaspiStill.c b/sys/rpicamsrc/RaspiStill.c
new file mode 100644
index 000000000..32747b866
--- /dev/null
+++ b/sys/rpicamsrc/RaspiStill.c
@@ -0,0 +1,1516 @@
+/*
+ * Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
+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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sysexits.h>
+
+#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 <semaphore.h>
+
+/// 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 <size>", 1 },
+ { CommandHeight, "-height", "h", "Set image height <size>", 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 <filename> (to write to stdout, use '-o -'). If not specified, no file is saved", 1 },
+ { CommandLink, "-latest", "l", "Link latest complete image to filename <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 <t>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;i<state->numExifTags;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 --<something> or -<abreviation of something>
+
+ 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;j<encoding_xref_size;j++)
+ {
+ if (strcmp(encoding_xref[j].format, argv[i+1]) == 0)
+ {
+ state->encoding = 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, &param_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;i<state->numExifTags && 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;i<num_iterations;i++)
+ {
+ raspicamcontrol_cycle_test(state.camera_component);
+ vcos_sleep(state.demoInterval);
+ }
+ }
+ else
+ {
+ int num_iterations = state.timelapse ? state.timeout / state.timelapse : 1;
+ int frame;
+ FILE *output_file = NULL;
+ char *use_filename = NULL; // Temporary filename while image being written
+ char *final_filename = NULL; // Name that gets file once complete
+ int64_t next_frame_ms = vcos_getmicrosecs64()/1000;
+
+ // If in timelapse mode, and timeout set to zero (or less), then take frames forever
+ for (frame = 1; (num_iterations <= 0) || (frame<=num_iterations); frame++)
+ {
+ if (state.timelapse)
+ {
+ int64_t this_delay_ms = next_frame_ms - vcos_getmicrosecs64()/1000;
+ if (this_delay_ms < 0)
+ { // We are already past the next exposure time
+ if (-this_delay_ms < -state.timelapse/2)
+ { // Less than a half frame late, take a frame and hope to catch up next time
+ next_frame_ms += state.timelapse;
+ vcos_log_error("Frame %d is %d ms late", frame, (int)(-this_delay_ms));
+ }
+ else
+ {
+ int nskip = 1 + (-this_delay_ms)/state.timelapse;
+ vcos_log_error("Skipping frame %d to restart at frame %d", frame, frame+nskip);
+ frame += nskip;
+ this_delay_ms += nskip * state.timelapse;
+ vcos_sleep(this_delay_ms);
+ next_frame_ms += (nskip + 1) * state.timelapse;
+ }
+ }
+ else
+ {
+ vcos_sleep(this_delay_ms);
+ next_frame_ms += 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
+ {
+ vcos_assert(use_filename == NULL && final_filename == NULL);
+ status = create_filenames(&final_filename, &use_filename, state.filename, frame);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to create filenames");
+ goto error;
+ }
+
+ if (state.verbose)
+ fprintf(stderr, "Opening output file %s\n", final_filename);
+ // Technically it is opening the temp~ filename which will be ranamed to the final 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
+ }
+
+ callback_data.file_handle = output_file;
+ }
+
+ // We only capture if a filename was specified and it opened
+ if (output_file)
+ {
+ int num, q;
+
+ // Must do this before the encoder output port is enabled since
+ // once enabled no further exif data is accepted
+ add_exif_tags(&state);
+
+ // Same with raw, apparently need to set it for each capture, whilst port
+ // is not enabled
+ if (state.wantRAW)
+ {
+ if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_ENABLE_RAW_CAPTURE, 1) != MMAL_SUCCESS)
+ {
+ vcos_log_error("RAW was requested, but failed to enable");
+ }
+ }
+
+ // Enable the encoder output port
+ 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);
+
+ // Send all the buffers to the encoder output port
+ num = mmal_queue_length(state.encoder_pool->queue);
+
+ for (q=0;q<num;q++)
+ {
+ MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.encoder_pool->queue);
+
+ 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 000000000..aad9edb7b
--- /dev/null
+++ b/sys/rpicamsrc/RaspiStillYUV.c
@@ -0,0 +1,957 @@
+/*
+ * Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
+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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <sysexits.h>
+
+#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 <semaphore.h>
+
+/// 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 <size>", 1 },
+ { CommandHeight, "-height", "h", "Set image height <size>", 1 },
+ { CommandOutput, "-output", "o", "Output filename <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 <t>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 --<something> or -<abreviation of something>
+
+ 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;q<num;q++)
+ {
+ MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.camera_pool->queue);
+
+ 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/gstrpicam-enums-template.c b/sys/rpicamsrc/gstrpicam-enums-template.c
new file mode 100644
index 000000000..8631751a5
--- /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 "@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 volatile gsize gtype_id = 0;
+
+ if (g_once_init_enter (&gtype_id)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ GType new_type = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ g_once_init_leave (&gtype_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
new file mode 100644
index 000000000..eb25042ba
--- /dev/null
+++ b/sys/rpicamsrc/gstrpicam-enums-template.h
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* Enumerations from "@basename@" */
+
+/*** END file-production ***/
+
+/*** BEGIN enumeration-production ***/
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+GType @enum_name@_get_type (void);
+
+/*** END enumeration-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/sys/rpicamsrc/gstrpicam_types.h b/sys/rpicamsrc/gstrpicam_types.h
new file mode 100644
index 000000000..5baa491e7
--- /dev/null
+++ b/sys/rpicamsrc/gstrpicam_types.h
@@ -0,0 +1,101 @@
+#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,
+ 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;
+
+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;
+
+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;
+
+typedef enum {
+ GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE = -1,
+ 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;
diff --git a/sys/rpicamsrc/gstrpicamsrc.c b/sys/rpicamsrc/gstrpicamsrc.c
new file mode 100644
index 000000000..544efe346
--- /dev/null
+++ b/sys/rpicamsrc/gstrpicamsrc.c
@@ -0,0 +1,1477 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013-2015 Jan Schmidt <jan@centricular.com>
+ *
+ * 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
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m rpicamsrc ! fakesink
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <gst/video/video.h>
+
+#include "gstrpicamsrc.h"
+#include "gstrpicam_types.h"
+#include "gstrpicam-enum-types.h"
+#include "gstrpicamsrcdeviceprovider.h"
+#include "RaspiCapture.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 (gst_rpi_cam_src_debug);
+
+/* comment out to use JPEG codec instead of MJPEG */
+// #define USE_JPEG_CODEC
+
+/* Filter signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_CAMERA_NUMBER,
+ PROP_BITRATE,
+ PROP_KEYFRAME_INTERVAL,
+ 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,
+ PROP_BRIGHTNESS,
+ PROP_SATURATION,
+ PROP_ISO,
+ PROP_VIDEO_STABILISATION,
+ PROP_EXPOSURE_COMPENSATION,
+ PROP_EXPOSURE_MODE,
+ PROP_EXPOSURE_METERING_MODE,
+ PROP_AWB_MODE,
+ PROP_AWB_GAIN_RED,
+ PROP_AWB_GAIN_BLUE,
+ 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,
+ PROP_QUANTISATION_PARAMETER,
+ PROP_INLINE_HEADERS,
+ PROP_SHUTTER_SPEED,
+ PROP_SENSOR_MODE,
+ 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,
+#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION
+ PROP_VIDEO_DIRECTION,
+#endif
+ PROP_JPEG_QUALITY,
+ PROP_USE_STC
+};
+
+#define CAMERA_DEFAULT 0
+
+#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
+#define SATURATION_DEFAULT 0
+#define ISO_DEFAULT 0
+#define VIDEO_STABILISATION_DEFAULT FALSE
+#define EXPOSURE_COMPENSATION_DEFAULT 0
+#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
+
+#define DEFAULT_JPEG_QUALITY 50
+
+/*
+ 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 JPEG_CAPS \
+ "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) nal, " \
+ "profile = (string) { constrained-baseline, 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; }
+#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,
+ GST_STATIC_CAPS ( H264_CAPS "; " JPEG_CAPS "; " RAW_CAPS)
+ );
+
+
+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);
+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_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 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);
+#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)
+
+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 / 3240x2464 15fps w/ v.2 board",
+ "2592x1944-fast"},
+ {C_ENUM (GST_RPI_CAM_SRC_SENSOR_MODE_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",
+ "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)
+{
+ GObjectClass *gobject_class;
+ 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->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,
+ 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,
+ 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",
+ -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",
+ "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_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,
+ 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_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 (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_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_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_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_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,
+ 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_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));
+ 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_SRC_TYPE_IMAGE_EFFECT,
+ GST_RPI_CAM_SRC_IMAGEFX_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#if 0
+ 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));
+ 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));
+ 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));
+ 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_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",
+ "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_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,
+ 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 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 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,
+ "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",
+ "Raspberry Pi camera module source", "Jan Schmidt <jan@centricular.com>");
+ 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);
+ 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 ();
+}
+
+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);
+ src->capture_config.intraperiod = KEYFRAME_INTERVAL_DEFAULT;
+ src->capture_config.verbose = 1;
+ src->capture_config.useSTC = TRUE;
+
+ g_mutex_init (&src->config_lock);
+
+ /* 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);
+ 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);
+
+ 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;
+ } 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;
+
+ g_mutex_unlock (&src->config_lock);
+
+ 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);
+
+ 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")) {
+ value = src->capture_config.camera_parameters.brightness;
+ } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
+ value = src->capture_config.camera_parameters.contrast;
+ }
+
+ g_mutex_unlock (&src->config_lock);
+
+ return value;
+}
+
+static GstColorBalanceType
+gst_rpi_cam_src_colorbalance_get_balance_type (GstColorBalance * balance)
+{
+ return GST_COLOR_BALANCE_HARDWARE;
+}
+
+#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION
+static void gst_rpi_cam_src_set_orientation (GstRpiCamSrc * src, GstVideoOrientationMethod orientation)
+{
+ switch (orientation) {
+ 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_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_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_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_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_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_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_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_VIDEO_ORIENTATION_CUSTOM:
+ break;
+ default:
+ GST_WARNING_OBJECT (src, "unsupported orientation %d", orientation);
+ break;
+ }
+ src->orientation =
+ 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)
+{
+ 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 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);
+ 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);
+
+ 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);
+ 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);
+
+ 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)
+{
+ 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_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;
+ 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_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;
+ 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:
+ gst_rpi_cam_src_reset_custom_orientation(src);
+ 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:
+ 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:
+ 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;
+ 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);
+ break;
+ 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,
+ 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.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;
+ 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
+ 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;
+ }
+
+ g_mutex_unlock (&src->config_lock);
+}
+
+static void
+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);
+ break;
+ 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;
+ 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_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;
+ 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;
+ 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_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);
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION
+ case PROP_VIDEO_DIRECTION:
+ 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;
+ }
+ g_mutex_unlock (&src->config_lock);
+}
+
+static gboolean
+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 */
+ src->capture_config.change_flags = 0;
+ g_mutex_unlock (&src->config_lock);
+ if (src->capture_state == NULL)
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+gst_rpi_cam_src_stop (GstBaseSrc * parent)
+{
+ GstRpiCamSrc *src = GST_RPICAMSRC (parent);
+ if (src->started)
+ raspi_capture_stop (src->capture_state);
+ raspi_capture_free (src->capture_state);
+ src->capture_state = NULL;
+ 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;
+ }
+ } 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)
+{
+ 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);
+ 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,
+ 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,
+ RPICAMSRC_MAX_FPS, 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;
+ 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);
+ 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, "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"))
+ 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")) {
+#ifdef USE_JPEG_CODEC
+ src->capture_config.encoding = MMAL_ENCODING_JPEG;
+#else
+ src->capture_config.encoding = MMAL_ENCODING_MJPEG;
+#endif
+ }
+ 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;
+ 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;
+}
+
+static gboolean
+gst_rpi_cam_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
+{
+ GST_LOG_OBJECT (bsrc, "In decide_allocation");
+ 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 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", 30, 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;
+ GstClock *clock = NULL;
+ 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;
+ }
+
+ 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);
+
+ g_mutex_lock (&src->config_lock);
+ if (src->capture_config.change_flags) {
+ raspi_capture_update_config (src->capture_state, &src->capture_config, TRUE);
+ 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) {
+ GST_LOG_OBJECT (src, "Made buffer of size %" G_GSIZE_FORMAT,
+ gst_buffer_get_size (*buf));
+ /* 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 (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;
+}
+
+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;
+}
+
+#ifndef PACKAGE
+#define PACKAGE "gstrpicamsrc"
+#endif
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ rpicamsrc,
+ "Raspberry Pi Camera Source",
+ plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
diff --git a/sys/rpicamsrc/gstrpicamsrc.h b/sys/rpicamsrc/gstrpicamsrc.h
new file mode 100644
index 000000000..68fdc1829
--- /dev/null
+++ b/sys/rpicamsrc/gstrpicamsrc.h
@@ -0,0 +1,113 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013 Jan Schmidt <jan@centricular.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/base/gstpushsrc.h>
+#include <gst/pbutils/pbutils.h> /* only used for GST_PLUGINS_BASE_VERSION_* */
+#include "RaspiCapture.h"
+
+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))
+
+#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;
+
+struct _GstRpiCamSrc
+{
+ GstPushSrc parent;
+
+ GstPad *video_srcpad;
+
+ RASPIVID_CONFIG capture_config;
+ RASPIVID_STATE *capture_state;
+ gboolean started;
+
+ GMutex config_lock;
+
+ /* channels for interface */
+ GList *channels;
+
+#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION
+ GstVideoOrientationMethod orientation;
+#endif
+
+ GstClockTime duration;
+};
+
+struct _GstRpiCamSrcClass
+{
+ GstPushSrcClass parent_class;
+};
+
+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__ */
diff --git a/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c
new file mode 100644
index 000000000..ff8083227
--- /dev/null
+++ b/sys/rpicamsrc/gstrpicamsrcdeviceprovider.c
@@ -0,0 +1,149 @@
+/* GStreamer Raspberry Pi Camera Source Device Provider
+ * Copyright (C) 2014 Tim-Philipp Müller <tim@centricular.com>
+ *
+ * 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 <string.h>
+
+#if GST_CHECK_VERSION (1,4,0)
+
+#include "RaspiCapture.h"
+
+/* 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 <tim@centricular.com>");
+}
+
+static void
+gst_rpi_cam_src_device_provider_init (GstRpiCamSrcDeviceProvider * provider)
+{
+ 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.");
+
+ 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, RPICAMSRC_MAX_FPS, 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 000000000..cd5e9aad3
--- /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 <tim@centricular.com>
+ *
+ * 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 <gst/gst.h>
+
+#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__ */
diff --git a/sys/rpicamsrc/meson.build b/sys/rpicamsrc/meson.build
new file mode 100644
index 000000000..c3a1651ab
--- /dev/null
+++ b/sys/rpicamsrc/meson.build
@@ -0,0 +1,24 @@
+rpicamsrc_sources = [
+ 'gstrpicamsrc.c',
+ 'gstrpicamsrcdeviceprovider.c',
+ 'RaspiCapture.c',
+ 'RaspiCamControl.c',
+ 'RaspiPreview.c',
+ 'RaspiCLI.c',
+]
+
+# glib-mkenums
+gnome = import('gnome')
+
+enums = gnome.mkenums_simple('gstrpicam-enum-types',
+ sources: 'gstrpicam_types.h',
+ identifier_prefix: 'GstRpiCamSrc',
+ symbol_prefix: 'gst_rpi_cam_src')
+
+library('gstrpicamsrc',
+ rpicamsrc_sources, enums,
+ c_args : gst_rpicamsrc_args,
+ include_directories : config_inc,
+ dependencies : [gst_dep, gstbase_dep, gstvideo_dep] + mmal_deps,
+ install : true,
+ install_dir : plugins_install_dir)