rpicamsrc: checkpoint
authorJan Schmidt <thaytan@noraisin.net>
Fri, 11 Oct 2013 08:17:05 +0000 (19:17 +1100)
committerTim-Philipp Müller <tim@centricular.com>
Fri, 10 Jul 2020 15:42:04 +0000 (16:42 +0100)
sys/rpicamsrc/RaspiCamControl.c [new file with mode: 0644]
sys/rpicamsrc/RaspiCamControl.h [new file with mode: 0644]
sys/rpicamsrc/RaspiCapture.c [new file with mode: 0644]
sys/rpicamsrc/RaspiCapture.h [new file with mode: 0644]
sys/rpicamsrc/RaspiPreview.c [new file with mode: 0644]
sys/rpicamsrc/RaspiPreview.h [new file with mode: 0644]
sys/rpicamsrc/RaspiStill.c [new file with mode: 0644]
sys/rpicamsrc/RaspiStillYUV.c [new file with mode: 0644]
sys/rpicamsrc/gstrpicamsrc.c
sys/rpicamsrc/gstrpicamsrc.h

diff --git a/sys/rpicamsrc/RaspiCamControl.c b/sys/rpicamsrc/RaspiCamControl.c
new file mode 100644 (file)
index 0000000..f379015
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+ * 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 "interface/vcos/vcos.h"
+
+#include "interface/vmcs_host/vc_vchi_gencmd.h"
+#include "interface/mmal/mmal.h"
+#include "interface/mmal/mmal_logging.h"
+#include "interface/mmal/util/mmal_util.h"
+#include "interface/mmal/util/mmal_util_params.h"
+#include "interface/mmal/util/mmal_default_components.h"
+#include "RaspiCamControl.h"
+
+#if 0
+/// Structure to cross reference exposure strings against the MMAL parameter equivalent
+static XREF_T  exposure_map[] =
+{
+   {"auto",          MMAL_PARAM_EXPOSUREMODE_AUTO},
+   {"night",         MMAL_PARAM_EXPOSUREMODE_NIGHT},
+   {"nightpreview",  MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW},
+   {"backlight",     MMAL_PARAM_EXPOSUREMODE_BACKLIGHT},
+   {"spotlight",     MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT},
+   {"sports",        MMAL_PARAM_EXPOSUREMODE_SPORTS},
+   {"snow",          MMAL_PARAM_EXPOSUREMODE_SNOW},
+   {"beach",         MMAL_PARAM_EXPOSUREMODE_BEACH},
+   {"verylong",      MMAL_PARAM_EXPOSUREMODE_VERYLONG},
+   {"fixedfps",      MMAL_PARAM_EXPOSUREMODE_FIXEDFPS},
+   {"antishake",     MMAL_PARAM_EXPOSUREMODE_ANTISHAKE},
+   {"fireworks",     MMAL_PARAM_EXPOSUREMODE_FIREWORKS}
+};
+
+static const int exposure_map_size = sizeof(exposure_map) / sizeof(exposure_map[0]);
+
+/// Structure to cross reference awb strings against the MMAL parameter equivalent
+static XREF_T awb_map[] =
+{
+   {"off",           MMAL_PARAM_AWBMODE_OFF},
+   {"auto",          MMAL_PARAM_AWBMODE_AUTO},
+   {"sun",           MMAL_PARAM_AWBMODE_SUNLIGHT},
+   {"cloud",         MMAL_PARAM_AWBMODE_CLOUDY},
+   {"shade",         MMAL_PARAM_AWBMODE_SHADE},
+   {"tungsten",      MMAL_PARAM_AWBMODE_TUNGSTEN},
+   {"fluorescent",   MMAL_PARAM_AWBMODE_FLUORESCENT},
+   {"incandescent",  MMAL_PARAM_AWBMODE_INCANDESCENT},
+   {"flash",         MMAL_PARAM_AWBMODE_FLASH},
+   {"horizon",       MMAL_PARAM_AWBMODE_HORIZON}
+};
+
+static const int awb_map_size = sizeof(awb_map) / sizeof(awb_map[0]);
+
+/// Structure to cross reference image effect against the MMAL parameter equivalent
+static XREF_T imagefx_map[] =
+{
+   {"none",          MMAL_PARAM_IMAGEFX_NONE},
+   {"negative",      MMAL_PARAM_IMAGEFX_NEGATIVE},
+   {"solarise",      MMAL_PARAM_IMAGEFX_SOLARIZE},
+   {"sketch",        MMAL_PARAM_IMAGEFX_SKETCH},
+   {"denoise",       MMAL_PARAM_IMAGEFX_DENOISE},
+   {"emboss",        MMAL_PARAM_IMAGEFX_EMBOSS},
+   {"oilpaint",      MMAL_PARAM_IMAGEFX_OILPAINT},
+   {"hatch",         MMAL_PARAM_IMAGEFX_HATCH},
+   {"gpen",          MMAL_PARAM_IMAGEFX_GPEN},
+   {"pastel",        MMAL_PARAM_IMAGEFX_PASTEL},
+   {"watercolour",   MMAL_PARAM_IMAGEFX_WATERCOLOUR},
+   {"film",          MMAL_PARAM_IMAGEFX_FILM},
+   {"blur",          MMAL_PARAM_IMAGEFX_BLUR},
+   {"saturation",    MMAL_PARAM_IMAGEFX_SATURATION},
+   {"colourswap",    MMAL_PARAM_IMAGEFX_COLOURSWAP},
+   {"washedout",     MMAL_PARAM_IMAGEFX_WASHEDOUT},
+   {"posterise",     MMAL_PARAM_IMAGEFX_POSTERISE},
+   {"colourpoint",   MMAL_PARAM_IMAGEFX_COLOURPOINT},
+   {"colourbalance", MMAL_PARAM_IMAGEFX_COLOURBALANCE},
+   {"cartoon",       MMAL_PARAM_IMAGEFX_CARTOON}
+ };
+
+static const int imagefx_map_size = sizeof(imagefx_map) / sizeof(imagefx_map[0]);
+
+static XREF_T metering_mode_map[] =
+{
+   {"average",       MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE},
+   {"spot",          MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT},
+   {"backlit",       MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT},
+   {"matrix",        MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX}
+};
+
+static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]);
+
+
+#define CommandSharpness   0
+#define CommandContrast    1
+#define CommandBrightness  2
+#define CommandSaturation  3
+#define CommandISO         4
+#define CommandVideoStab   5
+#define CommandEVComp      6
+#define CommandExposure  7
+#define CommandAWB         8
+#define CommandImageFX     9
+#define CommandColourFX    10
+#define CommandMeterMode   11
+#define CommandRotation    12
+#define CommandHFlip       13
+#define CommandVFlip       14
+#define CommandROI         15
+
+static COMMAND_LIST  cmdline_commands[] =
+{
+   {CommandSharpness,   "-sharpness", "sh", "Set image sharpness (-100 to 100)",  1},
+   {CommandContrast,    "-contrast",  "co", "Set image contrast (-100 to 100)",  1},
+   {CommandBrightness,  "-brightness","br", "Set image brightness (0 to 100)",  1},
+   {CommandSaturation,  "-saturation","sa", "Set image saturation (-100 to 100)", 1},
+   {CommandISO,         "-ISO",       "ISO","Set capture ISO",  1},
+   {CommandVideoStab,   "-vstab",     "vs", "Turn on video stablisation", 0},
+   {CommandEVComp,      "-ev",        "ev", "Set EV compensation",  1},
+   {CommandExposure,    "-exposure",  "ex", "Set exposure mode (see Notes)", 1},
+   {CommandAWB,         "-awb",       "awb","Set AWB mode (see Notes)", 1},
+   {CommandImageFX,     "-imxfx",     "ifx","Set image effect (see Notes)", 1},
+   {CommandColourFX,    "-colfx",     "cfx","Set colour effect (U:V)",  1},
+   {CommandMeterMode,   "-metering",  "mm", "Set metering mode (see Notes)", 1},
+   {CommandRotation,    "-rotation",  "rot","Set image rotation (0-359)", 1},
+   {CommandHFlip,       "-hflip",     "hf", "Set horizontal flip", 0},
+   {CommandVFlip,       "-vflip",     "vf", "Set vertical flip", 0},
+   {CommandROI,         "-roi",       "roi","Set region of interest (x,y,w,d as normalised coordinates [0.0-1.0])", 1}
+};
+
+static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
+
+static const int exposure_map_size = 1;
+static const int awb_map_size = 1;
+static const int metering_mode_map_size = 1;
+
+#define parameter_reset -99999
+
+/**
+ * Update the passed in parameter according to the rest of the parameters
+ * passed in.
+ *
+ *
+ * @return 0 if reached end of cycle for this parameter, !0 otherwise
+ */
+static int update_cycle_parameter(int *option, int min, int max, int increment)
+{
+   vcos_assert(option);
+   if (!option)
+      return 0;
+
+   if (*option == parameter_reset)
+      *option = min - increment;
+
+   *option += increment;
+
+   if (*option > max)
+   {
+      *option = parameter_reset;
+      return 0;
+   }
+   else
+      return 1;
+}
+
+
+/**
+ * Test/Demo code to cycle through a bunch of camera settings
+ * This code is pretty hacky so please don't complain!!
+ * It only does stuff that should have a visual impact (hence demo!)
+ * This will override any user supplied parameters
+ *
+ * Each call of this function will move on to the next setting
+ *
+ * @param camera Pointer to the camera to change settings on.
+ * @return 0 if reached end of complete sequence, !0 otherwise
+ */
+
+int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera)
+{
+   static int parameter = 0;
+   static int parameter_option = parameter_reset; // which value the parameter currently has
+
+   vcos_assert(camera);
+
+   // We are going to cycle through all the relevant entries in the parameter block
+   // and send options to the camera.
+   if (parameter == 0)
+   {
+      // sharpness
+      if (update_cycle_parameter(&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;
+   }
+
+   }
+
+   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");
+}
+
+/**
+ * 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, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v);
+   fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No");
+   fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h);
+}
+#endif
+
+/**
+ * Convert a MMAL status return value to a simple boolean of success
+ * ALso displays a fault if code is not success
+ *
+ * @param status The error code to convert
+ * @return 0 if status is sucess, 1 otherwise
+ */
+int mmal_status_to_int(MMAL_STATUS_T status)
+{
+   if (status == MMAL_SUCCESS)
+      return 0;
+   else
+   {
+      switch (status)
+      {
+      case MMAL_ENOMEM :   vcos_log_error("Out of memory"); break;
+      case MMAL_ENOSPC :   vcos_log_error("Out of resources (other than memory)"); break;
+      case MMAL_EINVAL:    vcos_log_error("Argument is invalid"); break;
+      case MMAL_ENOSYS :   vcos_log_error("Function not implemented"); break;
+      case MMAL_ENOENT :   vcos_log_error("No such file or directory"); break;
+      case MMAL_ENXIO :    vcos_log_error("No such device or address"); break;
+      case MMAL_EIO :      vcos_log_error("I/O error"); break;
+      case MMAL_ESPIPE :   vcos_log_error("Illegal seek"); break;
+      case MMAL_ECORRUPT : vcos_log_error("Data is corrupt \attention FIXME: not POSIX"); break;
+      case MMAL_ENOTREADY :vcos_log_error("Component is not ready \attention FIXME: not POSIX"); break;
+      case MMAL_ECONFIG :  vcos_log_error("Component is not configured \attention FIXME: not POSIX"); break;
+      case MMAL_EISCONN :  vcos_log_error("Port is already connected "); break;
+      case MMAL_ENOTCONN : vcos_log_error("Port is disconnected"); break;
+      case MMAL_EAGAIN :   vcos_log_error("Resource temporarily unavailable. Try again later"); break;
+      case MMAL_EFAULT :   vcos_log_error("Bad address"); break;
+      default :            vcos_log_error("Unknown status error"); break;
+      }
+
+      return 1;
+   }
+}
+
+/**
+ * Give the supplied parameter block a set of default values
+ * @params Pointer to parameter block
+ */
+void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params)
+{
+   vcos_assert(params);
+
+   params->sharpness = 0;
+   params->contrast = 0;
+   params->brightness = 50;
+   params->saturation = 0;
+   params->ISO = 0;                    // 0 = auto
+   params->videoStabilisation = 0;
+   params->exposureCompensation = 0;
+   params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO;
+   params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE;
+   params->awbMode = MMAL_PARAM_AWBMODE_AUTO;
+   params->imageEffect = MMAL_PARAM_IMAGEFX_NONE;
+   params->colourEffects.enable = 0;
+   params->colourEffects.u = 128;
+   params->colourEffects.v = 128;
+   params->rotation = 0;
+   params->hflip = params->vflip = 0;
+   params->roi.x = params->roi.y = 0.0;
+   params->roi.w = params->roi.h = 1.0;
+}
+
+/**
+ * Get all the current camera parameters from specified camera component
+ * @param camera Pointer to camera component
+ * @param params Pointer to parameter block to accept settings
+ * @return 0 if successful, non-zero if unsuccessful
+ */
+int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params)
+{
+   vcos_assert(camera);
+   vcos_assert(params);
+
+   if (!camera || !params)
+      return 1;
+
+/* TODO : Write these get functions
+   params->sharpness = raspicamcontrol_get_sharpness(camera);
+   params->contrast = raspicamcontrol_get_contrast(camera);
+   params->brightness = raspicamcontrol_get_brightness(camera);
+   params->saturation = raspicamcontrol_get_saturation(camera);
+   params->ISO = raspicamcontrol_get_ISO(camera);
+   params->videoStabilisation = raspicamcontrol_get_video_stabilisation(camera);
+   params->exposureCompensation = raspicamcontrol_get_exposure_compensation(camera);
+   params->exposureMode = raspicamcontrol_get_exposure_mode(camera);
+   params->awbMode = raspicamcontrol_get_awb_mode(camera);
+   params->imageEffect = raspicamcontrol_get_image_effect(camera);
+   params->colourEffects = raspicamcontrol_get_colour_effect(camera);
+   params->thumbnailConfig = raspicamcontrol_get_thumbnail_config(camera);
+*/
+   return 0;
+}
+
+/**
+ * Set the specified camera to all the specified settings
+ * @param camera Pointer to camera component
+ * @param params Pointer to parameter block containing parameters
+ * @return 0 if successful, none-zero if unsuccessful.
+ */
+int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params)
+{
+   int result;
+
+   result  = raspicamcontrol_set_saturation(camera, params->saturation);
+   result += raspicamcontrol_set_sharpness(camera, params->sharpness);
+   result += raspicamcontrol_set_contrast(camera, params->contrast);
+   result += raspicamcontrol_set_brightness(camera, params->brightness);
+   result += raspicamcontrol_set_ISO(camera, params->ISO);
+   result += raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation);
+   result += raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation);
+   result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode);
+   result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode);
+   result += raspicamcontrol_set_awb_mode(camera, params->awbMode);
+   result += raspicamcontrol_set_imageFX(camera, params->imageEffect);
+   result += raspicamcontrol_set_colourFX(camera, &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);
+
+   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));
+}
+
+/**
+ * Set the image effect for the images
+ * @param camera Pointer to camera component
+ * @param imageFX Value from
+ *   - MMAL_PARAM_IMAGEFX_NONE,
+ *   - MMAL_PARAM_IMAGEFX_NEGATIVE,
+ *   - MMAL_PARAM_IMAGEFX_SOLARIZE,
+ *   - MMAL_PARAM_IMAGEFX_POSTERIZE,
+ *   - MMAL_PARAM_IMAGEFX_WHITEBOARD,
+ *   - MMAL_PARAM_IMAGEFX_BLACKBOARD,
+ *   - MMAL_PARAM_IMAGEFX_SKETCH,
+ *   - MMAL_PARAM_IMAGEFX_DENOISE,
+ *   - MMAL_PARAM_IMAGEFX_EMBOSS,
+ *   - MMAL_PARAM_IMAGEFX_OILPAINT,
+ *   - MMAL_PARAM_IMAGEFX_HATCH,
+ *   - MMAL_PARAM_IMAGEFX_GPEN,
+ *   - MMAL_PARAM_IMAGEFX_PASTEL,
+ *   - MMAL_PARAM_IMAGEFX_WATERCOLOUR,
+ *   - MMAL_PARAM_IMAGEFX_FILM,
+ *   - MMAL_PARAM_IMAGEFX_BLUR,
+ *   - MMAL_PARAM_IMAGEFX_SATURATION,
+ *   - MMAL_PARAM_IMAGEFX_COLOURSWAP,
+ *   - MMAL_PARAM_IMAGEFX_WASHEDOUT,
+ *   - MMAL_PARAM_IMAGEFX_POSTERISE,
+ *   - MMAL_PARAM_IMAGEFX_COLOURPOINT,
+ *   - MMAL_PARAM_IMAGEFX_COLOURBALANCE,
+ *   - MMAL_PARAM_IMAGEFX_CARTOON,
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX)
+{
+   MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX};
+
+   if (!camera)
+      return 1;
+
+   return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr));
+}
+
+/* TODO :what to do with the image effects parameters?
+   MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)},
+                              imageFX, 0, {0}};
+mmal_port_parameter_set(camera->control, &imfx_param.hdr);
+                             */
+
+/**
+ * Set the colour effect  for images (Set UV component)
+ * @param camera Pointer to camera component
+ * @param colourFX  Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white)
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX)
+{
+   MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0};
+
+   if (!camera)
+      return 1;
+
+   colfx.enable = colourFX->enable;
+   colfx.u = colourFX->u;
+   colfx.v = colourFX->v;
+
+   return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr));
+
+}
+
+
+/**
+ * Set the rotation of the image
+ * @param camera Pointer to camera component
+ * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only)
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation)
+{
+   int ret;
+   int my_rotation = ((rotation % 360 ) / 90) * 90;
+
+   ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation);
+   mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation);
+   mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation);
+
+   return ret;
+}
+
+/**
+ * Set the flips state of the image
+ * @param camera Pointer to camera component
+ * @param hflip If true, horizontally flip the image
+ * @param vflip If true, vertically flip the image
+ *
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip)
+{
+   MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE};
+
+   if (hflip && vflip)
+      mirror.value = MMAL_PARAM_MIRROR_BOTH;
+   else
+   if (hflip)
+      mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL;
+   else
+   if (vflip)
+      mirror.value = MMAL_PARAM_MIRROR_VERTICAL;
+
+   mmal_port_parameter_set(camera->output[0], &mirror.hdr);
+   mmal_port_parameter_set(camera->output[1], &mirror.hdr);
+   return mmal_port_parameter_set(camera->output[2], &mirror.hdr);
+}
+
+/**
+ * Set the ROI of the sensor to use for captures/preview
+ * @param camera Pointer to camera component
+ * @param rect   Normalised coordinates of ROI rectangle
+ *
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect)
+{
+   MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}};
+
+   crop.rect.x = (65536 * rect.x);
+   crop.rect.y = (65536 * rect.y);
+   crop.rect.width = (65536 * rect.w);
+   crop.rect.height = (65536 * rect.h);
+
+   return mmal_port_parameter_set(camera->control, &crop.hdr);
+}
+
+
+/**
+ * Asked GPU how much memory it has allocated
+ *
+ * @return amount of memory in MB
+ */
+static int raspicamcontrol_get_mem_gpu(void)
+{
+   char response[80] = "";
+   int gpu_mem = 0;
+   if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0)
+      vc_gencmd_number_property(response, "gpu", &gpu_mem);
+   return gpu_mem;
+}
+
+/**
+ * Ask GPU about its camera abilities
+ * @param supported None-zero if software supports the camera 
+ * @param detected  None-zero if a camera has been detected
+ */
+static void raspicamcontrol_get_camera(int *supported, int *detected)
+{
+   char response[80] = "";
+   if (vc_gencmd(response, sizeof response, "get_camera") == 0)
+   {
+      if (supported)
+         vc_gencmd_number_property(response, "supported", supported);
+      if (detected)
+         vc_gencmd_number_property(response, "detected", detected);
+   }
+}
+
+/**
+ * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities
+ * @param supported None-zero if software supports the camera 
+ * @param detected  None-zero if a camera has been detected
+ */
+void raspicamcontrol_check_configuration(int min_gpu_mem)
+{
+   int gpu_mem = raspicamcontrol_get_mem_gpu();
+   int supported = 0, detected = 0;
+   raspicamcontrol_get_camera(&supported, &detected);
+   if (!supported)
+      vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n");
+   else if (gpu_mem < min_gpu_mem)
+      vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem);
+   else if (!detected)
+      vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n");
+   else
+      vcos_log_error("Failed to run camera app. Please check for firmware updates\n");
+}
+
diff --git a/sys/rpicamsrc/RaspiCamControl.h b/sys/rpicamsrc/RaspiCamControl.h
new file mode 100644 (file)
index 0000000..847b447
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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.
+*/
+
+#ifndef RASPICAMCONTROL_H_
+#define RASPICAMCONTROL_H_
+
+/* Various parameters
+ *
+ * Exposure Mode
+ *          MMAL_PARAM_EXPOSUREMODE_OFF,
+            MMAL_PARAM_EXPOSUREMODE_AUTO,
+            MMAL_PARAM_EXPOSUREMODE_NIGHT,
+            MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW,
+            MMAL_PARAM_EXPOSUREMODE_BACKLIGHT,
+            MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT,
+            MMAL_PARAM_EXPOSUREMODE_SPORTS,
+            MMAL_PARAM_EXPOSUREMODE_SNOW,
+            MMAL_PARAM_EXPOSUREMODE_BEACH,
+            MMAL_PARAM_EXPOSUREMODE_VERYLONG,
+            MMAL_PARAM_EXPOSUREMODE_FIXEDFPS,
+            MMAL_PARAM_EXPOSUREMODE_ANTISHAKE,
+            MMAL_PARAM_EXPOSUREMODE_FIREWORKS,
+ *
+ * AWB Mode
+ *          MMAL_PARAM_AWBMODE_OFF,
+            MMAL_PARAM_AWBMODE_AUTO,
+            MMAL_PARAM_AWBMODE_SUNLIGHT,
+            MMAL_PARAM_AWBMODE_CLOUDY,
+            MMAL_PARAM_AWBMODE_SHADE,
+            MMAL_PARAM_AWBMODE_TUNGSTEN,
+            MMAL_PARAM_AWBMODE_FLUORESCENT,
+            MMAL_PARAM_AWBMODE_INCANDESCENT,
+            MMAL_PARAM_AWBMODE_FLASH,
+            MMAL_PARAM_AWBMODE_HORIZON,
+ *
+ * Image FX
+            MMAL_PARAM_IMAGEFX_NONE,
+            MMAL_PARAM_IMAGEFX_NEGATIVE,
+            MMAL_PARAM_IMAGEFX_SOLARIZE,
+            MMAL_PARAM_IMAGEFX_POSTERIZE,
+            MMAL_PARAM_IMAGEFX_WHITEBOARD,
+            MMAL_PARAM_IMAGEFX_BLACKBOARD,
+            MMAL_PARAM_IMAGEFX_SKETCH,
+            MMAL_PARAM_IMAGEFX_DENOISE,
+            MMAL_PARAM_IMAGEFX_EMBOSS,
+            MMAL_PARAM_IMAGEFX_OILPAINT,
+            MMAL_PARAM_IMAGEFX_HATCH,
+            MMAL_PARAM_IMAGEFX_GPEN,
+            MMAL_PARAM_IMAGEFX_PASTEL,
+            MMAL_PARAM_IMAGEFX_WATERCOLOUR,
+            MMAL_PARAM_IMAGEFX_FILM,
+            MMAL_PARAM_IMAGEFX_BLUR,
+            MMAL_PARAM_IMAGEFX_SATURATION,
+            MMAL_PARAM_IMAGEFX_COLOURSWAP,
+            MMAL_PARAM_IMAGEFX_WASHEDOUT,
+            MMAL_PARAM_IMAGEFX_POSTERISE,
+            MMAL_PARAM_IMAGEFX_COLOURPOINT,
+            MMAL_PARAM_IMAGEFX_COLOURBALANCE,
+            MMAL_PARAM_IMAGEFX_CARTOON,
+
+ */
+
+
+
+// There isn't actually a MMAL structure for the following, so make one
+typedef struct
+{
+   int enable;       /// Turn colourFX on or off
+   int u,v;          /// U and V to use
+} MMAL_PARAM_COLOURFX_T;
+
+typedef struct
+{
+   int enable;
+   int width,height;
+   int quality;
+} MMAL_PARAM_THUMBNAIL_CONFIG_T;
+
+typedef struct
+{
+   double x;
+   double y;
+   double w;
+   double h;
+} PARAM_FLOAT_RECT_T;
+
+/// struct contain camera settings
+typedef struct
+{
+   int sharpness;             /// -100 to 100
+   int contrast;              /// -100 to 100
+   int brightness;            ///  0 to 100
+   int saturation;            ///  -100 to 100
+   int ISO;                   ///  TODO : what range?
+   int videoStabilisation;    /// 0 or 1 (false or true)
+   int exposureCompensation;  /// -10 to +10 ?
+   MMAL_PARAM_EXPOSUREMODE_T exposureMode;
+   MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode;
+   MMAL_PARAM_AWBMODE_T awbMode;
+   MMAL_PARAM_IMAGEFX_T imageEffect;
+   MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters;
+   MMAL_PARAM_COLOURFX_T colourEffects;
+   int rotation;              /// 0-359
+   int hflip;                 /// 0 or 1
+   int vflip;                 /// 0 or 1
+   PARAM_FLOAT_RECT_T  roi;   /// region of interest to use on the sensor. Normalised [0,1] values in the rect
+} RASPICAM_CAMERA_PARAMETERS;
+
+
+void raspicamcontrol_check_configuration(int min_gpu_mem);
+
+int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2);
+void raspicamcontrol_display_help();
+int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera);
+
+int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params);
+int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params);
+void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params);
+
+void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params);
+
+void raspicamcontrol_check_configuration(int min_gpu_mem);
+
+// Individual setting functions
+int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation);
+int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness);
+int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast);
+int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness);
+int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO);
+int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode);
+int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation);
+int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp);
+int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode);
+int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode);
+int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX);
+int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX);
+int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation);
+int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip);
+int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect);
+
+//Individual getting functions
+int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera);
+int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera);
+int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera);
+int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera);
+int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera);
+MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera);
+int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera);
+int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera);
+MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera);
+MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera);
+MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera);
+MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera);
+MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera);
+
+
+#endif /* RASPICAMCONTROL_H_ */
diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c
new file mode 100644 (file)
index 0000000..d6f9b12
--- /dev/null
@@ -0,0 +1,1211 @@
+/*
+ * 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 RaspiCapture.c
+ *
+ * Modification of the RaspiVid command line capture program for GStreamer
+ * use.
+ *
+ * \date 28th Feb 2013, 11 Oct 2013
+ * \Author: James Hughes, Jan Schmidt
+ *
+ * Description
+ *
+ * 3 components are created; camera, preview and video encoder.
+ * Camera component has three ports, preview, video and stills.
+ * This program connects preview and stills to the preview and video
+ * encoder. Using mmal we don't need to worry about buffers between these
+ * components, but we do need to handle buffers from the encoder, which
+ * are simply written straight to the file in the requisite buffer callback.
+ *
+ * We use the RaspiCamControl code to handle the specific camera settings.
+ * We use the RaspiPreview code to handle the (generic) preview window
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <sysexits.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 "RaspiCamControl.h"
+#include "RaspiPreview.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
+
+// Video format information
+#define VIDEO_FRAME_RATE_NUM 30
+#define VIDEO_FRAME_RATE_DEN 1
+
+/// Video render needs at least 2 buffers.
+#define VIDEO_OUTPUT_BUFFERS_NUM 3
+
+// Max bitrate we allow for recording
+const int MAX_BITRATE = 30000000; // 30Mbits/s
+
+/// Interval at which we check for an failure abort during capture
+const int ABORT_INTERVAL = 100; // ms
+
+
+int mmal_status_to_int(MMAL_STATUS_T status);
+
+/** Structure containing all state information for the current run
+ */
+typedef struct
+{
+   int timeout;                        /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
+   int width;                          /// Requested width of image
+   int height;                         /// requested height of image
+   int bitrate;                        /// Requested bitrate
+   int framerate;                      /// Requested frame rate (fps)
+   int intraperiod;                    /// Intra-refresh period (key frame rate)
+   char *filename;                     /// filename of output file
+   int verbose;                        /// !0 if want detailed run information
+   int demoMode;                       /// Run app in demo mode
+   int demoInterval;                   /// Interval between camera settings changes
+   int immutableInput;                 /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either
+                                       /// the camera output or the encoder output (with compression artifacts)
+   int profile;                        /// H264 profile to use for encoding
+   RASPIPREVIEW_PARAMETERS preview_parameters;   /// Preview setup parameters
+   RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
+
+   MMAL_COMPONENT_T *camera_component;    /// Pointer to the camera component
+   MMAL_COMPONENT_T *encoder_component;   /// Pointer to the encoder component
+   MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
+   MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder
+
+   MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
+} RASPIVID_STATE;
+
+/** Struct used to pass information in encoder port userdata to callback
+ */
+typedef struct
+{
+   FILE *file_handle;                   /// File handle to write buffer data to.
+   RASPIVID_STATE *pstate;              /// pointer to our state in case required in callback
+   int abort;                           /// Set to 1 in callback if an error occurs to attempt to abort the capture
+} PORT_USERDATA;
+
+#if 0
+/// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
+static XREF_T  profile_map[] =
+{
+   {"baseline",     MMAL_VIDEO_PROFILE_H264_BASELINE},
+   {"main",         MMAL_VIDEO_PROFILE_H264_MAIN},
+   {"high",         MMAL_VIDEO_PROFILE_H264_HIGH},
+//   {"constrained",  MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this?
+};
+
+static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]);
+
+
+static void display_valid_parameters(char *app_name);
+
+/// Command ID's and Structure defining our command line options
+#define CommandHelp         0
+#define CommandWidth        1
+#define CommandHeight       2
+#define CommandBitrate      3
+#define CommandOutput       4
+#define CommandVerbose      5
+#define CommandTimeout      6
+#define CommandDemoMode     7
+#define CommandFramerate    8
+#define CommandPreviewEnc   9
+#define CommandIntraPeriod  10
+#define CommandProfile      11
+
+static COMMAND_LIST cmdline_commands[] =
+{
+   { CommandHelp,    "-help",       "?",  "This help information", 0 },
+   { CommandWidth,   "-width",      "w",  "Set image width <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)", 1},
+   { CommandProfile,  "-profile",   "pf", "Specify H264 profile to use for encoding", 1},
+};
+
+static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
+#endif
+
+static void dump_state(RASPIVID_STATE *state);
+
+/**
+ * Assign a default set of parameters to the state passed in
+ *
+ * @param state Pointer to state structure to assign defaults to
+ */
+void raspi_capture_default_state(RASPIVID_STATE *state)
+{
+   if (!state)
+   {
+      vcos_assert(0);
+      return;
+   }
+
+   // Default everything to zero
+   memset(state, 0, sizeof(RASPIVID_STATE));
+
+   // Now set anything non-zero
+   state->timeout = 5000;     // 5s delay before take image
+   state->width = 1920;       // Default to 1080p
+   state->height = 1080;
+   state->bitrate = 17000000; // This is a decent default bitrate for 1080p
+   state->framerate = VIDEO_FRAME_RATE_NUM;
+   state->intraperiod = 0;    // Not set
+   state->demoMode = 0;
+   state->demoInterval = 250; // ms
+   state->immutableInput = 1;
+   state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
+
+   // Setup preview window defaults
+   raspipreview_set_defaults(&state->preview_parameters);
+
+   // Set up the camera_parameters to default
+   raspicamcontrol_set_defaults(&state->camera_parameters);
+
+   dump_state(state);
+}
+
+/**
+ * Dump image state parameters to printf. Used for debugging
+ *
+ * @param state Pointer to state structure to assign defaults to
+ */
+static void dump_state(RASPIVID_STATE *state)
+{
+   if (!state)
+   {
+      vcos_assert(0);
+      return;
+   }
+
+   fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename);
+   fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->bitrate, state->framerate, state->timeout);
+   //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->profile, profile_map, profile_map_size));
+
+   raspipreview_dump_parameters(&state->preview_parameters);
+   raspicamcontrol_dump_parameters(&state->camera_parameters);
+}
+
+#if 0
+/**
+ * Parse the incoming command line and put resulting parameters in to the state
+ *
+ * @param argc Number of arguments in command line
+ * @param argv Array of pointers to strings from command line
+ * @param state Pointer to state structure to assign any discovered parameters to
+ * @return Non-0 if failed for some reason, 0 otherwise
+ */
+static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
+{
+   // Parse the command line arguments.
+   // We are looking for --<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);
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandVerbose: // display lots of data during run
+         state->verbose = 1;
+         break;
+
+      case CommandTimeout: // Time to run viewfinder/capture
+      {
+         if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
+         {
+            // TODO : What limits do we need for timeout?
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandDemoMode: // Run in demo mode - no capture
+      {
+         // Demo mode might have a timing parameter
+         // so check if a) we have another parameter, b) its not the start of the next option
+         if (i + 1 < argc  && argv[i+1][0] != '-')
+         {
+            if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
+            {
+               // TODO : What limits do we need for timeout?
+               if (state->demoInterval == 0)
+                  state->demoInterval = 250; // ms
+
+               state->demoMode = 1;
+               i++;
+            }
+            else
+               valid = 0;
+         }
+         else
+         {
+            state->demoMode = 1;
+         }
+
+         break;
+      }
+
+      case CommandFramerate: // fps to record
+      {
+         if (sscanf(argv[i + 1], "%u", &state->framerate) == 1)
+         {
+            // TODO : What limits do we need for fps 1 - 30 - 120??
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandPreviewEnc:
+         state->immutableInput = 0;
+         break;
+
+      case CommandIntraPeriod: // key frame rate
+      {
+         if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1)
+            i++;
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandProfile: // H264 profile
+      {
+         state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size);
+
+         if( state->profile == -1)
+            state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
+
+         i++;
+         break;
+      }
+
+      default:
+      {
+         // Try parsing for any image specific parameters
+         // result indicates how many parameters were used up, 0,1,2
+         // but we adjust by -1 as we have used one already
+         const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
+         int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
+
+         // Still unused, try preview options
+         if (!parms_used)
+            parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
+
+
+         // If no parms were used, this must be a bad parameters
+         if (!parms_used)
+            valid = 0;
+         else
+            i += parms_used - 1;
+
+         break;
+      }
+      }
+   }
+
+   if (!valid)
+   {
+      fprintf(stderr, "Invalid command line option (%s)\n", argv[i]);
+      return 1;
+   }
+
+   // Always disable verbose if output going to stdout
+   if (state->filename && state->filename[0] == '-')
+   {
+      state->verbose = 0;
+   }
+
+   return 0;
+}
+
+/**
+ * Display usage information for the application to stdout
+ *
+ * @param app_name String to display as the application name
+ */
+static void display_valid_parameters(char *app_name)
+{
+   int i;
+
+   fprintf(stderr, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n");
+   fprintf(stderr, "\nusage: %s [options]\n\n", app_name);
+
+   fprintf(stderr, "Image parameter commands\n\n");
+
+   raspicli_display_help(cmdline_commands, cmdline_commands_size);
+
+   // Profile options
+   fprintf(stderr, "\n\nH264 Profile options :\n%s", profile_map[0].mode );
+
+   for (i=1;i<profile_map_size;i++)
+   {
+      fprintf(stderr, ",%s", profile_map[i].mode);
+   }
+
+   // 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)
+   {
+   }
+   else
+   {
+      vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
+   }
+
+   mmal_buffer_header_release(buffer);
+}
+
+/**
+ *  buffer header callback function for encoder
+ *
+ *  Callback will dump buffer data to the specific file
+ *
+ * @param port Pointer to port from which callback originated
+ * @param buffer mmal buffer header pointer
+ */
+static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+   MMAL_BUFFER_HEADER_T *new_buffer;
+
+   // We pass our file handle and other stuff in via the userdata field.
+
+   PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
+
+   if (pData)
+   {
+      int bytes_written = buffer->length;
+
+      vcos_assert(pData->file_handle);
+
+      if (buffer->length)
+      {
+         mmal_buffer_header_mem_lock(buffer);
+
+         bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle);
+
+         mmal_buffer_header_mem_unlock(buffer);
+      }
+
+      if (bytes_written != buffer->length)
+      {
+         vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length);
+         pData->abort = 1;
+      }
+   }
+   else
+   {
+      vcos_log_error("Received a encoder buffer callback with no state");
+   }
+
+   // release buffer back to the pool
+   mmal_buffer_header_release(buffer);
+
+   // and send one back to the port (if still open)
+   if (port->is_enabled)
+   {
+      MMAL_STATUS_T status;
+
+      new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue);
+
+      if (new_buffer)
+         status = mmal_port_send_buffer(port, new_buffer);
+
+      if (!new_buffer || status != MMAL_SUCCESS)
+         vcos_log_error("Unable to return a buffer to the encoder port");
+   }
+}
+
+
+/**
+ * Create the camera component, set up its ports
+ *
+ * @param state Pointer to state control struct
+ *
+ * @return MMAL_SUCCESS if all OK, something else otherwise
+ *
+ */
+static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
+{
+   MMAL_COMPONENT_T *camera = 0;
+   MMAL_ES_FORMAT_T *format;
+   MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
+   MMAL_STATUS_T status;
+
+   /* Create the component */
+   status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Failed to create camera component");
+      goto error;
+   }
+
+   if (!camera->output_num)
+   {
+      status = MMAL_ENOSYS;
+      vcos_log_error("Camera doesn't have output ports");
+      goto error;
+   }
+
+   preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
+   video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
+   still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
+
+   // Enable the camera, and tell it its control callback function
+   status = mmal_port_enable(camera->control, camera_control_callback);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to enable control port : error %d", status);
+      goto error;
+   }
+
+   //  set up the camera configuration
+   {
+      MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
+      {
+         { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
+         .max_stills_w = state->width,
+         .max_stills_h = state->height,
+         .stills_yuv422 = 0,
+         .one_shot_stills = 0,
+         .max_preview_video_w = state->width,
+         .max_preview_video_h = state->height,
+         .num_preview_video_frames = 3,
+         .stills_capture_circular_buffer_height = 0,
+         .fast_preview_resume = 0,
+         .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
+      };
+      mmal_port_parameter_set(camera->control, &cam_config.hdr);
+   }
+
+   // Now set up the port formats
+
+   // Set the encode format on the Preview port
+   // HW limitations mean we need the preview to be the same size as the required recorded output
+
+   format = preview_port->format;
+
+   format->encoding = MMAL_ENCODING_OPAQUE;
+   format->encoding_variant = MMAL_ENCODING_I420;
+
+   format->encoding = MMAL_ENCODING_OPAQUE;
+   format->es->video.width = state->width;
+   format->es->video.height = state->height;
+   format->es->video.crop.x = 0;
+   format->es->video.crop.y = 0;
+   format->es->video.crop.width = state->width;
+   format->es->video.crop.height = state->height;
+   format->es->video.frame_rate.num = state->framerate;
+   format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN;
+
+   status = mmal_port_format_commit(preview_port);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("camera viewfinder format couldn't be set");
+      goto error;
+   }
+
+   // Set the encode format on the video  port
+
+   format = video_port->format;
+   format->encoding_variant = MMAL_ENCODING_I420;
+
+   format->encoding = MMAL_ENCODING_OPAQUE;
+   format->es->video.width = state->width;
+   format->es->video.height = state->height;
+   format->es->video.crop.x = 0;
+   format->es->video.crop.y = 0;
+   format->es->video.crop.width = state->width;
+   format->es->video.crop.height = state->height;
+   format->es->video.frame_rate.num = state->framerate;
+   format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN;
+
+   status = mmal_port_format_commit(video_port);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("camera video format couldn't be set");
+      goto error;
+   }
+
+   // Ensure there are enough buffers to avoid dropping frames
+   if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
+      video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
+
+
+   // Set the encode format on the still  port
+
+   format = still_port->format;
+
+   format->encoding = MMAL_ENCODING_OPAQUE;
+   format->encoding_variant = MMAL_ENCODING_I420;
+
+   format->es->video.width = state->width;
+   format->es->video.height = state->height;
+   format->es->video.crop.x = 0;
+   format->es->video.crop.y = 0;
+   format->es->video.crop.width = state->width;
+   format->es->video.crop.height = state->height;
+   format->es->video.frame_rate.num = 1;
+   format->es->video.frame_rate.den = 1;
+
+   status = mmal_port_format_commit(still_port);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("camera still format couldn't be set");
+      goto error;
+   }
+
+   /* Ensure there are enough buffers to avoid dropping frames */
+   if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
+      still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
+
+   /* Enable component */
+   status = mmal_component_enable(camera);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("camera component couldn't be enabled");
+      goto error;
+   }
+
+   raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
+
+   state->camera_component = camera;
+
+   if (state->verbose)
+      fprintf(stderr, "Camera component done\n");
+
+   return status;
+
+error:
+
+   if (camera)
+      mmal_component_destroy(camera);
+
+   return status;
+}
+
+/**
+ * Destroy the camera component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void destroy_camera_component(RASPIVID_STATE *state)
+{
+   if (state->camera_component)
+   {
+      mmal_component_destroy(state->camera_component);
+      state->camera_component = NULL;
+   }
+}
+
+/**
+ * Create the encoder component, set up its ports
+ *
+ * @param state Pointer to state control struct
+ *
+ * @return MMAL_SUCCESS if all OK, something else otherwise
+ *
+ */
+static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
+{
+   MMAL_COMPONENT_T *encoder = 0;
+   MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
+   MMAL_STATUS_T status;
+   MMAL_POOL_T *pool;
+
+   status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to create video encoder component");
+      goto error;
+   }
+
+   if (!encoder->input_num || !encoder->output_num)
+   {
+      status = MMAL_ENOSYS;
+      vcos_log_error("Video encoder doesn't have input/output ports");
+      goto error;
+   }
+
+   encoder_input = encoder->input[0];
+   encoder_output = encoder->output[0];
+
+   // We want same format on input and output
+   mmal_format_copy(encoder_output->format, encoder_input->format);
+
+   // Only supporting H264 at the moment
+   encoder_output->format->encoding = MMAL_ENCODING_H264;
+
+   encoder_output->format->bitrate = state->bitrate;
+
+   encoder_output->buffer_size = encoder_output->buffer_size_recommended;
+
+   if (encoder_output->buffer_size < encoder_output->buffer_size_min)
+      encoder_output->buffer_size = encoder_output->buffer_size_min;
+
+   encoder_output->buffer_num = encoder_output->buffer_num_recommended;
+
+   if (encoder_output->buffer_num < encoder_output->buffer_num_min)
+      encoder_output->buffer_num = encoder_output->buffer_num_min;
+
+   // Commit the port changes to the output port
+   status = mmal_port_format_commit(encoder_output);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to set format on video encoder output port");
+      goto error;
+   }
+
+
+   // Set the rate control parameter
+   if (0)
+   {
+      MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT};
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to set ratecontrol");
+         goto error;
+      }
+
+   }
+
+   if (state->intraperiod)
+   {
+      MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod};
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to set intraperiod");
+         goto error;
+      }
+
+   }
+
+   {
+      MMAL_PARAMETER_VIDEO_PROFILE_T  param;
+      param.hdr.id = MMAL_PARAMETER_PROFILE;
+      param.hdr.size = sizeof(param);
+
+      param.profile[0].profile = state->profile;
+      param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
+
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to set H264 profile");
+         goto error;
+      }
+   }
+
+
+   if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to set immutable input flag");
+      // Continue rather than abort..
+   }
+
+   //  Enable component
+   status = mmal_component_enable(encoder);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to enable video encoder component");
+      goto error;
+   }
+
+   /* Create pool of buffer headers for the output port to consume */
+   pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
+
+   if (!pool)
+   {
+      vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
+   }
+
+   state->encoder_pool = pool;
+   state->encoder_component = encoder;
+
+   if (state->verbose)
+      fprintf(stderr, "Encoder component done\n");
+
+   return status;
+
+   error:
+   if (encoder)
+      mmal_component_destroy(encoder);
+
+   return status;
+}
+
+/**
+ * Destroy the encoder component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void destroy_encoder_component(RASPIVID_STATE *state)
+{
+   // Get rid of any port buffers first
+   if (state->encoder_pool)
+   {
+      mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
+   }
+
+   if (state->encoder_component)
+   {
+      mmal_component_destroy(state->encoder_component);
+      state->encoder_component = NULL;
+   }
+}
+
+/**
+ * Connect two specific ports together
+ *
+ * @param output_port Pointer the output port
+ * @param input_port Pointer the input port
+ * @param Pointer to a mmal connection pointer, reassigned if function successful
+ * @return Returns a MMAL_STATUS_T giving result of operation
+ *
+ */
+static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
+{
+   MMAL_STATUS_T status;
+
+   status =  mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
+
+   if (status == MMAL_SUCCESS)
+   {
+      status =  mmal_connection_enable(*connection);
+      if (status != MMAL_SUCCESS)
+         mmal_connection_destroy(*connection);
+   }
+
+   return status;
+}
+
+/**
+ * Checks if specified port is valid and enabled, then disables it
+ *
+ * @param port  Pointer the port
+ *
+ */
+static void check_disable_port(MMAL_PORT_T *port)
+{
+   if (port && port->is_enabled)
+      mmal_port_disable(port);
+}
+
+
+int raspi_capture_start()
+{
+   // Our main data storage vessel..
+   RASPIVID_STATE state;
+   int exit_code = EX_OK;
+
+   MMAL_STATUS_T status = MMAL_SUCCESS;
+   MMAL_PORT_T *camera_preview_port = NULL;
+   MMAL_PORT_T *camera_video_port = NULL;
+   MMAL_PORT_T *camera_still_port = NULL;
+   MMAL_PORT_T *preview_input_port = NULL;
+   MMAL_PORT_T *encoder_input_port = NULL;
+   MMAL_PORT_T *encoder_output_port = NULL;
+   FILE *output_file = NULL;
+
+   bcm_host_init();
+
+   // Register our application with the logging system
+   vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
+
+   raspi_capture_default_state(&state);
+
+   if (state.verbose)
+   {
+      dump_state(&state);
+   }
+
+   // OK, we have a nice set of parameters. Now set up our components
+   // We have three components. Camera, Preview and encoder.
+
+   if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
+   {
+      vcos_log_error("%s: Failed to create camera component", __func__);
+      exit_code = EX_SOFTWARE;
+   }
+   else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
+   {
+      vcos_log_error("%s: Failed to create preview component", __func__);
+      destroy_camera_component(&state);
+      exit_code = EX_SOFTWARE;
+   }
+   else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS)
+   {
+      vcos_log_error("%s: Failed to create encode component", __func__);
+      raspipreview_destroy(&state.preview_parameters);
+      destroy_camera_component(&state);
+      exit_code = EX_SOFTWARE;
+   }
+   else
+   {
+      PORT_USERDATA callback_data;
+
+      if (state.verbose)
+         fprintf(stderr, "Starting component connection stage\n");
+
+      camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
+      camera_video_port   = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
+      camera_still_port   = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
+      preview_input_port  = state.preview_parameters.preview_component->input[0];
+      encoder_input_port  = state.encoder_component->input[0];
+      encoder_output_port = state.encoder_component->output[0];
+
+      if (state.preview_parameters.wantPreview )
+      {
+         if (state.verbose)
+         {
+            fprintf(stderr, "Connecting camera preview port to preview input port\n");
+            fprintf(stderr, "Starting video preview\n");
+         }
+
+         // Connect camera to preview
+         status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
+      }
+      else
+      {
+         status = MMAL_SUCCESS;
+      }
+
+      if (status == MMAL_SUCCESS)
+      {
+         if (state.verbose)
+            fprintf(stderr, "Connecting camera stills port to encoder input port\n");
+
+         // Now connect the camera to the encoder
+         status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection);
+
+         if (status != MMAL_SUCCESS)
+         {
+            vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
+            goto error;
+         }
+
+         if (state.filename)
+         {
+            if (state.filename[0] == '-')
+            {
+               output_file = stdout;
+
+               // Ensure we don't upset the output stream with diagnostics/info
+               state.verbose = 0;
+            }
+            else
+            {
+               if (state.verbose)
+                  fprintf(stderr, "Opening output file \"%s\"\n", state.filename);
+
+               output_file = fopen(state.filename, "wb");
+            }
+
+            if (!output_file)
+            {
+               // Notify user, carry on but discarding encoded output buffers
+               vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename);
+            }
+         }
+
+         // Set up our userdata - this is passed though to the callback where we need the information.
+         callback_data.file_handle = output_file;
+         callback_data.pstate = &state;
+         callback_data.abort = 0;
+
+         encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
+
+         if (state.verbose)
+            fprintf(stderr, "Enabling encoder output port\n");
+
+         // Enable the encoder output port and tell it its callback function
+         status = mmal_port_enable(encoder_output_port, encoder_buffer_callback);
+
+         if (status != MMAL_SUCCESS)
+         {
+            vcos_log_error("Failed to setup encoder output");
+            goto error;
+         }
+
+         if (state.demoMode)
+         {
+            // Run for the user specific time..
+            int num_iterations = state.timeout / state.demoInterval;
+            int i;
+
+            if (state.verbose)
+               fprintf(stderr, "Running in demo mode\n");
+
+            for (i=0;state.timeout == 0 || i<num_iterations;i++)
+            {
+               raspicamcontrol_cycle_test(state.camera_component);
+               vcos_sleep(state.demoInterval);
+            }
+         }
+         else
+         {
+            // Only encode stuff if we have a filename and it opened
+            if (output_file)
+            {
+               int wait;
+
+               if (state.verbose)
+                  fprintf(stderr, "Starting video capture\n");
+
+               if (mmal_port_parameter_set_boolean(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(encoder_output_port, buffer)!= MMAL_SUCCESS)
+                        vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
+
+                  }
+               }
+
+               // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
+               // out of storage space)
+               // Going to check every ABORT_INTERVAL milliseconds
+
+               for (wait = 0; state.timeout == 0 || wait < state.timeout; wait+= ABORT_INTERVAL)
+               {
+                  vcos_sleep(ABORT_INTERVAL);
+                  if (callback_data.abort)
+                     break;
+               }
+
+               if (state.verbose)
+                  fprintf(stderr, "Finished capture\n");
+            }
+            else
+            {
+               if (state.timeout)
+                  vcos_sleep(state.timeout);
+               else
+                  for (;;) vcos_sleep(ABORT_INTERVAL);
+            }
+         }
+      }
+      else
+      {
+         mmal_status_to_int(status);
+         vcos_log_error("%s: Failed to connect camera to preview", __func__);
+      }
+
+error:
+
+      mmal_status_to_int(status);
+
+      if (state.verbose)
+         fprintf(stderr, "Closing down\n");
+
+      // Disable all our ports that are not handled by connections
+      check_disable_port(camera_still_port);
+      check_disable_port(encoder_output_port);
+
+      if (state.preview_parameters.wantPreview )
+         mmal_connection_destroy(state.preview_connection);
+      mmal_connection_destroy(state.encoder_connection);
+
+      // Can now close our file. Note disabling ports may flush buffers which causes
+      // problems if we have already closed the file!
+      if (output_file && output_file != stdout)
+         fclose(output_file);
+
+      /* Disable components */
+      if (state.encoder_component)
+         mmal_component_disable(state.encoder_component);
+
+      if (state.preview_parameters.preview_component)
+         mmal_component_disable(state.preview_parameters.preview_component);
+
+      if (state.camera_component)
+         mmal_component_disable(state.camera_component);
+
+      destroy_encoder_component(&state);
+      raspipreview_destroy(&state.preview_parameters);
+      destroy_camera_component(&state);
+
+      if (state.verbose)
+         fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
+   }
+
+   if (status != MMAL_SUCCESS)
+      raspicamcontrol_check_configuration(128);
+
+   return exit_code;
+}
diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h
new file mode 100644 (file)
index 0000000..f24ab4b
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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 __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"
+
+G_BEGIN_DECLS
+
+/** Structure containing all state information for the current run
+ */
+typedef struct
+{
+   int timeout;                        /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
+   int width;                          /// Requested width of image
+   int height;                         /// requested height of image
+   int bitrate;                        /// Requested bitrate
+   int framerate;                      /// Requested frame rate (fps)
+   int intraperiod;                    /// Intra-refresh period (key frame rate)
+   int demoMode;                       /// Run app in demo mode
+   int demoInterval;                   /// Interval between camera settings changes
+   int immutableInput;                 /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either
+                                       /// the camera output or the encoder output (with compression artifacts)
+   int profile;                        /// H264 profile to use for encoding
+   RASPIPREVIEW_PARAMETERS preview_parameters;   /// Preview setup parameters
+   RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
+} RASPIVID_STATE;
+
+int raspi_capture_start();
+
+G_END_DECLS
+
+#endif
diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c
new file mode 100644 (file)
index 0000000..08d2a74
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <memory.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"
+
+#if 0
+#define CommandPreview        1
+#define CommandFullScreen     2
+#define CommandOpacity        3
+#define CommandDisablePreview 4
+
+static COMMAND_LIST cmdline_commands[] =
+{
+   { CommandPreview,       "-preview",    "p",  "Preview window settings <'x,y,w,h'>", 1 },
+   { CommandFullScreen,    "-fullscreen", "f",  "Fullscreen preview mode", 0 },
+   { CommandOpacity,       "-opacity",    "op", "Preview window opacity (0-255)", 1},
+   { CommandDisablePreview,"-nopreview",  "n",  "Do not display a preview window", 0},
+};
+
+static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
+#endif
+
+/**
+ * Create the preview component, set up its ports
+ *
+ * @param state Pointer to state control struct
+ *
+ * @return MMAL_SUCCESS if all OK, something else otherwise
+ *
+ */
+MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state)
+{
+   MMAL_COMPONENT_T *preview = 0;
+   MMAL_PORT_T *preview_port = NULL;
+   MMAL_STATUS_T status;
+
+   if (!state->wantPreview)
+   {
+      // No preview required, so create a null sink component to take its place
+      status = mmal_component_create("vc.null_sink", &preview);
+
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to create null sink component");
+         goto error;
+      }
+   }
+   else
+   {
+      status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER,
+            &preview);
+
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to create preview component");
+         goto error;
+      }
+
+      if (!preview->input_num)
+      {
+         status = MMAL_ENOSYS;
+         vcos_log_error("No input ports found on component");
+         goto error;
+      }
+
+      preview_port = preview->input[0];
+
+      MMAL_DISPLAYREGION_T param;
+      param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
+      param.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
+
+      param.set = MMAL_DISPLAY_SET_LAYER;
+      param.layer = PREVIEW_LAYER;
+
+      param.set |= MMAL_DISPLAY_SET_ALPHA;
+      param.alpha = state->opacity;
+
+      if (state->wantFullScreenPreview)
+      {
+         param.set |= MMAL_DISPLAY_SET_FULLSCREEN;
+         param.fullscreen = 1;
+      }
+      else
+      {
+         param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN);
+         param.fullscreen = 0;
+         param.dest_rect = state->previewWindow;
+      }
+
+      status = mmal_port_parameter_set(preview_port, &param.hdr);
+
+      if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
+      {
+         vcos_log_error("unable to set preview port parameters (%u)", status);
+         goto error;
+      }
+   }
+
+   /* Enable component */
+   status = mmal_component_enable(preview);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to enable preview/null sink component (%u)", status);
+      goto error;
+   }
+
+   state->preview_component = preview;
+
+   return status;
+
+error:
+
+   if (preview)
+      mmal_component_destroy(preview);
+
+   return status;
+}
+
+
+/**
+ * Destroy the preview component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state)
+{
+   if (state->preview_component)
+   {
+      mmal_component_destroy(state->preview_component);
+      state->preview_component = NULL;
+   }
+}
+
+/**
+ * Assign set of default parameters to the passed in parameter block
+ *
+ * @param state Pointer to parameter block
+ *
+ */
+void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state)
+{
+   state->wantPreview = 1;
+   state->wantFullScreenPreview = 1;
+   state->opacity = 255;
+   state->previewWindow.x = 0;
+   state->previewWindow.y = 0;
+   state->previewWindow.width = 1024;
+   state->previewWindow.height = 768;
+   state->preview_component = NULL;
+}
+
+/**
+ * Dump parameters as human readable to stdout
+ *
+ * @param state Pointer to parameter block
+ *
+ */
+void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state)
+{
+   fprintf(stderr, "Preview %s, Full screen %s\n", state->wantPreview ? "Yes" : "No",
+      state->wantFullScreenPreview ? "Yes" : "No");
+
+   fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", state->previewWindow.x,
+      state->previewWindow.y, state->previewWindow.width,
+      state->previewWindow.height, state->opacity);
+};
+
+#if 0
+/**
+ * Parse a possible command pair - command and parameter
+ * @param arg1 Command
+ * @param arg2 Parameter (could be NULL)
+ * @return How many parameters were used, 0,1,2
+ */
+int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2)
+{
+   int command_id, used = 0, num_parameters;
+
+   if (!arg1)
+       return 0;
+
+   command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters);
+
+   // If invalid command, or we are missing a parameter, drop out
+   if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL))
+      return 0;
+
+   switch (command_id)
+   {
+      case CommandPreview: // Preview window
+      {
+         int tmp;
+
+         params->wantPreview = 1;
+
+         tmp = sscanf(arg2, "%d,%d,%d,%d",
+               &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 (file)
index 0000000..c986d2d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+Copyright (c) 2013, Broadcom Europe Ltd
+Copyright (c) 2013, James Hughes
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef RASPIPREVIEW_H_
+#define RASPIPREVIEW_H_
+
+/// Layer that preview window should be displayed on
+#define PREVIEW_LAYER      2
+#define PREVIEW_FRAME_RATE_NUM 30
+#define PREVIEW_FRAME_RATE_DEN 1
+
+#define FULL_RES_PREVIEW_FRAME_RATE_NUM 15
+#define FULL_RES_PREVIEW_FRAME_RATE_DEN 1
+
+
+typedef struct
+{
+   int wantPreview;                       /// Display a preview
+   int wantFullScreenPreview;             /// 0 is use previewRect, non-zero to use full screen
+   int opacity;                           /// Opacity of window - 0 = transparent, 255 = opaque
+   MMAL_RECT_T previewWindow;             /// Destination rectangle for the preview window.
+   MMAL_COMPONENT_T *preview_component;   /// Pointer to the created preview display component
+} RASPIPREVIEW_PARAMETERS;
+
+MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state);
+void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state);
+void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state);
+void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state);
+int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2);
+void raspipreview_display_help();
+
+#endif /* RASPIPREVIEW_H_ */
diff --git a/sys/rpicamsrc/RaspiStill.c b/sys/rpicamsrc/RaspiStill.c
new file mode 100644 (file)
index 0000000..62d0b25
--- /dev/null
@@ -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 (file)
index 0000000..aad9edb
--- /dev/null
@@ -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;
+}
+
+
+
+
index 391d678..94ee7a7 100644 (file)
@@ -62,6 +62,7 @@
 #include <gst/video/video.h>
 
 #include "gstrpicamsrc.h"
+#include "RaspiCapture.h"
 
 #include "bcm_host.h"
 #include "interface/vcos/vcos.h"
@@ -95,7 +96,6 @@ enum
   "width = " GST_VIDEO_SIZE_RANGE ","             \
   "height = " GST_VIDEO_SIZE_RANGE ","            \
   "framerate = " GST_VIDEO_FPS_RANGE
-
 #define H264_CAPS                              \
   "video/x-h264, "                              \
   "width = " GST_VIDEO_SIZE_RANGE ", "          \
@@ -106,49 +106,34 @@ enum
   "profile = (string) { baseline, main, high }"
 
 static GstStaticPadTemplate video_src_template =
-  GST_STATIC_PAD_TEMPLATE ("vidsrc",
-    GST_PAD_SRC,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS)
-    );
-static GstStaticPadTemplate viewfind_src_template =
-  GST_STATIC_PAD_TEMPLATE ("vfsrc",
+  GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS (RAW_AND_JPEG_CAPS "; " H264_CAPS)
     );
-static GstStaticPadTemplate image_src_template =
-  GST_STATIC_PAD_TEMPLATE ("imgsrc",
-    GST_PAD_SRC,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (RAW_AND_JPEG_CAPS)
-    );
 
 #define gst_rpi_cam_src_parent_class parent_class
-G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_BASE_CAMERA_SRC);
+G_DEFINE_TYPE (GstRpiCamSrc, gst_rpi_cam_src, GST_TYPE_PUSH_SRC);
 
 static void gst_rpi_cam_src_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
-
-static gboolean
-gst_rpi_cam_src_setup_pipeline (GstBaseCameraSrc *parent)
-{
-  GstRpiCamSrc *self = GST_RPICAMSRC(parent);
-  g_print ("In setup_pipeline\n");
-}
+static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent);
+static GstFlowReturn gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf);
 
 static void
 gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass)
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
-  GstBaseCameraSrcClass *basecamsrc_class;
+  GstBaseSrcClass *basesrc_class;
+  GstPushSrcClass *pushsrc_class;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
-  basecamsrc_class = (GstBaseCameraSrcClass *) klass;
+  basesrc_class = (GstBaseSrcClass *) klass;
+  pushsrc_class = (GstPushSrcClass *) klass;
 
   gobject_class->set_property = gst_rpi_cam_src_set_property;
   gobject_class->get_property = gst_rpi_cam_src_get_property;
@@ -160,18 +145,17 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass)
     "Jan Schmidt <jan@centricular.com>");
 
   gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&viewfind_src_template));
-  gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&video_src_template));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&image_src_template));
 
-  basecamsrc_class->setup_pipeline = gst_rpi_cam_src_setup_pipeline;
+  basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start);
+  pushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fill_buffer);
 }
 
 static void
 gst_rpi_cam_src_init (GstRpiCamSrc *src)
 {
+  gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
+  gst_base_src_set_live (GST_BASE_SRC (src), TRUE); 
 }
 
 static void
@@ -210,6 +194,21 @@ rpicamsrc_init (GstPlugin * rpicamsrc)
       GST_TYPE_RPICAMSRC);
 }
 
+static gboolean
+gst_rpi_cam_src_start (GstBaseSrc *parent)
+{
+  GstRpiCamSrc *src = GST_RPICAMSRC(parent);
+  g_print ("In start()\n");
+  raspi_capture_start();
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf)
+{
+  return GST_FLOW_ERROR;
+}
+
 #ifndef PACKAGE
 #define PACKAGE "gstrpicamsrc"
 #endif
index 6917bb1..b8eb2e6 100644 (file)
@@ -45,7 +45,7 @@
 #define __GST_RPICAMSRC_H__
 
 #include <gst/gst.h>
-#include <gst/basecamerabinsrc/gstbasecamerasrc.h>
+#include <gst/base/gstpushsrc.h>
 
 G_BEGIN_DECLS
 
@@ -64,16 +64,14 @@ typedef struct _GstRpiCamSrcClass GstRpiCamSrcClass;
 
 struct _GstRpiCamSrc
 {
-  GstBaseCameraSrc parent;
+  GstPushSrc parent;
 
-  GstPad *viewfind_srcpad;
   GstPad *video_srcpad;
-  GstPad *image_srcpad;
 };
 
 struct _GstRpiCamSrcClass 
 {
-  GstBaseCameraSrcClass parent_class;
+  GstPushSrcClass parent_class;
 };
 
 GType gst_rpi_cam_src_get_type (void);