--- /dev/null
+/*
+ * 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(¶meter_option, -100, 100, 10))
+ raspicamcontrol_set_sharpness(camera, parameter_option);
+ else
+ {
+ raspicamcontrol_set_sharpness(camera, 0);
+ parameter++;
+ }
+ }
+ else
+ if (parameter == 1)
+ {
+ // contrast
+ if (update_cycle_parameter(¶meter_option, -100, 100, 10))
+ raspicamcontrol_set_contrast(camera, parameter_option);
+ else
+ {
+ raspicamcontrol_set_contrast(camera, 0);
+ parameter++;
+ }
+ }
+ else
+ if (parameter == 2)
+ {
+ // brightness
+ if (update_cycle_parameter(¶meter_option, 0, 100, 10))
+ raspicamcontrol_set_brightness(camera, parameter_option);
+ else
+ {
+ raspicamcontrol_set_brightness(camera, 50);
+ parameter++;
+ }
+ }
+ else
+ if (parameter == 3)
+ {
+ // contrast
+ if (update_cycle_parameter(¶meter_option, -100, 100, 10))
+ raspicamcontrol_set_saturation(camera, parameter_option);
+ else
+ {
+ parameter++;
+ raspicamcontrol_set_saturation(camera, 0);
+ }
+ }
+ else
+ if (parameter == 4)
+ {
+ // EV
+ if (update_cycle_parameter(¶meter_option, -10, 10, 4))
+ raspicamcontrol_set_exposure_compensation(camera, parameter_option);
+ else
+ {
+ raspicamcontrol_set_exposure_compensation(camera, 0);
+ parameter++;
+ }
+ }
+ else
+ if (parameter == 5)
+ {
+ // MMAL_PARAM_EXPOSUREMODE_T
+ if (update_cycle_parameter(¶meter_option, 0, exposure_map_size, 1))
+ raspicamcontrol_set_exposure_mode(camera, exposure_map[parameter_option].mmal_mode);
+ else
+ {
+ raspicamcontrol_set_exposure_mode(camera, MMAL_PARAM_EXPOSUREMODE_AUTO);
+ parameter++;
+ }
+ }
+ else
+ if (parameter == 6)
+ {
+ // MMAL_PARAM_AWB_T
+ if (update_cycle_parameter(¶meter_option, 0, awb_map_size, 1))
+ raspicamcontrol_set_awb_mode(camera, awb_map[parameter_option].mmal_mode);
+ else
+ {
+ raspicamcontrol_set_awb_mode(camera, MMAL_PARAM_AWBMODE_AUTO);
+ parameter++;
+ }
+ }
+ if (parameter == 7)
+ {
+ // MMAL_PARAM_IMAGEFX_T
+ if (update_cycle_parameter(¶meter_option, 0, imagefx_map_size, 1))
+ raspicamcontrol_set_imageFX(camera, imagefx_map[parameter_option].mmal_mode);
+ else
+ {
+ raspicamcontrol_set_imageFX(camera, MMAL_PARAM_IMAGEFX_NONE);
+ parameter++;
+ }
+ }
+ if (parameter == 8)
+ {
+ MMAL_PARAM_COLOURFX_T colfx = {0,0,0};
+ switch (parameter_option)
+ {
+ case parameter_reset :
+ parameter_option = 1;
+ colfx.u = 128;
+ colfx.v = 128;
+ break;
+ case 1 :
+ parameter_option = 2;
+ colfx.u = 100;
+ colfx.v = 200;
+ break;
+ case 2 :
+ parameter_option = parameter_reset;
+ colfx.enable = 0;
+ parameter++;
+ break;
+ }
+ raspicamcontrol_set_colourFX(camera, &colfx);
+ }
+
+ // Orientation
+ if (parameter == 9)
+ {
+ switch (parameter_option)
+ {
+ case parameter_reset:
+ raspicamcontrol_set_rotation(camera, 90);
+ parameter_option = 1;
+ break;
+
+ case 1 :
+ raspicamcontrol_set_rotation(camera, 180);
+ parameter_option = 2;
+ break;
+
+ case 2 :
+ raspicamcontrol_set_rotation(camera, 270);
+ parameter_option = 3;
+ break;
+
+ case 3 :
+ {
+ raspicamcontrol_set_rotation(camera, 0);
+ raspicamcontrol_set_flips(camera, 1,0);
+ parameter_option = 4;
+ break;
+ }
+ case 4 :
+ {
+ raspicamcontrol_set_flips(camera, 0,1);
+ parameter_option = 5;
+ break;
+ }
+ case 5 :
+ {
+ raspicamcontrol_set_flips(camera, 1, 1);
+ parameter_option = 6;
+ break;
+ }
+ case 6 :
+ {
+ raspicamcontrol_set_flips(camera, 0, 0);
+ parameter_option = parameter_reset;
+ parameter++;
+ break;
+ }
+ }
+ }
+
+ if (parameter == 10)
+ {
+ parameter = 1;
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+
+#if 0
+/**
+ * Parse a possible command pair - command and parameter
+ * @param arg1 Command
+ * @param arg2 Parameter (could be NULL)
+ * @return How many parameters were used, 0,1,2
+ */
+int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2)
+{
+ int command_id, used = 0, num_parameters;
+
+ if (!arg1)
+ return 0;
+
+ command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters);
+
+ // If invalid command, or we are missing a parameter, drop out
+ if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL))
+ return 0;
+
+ switch (command_id)
+ {
+ case CommandSharpness : // sharpness - needs single number parameter
+ sscanf(arg2, "%d", ¶ms->sharpness);
+ used = 2;
+ break;
+
+ case CommandContrast : // contrast - needs single number parameter
+ sscanf(arg2, "%d", ¶ms->contrast);
+ used = 2;
+ break;
+
+ case CommandBrightness : // brightness - needs single number parameter
+ sscanf(arg2, "%d", ¶ms->brightness);
+ used = 2;
+ break;
+
+ case CommandSaturation : // saturation - needs single number parameter
+ sscanf(arg2, "%d", ¶ms->saturation);
+ used = 2;
+ break;
+
+ case CommandISO : // ISO - needs single number parameter
+ sscanf(arg2, "%d", ¶ms->ISO);
+ used = 2;
+ break;
+
+ case CommandVideoStab : // video stabilisation - if here, its on
+ params->videoStabilisation = 1;
+ used = 1;
+ break;
+
+ case CommandEVComp : // EV - needs single number parameter
+ sscanf(arg2, "%d", ¶ms->exposureCompensation);
+ used = 2;
+ break;
+
+ case CommandExposure : // exposure mode - needs string
+ params->exposureMode = exposure_mode_from_string(arg2);
+ used = 2;
+ break;
+
+ case CommandAWB : // AWB mode - needs single number parameter
+ params->awbMode = awb_mode_from_string(arg2);
+ used = 2;
+ break;
+
+ case CommandImageFX : // Image FX - needs string
+ params->imageEffect = imagefx_mode_from_string(arg2);
+ used = 2;
+ break;
+
+ case CommandColourFX : // Colour FX - needs string "u:v"
+ sscanf(arg2, "%d:%d", ¶ms->colourEffects.u, ¶ms->colourEffects.u);
+ params->colourEffects.enable = 1;
+ used = 2;
+ break;
+
+ case CommandMeterMode:
+ params->exposureMeterMode = metering_mode_from_string(arg2);
+ used = 2;
+ break;
+
+ case CommandRotation : // Rotation - degree
+ sscanf(arg2, "%d", ¶ms->rotation);
+ used = 2;
+ break;
+
+ case CommandHFlip :
+ params->hflip = 1;
+ used = 1;
+ break;
+
+ case CommandVFlip :
+ params->vflip = 1;
+ used = 1;
+ break;
+
+ case CommandROI :
+ {
+ double x,y,w,h;
+ int args;
+
+ args = sscanf(arg2, "%lf,%lf,%lf,%lf", &x,&y,&w,&h);
+
+ if (args != 4 || x > 1.0 || y > 1.0 || w > 1.0 || h > 1.0)
+ {
+ return 0;
+ }
+
+ // Make sure we stay within bounds
+ if (x + w > 1.0)
+ w = 1 - x;
+
+ if (y + h > 1.0)
+ h = 1 - y;
+
+ params->roi.x = x;
+ params->roi.y = y;
+ params->roi.w = w;
+ params->roi.h = h;
+
+ used = 2;
+ break;
+ }
+
+ }
+
+ return used;
+}
+
+/**
+ * Display help for command line options
+ */
+void raspicamcontrol_display_help()
+{
+ int i;
+
+ fprintf(stderr, "\nImage parameter commands\n\n");
+
+ raspicli_display_help(cmdline_commands, cmdline_commands_size);
+
+ fprintf(stderr, "\n\nNotes\n\nExposure mode options :\n%s", exposure_map[0].mode );
+
+ for (i=1;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, ¶ms->colourEffects);
+ //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason
+ result += raspicamcontrol_set_rotation(camera, params->rotation);
+ result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
+ result += raspicamcontrol_set_ROI(camera, params->roi);
+
+ return result;
+}
+
+/**
+ * Adjust the saturation level for images
+ * @param camera Pointer to camera component
+ * @param saturation Value to adjust, -100 to 100
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation)
+{
+ int ret = 0;
+
+ if (!camera)
+ return 1;
+
+ if (saturation >= -100 && saturation <= 100)
+ {
+ MMAL_RATIONAL_T value = {saturation, 100};
+ ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SATURATION, value));
+ }
+ else
+ {
+ vcos_log_error("Invalid saturation value");
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * Set the sharpness of the image
+ * @param camera Pointer to camera component
+ * @param sharpness Sharpness adjustment -100 to 100
+ */
+int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness)
+{
+ int ret = 0;
+
+ if (!camera)
+ return 1;
+
+ if (sharpness >= -100 && sharpness <= 100)
+ {
+ MMAL_RATIONAL_T value = {sharpness, 100};
+ ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SHARPNESS, value));
+ }
+ else
+ {
+ vcos_log_error("Invalid sharpness value");
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * Set the contrast adjustment for the image
+ * @param camera Pointer to camera component
+ * @param contrast Contrast adjustment -100 to 100
+ * @return
+ */
+int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast)
+{
+ int ret = 0;
+
+ if (!camera)
+ return 1;
+
+ if (contrast >= -100 && contrast <= 100)
+ {
+ MMAL_RATIONAL_T value = {contrast, 100};
+ ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_CONTRAST, value));
+ }
+ else
+ {
+ vcos_log_error("Invalid contrast value");
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * Adjust the brightness level for images
+ * @param camera Pointer to camera component
+ * @param brightness Value to adjust, 0 to 100
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness)
+{
+ int ret = 0;
+
+ if (!camera)
+ return 1;
+
+ if (brightness >= 0 && brightness <= 100)
+ {
+ MMAL_RATIONAL_T value = {brightness, 100};
+ ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_BRIGHTNESS, value));
+ }
+ else
+ {
+ vcos_log_error("Invalid brightness value");
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * Adjust the ISO used for images
+ * @param camera Pointer to camera component
+ * @param ISO Value to set TODO :
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO)
+{
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_ISO, ISO));
+}
+
+/**
+ * Adjust the metering mode for images
+ * @param camera Pointer to camera component
+ * @param saturation Value from following
+ * - MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE,
+ * - MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT,
+ * - MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT,
+ * - MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T m_mode )
+{
+ MMAL_PARAMETER_EXPOSUREMETERINGMODE_T meter_mode = {{MMAL_PARAMETER_EXP_METERING_MODE,sizeof(meter_mode)},
+ m_mode};
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set(camera->control, &meter_mode.hdr));
+}
+
+
+/**
+ * Set the video stabilisation flag. Only used in video mode
+ * @param camera Pointer to camera component
+ * @param saturation Flag 0 off 1 on
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation)
+{
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_VIDEO_STABILISATION, vstabilisation));
+}
+
+/**
+ * Adjust the exposure compensation for images (EV)
+ * @param camera Pointer to camera component
+ * @param exp_comp Value to adjust, -10 to +10
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp)
+{
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set_int32(camera->control, MMAL_PARAMETER_EXPOSURE_COMP , exp_comp));
+}
+
+
+/**
+ * Set exposure mode for images
+ * @param camera Pointer to camera component
+ * @param mode Exposure mode to set from
+ * - MMAL_PARAM_EXPOSUREMODE_OFF,
+ * - MMAL_PARAM_EXPOSUREMODE_AUTO,
+ * - MMAL_PARAM_EXPOSUREMODE_NIGHT,
+ * - MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW,
+ * - MMAL_PARAM_EXPOSUREMODE_BACKLIGHT,
+ * - MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT,
+ * - MMAL_PARAM_EXPOSUREMODE_SPORTS,
+ * - MMAL_PARAM_EXPOSUREMODE_SNOW,
+ * - MMAL_PARAM_EXPOSUREMODE_BEACH,
+ * - MMAL_PARAM_EXPOSUREMODE_VERYLONG,
+ * - MMAL_PARAM_EXPOSUREMODE_FIXEDFPS,
+ * - MMAL_PARAM_EXPOSUREMODE_ANTISHAKE,
+ * - MMAL_PARAM_EXPOSUREMODE_FIREWORKS,
+ *
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode)
+{
+ MMAL_PARAMETER_EXPOSUREMODE_T exp_mode = {{MMAL_PARAMETER_EXPOSURE_MODE,sizeof(exp_mode)}, mode};
+
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set(camera->control, &exp_mode.hdr));
+}
+
+
+/**
+ * Set the aWB (auto white balance) mode for images
+ * @param camera Pointer to camera component
+ * @param awb_mode Value to set from
+ * - MMAL_PARAM_AWBMODE_OFF,
+ * - MMAL_PARAM_AWBMODE_AUTO,
+ * - MMAL_PARAM_AWBMODE_SUNLIGHT,
+ * - MMAL_PARAM_AWBMODE_CLOUDY,
+ * - MMAL_PARAM_AWBMODE_SHADE,
+ * - MMAL_PARAM_AWBMODE_TUNGSTEN,
+ * - MMAL_PARAM_AWBMODE_FLUORESCENT,
+ * - MMAL_PARAM_AWBMODE_INCANDESCENT,
+ * - MMAL_PARAM_AWBMODE_FLASH,
+ * - MMAL_PARAM_AWBMODE_HORIZON,
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode)
+{
+ MMAL_PARAMETER_AWBMODE_T param = {{MMAL_PARAMETER_AWB_MODE,sizeof(param)}, awb_mode};
+
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr));
+}
+
+/**
+ * Set the image effect for the images
+ * @param camera Pointer to camera component
+ * @param imageFX Value from
+ * - MMAL_PARAM_IMAGEFX_NONE,
+ * - MMAL_PARAM_IMAGEFX_NEGATIVE,
+ * - MMAL_PARAM_IMAGEFX_SOLARIZE,
+ * - MMAL_PARAM_IMAGEFX_POSTERIZE,
+ * - MMAL_PARAM_IMAGEFX_WHITEBOARD,
+ * - MMAL_PARAM_IMAGEFX_BLACKBOARD,
+ * - MMAL_PARAM_IMAGEFX_SKETCH,
+ * - MMAL_PARAM_IMAGEFX_DENOISE,
+ * - MMAL_PARAM_IMAGEFX_EMBOSS,
+ * - MMAL_PARAM_IMAGEFX_OILPAINT,
+ * - MMAL_PARAM_IMAGEFX_HATCH,
+ * - MMAL_PARAM_IMAGEFX_GPEN,
+ * - MMAL_PARAM_IMAGEFX_PASTEL,
+ * - MMAL_PARAM_IMAGEFX_WATERCOLOUR,
+ * - MMAL_PARAM_IMAGEFX_FILM,
+ * - MMAL_PARAM_IMAGEFX_BLUR,
+ * - MMAL_PARAM_IMAGEFX_SATURATION,
+ * - MMAL_PARAM_IMAGEFX_COLOURSWAP,
+ * - MMAL_PARAM_IMAGEFX_WASHEDOUT,
+ * - MMAL_PARAM_IMAGEFX_POSTERISE,
+ * - MMAL_PARAM_IMAGEFX_COLOURPOINT,
+ * - MMAL_PARAM_IMAGEFX_COLOURBALANCE,
+ * - MMAL_PARAM_IMAGEFX_CARTOON,
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX)
+{
+ MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX};
+
+ if (!camera)
+ return 1;
+
+ return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr));
+}
+
+/* TODO :what to do with the image effects parameters?
+ MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)},
+ imageFX, 0, {0}};
+mmal_port_parameter_set(camera->control, &imfx_param.hdr);
+ */
+
+/**
+ * Set the colour effect for images (Set UV component)
+ * @param camera Pointer to camera component
+ * @param colourFX Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white)
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX)
+{
+ MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0};
+
+ if (!camera)
+ return 1;
+
+ colfx.enable = colourFX->enable;
+ colfx.u = colourFX->u;
+ colfx.v = colourFX->v;
+
+ return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr));
+
+}
+
+
+/**
+ * Set the rotation of the image
+ * @param camera Pointer to camera component
+ * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only)
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation)
+{
+ int ret;
+ int my_rotation = ((rotation % 360 ) / 90) * 90;
+
+ ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation);
+ mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation);
+ mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation);
+
+ return ret;
+}
+
+/**
+ * Set the flips state of the image
+ * @param camera Pointer to camera component
+ * @param hflip If true, horizontally flip the image
+ * @param vflip If true, vertically flip the image
+ *
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip)
+{
+ MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE};
+
+ if (hflip && vflip)
+ mirror.value = MMAL_PARAM_MIRROR_BOTH;
+ else
+ if (hflip)
+ mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL;
+ else
+ if (vflip)
+ mirror.value = MMAL_PARAM_MIRROR_VERTICAL;
+
+ mmal_port_parameter_set(camera->output[0], &mirror.hdr);
+ mmal_port_parameter_set(camera->output[1], &mirror.hdr);
+ return mmal_port_parameter_set(camera->output[2], &mirror.hdr);
+}
+
+/**
+ * Set the ROI of the sensor to use for captures/preview
+ * @param camera Pointer to camera component
+ * @param rect Normalised coordinates of ROI rectangle
+ *
+ * @return 0 if successful, non-zero if any parameters out of range
+ */
+int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect)
+{
+ MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}};
+
+ crop.rect.x = (65536 * rect.x);
+ crop.rect.y = (65536 * rect.y);
+ crop.rect.width = (65536 * rect.w);
+ crop.rect.height = (65536 * rect.h);
+
+ return mmal_port_parameter_set(camera->control, &crop.hdr);
+}
+
+
+/**
+ * Asked GPU how much memory it has allocated
+ *
+ * @return amount of memory in MB
+ */
+static int raspicamcontrol_get_mem_gpu(void)
+{
+ char response[80] = "";
+ int gpu_mem = 0;
+ if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0)
+ vc_gencmd_number_property(response, "gpu", &gpu_mem);
+ return gpu_mem;
+}
+
+/**
+ * Ask GPU about its camera abilities
+ * @param supported None-zero if software supports the camera
+ * @param detected None-zero if a camera has been detected
+ */
+static void raspicamcontrol_get_camera(int *supported, int *detected)
+{
+ char response[80] = "";
+ if (vc_gencmd(response, sizeof response, "get_camera") == 0)
+ {
+ if (supported)
+ vc_gencmd_number_property(response, "supported", supported);
+ if (detected)
+ vc_gencmd_number_property(response, "detected", detected);
+ }
+}
+
+/**
+ * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities
+ * @param supported None-zero if software supports the camera
+ * @param detected None-zero if a camera has been detected
+ */
+void raspicamcontrol_check_configuration(int min_gpu_mem)
+{
+ int gpu_mem = raspicamcontrol_get_mem_gpu();
+ int supported = 0, detected = 0;
+ raspicamcontrol_get_camera(&supported, &detected);
+ if (!supported)
+ vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n");
+ else if (gpu_mem < min_gpu_mem)
+ vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem);
+ else if (!detected)
+ vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n");
+ else
+ vcos_log_error("Failed to run camera app. Please check for firmware updates\n");
+}
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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, ¶m.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set ratecontrol");
+ goto error;
+ }
+
+ }
+
+ if (state->intraperiod)
+ {
+ MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod};
+ status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set intraperiod");
+ goto error;
+ }
+
+ }
+
+ {
+ MMAL_PARAMETER_VIDEO_PROFILE_T param;
+ param.hdr.id = MMAL_PARAMETER_PROFILE;
+ param.hdr.size = sizeof(param);
+
+ param.profile[0].profile = state->profile;
+ param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
+
+ status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set H264 profile");
+ goto error;
+ }
+ }
+
+
+ if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to set immutable input flag");
+ // Continue rather than abort..
+ }
+
+ // Enable component
+ status = mmal_component_enable(encoder);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to enable video encoder component");
+ goto error;
+ }
+
+ /* Create pool of buffer headers for the output port to consume */
+ pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
+
+ if (!pool)
+ {
+ vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
+ }
+
+ state->encoder_pool = pool;
+ state->encoder_component = encoder;
+
+ if (state->verbose)
+ fprintf(stderr, "Encoder component done\n");
+
+ return status;
+
+ error:
+ if (encoder)
+ mmal_component_destroy(encoder);
+
+ return status;
+}
+
+/**
+ * Destroy the encoder component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void destroy_encoder_component(RASPIVID_STATE *state)
+{
+ // Get rid of any port buffers first
+ if (state->encoder_pool)
+ {
+ mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
+ }
+
+ if (state->encoder_component)
+ {
+ mmal_component_destroy(state->encoder_component);
+ state->encoder_component = NULL;
+ }
+}
+
+/**
+ * Connect two specific ports together
+ *
+ * @param output_port Pointer the output port
+ * @param input_port Pointer the input port
+ * @param Pointer to a mmal connection pointer, reassigned if function successful
+ * @return Returns a MMAL_STATUS_T giving result of operation
+ *
+ */
+static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
+{
+ MMAL_STATUS_T status;
+
+ status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
+
+ if (status == MMAL_SUCCESS)
+ {
+ status = mmal_connection_enable(*connection);
+ if (status != MMAL_SUCCESS)
+ mmal_connection_destroy(*connection);
+ }
+
+ return status;
+}
+
+/**
+ * Checks if specified port is valid and enabled, then disables it
+ *
+ * @param port Pointer the port
+ *
+ */
+static void check_disable_port(MMAL_PORT_T *port)
+{
+ if (port && port->is_enabled)
+ mmal_port_disable(port);
+}
+
+
+int raspi_capture_start()
+{
+ // Our main data storage vessel..
+ RASPIVID_STATE state;
+ int exit_code = EX_OK;
+
+ MMAL_STATUS_T status = MMAL_SUCCESS;
+ MMAL_PORT_T *camera_preview_port = NULL;
+ MMAL_PORT_T *camera_video_port = NULL;
+ MMAL_PORT_T *camera_still_port = NULL;
+ MMAL_PORT_T *preview_input_port = NULL;
+ MMAL_PORT_T *encoder_input_port = NULL;
+ MMAL_PORT_T *encoder_output_port = NULL;
+ FILE *output_file = NULL;
+
+ bcm_host_init();
+
+ // Register our application with the logging system
+ vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
+
+ raspi_capture_default_state(&state);
+
+ if (state.verbose)
+ {
+ dump_state(&state);
+ }
+
+ // OK, we have a nice set of parameters. Now set up our components
+ // We have three components. Camera, Preview and encoder.
+
+ if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
+ {
+ vcos_log_error("%s: Failed to create camera component", __func__);
+ exit_code = EX_SOFTWARE;
+ }
+ else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
+ {
+ vcos_log_error("%s: Failed to create preview component", __func__);
+ destroy_camera_component(&state);
+ exit_code = EX_SOFTWARE;
+ }
+ else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS)
+ {
+ vcos_log_error("%s: Failed to create encode component", __func__);
+ raspipreview_destroy(&state.preview_parameters);
+ destroy_camera_component(&state);
+ exit_code = EX_SOFTWARE;
+ }
+ else
+ {
+ PORT_USERDATA callback_data;
+
+ if (state.verbose)
+ fprintf(stderr, "Starting component connection stage\n");
+
+ camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
+ camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
+ camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
+ preview_input_port = state.preview_parameters.preview_component->input[0];
+ encoder_input_port = state.encoder_component->input[0];
+ encoder_output_port = state.encoder_component->output[0];
+
+ if (state.preview_parameters.wantPreview )
+ {
+ if (state.verbose)
+ {
+ fprintf(stderr, "Connecting camera preview port to preview input port\n");
+ fprintf(stderr, "Starting video preview\n");
+ }
+
+ // Connect camera to preview
+ status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
+ }
+ else
+ {
+ status = MMAL_SUCCESS;
+ }
+
+ if (status == MMAL_SUCCESS)
+ {
+ if (state.verbose)
+ fprintf(stderr, "Connecting camera stills port to encoder input port\n");
+
+ // Now connect the camera to the encoder
+ status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
+ goto error;
+ }
+
+ if (state.filename)
+ {
+ if (state.filename[0] == '-')
+ {
+ output_file = stdout;
+
+ // Ensure we don't upset the output stream with diagnostics/info
+ state.verbose = 0;
+ }
+ else
+ {
+ if (state.verbose)
+ fprintf(stderr, "Opening output file \"%s\"\n", state.filename);
+
+ output_file = fopen(state.filename, "wb");
+ }
+
+ if (!output_file)
+ {
+ // Notify user, carry on but discarding encoded output buffers
+ vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename);
+ }
+ }
+
+ // Set up our userdata - this is passed though to the callback where we need the information.
+ callback_data.file_handle = output_file;
+ callback_data.pstate = &state;
+ callback_data.abort = 0;
+
+ encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
+
+ if (state.verbose)
+ fprintf(stderr, "Enabling encoder output port\n");
+
+ // Enable the encoder output port and tell it its callback function
+ status = mmal_port_enable(encoder_output_port, encoder_buffer_callback);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Failed to setup encoder output");
+ goto error;
+ }
+
+ if (state.demoMode)
+ {
+ // Run for the user specific time..
+ int num_iterations = state.timeout / state.demoInterval;
+ int i;
+
+ if (state.verbose)
+ fprintf(stderr, "Running in demo mode\n");
+
+ for (i=0;state.timeout == 0 || 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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, ¶m.hdr);
+
+ if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
+ {
+ vcos_log_error("unable to set preview port parameters (%u)", status);
+ goto error;
+ }
+ }
+
+ /* Enable component */
+ status = mmal_component_enable(preview);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to enable preview/null sink component (%u)", status);
+ goto error;
+ }
+
+ state->preview_component = preview;
+
+ return status;
+
+error:
+
+ if (preview)
+ mmal_component_destroy(preview);
+
+ return status;
+}
+
+
+/**
+ * Destroy the preview component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state)
+{
+ if (state->preview_component)
+ {
+ mmal_component_destroy(state->preview_component);
+ state->preview_component = NULL;
+ }
+}
+
+/**
+ * Assign set of default parameters to the passed in parameter block
+ *
+ * @param state Pointer to parameter block
+ *
+ */
+void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state)
+{
+ state->wantPreview = 1;
+ state->wantFullScreenPreview = 1;
+ state->opacity = 255;
+ state->previewWindow.x = 0;
+ state->previewWindow.y = 0;
+ state->previewWindow.width = 1024;
+ state->previewWindow.height = 768;
+ state->preview_component = NULL;
+}
+
+/**
+ * Dump parameters as human readable to stdout
+ *
+ * @param state Pointer to parameter block
+ *
+ */
+void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state)
+{
+ fprintf(stderr, "Preview %s, Full screen %s\n", state->wantPreview ? "Yes" : "No",
+ state->wantFullScreenPreview ? "Yes" : "No");
+
+ fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", state->previewWindow.x,
+ state->previewWindow.y, state->previewWindow.width,
+ state->previewWindow.height, state->opacity);
+};
+
+#if 0
+/**
+ * Parse a possible command pair - command and parameter
+ * @param arg1 Command
+ * @param arg2 Parameter (could be NULL)
+ * @return How many parameters were used, 0,1,2
+ */
+int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2)
+{
+ int command_id, used = 0, num_parameters;
+
+ if (!arg1)
+ return 0;
+
+ command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters);
+
+ // If invalid command, or we are missing a parameter, drop out
+ if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL))
+ return 0;
+
+ switch (command_id)
+ {
+ case CommandPreview: // Preview window
+ {
+ int tmp;
+
+ params->wantPreview = 1;
+
+ tmp = sscanf(arg2, "%d,%d,%d,%d",
+ ¶ms->previewWindow.x, ¶ms->previewWindow.y,
+ ¶ms->previewWindow.width, ¶ms->previewWindow.height);
+
+ // Failed to get any window parameters, so revert to full screen
+ if (tmp == 0)
+ params->wantFullScreenPreview = 1;
+ else
+ params->wantFullScreenPreview = 0;
+
+ used = 2;
+
+ break;
+ }
+
+ case CommandFullScreen: // Want full screen preview mode (overrides display rect)
+ params->wantPreview = 1;
+ params->wantFullScreenPreview = 1;
+
+ used = 1;
+ break;
+
+ case CommandOpacity: // Define preview window opacity
+ if (sscanf(arg2, "%u", ¶ms->opacity) != 1)
+ params->opacity = 255;
+ else
+ used = 2;
+ break;
+
+ case CommandDisablePreview: // Turn off preview output
+ params->wantPreview = 0;
+ used = 1;
+ break;
+ }
+
+ return used;
+}
+
+/**
+ * Display help for command line options
+ */
+void raspipreview_display_help()
+{
+ fprintf(stderr, "\nPreview parameter commands\n\n");
+ raspicli_display_help(cmdline_commands, cmdline_commands_size);
+}
+#endif
--- /dev/null
+/*
+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_ */
--- /dev/null
+/*
+ * 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, ¶m_thumb.hdr);
+ }
+
+ // Enable component
+ status = mmal_component_enable(encoder);
+
+ if (status != MMAL_SUCCESS)
+ {
+ vcos_log_error("Unable to enable video encoder component");
+ goto error;
+ }
+
+ /* Create pool of buffer headers for the output port to consume */
+ pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
+
+ if (!pool)
+ {
+ vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
+ }
+
+ state->encoder_pool = pool;
+ state->encoder_component = encoder;
+
+ if (state->verbose)
+ fprintf(stderr, "Encoder component done\n");
+
+ return status;
+
+ error:
+
+ if (encoder)
+ mmal_component_destroy(encoder);
+
+ return status;
+}
+
+/**
+ * Destroy the encoder component
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void destroy_encoder_component(RASPISTILL_STATE *state)
+{
+ // Get rid of any port buffers first
+ if (state->encoder_pool)
+ {
+ mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
+ }
+
+ if (state->encoder_component)
+ {
+ mmal_component_destroy(state->encoder_component);
+ state->encoder_component = NULL;
+ }
+}
+
+
+/**
+ * Add an exif tag to the capture
+ *
+ * @param state Pointer to state control struct
+ * @param exif_tag String containing a "key=value" pair.
+ * @return Returns a MMAL_STATUS_T giving result of operation
+ */
+static MMAL_STATUS_T add_exif_tag(RASPISTILL_STATE *state, const char *exif_tag)
+{
+ MMAL_STATUS_T status;
+ MMAL_PARAMETER_EXIF_T *exif_param = (MMAL_PARAMETER_EXIF_T*)calloc(sizeof(MMAL_PARAMETER_EXIF_T) + MAX_EXIF_PAYLOAD_LENGTH, 1);
+
+ vcos_assert(state);
+ vcos_assert(state->encoder_component);
+
+ // Check to see if the tag is present or is indeed a key=value pair.
+ if (!exif_tag || strchr(exif_tag, '=') == NULL || strlen(exif_tag) > MAX_EXIF_PAYLOAD_LENGTH-1)
+ return MMAL_EINVAL;
+
+ exif_param->hdr.id = MMAL_PARAMETER_EXIF;
+
+ strncpy((char*)exif_param->data, exif_tag, MAX_EXIF_PAYLOAD_LENGTH-1);
+
+ exif_param->hdr.size = sizeof(MMAL_PARAMETER_EXIF_T) + strlen((char*)exif_param->data);
+
+ status = mmal_port_parameter_set(state->encoder_component->output[0], &exif_param->hdr);
+
+ free(exif_param);
+
+ return status;
+}
+
+/**
+ * Add a basic set of EXIF tags to the capture
+ * Make, Time etc
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void add_exif_tags(RASPISTILL_STATE *state)
+{
+ time_t rawtime;
+ struct tm *timeinfo;
+ char time_buf[32];
+ char exif_buf[128];
+ int i;
+
+ add_exif_tag(state, "IFD0.Model=RP_OV5647");
+ add_exif_tag(state, "IFD0.Make=RaspberryPi");
+
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+
+ snprintf(time_buf, sizeof(time_buf),
+ "%04d:%02d:%02d %02d:%02d:%02d",
+ timeinfo->tm_year+1900,
+ timeinfo->tm_mon+1,
+ timeinfo->tm_mday,
+ timeinfo->tm_hour,
+ timeinfo->tm_min,
+ timeinfo->tm_sec);
+
+ snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeDigitized=%s", time_buf);
+ add_exif_tag(state, exif_buf);
+
+ snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeOriginal=%s", time_buf);
+ add_exif_tag(state, exif_buf);
+
+ snprintf(exif_buf, sizeof(exif_buf), "IFD0.DateTime=%s", time_buf);
+ add_exif_tag(state, exif_buf);
+
+ // Now send any user supplied tags
+
+ for (i=0;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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+
+
#include <gst/video/video.h>
#include "gstrpicamsrc.h"
+#include "RaspiCapture.h"
#include "bcm_host.h"
#include "interface/vcos/vcos.h"
"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 ", " \
"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;
"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
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
#define __GST_RPICAMSRC_H__
#include <gst/gst.h>
-#include <gst/basecamerabinsrc/gstbasecamerasrc.h>
+#include <gst/base/gstpushsrc.h>
G_BEGIN_DECLS
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);