From 6bd0347bf0fcda6db28d00745ef75431f11a0e4a Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 5 Mar 2015 17:01:33 +1100 Subject: [PATCH] rpicamsrc: Incorporate raspivid changes from upstream Merge all changes for new features from upstream raspberrypi userland, up to commit 0de0b2 --- sys/rpicamsrc/RaspiCLI.c | 155 ++++++++ sys/rpicamsrc/RaspiCLI.h | 56 +++ sys/rpicamsrc/RaspiCamControl.c | 228 ++++++++++- sys/rpicamsrc/RaspiCamControl.h | 32 +- sys/rpicamsrc/RaspiCapture.c | 654 +++++++++++++++++++++++++++++--- sys/rpicamsrc/RaspiCapture.h | 13 +- sys/rpicamsrc/RaspiPreview.c | 2 +- sys/rpicamsrc/RaspiPreview.h | 14 +- 8 files changed, 1094 insertions(+), 60 deletions(-) create mode 100644 sys/rpicamsrc/RaspiCLI.c create mode 100644 sys/rpicamsrc/RaspiCLI.h diff --git a/sys/rpicamsrc/RaspiCLI.c b/sys/rpicamsrc/RaspiCLI.c new file mode 100644 index 0000000000..2bbb3ce095 --- /dev/null +++ b/sys/rpicamsrc/RaspiCLI.c @@ -0,0 +1,155 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiCLI.c + * Code for handling command line parameters + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * Some functions/structures for command line parameter parsing + * + */ +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "RaspiCLI.h" + + +/** + * Convert a string from command line to a comand_id from the list + * + * @param commands Array of command to check + * @param num_command Number of commands in the array + * @param arg String to search for in the list + * @param num_parameters Returns the number of parameters used by the command + * + * @return command ID if found, -1 if not found + * + */ +int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters) +{ + int command_id = -1; + int j; + + vcos_assert(commands); + vcos_assert(num_parameters); + vcos_assert(arg); + + if (!commands || !num_parameters || !arg) + return -1; + + for (j = 0; j < num_commands; j++) + { + if (!strcmp(arg, commands[j].command) || + !strcmp(arg, commands[j].abbrev)) + { + // match + command_id = commands[j].id; + *num_parameters = commands[j].num_parameters; + break; + } + } + + return command_id; +} + + +/** + * Display the list of commands in help format + * + * @param commands Array of command to check + * @param num_command Number of commands in the arry + * + * + */ +void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands) +{ + int i; + + vcos_assert(commands); + + if (!commands) + return; + + for (i = 0; i < num_commands; i++) + { + fprintf(stderr, "-%s, -%s\t: %s\n", commands[i].abbrev, + commands[i].command, commands[i].help); + } +} + + +/** + * Function to take a string, a mapping, and return the int equivalent + * @param str Incoming string to match + * @param map Mapping data + * @param num_refs The number of items in the mapping data + * @return The integer match for the string, or -1 if no match + */ +int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs) +{ + int i; + + for (i=0;ishutter_speed); + used = 2; + break; + } + + case CommandAwbGains : + { + double r,b; + int args; + + args = sscanf(arg2, "%lf,%lf", &r,&b); + + if (args != 2 || r > 8.0 || b > 8.0) + { + return 0; + } + + params->awb_gains_r = r; + params->awb_gains_b = b; + + used = 2; + break; + } + + case CommandDRCLevel: + { + params->drc_level = drc_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStatsPass: + { + params->stats_pass = MMAL_TRUE; + used = 1; + break; + } + + case CommandAnnotate: + { + // If parameter is a number, assume its a bitmask, otherwise a string + if (isdigit(*arg2)) + { + sscanf(arg2, "%u", ¶ms->enable_annotate); + } + else + { + params->enable_annotate = ANNOTATE_USER_TEXT; + strncpy(params->annotate_string, arg2, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); + params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2-1] = '\0'; + } + used=2; + break; + } + } return used; @@ -565,6 +642,13 @@ void raspicamcontrol_display_help() fprintf(stderr, ",%s", metering_mode_map[i].mode); } + fprintf(stderr, "\n\nDynamic Range Compression (DRC) options :\n%s", drc_mode_map[0].mode ); + + for (i=1;ihflip = params->vflip = 0; params->roi.x = params->roi.y = 0.0; params->roi.w = params->roi.h = 1.0; + params->shutter_speed = 0; // 0 = auto + params->awb_gains_r = 0; // Only have any function if AWB OFF is used. + params->awb_gains_b = 0; + params->drc_level = MMAL_PARAMETER_DRC_STRENGTH_OFF; + params->stats_pass = MMAL_FALSE; + params->enable_annotate = 0; + params->annotate_string[0] = '\0'; } /** @@ -706,12 +797,17 @@ int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_ result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); result += raspicamcontrol_set_awb_mode(camera, params->awbMode); + result += raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b); result += raspicamcontrol_set_imageFX(camera, params->imageEffect); result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason result += raspicamcontrol_set_rotation(camera, params->rotation); result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); result += raspicamcontrol_set_ROI(camera, params->roi); + result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); + result += raspicamcontrol_set_DRC(camera, params->drc_level); + result += raspicamcontrol_set_stats_pass(camera, params->stats_pass); + result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string); return result; } @@ -944,6 +1040,22 @@ int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); } +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain) +{ + MMAL_PARAMETER_AWB_GAINS_T param = {{MMAL_PARAMETER_CUSTOM_AWB_GAINS,sizeof(param)}, {0,0}, {0,0}}; + + if (!camera) + return 1; + + if (!r_gain || !b_gain) + return 0; + + param.r_gain.num = (unsigned int)(r_gain * 65536); + param.b_gain.num = (unsigned int)(b_gain * 65536); + param.r_gain.den = param.b_gain.den = 65536; + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + /** * Set the image effect for the images * @param camera Pointer to camera component @@ -1074,6 +1186,116 @@ int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) return mmal_port_parameter_set(camera->control, &crop.hdr); } +/** + * Adjust the exposure time used for images + * @param camera Pointer to camera component + * @param shutter speed in microseconds + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_SHUTTER_SPEED, speed)); +} + +/** + * Adjust the Dynamic range compression level + * @param camera Pointer to camera component + * @param strength Strength of DRC to apply + * MMAL_PARAMETER_DRC_STRENGTH_OFF + * MMAL_PARAMETER_DRC_STRENGTH_LOW + * MMAL_PARAMETER_DRC_STRENGTH_MEDIUM + * MMAL_PARAMETER_DRC_STRENGTH_HIGH + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength) +{ + MMAL_PARAMETER_DRC_T drc = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, sizeof(MMAL_PARAMETER_DRC_T)}, strength}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &drc.hdr)); +} + +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_CAPTURE_STATS_PASS, stats_pass)); +} + + +/** + * Set the annotate data + * @param camera Pointer to camera component + * @param Bitmask of required annotation data. 0 for off. + * @param If set, a pointer to text string to use instead of bitmask, max length 32 characters + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string) +{ + MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T annotate = + {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T)}}; + + if (settings) + { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; + + annotate.enable = 1; + + if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT)) + { + strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2); + annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2-1] = '\0'; + } + + if (settings & ANNOTATE_TIME_TEXT) + { + strftime(tmp, 32, "%X ", &tm ); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - strlen(annotate.text) - 1); + } + + if (settings & ANNOTATE_DATE_TEXT) + { + strftime(tmp, 32, "%x", &tm ); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 - strlen(annotate.text) - 1); + } + + if (settings & ANNOTATE_SHUTTER_SETTINGS) + annotate.show_shutter = 1; + + if (settings & ANNOTATE_GAIN_SETTINGS) + annotate.show_analog_gain = 1; + + if (settings & ANNOTATE_LENS_SETTINGS) + annotate.show_lens = 1; + + if (settings & ANNOTATE_CAF_SETTINGS) + annotate.show_caf = 1; + + if (settings & ANNOTATE_MOTION_SETTINGS) + annotate.show_motion = 1; + + if (settings & ANNOTATE_FRAME_NUMBER) + annotate.show_frame_num = 1; + + if (settings & ANNOTATE_BLACK_BACKGROUND) + annotate.black_text_background = 1; + } + else + annotate.enable = 0; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &annotate.hdr)); +} + /** * Asked GPU how much memory it has allocated diff --git a/sys/rpicamsrc/RaspiCamControl.h b/sys/rpicamsrc/RaspiCamControl.h index aac3e19cad..3393947db7 100644 --- a/sys/rpicamsrc/RaspiCamControl.h +++ b/sys/rpicamsrc/RaspiCamControl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jan Schmidt + * Copyright (c) 2013-2015 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes @@ -87,6 +87,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/// Annotate bitmask options +/// Supplied by user on command line +#define ANNOTATE_USER_TEXT 1 +/// Supplied by app using this module +#define ANNOTATE_APP_TEXT 2 +/// Insert current date +#define ANNOTATE_DATE_TEXT 4 +// Insert current time +#define ANNOTATE_TIME_TEXT 8 + +#define ANNOTATE_SHUTTER_SETTINGS 16 +#define ANNOTATE_CAF_SETTINGS 32 +#define ANNOTATE_GAIN_SETTINGS 64 +#define ANNOTATE_LENS_SETTINGS 128 +#define ANNOTATE_MOTION_SETTINGS 256 +#define ANNOTATE_FRAME_NUMBER 512 +#define ANNOTATE_BLACK_BACKGROUND 1024 // There isn't actually a MMAL structure for the following, so make one @@ -131,6 +148,14 @@ typedef struct int hflip; /// 0 or 1 int vflip; /// 0 or 1 PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect + int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms + float awb_gains_r; /// AWB red gain + float awb_gains_b; /// AWB blue gain + MMAL_PARAMETER_DRC_STRENGTH_T drc_level; // Strength of Dynamic Range compression to apply + MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off + int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed + char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings + } RASPICAM_CAMERA_PARAMETERS; @@ -160,11 +185,16 @@ int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabi int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain); int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength); +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass); +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string); //Individual getting functions int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); diff --git a/sys/rpicamsrc/RaspiCapture.c b/sys/rpicamsrc/RaspiCapture.c index 3688ae4152..d5c9e15300 100644 --- a/sys/rpicamsrc/RaspiCapture.c +++ b/sys/rpicamsrc/RaspiCapture.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jan Schmidt + * Copyright (c) 2013-2015 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Modification of the RaspiVid command line capture program for GStreamer * use. * - * \date 28th Feb 2013, 11 Oct 2013 + * \date 28th Feb 2013, 11 Oct 2013, 5 Mar 2015 * \Author: James Hughes, Jan Schmidt * * Description @@ -50,6 +50,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * We use the RaspiPreview code to handle the (generic) preview window */ +// We use some GNU extensions (basename, asprintf) +#define _GNU_SOURCE #include #include @@ -73,18 +75,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "RaspiCapture.h" #include "RaspiCamControl.h" #include "RaspiPreview.h" +#include "RaspiCLI.h" #include -/// Camera number to use - we only have one camera, indexed from 0. -#define CAMERA_NUMBER 0 - // Standard port setting for the camera component #define MMAL_CAMERA_PREVIEW_PORT 0 #define MMAL_CAMERA_VIDEO_PORT 1 #define MMAL_CAMERA_CAPTURE_PORT 2 // Video format information +// 0 implies variable #define VIDEO_FRAME_RATE_NUM 30 #define VIDEO_FRAME_RATE_DEN 1 @@ -92,7 +93,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define VIDEO_OUTPUT_BUFFERS_NUM 3 // Max bitrate we allow for recording -const int MAX_BITRATE = 30000000; // 30Mbits/s +const int MAX_BITRATE = 25000000; // 25Mbits/s /// Interval at which we check for an failure abort during capture const int ABORT_INTERVAL = 100; // ms @@ -128,9 +129,12 @@ struct RASPIVID_STATE_T PORT_USERDATA callback_data; MMAL_QUEUE_T *encoded_buffer_q; + + int64_t base_time; + int64_t last_second; }; -#if 0 + /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent static XREF_T profile_map[] = { @@ -142,6 +146,28 @@ static XREF_T profile_map[] = static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]); +#if 0 +static XREF_T initial_map[] = +{ + {"record", 0}, + {"pause", 1}, +}; + +static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]); +#endif + +static XREF_T intra_refresh_map[] = +{ + {"cyclic", MMAL_VIDEO_INTRA_REFRESH_CYCLIC}, + {"adaptive", MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE}, + {"both", MMAL_VIDEO_INTRA_REFRESH_BOTH}, + {"cyclicrows", MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS}, +// {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why. +}; + +static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]); + +#if 0 static void display_valid_parameters(char *app_name); @@ -158,20 +184,53 @@ static void display_valid_parameters(char *app_name); #define CommandPreviewEnc 9 #define CommandIntraPeriod 10 #define CommandProfile 11 +#define CommandTimed 12 +#define CommandSignal 13 +#define CommandKeypress 14 +#define CommandInitialState 15 +#define CommandQP 16 +#define CommandInlineHeaders 17 +#define CommandSegmentFile 18 +#define CommandSegmentWrap 19 +#define CommandSegmentStart 20 +#define CommandSplitWait 21 +#define CommandCircular 22 +#define CommandIMV 23 +#define CommandCamSelect 24 +#define CommandSettings 25 +#define CommandSensorMode 26 +#define CommandIntraRefreshType 27 static COMMAND_LIST cmdline_commands[] = { - { CommandHelp, "-help", "?", "This help information", 0 }, - { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, - { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, - { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, - { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, - { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, - { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, - { CommandFramerate,"-framerate", "fps","Specify the frames per second to record", 1}, - { CommandPreviewEnc,"-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, - { CommandIntraPeriod,"-intra", "g", "Specify the intra refresh period (key frame rate/GoP size)", 1}, - { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -')", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1}, + { CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, + { CommandIntraPeriod, "-intra", "g", "Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames.", 1}, + { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, + { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0}, + { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0}, + { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0}, + { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1}, + { CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1}, + { CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0}, + { CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval ", 1}, + { CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1}, + { CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1}, + { CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0}, + { CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0}, + { CommandIMV, "-vectors", "x", "Output filename for inline motion vectors", 1 }, + { CommandCamSelect, "-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1}, }; static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); @@ -186,6 +245,14 @@ static void dump_state(RASPIVID_STATE *state); */ void raspicapture_default_config(RASPIVID_CONFIG *config) { + if (!config) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(config, 0, sizeof(RASPIVID_CONFIG)); // Now set anything non-zero config->timeout = 5000; // 5s delay before take image @@ -194,12 +261,23 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) config->bitrate = 17000000; // This is a decent default bitrate for 1080p config->fps_n = VIDEO_FRAME_RATE_NUM; config->fps_d = VIDEO_FRAME_RATE_DEN; - config->intraperiod = 0; // Not set + config->intraperiod = -1; // Not set + config->quantisationParameter = 0; config->demoMode = 0; config->demoInterval = 250; // ms config->immutableInput = 1; config->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + config->bInlineHeaders = 0; + + config->inlineMotionVectors = 0; + + config->cameraNum = 0; + config->settings = 0; + config->sensor_mode = 0; + + config->intra_refresh_type = -1; + // Setup preview window defaults raspipreview_set_defaults(&config->preview_parameters); @@ -208,6 +286,7 @@ void raspicapture_default_config(RASPIVID_CONFIG *config) } + /** * Dump image state parameters to printf. Used for debugging * @@ -311,7 +390,7 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) state->filename = malloc(len + 1); vcos_assert(state->filename); if (state->filename) - strncpy(state->filename, argv[i + 1], len); + strncpy(state->filename, argv[i + 1], len+1); i++; } else @@ -327,7 +406,10 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) { if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) { - // TODO : What limits do we need for timeout? + // Ensure that if previously selected a waitMethod we dont overwrite it + if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE) + state->waitMethod = WAIT_METHOD_FOREVER; + i++; } else @@ -386,6 +468,15 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) break; } + case CommandQP: // quantisation parameter + { + if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1) + i++; + else + valid = 0; + break; + } + case CommandProfile: // H264 profile { state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size); @@ -397,6 +488,146 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) break; } + case CommandInlineHeaders: // H264 inline headers + { + state->bInlineHeaders = 1; + break; + } + + case CommandTimed: + { + if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2) + { + i++; + + if (state->onTime < 1000) + state->onTime = 1000; + + if (state->offTime < 1000) + state->offTime = 1000; + + state->waitMethod = WAIT_METHOD_TIMED; + } + else + valid = 0; + break; + } + + case CommandKeypress: + state->waitMethod = WAIT_METHOD_KEYPRESS; + break; + + case CommandSignal: + state->waitMethod = WAIT_METHOD_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandInitialState: + { + state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size); + + if( state->bCapturing == -1) + state->bCapturing = 0; + + i++; + break; + } + + case CommandSegmentFile: // Segment file in to chunks of specified time + { + if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1) + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + i++; + } + else + valid = 0; + break; + } + + case CommandSegmentWrap: // segment wrap value + { + if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1) + i++; + else + valid = 0; + break; + } + + case CommandSegmentStart: // initial segment number + { + if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap))) + i++; + else + valid = 0; + break; + } + + case CommandSplitWait: // split files on restart + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + state->splitWait = 1; + break; + } + + case CommandCircular: + { + state->bCircularBuffer = 1; + break; + } + + case CommandIMV: // output filename + { + state->inlineMotionVectors = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->imv_filename = malloc(len + 1); + vcos_assert(state->imv_filename); + if (state->imv_filename) + strncpy(state->imv_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandSettings: + state->settings = 1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandIntraRefreshType: + { + state->config->intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size); + i++; + break; + } + default: { // Try parsing for any image specific parameters @@ -423,10 +654,16 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) if (!valid) { - fprintf(stderr, "Invalid command line option (%s)\n", argv[i]); + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); return 1; } + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + return 0; } @@ -454,6 +691,18 @@ static void display_valid_parameters(char *app_name) fprintf(stderr, ",%s", profile_map[i].mode); } + fprintf(stderr, "\n"); + + // Intra refresh options + fprintf(stderr, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode ); + + for (i=1;icmd == MMAL_EVENT_PARAMETER_CHANGED) { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } } else { @@ -487,6 +752,123 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf mmal_buffer_header_release(buffer); } +#if 0 +/** + * Open a file based on the settings in state + * + * @param state Pointer to state + */ +static FILE *open_filename(RASPIVID_STATE *pState) +{ + FILE *new_handle = NULL; + char *tempname = NULL, *filename = NULL; + + if (pState->segmentSize || pState->splitWait) + { + // Create a new filename string + asprintf(&tempname, pState->filename, pState->segmentNumber); + filename = tempname; + } + else + { + filename = pState->filename; + } + + if (filename) + new_handle = fopen(filename, "wb"); + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new file \"%s\"\n", filename); + } + + if (tempname) + free(tempname); + + return new_handle; +} + +/** + * Open a file based on the settings in state + * + * This time for the imv output file + * + * @param state Pointer to state + */ +static FILE *open_imv_filename(RASPIVID_STATE *pState) +{ + FILE *new_handle = NULL; + char *tempname = NULL, *filename = NULL; + + if (pState->segmentSize || pState->splitWait) + { + // Create a new filename string + asprintf(&tempname, pState->imv_filename, pState->segmentNumber); + filename = tempname; + } + else + { + filename = pState->imv_filename; + } + + if (filename) + new_handle = fopen(filename, "wb"); + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening imv output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new imv file \"%s\"\n", filename); + } + + if (tempname) + free(tempname); + + return new_handle; +} +#endif + +/** + * Update any annotation data specific to the video. + * This simply passes on the setting from cli, or + * if application defined annotate requested, updates + * with the H264 parameters + * + * @param state Pointer to state control struct + * + */ +static void update_annotation_data(RASPIVID_STATE *state) +{ + RASPIVID_CONFIG *config = state->config; + + // So, if we have asked for a application supplied string, set it to the H264 parameters + if (config->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) + { + char *text; + const char *refresh = raspicli_unmap_xref(config->intra_refresh_type, intra_refresh_map, intra_refresh_map_size); + + asprintf(&text, "%dk,%ff,%s,%d,%s", + config->bitrate / 1000, ((float)(config->fps_n) / config->fps_d), + refresh ? refresh : "(none)", + config->intraperiod, + raspicli_unmap_xref(config->profile, profile_map, profile_map_size)); + + raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text); + + free(text); + } + else + { + raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, config->camera_parameters.annotate_string); + } +} + + + /** * buffer header callback function for encoder * @@ -499,6 +881,11 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf { PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; RASPIVID_STATE *state = pData->state; + int64_t current_time; + + // All our segment times based on the receipt of the first encoder callback + if (state->base_time == -1) + state->base_time = vcos_getmicrosecs64()/1000; if (pData == NULL) { @@ -508,6 +895,17 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf return; } + current_time = vcos_getmicrosecs64()/1000; + if (state->base_time == -1) + state->base_time = current_time; + + // See if the second count has changed and we need to update any annotation + if (current_time/1000 != state->last_second) + { + update_annotation_data(state); + state->last_second = current_time/1000; + } + /* Send buffer to GStreamer element for pushing to the pipeline */ mmal_queue_put(state->encoded_buffer_q, buffer); } @@ -565,6 +963,7 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) { MMAL_COMPONENT_T *camera = NULL; MMAL_STATUS_T status; + RASPIVID_CONFIG *config = state->config; /* Create the component */ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); @@ -574,7 +973,18 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) vcos_log_error("Failed to create camera component"); goto error; } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum}; + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + if (!camera->output_num) { status = MMAL_ENOSYS; @@ -582,6 +992,26 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) goto error; } + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + if (config->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } // Enable the camera, and tell it its control callback function status = mmal_port_enable(camera->control, camera_control_callback); @@ -610,18 +1040,19 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) MMAL_STATUS_T status; MMAL_ES_FORMAT_T *format; MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + RASPIVID_CONFIG *config = state->config; // set up the camera configuration MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = { { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, - .max_stills_w = state->config->width, - .max_stills_h = state->config->height, + .max_stills_w = config->width, + .max_stills_h = config->height, .stills_yuv422 = 0, .one_shot_stills = 0, - .max_preview_video_w = state->config->width, - .max_preview_video_h = state->config->height, + .max_preview_video_w = config->width, + .max_preview_video_h = config->height, .num_preview_video_frames = 3, .stills_capture_circular_buffer_height = 0, .fast_preview_resume = 0, @@ -645,15 +1076,40 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; + if(config->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(config->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + + //enable dynamic framerate if necessary + if (config->camera_parameters.shutter_speed) + { + if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed) + { + config->fps_n = 0; + config->fps_d = 1; + if (config->verbose) + fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + } + } + format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->config->width; - format->es->video.height = state->config->height; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); + format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config->width; - format->es->video.crop.height = state->config->height; - format->es->video.frame_rate.num = state->config->fps_n; - format->es->video.frame_rate.den = state->config->fps_d; + format->es->video.crop.width = config->width; + format->es->video.crop.height = config->height; + format->es->video.frame_rate.num = config->fps_n; + format->es->video.frame_rate.den = config->fps_d; status = mmal_port_format_commit(preview_port); @@ -668,15 +1124,28 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format = video_port->format; format->encoding_variant = MMAL_ENCODING_I420; + if(config->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + else if(config->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + format->encoding = MMAL_ENCODING_OPAQUE; - format->es->video.width = state->config->width; - format->es->video.height = state->config->height; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); + format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config->width; - format->es->video.crop.height = state->config->height; - format->es->video.frame_rate.num = state->config->fps_n; - format->es->video.frame_rate.den = state->config->fps_d; + format->es->video.crop.width = config->width; + format->es->video.crop.height = config->height; + format->es->video.frame_rate.num = config->fps_n; + format->es->video.frame_rate.den = config->fps_d; status = mmal_port_format_commit(video_port); @@ -698,13 +1167,13 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; - format->es->video.width = state->config->width; - format->es->video.height = state->config->height; + format->es->video.width = VCOS_ALIGN_UP(config->width, 32); + format->es->video.height = VCOS_ALIGN_UP(config->height, 16); format->es->video.crop.x = 0; format->es->video.crop.y = 0; - format->es->video.crop.width = state->config->width; - format->es->video.crop.height = state->config->height; - format->es->video.frame_rate.num = 1; + format->es->video.crop.width = config->width; + format->es->video.crop.height = config->height; + format->es->video.frame_rate.num = 0; format->es->video.frame_rate.den = 1; status = mmal_port_format_commit(still_port); @@ -728,12 +1197,20 @@ raspi_capture_set_format_and_start(RASPIVID_STATE *state) goto error; } - raspicamcontrol_set_all_parameters(camera, &state->config->camera_parameters); + raspicamcontrol_set_all_parameters(camera, &config->camera_parameters); - if (state->config->verbose) + update_annotation_data(state); + + if (config->verbose) fprintf(stderr, "Camera component done\n"); + return status; + error: + + if (camera) + mmal_component_destroy(camera); + return status; } @@ -781,6 +1258,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; MMAL_STATUS_T status; MMAL_POOL_T *pool; + RASPIVID_CONFIG *config = state->config; status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); @@ -806,7 +1284,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) // Only supporting H264 at the moment encoder_output->format->encoding = MMAL_ENCODING_H264; - encoder_output->format->bitrate = state->config->bitrate; + encoder_output->format->bitrate = config->bitrate; encoder_output->buffer_size = encoder_output->buffer_size_recommended; @@ -820,6 +1298,11 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (encoder_output->buffer_num < encoder_output->buffer_num_min) encoder_output->buffer_num = encoder_output->buffer_num_min; + // We need to set the frame rate on output to 0, to ensure it gets + // updated correctly from the input framerate when port connected + encoder_output->format->es->video.frame_rate.num = 0; + encoder_output->format->es->video.frame_rate.den = 1; + // Commit the port changes to the output port status = mmal_port_format_commit(encoder_output); @@ -829,7 +1312,6 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) goto error; } - // Set the rate control parameter if (0) { @@ -843,7 +1325,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (state->config->intraperiod) + if (config->intraperiod != -1) { MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config->intraperiod}; status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -852,6 +1334,33 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) vcos_log_error("Unable to set intraperiod"); goto error; } + } + + if (config->quantisationParameter) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set initial QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m2.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set min QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m3.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set max QP"); + goto error; + } } @@ -860,7 +1369,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) param.hdr.id = MMAL_PARAMETER_PROFILE; param.hdr.size = sizeof(param); - param.profile[0].profile = state->config->profile; + param.profile[0].profile = config->profile; param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported status = mmal_port_parameter_set(encoder_output, ¶m.hdr); @@ -872,12 +1381,49 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) } - if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->config->immutableInput) != MMAL_SUCCESS) + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS) { vcos_log_error("Unable to set immutable input flag"); // Continue rather than abort.. } + //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE HEADER FLAG parameters"); + // Continue rather than abort.. + } + + //set INLINE VECTORS flag to request motion vector estimates + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE VECTORS parameters"); + // Continue rather than abort.. + } + + // Adaptive intra refresh settings + if (config->intra_refresh_type != -1) + { + MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; + param.hdr.size = sizeof(param); + + // Get first so we don't overwrite anything unexpectedly + status = mmal_port_parameter_get(encoder_output, ¶m.hdr); + + param.refresh_mode = config->intra_refresh_type; + + //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS) + // param.cir_mbs = 10; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 intra-refresh values"); + goto error; + } + } + // Enable component status = mmal_component_enable(encoder); @@ -889,6 +1435,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) /* Create pool of buffer headers for the output port to consume */ pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + if (!pool) { vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); @@ -897,7 +1444,7 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) state->encoder_pool = pool; state->encoder_component = encoder; - if (state->config->verbose) + if (config->verbose) fprintf(stderr, "Encoder component done\n"); return status; @@ -906,6 +1453,8 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) if (encoder) mmal_component_destroy(encoder); + state->encoder_component = NULL; + return status; } @@ -996,6 +1545,9 @@ raspi_capture_setup(RASPIVID_CONFIG *config) /* Apply passed in config */ state->config = config; + /* Initialize timestamping */ + state->base_time = state->last_second = -1; + /* So far, all we can do is create the camera component. Actual * config and connection of encoders etc happens in _start() */ diff --git a/sys/rpicamsrc/RaspiCapture.h b/sys/rpicamsrc/RaspiCapture.h index 37a7ba7bef..84ad8c023b 100644 --- a/sys/rpicamsrc/RaspiCapture.h +++ b/sys/rpicamsrc/RaspiCapture.h @@ -1,7 +1,7 @@ /* * GStreamer * Copyright (C) 2013 Jan Schmidt - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -66,7 +66,7 @@ G_BEGIN_DECLS typedef struct { int verbose; /// !0 if want detailed run information - + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds int width; /// Requested width of image int height; /// requested height of image @@ -74,6 +74,8 @@ typedef struct int fps_n; /// Requested frame rate (fps) numerator int fps_d; /// Requested frame rate (fps) denominator int intraperiod; /// Intra-refresh period (key frame rate) + int quantisationParameter; /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate + int bInlineHeaders; /// Insert inline headers to stream (SPS, PPS) int demoMode; /// Run app in demo mode int demoInterval; /// Interval between camera settings changes int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either @@ -81,6 +83,13 @@ typedef struct int profile; /// H264 profile to use for encoding RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + int inlineMotionVectors; /// Encoder outputs inline Motion Vectors + + int cameraNum; /// Camera number + int settings; /// Request settings from the camera + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + int intra_refresh_type; /// What intra refresh type to use. -1 to not set. } RASPIVID_CONFIG; typedef struct RASPIVID_STATE_T RASPIVID_STATE; diff --git a/sys/rpicamsrc/RaspiPreview.c b/sys/rpicamsrc/RaspiPreview.c index de11df3471..1632ae09cd 100644 --- a/sys/rpicamsrc/RaspiPreview.c +++ b/sys/rpicamsrc/RaspiPreview.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jan Schmidt + * Copyright (c) 2013-2015 Jan Schmidt Portions: Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes diff --git a/sys/rpicamsrc/RaspiPreview.h b/sys/rpicamsrc/RaspiPreview.h index c986d2d862..409dbffb1a 100644 --- a/sys/rpicamsrc/RaspiPreview.h +++ b/sys/rpicamsrc/RaspiPreview.h @@ -31,12 +31,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// Layer that preview window should be displayed on #define PREVIEW_LAYER 2 -#define PREVIEW_FRAME_RATE_NUM 30 + +// Frames rates of 0 implies variable, but denominator needs to be 1 to prevent div by 0 +#define PREVIEW_FRAME_RATE_NUM 0 #define PREVIEW_FRAME_RATE_DEN 1 -#define FULL_RES_PREVIEW_FRAME_RATE_NUM 15 +#define FULL_RES_PREVIEW_FRAME_RATE_NUM 0 #define FULL_RES_PREVIEW_FRAME_RATE_DEN 1 +#define FULL_FOV_PREVIEW_16x9_X 1280 +#define FULL_FOV_PREVIEW_16x9_Y 720 + +#define FULL_FOV_PREVIEW_4x3_X 1296 +#define FULL_FOV_PREVIEW_4x3_Y 972 + +#define FULL_FOV_PREVIEW_FRAME_RATE_NUM 0 +#define FULL_FOV_PREVIEW_FRAME_RATE_DEN 1 typedef struct {