rpicamsrc: Incorporate raspivid changes from upstream

Merge all changes for new features from upstream
raspberrypi userland, up to commit 0de0b2
This commit is contained in:
Jan Schmidt 2015-03-05 17:01:33 +11:00 committed by Tim-Philipp Müller
parent eb345f032c
commit 6bd0347bf0
8 changed files with 1094 additions and 60 deletions

155
sys/rpicamsrc/RaspiCLI.c Normal file
View file

@ -0,0 +1,155 @@
/*
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file RaspiCLI.c
* Code for handling command line parameters
*
* \date 4th March 2013
* \Author: James Hughes
*
* Description
*
* Some functions/structures for command line parameter parsing
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include "interface/vcos/vcos.h"
#include "RaspiCLI.h"
/**
* Convert a string from command line to a comand_id from the list
*
* @param commands Array of command to check
* @param num_command Number of commands in the array
* @param arg String to search for in the list
* @param num_parameters Returns the number of parameters used by the command
*
* @return command ID if found, -1 if not found
*
*/
int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters)
{
int command_id = -1;
int j;
vcos_assert(commands);
vcos_assert(num_parameters);
vcos_assert(arg);
if (!commands || !num_parameters || !arg)
return -1;
for (j = 0; j < num_commands; j++)
{
if (!strcmp(arg, commands[j].command) ||
!strcmp(arg, commands[j].abbrev))
{
// match
command_id = commands[j].id;
*num_parameters = commands[j].num_parameters;
break;
}
}
return command_id;
}
/**
* Display the list of commands in help format
*
* @param commands Array of command to check
* @param num_command Number of commands in the arry
*
*
*/
void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands)
{
int i;
vcos_assert(commands);
if (!commands)
return;
for (i = 0; i < num_commands; i++)
{
fprintf(stderr, "-%s, -%s\t: %s\n", commands[i].abbrev,
commands[i].command, commands[i].help);
}
}
/**
* Function to take a string, a mapping, and return the int equivalent
* @param str Incoming string to match
* @param map Mapping data
* @param num_refs The number of items in the mapping data
* @return The integer match for the string, or -1 if no match
*/
int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs)
{
int i;
for (i=0;i<num_refs;i++)
{
if (!strcasecmp(str, map[i].mode))
{
return map[i].mmal_mode;
}
}
return -1;
}
/**
* Function to take a mmal enum (as int) and return the string equivalent
* @param en Incoming int to match
* @param map Mapping data
* @param num_refs The number of items in the mapping data
* @return const pointer to string, or NULL if no match
*/
const char *raspicli_unmap_xref(const int en, const XREF_T *map, int num_refs)
{
int i;
for (i=0;i<num_refs;i++)
{
if (en == map[i].mmal_mode)
{
return map[i].mode;
}
}
return NULL;
}

56
sys/rpicamsrc/RaspiCLI.h Normal file
View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RASPICLI_H_
#define RASPICLI_H_
typedef struct
{
int id;
char *command;
char *abbrev;
char *help;
int num_parameters;
} COMMAND_LIST;
/// Cross reference structure, mode string against mode id
typedef struct xref_t
{
char *mode;
int mmal_mode;
} XREF_T;
void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands);
int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters);
int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs);
const char *raspicli_unmap_xref(const int en, const XREF_T *map, int num_refs);
#endif

View file

@ -118,6 +118,16 @@ static XREF_T metering_mode_map[] =
static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]);
static XREF_T drc_mode_map[] =
{
{"off", MMAL_PARAMETER_DRC_STRENGTH_OFF},
{"low", MMAL_PARAMETER_DRC_STRENGTH_LOW},
{"med", MMAL_PARAMETER_DRC_STRENGTH_MEDIUM},
{"high", MMAL_PARAMETER_DRC_STRENGTH_HIGH}
};
static const int drc_mode_map_size = sizeof(drc_mode_map)/sizeof(drc_mode_map[0]);
#define CommandSharpness 0
#define CommandContrast 1
@ -126,7 +136,7 @@ static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(meter
#define CommandISO 4
#define CommandVideoStab 5
#define CommandEVComp 6
#define CommandExposure 7
#define CommandExposure 7
#define CommandAWB 8
#define CommandImageFX 9
#define CommandColourFX 10
@ -135,6 +145,11 @@ static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(meter
#define CommandHFlip 13
#define CommandVFlip 14
#define CommandROI 15
#define CommandShutterSpeed 16
#define CommandAwbGains 17
#define CommandDRCLevel 18
#define CommandStatsPass 19
#define CommandAnnotate 20
static COMMAND_LIST cmdline_commands[] =
{
@ -143,7 +158,7 @@ static COMMAND_LIST cmdline_commands[] =
{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},
{CommandVideoStab, "-vstab", "vs", "Turn on video stabilisation", 0},
{CommandEVComp, "-ev", "ev", "Set EV compensation", 1},
{CommandExposure, "-exposure", "ex", "Set exposure mode (see Notes)", 1},
{CommandAWB, "-awb", "awb","Set AWB mode (see Notes)", 1},
@ -153,7 +168,12 @@ static COMMAND_LIST cmdline_commands[] =
{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}
{CommandROI, "-roi", "roi","Set region of interest (x,y,w,d as normalised coordinates [0.0-1.0])", 1},
{CommandShutterSpeed,"-shutter", "ss", "Set shutter speed in microseconds", 1},
{CommandAwbGains, "-awbgains", "awbg", "Set AWB gains - AWB mode must be off", 1},
{CommandDRCLevel, "-drc", "drc", "Set DRC Level", 1},
{CommandStatsPass, "-stats", "st", "Force recomputation of statistics on stills capture pass"},
{CommandAnnotate, "-annotate", "a", "Enable/Set annotate flags or text", 1},
};
static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
@ -521,6 +541,63 @@ int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char
break;
}
case CommandShutterSpeed : // Shutter speed needs single number parameter
{
sscanf(arg2, "%d", &params->shutter_speed);
used = 2;
break;
}
case CommandAwbGains :
{
double r,b;
int args;
args = sscanf(arg2, "%lf,%lf", &r,&b);
if (args != 2 || r > 8.0 || b > 8.0)
{
return 0;
}
params->awb_gains_r = r;
params->awb_gains_b = b;
used = 2;
break;
}
case CommandDRCLevel:
{
params->drc_level = drc_mode_from_string(arg2);
used = 2;
break;
}
case CommandStatsPass:
{
params->stats_pass = MMAL_TRUE;
used = 1;
break;
}
case CommandAnnotate:
{
// If parameter is a number, assume its a bitmask, otherwise a string
if (isdigit(*arg2))
{
sscanf(arg2, "%u", &params->enable_annotate);
}
else
{
params->enable_annotate = ANNOTATE_USER_TEXT;
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;i<drc_mode_map_size;i++)
{
fprintf(stderr, ",%s", drc_mode_map[i].mode);
}
fprintf(stderr, "\n");
}
@ -653,6 +737,13 @@ void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params)
params->hflip = params->vflip = 0;
params->roi.x = params->roi.y = 0.0;
params->roi.w = params->roi.h = 1.0;
params->shutter_speed = 0; // 0 = auto
params->awb_gains_r = 0; // Only have any function if AWB OFF is used.
params->awb_gains_b = 0;
params->drc_level = MMAL_PARAMETER_DRC_STRENGTH_OFF;
params->stats_pass = MMAL_FALSE;
params->enable_annotate = 0;
params->annotate_string[0] = '\0';
}
/**
@ -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, &params->colourEffects);
//result += raspicamcontrol_set_thumbnail_parameters(camera, &params->thumbnailConfig); TODO Not working for some reason
result += raspicamcontrol_set_rotation(camera, params->rotation);
result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
result += raspicamcontrol_set_ROI(camera, params->roi);
result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed);
result += raspicamcontrol_set_DRC(camera, params->drc_level);
result += raspicamcontrol_set_stats_pass(camera, params->stats_pass);
result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string);
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, &param.hdr));
}
int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain)
{
MMAL_PARAMETER_AWB_GAINS_T param = {{MMAL_PARAMETER_CUSTOM_AWB_GAINS,sizeof(param)}, {0,0}, {0,0}};
if (!camera)
return 1;
if (!r_gain || !b_gain)
return 0;
param.r_gain.num = (unsigned int)(r_gain * 65536);
param.b_gain.num = (unsigned int)(b_gain * 65536);
param.r_gain.den = param.b_gain.den = 65536;
return mmal_status_to_int(mmal_port_parameter_set(camera->control, &param.hdr));
}
/**
* Set the image effect for the images
* @param camera Pointer to camera component
@ -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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
* Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
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);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
* Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
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 <stdio.h>
#include <stdlib.h>
@ -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 <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
// 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 <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 },
{ 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 <size>. Default 1920", 1 },
{ CommandHeight, "-height", "h", "Set image height <size>. Default 1080", 1 },
{ CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 },
{ CommandOutput, "-output", "o", "Output filename <filename> (to write to stdout, use '-o -')", 1 },
{ CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 },
{ CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
{ CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1},
{ CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1},
{ CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0},
{ CommandIntraPeriod, "-intra", "g", "Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames.", 1},
{ CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1},
{ CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0},
{ CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0},
{ CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0},
{ CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1},
{ CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1},
{ CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0},
{ CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval <ms>", 1},
{ CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1},
{ CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1},
{ CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0},
{ CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0},
{ CommandIMV, "-vectors", "x", "Output filename <filename> for inline motion vectors", 1 },
{ CommandCamSelect, "-camselect", "cs", "Select camera <number>. Default 0", 1 },
{ CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0},
{ CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1},
{ CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1},
};
static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
@ -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;i<intra_refresh_map_size;i++)
{
fprintf(stderr, ",%s", intra_refresh_map[i].mode);
}
fprintf(stderr, "\n");
// Help for preview options
raspipreview_display_help();
@ -478,6 +727,22 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf
{
if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
{
MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data;
switch (param->hdr.id) {
case MMAL_PARAMETER_CAMERA_SETTINGS:
{
MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param;
vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u",
settings->exposure,
settings->analog_gain.num, settings->analog_gain.den,
settings->digital_gain.num, settings->digital_gain.den);
vcos_log_error("AWB R=%u/%u, B=%u/%u",
settings->awb_red_gain.num, settings->awb_red_gain.den,
settings->awb_blue_gain.num, settings->awb_blue_gain.den
);
}
break;
}
}
else
{
@ -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, &param.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, &param.hdr);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to set initial QP");
goto error;
}
MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
status = mmal_port_parameter_set(encoder_output, &param2.hdr);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to set min QP");
goto error;
}
MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
status = mmal_port_parameter_set(encoder_output, &param3.hdr);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to set max QP");
goto error;
}
}
@ -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, &param.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, &param.hdr);
param.refresh_mode = config->intra_refresh_type;
//if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
// param.cir_mbs = 10;
status = mmal_port_parameter_set(encoder_output, &param.hdr);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to set H264 intra-refresh values");
goto error;
}
}
// 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()
*/

View file

@ -1,7 +1,7 @@
/*
* 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
@ -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;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
* Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
Portions:
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes

View file

@ -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
{