Merge branch 'plugin-move-rpicamsrc'

Move rpicamsrc from https://github.com/thaytan/gst-rpicamsrc/

It's a useful little element and works well, so might as well
make sure it's widely available so people can stop piping
raspivid output into fdsrc.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/667>
This commit is contained in:
Tim-Philipp Müller 2020-07-09 11:46:30 +00:00
commit 84dbf94313
22 changed files with 9764 additions and 0 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
Portions:
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RASPICAMCONTROL_H_
#define RASPICAMCONTROL_H_
/* Various parameters
*
* Exposure Mode
* MMAL_PARAM_EXPOSUREMODE_OFF,
MMAL_PARAM_EXPOSUREMODE_AUTO,
MMAL_PARAM_EXPOSUREMODE_NIGHT,
MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW,
MMAL_PARAM_EXPOSUREMODE_BACKLIGHT,
MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT,
MMAL_PARAM_EXPOSUREMODE_SPORTS,
MMAL_PARAM_EXPOSUREMODE_SNOW,
MMAL_PARAM_EXPOSUREMODE_BEACH,
MMAL_PARAM_EXPOSUREMODE_VERYLONG,
MMAL_PARAM_EXPOSUREMODE_FIXEDFPS,
MMAL_PARAM_EXPOSUREMODE_ANTISHAKE,
MMAL_PARAM_EXPOSUREMODE_FIREWORKS,
*
* AWB Mode
* MMAL_PARAM_AWBMODE_OFF,
MMAL_PARAM_AWBMODE_AUTO,
MMAL_PARAM_AWBMODE_SUNLIGHT,
MMAL_PARAM_AWBMODE_CLOUDY,
MMAL_PARAM_AWBMODE_SHADE,
MMAL_PARAM_AWBMODE_TUNGSTEN,
MMAL_PARAM_AWBMODE_FLUORESCENT,
MMAL_PARAM_AWBMODE_INCANDESCENT,
MMAL_PARAM_AWBMODE_FLASH,
MMAL_PARAM_AWBMODE_HORIZON,
*
* Image FX
MMAL_PARAM_IMAGEFX_NONE,
MMAL_PARAM_IMAGEFX_NEGATIVE,
MMAL_PARAM_IMAGEFX_SOLARIZE,
MMAL_PARAM_IMAGEFX_POSTERIZE,
MMAL_PARAM_IMAGEFX_WHITEBOARD,
MMAL_PARAM_IMAGEFX_BLACKBOARD,
MMAL_PARAM_IMAGEFX_SKETCH,
MMAL_PARAM_IMAGEFX_DENOISE,
MMAL_PARAM_IMAGEFX_EMBOSS,
MMAL_PARAM_IMAGEFX_OILPAINT,
MMAL_PARAM_IMAGEFX_HATCH,
MMAL_PARAM_IMAGEFX_GPEN,
MMAL_PARAM_IMAGEFX_PASTEL,
MMAL_PARAM_IMAGEFX_WATERCOLOUR,
MMAL_PARAM_IMAGEFX_FILM,
MMAL_PARAM_IMAGEFX_BLUR,
MMAL_PARAM_IMAGEFX_SATURATION,
MMAL_PARAM_IMAGEFX_COLOURSWAP,
MMAL_PARAM_IMAGEFX_WASHEDOUT,
MMAL_PARAM_IMAGEFX_POSTERISE,
MMAL_PARAM_IMAGEFX_COLOURPOINT,
MMAL_PARAM_IMAGEFX_COLOURBALANCE,
MMAL_PARAM_IMAGEFX_CARTOON,
*/
/// Annotate bitmask options
/// Supplied by user on command line
#define ANNOTATE_USER_TEXT 1
/// Supplied by app using this module
#define ANNOTATE_APP_TEXT 2
/// Insert current date
#define ANNOTATE_DATE_TEXT 4
// Insert current time
#define ANNOTATE_TIME_TEXT 8
#define ANNOTATE_SHUTTER_SETTINGS 16
#define ANNOTATE_CAF_SETTINGS 32
#define ANNOTATE_GAIN_SETTINGS 64
#define ANNOTATE_LENS_SETTINGS 128
#define ANNOTATE_MOTION_SETTINGS 256
#define ANNOTATE_FRAME_NUMBER 512
#define ANNOTATE_BLACK_BACKGROUND 1024
// There isn't actually a MMAL structure for the following, so make one
typedef struct
{
int enable; /// Turn colourFX on or off
int u,v; /// U and V to use
} MMAL_PARAM_COLOURFX_T;
typedef struct
{
int enable;
int width,height;
int quality;
} MMAL_PARAM_THUMBNAIL_CONFIG_T;
typedef struct
{
double x;
double y;
double w;
double h;
} PARAM_FLOAT_RECT_T;
/// struct contain camera settings
typedef struct
{
int sharpness; /// -100 to 100
int contrast; /// -100 to 100
int brightness; /// 0 to 100
int saturation; /// -100 to 100
int ISO; /// TODO : what range?
int videoStabilisation; /// 0 or 1 (false or true)
int exposureCompensation; /// -10 to +10 ?
MMAL_PARAM_EXPOSUREMODE_T exposureMode;
MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode;
MMAL_PARAM_AWBMODE_T awbMode;
MMAL_PARAM_IMAGEFX_T imageEffect;
MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters;
MMAL_PARAM_COLOURFX_T colourEffects;
int rotation; /// 0-359
int hflip; /// 0 or 1
int vflip; /// 0 or 1
PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect
int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms
float awb_gains_r; /// AWB red gain
float awb_gains_b; /// AWB blue gain
MMAL_PARAMETER_DRC_STRENGTH_T drc_level; // Strength of Dynamic Range compression to apply
MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off
int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed
char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings
int annotate_text_size; // Text size for annotation
int annotate_text_colour; // Text colour for annotation
int annotate_bg_colour; // Background colour for annotation
MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo_mode;
} RASPICAM_CAMERA_PARAMETERS;
void raspicamcontrol_check_configuration(int min_gpu_mem);
int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2);
void raspicamcontrol_display_help();
int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera);
int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params);
int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params);
void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params);
void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params);
void raspicamcontrol_check_configuration(int min_gpu_mem);
void raspicamcontrol_get_camera(int *supported, int *detected);
// Individual setting functions
int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation);
int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness);
int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast);
int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness);
int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO);
int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode);
int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation);
int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp);
int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode);
int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode);
int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain);
int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX);
int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX);
int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation);
int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip);
int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect);
int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms);
int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength);
int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass);
int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string,
const int text_size, const int text_colour, const int bg_colour);
int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode);
//Individual getting functions
int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera);
int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera);
int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera);
int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera);
int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera);
MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera);
int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera);
int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera);
MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera);
MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera);
MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera);
MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera);
MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera);
#endif /* RASPICAMCONTROL_H_ */

2026
sys/rpicamsrc/RaspiCapture.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
/*
* GStreamer
* Copyright (C) 2013-2015 Jan Schmidt <jan@centricular.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __RASPICAPTURE_H__
#define __RASPICAPTURE_H__
#include <glib.h>
#include <inttypes.h>
#include "interface/mmal/mmal_common.h"
#include "interface/mmal/mmal_types.h"
#include "interface/mmal/mmal_parameters_camera.h"
#include "interface/mmal/mmal_component.h"
#include "RaspiCamControl.h"
#include "RaspiPreview.h"
#define RPICAMSRC_MAX_FPS 1000
GST_DEBUG_CATEGORY_EXTERN (gst_rpi_cam_src_debug);
#define GST_CAT_DEFAULT gst_rpi_cam_src_debug
#undef fprintf
#define fprintf(f,...) GST_LOG(__VA_ARGS__)
#undef vcos_log_error
#define vcos_log_error GST_ERROR
#undef vcos_log_warn
#define vcos_log_warn GST_WARNING
#define GST_FLOW_ERROR_TIMEOUT GST_FLOW_CUSTOM_ERROR
G_BEGIN_DECLS
typedef enum
{
PROP_CHANGE_ENCODING = (1 << 0), /* BITRATE or QUANT or KEY Interval, intra refresh */
PROP_CHANGE_PREVIEW = (1 << 1), /* Preview opacity or fullscreen */
PROP_CHANGE_COLOURBALANCE = (1 << 2),
PROP_CHANGE_SENSOR_SETTINGS = (1 << 3), /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */
PROP_CHANGE_VIDEO_STABILISATION = (1 << 4),
PROP_CHANGE_AWB = (1 << 5),
PROP_CHANGE_IMAGE_COLOUR_EFFECT = (1 << 6),
PROP_CHANGE_ORIENTATION = (1 << 7),
PROP_CHANGE_ROI = (1 << 8),
PROP_CHANGE_ANNOTATION = (1 << 9)
} RpiPropChangeFlags;
/** Structure containing all state information for the current run
*/
typedef struct
{
RpiPropChangeFlags change_flags;
int verbose; /// !0 if want detailed run information
int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
int width; /// Requested width of image
int height; /// requested height of image
int bitrate; /// Requested bitrate
int fps_n; /// Requested frame rate (fps) numerator
int fps_d; /// Requested frame rate (fps) denominator
int intraperiod; /// Intra-refresh period (key frame rate)
int quantisationParameter; /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate
int bInlineHeaders; /// Insert inline headers to stream (SPS, PPS)
int demoMode; /// Run app in demo mode
int demoInterval; /// Interval between camera settings changes
int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either
/// the camera output or the encoder output (with compression artifacts)
int profile; /// H264 profile to use for encoding
RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
int inlineMotionVectors; /// Encoder outputs inline Motion Vectors
int cameraNum; /// Camera number
int settings; /// Request settings from the camera
int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values.
int intra_refresh_type; /// What intra refresh type to use. -1 to not set.
MMAL_FOURCC_T encoding; // Which encoding to use
int jpegQuality;
int jpegRestartInterval;
int useSTC;
} RASPIVID_CONFIG;
typedef struct RASPIVID_STATE_T RASPIVID_STATE;
void raspicapture_init();
void raspicapture_default_config(RASPIVID_CONFIG *config);
RASPIVID_STATE *raspi_capture_setup(RASPIVID_CONFIG *config);
gboolean raspi_capture_start(RASPIVID_STATE *state);
void raspi_capture_update_config (RASPIVID_STATE *state,
RASPIVID_CONFIG *config, gboolean dynamic);
GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf,
GstClock *clock, GstClockTime base_time);
void raspi_capture_stop(RASPIVID_STATE *state);
void raspi_capture_free(RASPIVID_STATE *state);
gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state);
G_END_DECLS
#endif

View file

@ -0,0 +1,307 @@
/*
* Copyright (c) 2013-2015 Jan Schmidt <jan@centricular.com>
Portions:
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <gst/gst.h>
#include "interface/vcos/vcos.h"
#include "interface/mmal/mmal.h"
#include "interface/mmal/mmal_logging.h"
#include "interface/mmal/mmal_buffer.h"
#include "interface/mmal/util/mmal_util.h"
#include "interface/mmal/util/mmal_util_params.h"
#include "interface/mmal/util/mmal_default_components.h"
#include "interface/mmal/util/mmal_connection.h"
#include "RaspiPreview.h"
#include "RaspiCapture.h"
#if 0
#define CommandPreview 1
#define CommandFullScreen 2
#define CommandOpacity 3
#define CommandDisablePreview 4
static COMMAND_LIST cmdline_commands[] =
{
{ CommandPreview, "-preview", "p", "Preview window settings <'x,y,w,h'>", 1 },
{ CommandFullScreen, "-fullscreen", "f", "Fullscreen preview mode", 0 },
{ CommandOpacity, "-opacity", "op", "Preview window opacity (0-255)", 1},
{ CommandDisablePreview,"-nopreview", "n", "Do not display a preview window", 0},
};
static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
#endif
/**
* Create the preview component, set up its ports
*
* @param state Pointer to state control struct
*
* @return MMAL_SUCCESS if all OK, something else otherwise
*
*/
MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_STATE *state,
RASPIPREVIEW_PARAMETERS *config)
{
MMAL_COMPONENT_T *preview = 0;
MMAL_STATUS_T status;
state->havePreview = config->wantPreview;
if (!config->wantPreview)
{
// No preview required, so create a null sink component to take its place
status = mmal_component_create("vc.null_sink", &preview);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to create null sink component");
goto error;
}
state->preview_component = preview;
}
else
{
status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER,
&preview);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to create preview component");
goto error;
}
if (!preview->input_num)
{
status = MMAL_ENOSYS;
vcos_log_error("No input ports found on component");
goto error;
}
state->preview_component = preview;
raspipreview_update_config (state, config);
if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
{
vcos_log_error("unable to set preview port parameters (%u)", status);
goto error;
}
}
/* Enable component */
status = mmal_component_enable(preview);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Unable to enable preview/null sink component (%u)", status);
goto error;
}
return status;
error:
if (preview) {
mmal_component_destroy(preview);
state->preview_component = NULL;
}
return status;
}
MMAL_STATUS_T
raspipreview_update_config (RASPIPREVIEW_STATE *state,
RASPIPREVIEW_PARAMETERS *config)
{
MMAL_COMPONENT_T *preview = state->preview_component;
MMAL_PORT_T *preview_port = NULL;
MMAL_DISPLAYREGION_T param;
MMAL_STATUS_T status;
/* Can't update props on the null preview component */
if (state->havePreview == 0)
return MMAL_SUCCESS;
preview_port = preview->input[0];
param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
param.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
param.set = MMAL_DISPLAY_SET_LAYER;
param.layer = PREVIEW_LAYER;
param.set |= MMAL_DISPLAY_SET_ALPHA;
param.alpha = config->opacity;
if (config->wantFullScreenPreview)
{
param.set |= MMAL_DISPLAY_SET_FULLSCREEN;
param.fullscreen = 1;
}
else
{
param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN);
param.fullscreen = 0;
param.dest_rect = config->previewWindow;
}
status = mmal_port_parameter_set(preview_port, &param.hdr);
if (status == MMAL_ENOSYS)
status = MMAL_SUCCESS;
return status;
}
/**
* Destroy the preview component
*
* @param state Pointer to state control struct
*
*/
void raspipreview_destroy(RASPIPREVIEW_STATE *state)
{
if (state->preview_component)
{
mmal_component_destroy(state->preview_component);
state->preview_component = NULL;
}
}
/**
* Assign set of default parameters to the passed in parameter block
*
* @param state Pointer to parameter block
*
*/
void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *config)
{
config->wantPreview = 1;
config->wantFullScreenPreview = 1;
config->opacity = 255;
config->previewWindow.x = 0;
config->previewWindow.y = 0;
config->previewWindow.width = 1024;
config->previewWindow.height = 768;
}
/**
* Dump parameters as human readable to stdout
*
* @param state Pointer to parameter block
*
*/
void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *config)
{
fprintf(stderr, "Preview %s, Full screen %s\n", config->wantPreview ? "Yes" : "No",
config->wantFullScreenPreview ? "Yes" : "No");
fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", config->previewWindow.x,
config->previewWindow.y, config->previewWindow.width,
config->previewWindow.height, config->opacity);
};
#if 0
/**
* Parse a possible command pair - command and parameter
* @param arg1 Command
* @param arg2 Parameter (could be NULL)
* @return How many parameters were used, 0,1,2
*/
int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2)
{
int command_id, used = 0, num_parameters;
if (!arg1)
return 0;
command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters);
// If invalid command, or we are missing a parameter, drop out
if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL))
return 0;
switch (command_id)
{
case CommandPreview: // Preview window
{
int tmp;
params->wantPreview = 1;
tmp = sscanf(arg2, "%d,%d,%d,%d",
&params->previewWindow.x, &params->previewWindow.y,
&params->previewWindow.width, &params->previewWindow.height);
// Failed to get any window parameters, so revert to full screen
if (tmp == 0)
params->wantFullScreenPreview = 1;
else
params->wantFullScreenPreview = 0;
used = 2;
break;
}
case CommandFullScreen: // Want full screen preview mode (overrides display rect)
params->wantPreview = 1;
params->wantFullScreenPreview = 1;
used = 1;
break;
case CommandOpacity: // Define preview window opacity
if (sscanf(arg2, "%u", &params->opacity) != 1)
params->opacity = 255;
else
used = 2;
break;
case CommandDisablePreview: // Turn off preview output
params->wantPreview = 0;
used = 1;
break;
}
return used;
}
/**
* Display help for command line options
*/
void raspipreview_display_help()
{
fprintf(stderr, "\nPreview parameter commands\n\n");
raspicli_display_help(cmdline_commands, cmdline_commands_size);
}
#endif

View file

@ -0,0 +1,75 @@
/*
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RASPIPREVIEW_H_
#define RASPIPREVIEW_H_
/// Layer that preview window should be displayed on
#define PREVIEW_LAYER 2
// Frames rates of 0 implies variable, but denominator needs to be 1 to prevent div by 0
#define PREVIEW_FRAME_RATE_NUM 0
#define PREVIEW_FRAME_RATE_DEN 1
#define FULL_RES_PREVIEW_FRAME_RATE_NUM 0
#define FULL_RES_PREVIEW_FRAME_RATE_DEN 1
#define FULL_FOV_PREVIEW_16x9_X 1280
#define FULL_FOV_PREVIEW_16x9_Y 720
#define FULL_FOV_PREVIEW_4x3_X 1296
#define FULL_FOV_PREVIEW_4x3_Y 972
#define FULL_FOV_PREVIEW_FRAME_RATE_NUM 0
#define FULL_FOV_PREVIEW_FRAME_RATE_DEN 1
typedef struct
{
MMAL_COMPONENT_T *preview_component; /// Pointer to the created preview display component
int havePreview; /// component is preview, else null sink
} RASPIPREVIEW_STATE;
typedef struct
{
int wantPreview; /// Display a preview
int wantFullScreenPreview; /// 0 is use previewRect, non-zero to use full screen
int opacity; /// Opacity of window - 0 = transparent, 255 = opaque
MMAL_RECT_T previewWindow; /// Destination rectangle for the preview window.
} RASPIPREVIEW_PARAMETERS;
MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_STATE *state,
RASPIPREVIEW_PARAMETERS *config);
void raspipreview_destroy(RASPIPREVIEW_STATE *state);
void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *config);
void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *config);
int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *config, const char *arg1, const char *arg2);
void raspipreview_display_help();
MMAL_STATUS_T raspipreview_update_config (RASPIPREVIEW_STATE *state,
RASPIPREVIEW_PARAMETERS *config);
#endif /* RASPIPREVIEW_H_ */

1516
sys/rpicamsrc/RaspiStill.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,957 @@
/*
* Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
Portions:
Copyright (c) 2013, Broadcom Europe Ltd
Copyright (c) 2013, James Hughes
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* \file RaspiStillYUV.c
* Command line program to capture a still frame and dump uncompressed it to file.
* Also optionally display a preview/viewfinder of current camera input.
*
* \date 4th March 2013
* \Author: James Hughes
*
* Description
*
* 2 components are created; camera and preview.
* Camera component has three ports, preview, video and stills.
* Preview is connected using standard mmal connections, the stills output
* is written straight to the file in YUV 420 format via the requisite buffer
* callback. video port is not used
*
* We use the RaspiCamControl code to handle the specific camera settings.
* We use the RaspiPreview code to handle the generic preview
*/
// We use some GNU extensions (basename)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <sysexits.h>
#define VERSION_STRING "v1.3.2"
#include "bcm_host.h"
#include "interface/vcos/vcos.h"
#include "interface/mmal/mmal.h"
#include "interface/mmal/mmal_logging.h"
#include "interface/mmal/mmal_buffer.h"
#include "interface/mmal/util/mmal_util.h"
#include "interface/mmal/util/mmal_util_params.h"
#include "interface/mmal/util/mmal_default_components.h"
#include "interface/mmal/util/mmal_connection.h"
#include "RaspiCamControl.h"
#include "RaspiPreview.h"
#include "RaspiCLI.h"
#include <semaphore.h>
/// Camera number to use - we only have one camera, indexed from 0.
#define CAMERA_NUMBER 0
// Standard port setting for the camera component
#define MMAL_CAMERA_PREVIEW_PORT 0
#define MMAL_CAMERA_VIDEO_PORT 1
#define MMAL_CAMERA_CAPTURE_PORT 2
// Stills format information
#define STILLS_FRAME_RATE_NUM 3
#define STILLS_FRAME_RATE_DEN 1
/// Video render needs at least 2 buffers.
#define VIDEO_OUTPUT_BUFFERS_NUM 3
int mmal_status_to_int(MMAL_STATUS_T status);
/** Structure containing all state information for the current run
*/
typedef struct
{
int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
int width; /// Requested width of image
int height; /// requested height of image
char *filename; /// filename of output file
int verbose; /// !0 if want detailed run information
int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse
int useRGB; /// Output RGB data rather than YUV
RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
MMAL_COMPONENT_T *null_sink_component; /// Pointer to the camera component
MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera stills port
} RASPISTILLYUV_STATE;
/** Struct used to pass information in camera still port userdata to callback
*/
typedef struct
{
FILE *file_handle; /// File handle to write buffer data to.
VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault)
RASPISTILLYUV_STATE *pstate; /// pointer to our state in case required in callback
} PORT_USERDATA;
static void display_valid_parameters(char *app_name);
/// Comamnd ID's and Structure defining our command line options
#define CommandHelp 0
#define CommandWidth 1
#define CommandHeight 2
#define CommandOutput 3
#define CommandVerbose 4
#define CommandTimeout 5
#define CommandTimelapse 6
#define CommandUseRGB 7
static COMMAND_LIST cmdline_commands[] =
{
{ CommandHelp, "-help", "?", "This help information", 0 },
{ CommandWidth, "-width", "w", "Set image width <size>", 1 },
{ CommandHeight, "-height", "h", "Set image height <size>", 1 },
{ CommandOutput, "-output", "o", "Output filename <filename>. If not specifed, no image is saved", 1 },
{ CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 },
{ CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down. If not specified set to 5s", 1 },
{ CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every <t>ms", 1},
{ CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0},
};
static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
/**
* Assign a default set of parameters to the state passed in
*
* @param state Pointer to state structure to assign defaults to
*/
static void default_status(RASPISTILLYUV_STATE *state)
{
if (!state)
{
vcos_assert(0);
return;
}
// Default everything to zero
memset(state, 0, sizeof(RASPISTILLYUV_STATE));
// Now set anything non-zero
state->timeout = 5000; // 5s delay before take image
state->width = 2592;
state->height = 1944;
state->timelapse = 0;
// Setup preview window defaults
raspipreview_set_defaults(&state->preview_parameters);
// Set up the camera_parameters to default
raspicamcontrol_set_defaults(&state->camera_parameters);
}
/**
* Dump image state parameters to stderr. Used for debugging
*
* @param state Pointer to state structure to assign defaults to
*/
static void dump_status(RASPISTILLYUV_STATE *state)
{
if (!state)
{
vcos_assert(0);
return;
}
fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename);
fprintf(stderr, "Time delay %d, Timelapse %d\n", state->timeout, state->timelapse);
raspipreview_dump_parameters(&state->preview_parameters);
raspicamcontrol_dump_parameters(&state->camera_parameters);
}
/**
* Parse the incoming command line and put resulting parameters in to the state
*
* @param argc Number of arguments in command line
* @param argv Array of pointers to strings from command line
* @param state Pointer to state structure to assign any discovered parameters to
* @return non-0 if failed for some reason, 0 otherwise
*/
static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state)
{
// Parse the command line arguments.
// We are looking for --<something> or -<abreviation of something>
int valid = 1; // set 0 if we have a bad parameter
int i;
for (i = 1; i < argc && valid; i++)
{
int command_id, num_parameters;
if (!argv[i])
continue;
if (argv[i][0] != '-')
{
valid = 0;
continue;
}
// Assume parameter is valid until proven otherwise
valid = 1;
command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
// If we found a command but are missing a parameter, continue (and we will drop out of the loop)
if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
continue;
// We are now dealing with a command line option
switch (command_id)
{
case CommandHelp:
display_valid_parameters(basename(argv[0]));
return -1;
case CommandWidth: // Width > 0
if (sscanf(argv[i + 1], "%u", &state->width) != 1)
valid = 0;
else
i++;
break;
case CommandHeight: // Height > 0
if (sscanf(argv[i + 1], "%u", &state->height) != 1)
valid = 0;
else
i++;
break;
case CommandOutput: // output filename
{
int len = strlen(argv[i + 1]);
if (len)
{
state->filename = malloc(len + 1);
vcos_assert(state->filename);
if (state->filename)
strncpy(state->filename, argv[i + 1], len);
i++;
}
else
valid = 0;
break;
}
case CommandVerbose: // display lots of data during run
state->verbose = 1;
break;
case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds
{
if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
{
// TODO : What limits do we need for timeout?
i++;
}
else
valid = 0;
break;
}
case CommandTimelapse:
if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1)
valid = 0;
else
i++;
break;
case CommandUseRGB: // display lots of data during run
state->useRGB = 1;
break;
default:
{
// Try parsing for any image specific parameters
// result indicates how many parameters were used up, 0,1,2
// but we adjust by -1 as we have used one already
const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
// Still unused, try preview options
if (!parms_used)
parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
// If no parms were used, this must be a bad parameters
if (!parms_used)
valid = 0;
else
i += parms_used - 1;
break;
}
}
}
if (!valid)
{
fprintf(stderr, "Invalid command line option (%s)\n", argv[i]);
return 1;
}
return 0;
}
/**
* Display usage information for the application to stdout
*
* @param app_name String to display as the application name
*
*/
static void display_valid_parameters(char *app_name)
{
fprintf(stderr, "Runs camera for specific time, and take uncompressed YUV capture at end if requested\n\n");
fprintf(stderr, "usage: %s [options]\n\n", app_name);
fprintf(stderr, "Image parameter commands\n\n");
raspicli_display_help(cmdline_commands, cmdline_commands_size);
// Help for preview options
raspipreview_display_help();
// Now display any help information from the camcontrol code
raspicamcontrol_display_help();
fprintf(stderr, "\n");
return;
}
/**
* buffer header callback function for camera control
*
* @param port Pointer to port from which callback originated
* @param buffer mmal buffer header pointer
*/
static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
fprintf(stderr, "Camera control callback cmd=0x%08x", buffer->cmd);
if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
{
}
else
{
vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
}
mmal_buffer_header_release(buffer);
}
/**
* buffer header callback function for camera output port
*
* Callback will dump buffer data to the specific file
*
* @param port Pointer to port from which callback originated
* @param buffer mmal buffer header pointer
*/
static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
int complete = 0;
// We pass our file handle and other stuff in via the userdata field.
PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
if (pData)
{
int bytes_written = buffer->length;
if (buffer->length)
{
mmal_buffer_header_mem_lock(buffer);
bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle);
mmal_buffer_header_mem_unlock(buffer);
}
// We need to check we wrote what we wanted - it's possible we have run out of storage.
if (bytes_written != buffer->length)
{
vcos_log_error("Unable to write buffer to file - aborting");
complete = 1;
}
// Check end of frame or error
if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED))
complete = 1;
}
else
{
vcos_log_error("Received a camera still buffer callback with no state");
}
// release buffer back to the pool
mmal_buffer_header_release(buffer);
// and send one back to the port (if still open)
if (port->is_enabled)
{
MMAL_STATUS_T status;
MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue);
// and back to the port from there.
if (new_buffer)
{
status = mmal_port_send_buffer(port, new_buffer);
}
if (!new_buffer || status != MMAL_SUCCESS)
vcos_log_error("Unable to return the buffer to the camera still port");
}
if (complete)
{
vcos_semaphore_post(&(pData->complete_semaphore));
}
}
/**
* Create the camera component, set up its ports
*
* @param state Pointer to state control struct
*
* @return 0 if failed, pointer to component if successful
*
*/
static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state)
{
MMAL_COMPONENT_T *camera = 0;
MMAL_ES_FORMAT_T *format;
MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
MMAL_STATUS_T status;
MMAL_POOL_T *pool;
/* Create the component */
status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Failed to create camera component");
goto error;
}
if (!camera->output_num)
{
vcos_log_error("Camera doesn't have output ports");
goto error;
}
preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
// Enable the camera, and tell it its control callback function
status = mmal_port_enable(camera->control, camera_control_callback);
if (status)
{
vcos_log_error("Unable to enable control port : error %d", status);
goto error;
}
// set up the camera configuration
{
MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
{
{ MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
.max_stills_w = state->width,
.max_stills_h = state->height,
.stills_yuv422 = 0,
.one_shot_stills = 1,
.max_preview_video_w = state->preview_parameters.previewWindow.width,
.max_preview_video_h = state->preview_parameters.previewWindow.height,
.num_preview_video_frames = 3,
.stills_capture_circular_buffer_height = 0,
.fast_preview_resume = 0,
.use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
};
mmal_port_parameter_set(camera->control, &cam_config.hdr);
}
raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
// Now set up the port formats
format = preview_port->format;
format->encoding = MMAL_ENCODING_OPAQUE;
format->encoding_variant = MMAL_ENCODING_I420;
format->es->video.width = state->preview_parameters.previewWindow.width;
format->es->video.height = state->preview_parameters.previewWindow.height;
format->es->video.crop.x = 0;
format->es->video.crop.y = 0;
format->es->video.crop.width = state->preview_parameters.previewWindow.width;
format->es->video.crop.height = state->preview_parameters.previewWindow.height;
format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM;
format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN;
status = mmal_port_format_commit(preview_port);
if (status)
{
vcos_log_error("camera viewfinder format couldn't be set");
goto error;
}
// Set the same format on the video port (which we dont use here)
mmal_format_full_copy(video_port->format, format);
status = mmal_port_format_commit(video_port);
if (status)
{
vcos_log_error("camera video format couldn't be set");
goto error;
}
// Ensure there are enough buffers to avoid dropping frames
if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
format = still_port->format;
// Set our stills format on the stills port
if (state->useRGB)
{
format->encoding = MMAL_ENCODING_BGR24;
format->encoding_variant = MMAL_ENCODING_BGR24;
}
else
{
format->encoding = MMAL_ENCODING_I420;
format->encoding_variant = MMAL_ENCODING_I420;
}
format->es->video.width = state->width;
format->es->video.height = state->height;
format->es->video.crop.x = 0;
format->es->video.crop.y = 0;
format->es->video.crop.width = state->width;
format->es->video.crop.height = state->height;
format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM;
format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN;
if (still_port->buffer_size < still_port->buffer_size_min)
still_port->buffer_size = still_port->buffer_size_min;
still_port->buffer_num = still_port->buffer_num_recommended;
status = mmal_port_format_commit(still_port);
if (status)
{
vcos_log_error("camera still format couldn't be set");
goto error;
}
/* Enable component */
status = mmal_component_enable(camera);
if (status)
{
vcos_log_error("camera component couldn't be enabled");
goto error;
}
/* Create pool of buffer headers for the output port to consume */
pool = mmal_port_pool_create(still_port, still_port->buffer_num, still_port->buffer_size);
if (!pool)
{
vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name);
}
state->camera_pool = pool;
state->camera_component = camera;
if (state->verbose)
fprintf(stderr, "Camera component done\n");
return status;
error:
if (camera)
mmal_component_destroy(camera);
return status;
}
/**
* Destroy the camera component
*
* @param state Pointer to state control struct
*
*/
static void destroy_camera_component(RASPISTILLYUV_STATE *state)
{
if (state->camera_component)
{
mmal_component_destroy(state->camera_component);
state->camera_component = NULL;
}
}
/**
* Connect two specific ports together
*
* @param output_port Pointer the output port
* @param input_port Pointer the input port
* @param Pointer to a mmal connection pointer, reassigned if function successful
* @return Returns a MMAL_STATUS_T giving result of operation
*
*/
static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
{
MMAL_STATUS_T status;
status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
if (status == MMAL_SUCCESS)
{
status = mmal_connection_enable(*connection);
if (status != MMAL_SUCCESS)
mmal_connection_destroy(*connection);
}
return status;
}
/**
* Checks if specified port is valid and enabled, then disables it
*
* @param port Pointer the port
*
*/
static void check_disable_port(MMAL_PORT_T *port)
{
if (port && port->is_enabled)
mmal_port_disable(port);
}
/**
* Handler for sigint signals
*
* @param signal_number ID of incoming signal.
*
*/
static void signal_handler(int signal_number)
{
// Going to abort on all signals
vcos_log_error("Aborting program\n");
// Need to close any open stuff...
exit(255);
}
/**
* main
*/
int main(int argc, const char **argv)
{
// Our main data storage vessel..
RASPISTILLYUV_STATE state;
int exit_code = EX_OK;
MMAL_STATUS_T status = MMAL_SUCCESS;
MMAL_PORT_T *camera_preview_port = NULL;
MMAL_PORT_T *camera_video_port = NULL;
MMAL_PORT_T *camera_still_port = NULL;
MMAL_PORT_T *preview_input_port = NULL;
FILE *output_file = NULL;
bcm_host_init();
// Register our application with the logging system
vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY);
signal(SIGINT, signal_handler);
// Do we have any parameters
if (argc == 1)
{
fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
display_valid_parameters(basename(argv[0]));
exit(EX_USAGE);
}
default_status(&state);
// Parse the command line and put options in to our status structure
if (parse_cmdline(argc, argv, &state))
{
status = -1;
exit(EX_USAGE);
}
if (state.verbose)
{
fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
dump_status(&state);
}
// OK, we have a nice set of parameters. Now set up our components
// We have two components. Camera and Preview
// Camera is different in stills/video, but preview
// is the same so handed off to a separate module
if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
{
vcos_log_error("%s: Failed to create camera component", __func__);
exit_code = EX_SOFTWARE;
}
else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
{
vcos_log_error("%s: Failed to create preview component", __func__);
destroy_camera_component(&state);
exit_code = EX_SOFTWARE;
}
else
{
PORT_USERDATA callback_data;
if (state.verbose)
fprintf(stderr, "Starting component connection stage\n");
camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
// Note we are lucky that the preview and null sink components use the same input port
// so we can simple do this without conditionals
preview_input_port = state.preview_parameters.preview_component->input[0];
// Connect camera to preview (which might be a null_sink if no preview required)
status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
if (status == MMAL_SUCCESS)
{
VCOS_STATUS_T vcos_status;
if (state.filename)
{
if (state.verbose)
fprintf(stderr, "Opening output file %s\n", state.filename);
output_file = fopen(state.filename, "wb");
if (!output_file)
{
// Notify user, carry on but discarding output buffers
vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename);
}
}
// Set up our userdata - this is passed though to the callback where we need the information.
callback_data.file_handle = output_file;
callback_data.pstate = &state;
vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0);
vcos_assert(vcos_status == VCOS_SUCCESS);
camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
if (state.verbose)
fprintf(stderr, "Enabling camera still output port\n");
// Enable the camera still output port and tell it its callback function
status = mmal_port_enable(camera_still_port, camera_buffer_callback);
if (status != MMAL_SUCCESS)
{
vcos_log_error("Failed to setup camera output");
goto error;
}
if (state.verbose)
fprintf(stderr, "Starting video preview\n");
int num_iterations = state.timelapse ? state.timeout / state.timelapse : 1;
int frame;
FILE *output_file = NULL;
for (frame = 1;frame<=num_iterations; frame++)
{
if (state.timelapse)
vcos_sleep(state.timelapse);
else
vcos_sleep(state.timeout);
// Open the file
if (state.filename)
{
if (state.filename[0] == '-')
{
output_file = stdout;
// Ensure we don't upset the output stream with diagnostics/info
state.verbose = 0;
}
else
{
char *use_filename = state.filename;
if (state.timelapse)
asprintf(&use_filename, state.filename, frame);
if (state.verbose)
fprintf(stderr, "Opening output file %s\n", use_filename);
output_file = fopen(use_filename, "wb");
if (!output_file)
{
// Notify user, carry on but discarding encoded output buffers
vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename);
}
// asprintf used in timelapse mode allocates its own memory which we need to free
if (state.timelapse)
free(use_filename);
}
callback_data.file_handle = output_file;
}
// And only do the capture if we have specified a filename and its opened OK
if (output_file)
{
// Send all the buffers to the camera output port
{
int num = mmal_queue_length(state.camera_pool->queue);
int q;
for (q=0;q<num;q++)
{
MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.camera_pool->queue);
if (!buffer)
vcos_log_error("Unable to get a required buffer %d from pool queue", q);
if (mmal_port_send_buffer(camera_still_port, buffer)!= MMAL_SUCCESS)
vcos_log_error("Unable to send a buffer to camera output port (%d)", q);
}
}
if (state.verbose)
fprintf(stderr, "Starting capture %d\n", frame);
// Fire the capture
if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
{
vcos_log_error("%s: Failed to start capture", __func__);
}
else
{
// Wait for capture to complete
// For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error
// even though it appears to be all correct, so reverting to untimed one until figure out why its erratic
vcos_semaphore_wait(&callback_data.complete_semaphore);
if (state.verbose)
fprintf(stderr, "Finished capture %d\n", frame);
}
// Ensure we don't die if get callback with no open file
callback_data.file_handle = NULL;
if (output_file != stdout)
fclose(output_file);
}
}
vcos_semaphore_delete(&callback_data.complete_semaphore);
}
else
{
mmal_status_to_int(status);
vcos_log_error("%s: Failed to connect camera to preview", __func__);
}
error:
mmal_status_to_int(status);
if (state.verbose)
fprintf(stderr, "Closing down\n");
if (output_file)
fclose(output_file);
// Disable all our ports that are not handled by connections
check_disable_port(camera_video_port);
mmal_connection_destroy(state.preview_connection);
/* Disable components */
if (state.preview_parameters.preview_component)
mmal_component_disable(state.preview_parameters.preview_component);
if (state.camera_component)
mmal_component_disable(state.camera_component);
raspipreview_destroy(&state.preview_parameters);
destroy_camera_component(&state);
if (state.verbose)
fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
}
if (status != MMAL_SUCCESS)
raspicamcontrol_check_configuration(128);
return exit_code;
}

View file

@ -0,0 +1,40 @@
/*** BEGIN file-header ***/
#include "gstrpicam-enum-types.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@basename@" */
#include "@filename@"
#define C_ENUM(v) ((gint) v)
#define C_FLAGS(v) ((guint) v)
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
static volatile gsize gtype_id = 0;
if (g_once_init_enter (&gtype_id)) {
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
GType new_type = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
g_once_init_leave (&gtype_id, new_type);
}
return (GType) gtype_id;
}
/*** END value-tail ***/

View file

@ -0,0 +1,24 @@
/*** BEGIN file-header ***/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* Enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN enumeration-production ***/
#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
GType @enum_name@_get_type (void);
/*** END enumeration-production ***/
/*** BEGIN file-tail ***/
G_END_DECLS
/*** END file-tail ***/

View file

@ -0,0 +1,101 @@
#include "interface/mmal/util/mmal_util_params.h"
#include "interface/mmal/mmal_parameters_camera.h"
#include "RaspiCamControl.h"
typedef enum {
GST_RPI_CAM_SRC_EXPOSURE_MODE_OFF = MMAL_PARAM_EXPOSUREMODE_OFF,
GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO = MMAL_PARAM_EXPOSUREMODE_AUTO,
GST_RPI_CAM_SRC_EXPOSURE_MODE_NIGHT = MMAL_PARAM_EXPOSUREMODE_NIGHT,
GST_RPI_CAM_SRC_EXPOSURE_MODE_NIGHTPREVIEW = MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW,
GST_RPI_CAM_SRC_EXPOSURE_MODE_BACKLIGHT = MMAL_PARAM_EXPOSUREMODE_BACKLIGHT,
GST_RPI_CAM_SRC_EXPOSURE_MODE_SPOTLIGHT = MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT,
GST_RPI_CAM_SRC_EXPOSURE_MODE_SPORTS = MMAL_PARAM_EXPOSUREMODE_SPORTS,
GST_RPI_CAM_SRC_EXPOSURE_MODE_SNOW = MMAL_PARAM_EXPOSUREMODE_SNOW,
GST_RPI_CAM_SRC_EXPOSURE_MODE_BEACH = MMAL_PARAM_EXPOSUREMODE_BEACH,
GST_RPI_CAM_SRC_EXPOSURE_MODE_VERYLONG = MMAL_PARAM_EXPOSUREMODE_VERYLONG,
GST_RPI_CAM_SRC_EXPOSURE_MODE_FIXEDFPS = MMAL_PARAM_EXPOSUREMODE_FIXEDFPS,
GST_RPI_CAM_SRC_EXPOSURE_MODE_ANTISHAKE = MMAL_PARAM_EXPOSUREMODE_ANTISHAKE,
GST_RPI_CAM_SRC_EXPOSURE_MODE_FIREWORKS = MMAL_PARAM_EXPOSUREMODE_FIREWORKS
} GstRpiCamSrcExposureMode;
typedef enum {
GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE,
GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_SPOT = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT,
GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_BACKLIST = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT,
GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_MATRIX = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX
} GstRpiCamSrcExposureMeteringMode;
typedef enum {
GST_RPI_CAM_SRC_AWB_MODE_OFF = MMAL_PARAM_AWBMODE_OFF,
GST_RPI_CAM_SRC_AWB_MODE_AUTO = MMAL_PARAM_AWBMODE_AUTO,
GST_RPI_CAM_SRC_AWB_MODE_SUNLIGHT = MMAL_PARAM_AWBMODE_SUNLIGHT,
GST_RPI_CAM_SRC_AWB_MODE_CLOUDY = MMAL_PARAM_AWBMODE_CLOUDY,
GST_RPI_CAM_SRC_AWB_MODE_SHADE = MMAL_PARAM_AWBMODE_SHADE,
GST_RPI_CAM_SRC_AWB_MODE_TUNGSTEN = MMAL_PARAM_AWBMODE_TUNGSTEN,
GST_RPI_CAM_SRC_AWB_MODE_FLUORESCENT = MMAL_PARAM_AWBMODE_FLUORESCENT,
GST_RPI_CAM_SRC_AWB_MODE_INCANDESCENT = MMAL_PARAM_AWBMODE_INCANDESCENT,
GST_RPI_CAM_SRC_AWB_MODE_FLASH = MMAL_PARAM_AWBMODE_FLASH,
GST_RPI_CAM_SRC_AWB_MODE_HORIZON = MMAL_PARAM_AWBMODE_HORIZON
} GstRpiCamSrcAWBMode;
typedef enum {
GST_RPI_CAM_SRC_IMAGEFX_NONE = MMAL_PARAM_IMAGEFX_NONE,
GST_RPI_CAM_SRC_IMAGEFX_NEGATIVE = MMAL_PARAM_IMAGEFX_NEGATIVE,
GST_RPI_CAM_SRC_IMAGEFX_SOLARIZE = MMAL_PARAM_IMAGEFX_SOLARIZE,
GST_RPI_CAM_SRC_IMAGEFX_POSTERIZE = MMAL_PARAM_IMAGEFX_POSTERIZE,
GST_RPI_CAM_SRC_IMAGEFX_WHITEBOARD = MMAL_PARAM_IMAGEFX_WHITEBOARD,
GST_RPI_CAM_SRC_IMAGEFX_BLACKBOARD = MMAL_PARAM_IMAGEFX_BLACKBOARD,
GST_RPI_CAM_SRC_IMAGEFX_SKETCH = MMAL_PARAM_IMAGEFX_SKETCH,
GST_RPI_CAM_SRC_IMAGEFX_DENOISE = MMAL_PARAM_IMAGEFX_DENOISE,
GST_RPI_CAM_SRC_IMAGEFX_EMBOSS = MMAL_PARAM_IMAGEFX_EMBOSS,
GST_RPI_CAM_SRC_IMAGEFX_OILPAINT = MMAL_PARAM_IMAGEFX_OILPAINT,
GST_RPI_CAM_SRC_IMAGEFX_HATCH = MMAL_PARAM_IMAGEFX_HATCH,
GST_RPI_CAM_SRC_IMAGEFX_GPEN = MMAL_PARAM_IMAGEFX_GPEN,
GST_RPI_CAM_SRC_IMAGEFX_PASTEL = MMAL_PARAM_IMAGEFX_PASTEL,
GST_RPI_CAM_SRC_IMAGEFX_WATERCOLOUR = MMAL_PARAM_IMAGEFX_WATERCOLOUR,
GST_RPI_CAM_SRC_IMAGEFX_FILM = MMAL_PARAM_IMAGEFX_FILM,
GST_RPI_CAM_SRC_IMAGEFX_BLUR = MMAL_PARAM_IMAGEFX_BLUR,
GST_RPI_CAM_SRC_IMAGEFX_SATURATION = MMAL_PARAM_IMAGEFX_SATURATION,
GST_RPI_CAM_SRC_IMAGEFX_COLOURSWAP = MMAL_PARAM_IMAGEFX_COLOURSWAP,
GST_RPI_CAM_SRC_IMAGEFX_WASHEDOUT = MMAL_PARAM_IMAGEFX_WASHEDOUT,
GST_RPI_CAM_SRC_IMAGEFX_POSTERISE = MMAL_PARAM_IMAGEFX_POSTERISE,
GST_RPI_CAM_SRC_IMAGEFX_COLOURPOINT = MMAL_PARAM_IMAGEFX_COLOURPOINT,
GST_RPI_CAM_SRC_IMAGEFX_COLOURBALANCE = MMAL_PARAM_IMAGEFX_COLOURBALANCE,
GST_RPI_CAM_SRC_IMAGEFX_CARTOON = MMAL_PARAM_IMAGEFX_CARTOON
} GstRpiCamSrcImageEffect;
typedef enum {
GST_RPI_CAM_SRC_FLICKERAVOID_OFF = MMAL_PARAM_FLICKERAVOID_OFF,
GST_RPI_CAM_SRC_FLICKERAVOID_AUTO = MMAL_PARAM_FLICKERAVOID_AUTO,
GST_RPI_CAM_SRC_FLICKERAVOID_50HZ = MMAL_PARAM_FLICKERAVOID_50HZ,
GST_RPI_CAM_SRC_FLICKERAVOID_60HZ = MMAL_PARAM_FLICKERAVOID_60HZ
} GstRpiCamSrcFlickerAvoidance;
typedef enum {
GST_RPI_CAM_SRC_DRC_LEVEL_OFF = MMAL_PARAMETER_DRC_STRENGTH_OFF,
GST_RPI_CAM_SRC_DRC_LEVEL_LOW = MMAL_PARAMETER_DRC_STRENGTH_LOW,
GST_RPI_CAM_SRC_DRC_LEVEL_MEDIUM = MMAL_PARAMETER_DRC_STRENGTH_MEDIUM,
GST_RPI_CAM_SRC_DRC_LEVEL_HIGH = MMAL_PARAMETER_DRC_STRENGTH_HIGH
} GstRpiCamSrcDRCLevel;
typedef enum /*< flags >*/ {
GST_RPI_CAM_SRC_ANNOTATION_MODE_CUSTOM_TEXT = ANNOTATE_USER_TEXT,
GST_RPI_CAM_SRC_ANNOTATION_MODE_TEXT = ANNOTATE_APP_TEXT,
GST_RPI_CAM_SRC_ANNOTATION_MODE_DATE = ANNOTATE_DATE_TEXT,
GST_RPI_CAM_SRC_ANNOTATION_MODE_TIME = ANNOTATE_TIME_TEXT,
GST_RPI_CAM_SRC_ANNOTATION_MODE_SHUTTER_SETTINGS = ANNOTATE_SHUTTER_SETTINGS,
GST_RPI_CAM_SRC_ANNOTATION_MODE_CAF_SETTINGS = ANNOTATE_CAF_SETTINGS,
GST_RPI_CAM_SRC_ANNOTATION_MODE_GAIN_SETTINGS = ANNOTATE_GAIN_SETTINGS,
GST_RPI_CAM_SRC_ANNOTATION_MODE_LENS_SETTINGS = ANNOTATE_LENS_SETTINGS,
GST_RPI_CAM_SRC_ANNOTATION_MODE_MOTION_SETTINGS = ANNOTATE_MOTION_SETTINGS,
GST_RPI_CAM_SRC_ANNOTATION_MODE_FRAME_NUMBER = ANNOTATE_FRAME_NUMBER,
GST_RPI_CAM_SRC_ANNOTATION_MODE_BLACK_BACKGROUND = ANNOTATE_BLACK_BACKGROUND
} GstRpiCamSrcAnnotationMode;
typedef enum {
GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_NONE = -1,
GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC = MMAL_VIDEO_INTRA_REFRESH_CYCLIC,
GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_ADAPTIVE = MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE,
GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_BOTH = MMAL_VIDEO_INTRA_REFRESH_BOTH,
GST_RPI_CAM_SRC_INTRA_REFRESH_TYPE_CYCLIC_ROWS = MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS
} GstRpiCamSrcIntraRefreshType;

1477
sys/rpicamsrc/gstrpicamsrc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,113 @@
/*
* GStreamer
* Copyright (C) 2013 Jan Schmidt <jan@centricular.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_RPICAMSRC_H__
#define __GST_RPICAMSRC_H__
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
#include <gst/pbutils/pbutils.h> /* only used for GST_PLUGINS_BASE_VERSION_* */
#include "RaspiCapture.h"
G_BEGIN_DECLS
#define GST_TYPE_RPICAMSRC (gst_rpi_cam_src_get_type())
#define GST_RPICAMSRC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RPICAMSRC,GstRpiCamSrc))
#define GST_RPICAMSRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RPICAMSRC,GstRpiCamSrcClass))
#define GST_IS_RPICAMSRC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RPICAMSRC))
#define GST_IS_RPICAMSRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RPICAMSRC))
#if GST_CHECK_PLUGINS_BASE_VERSION(1, 9, 2)
#define GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION
#endif
typedef struct _GstRpiCamSrc GstRpiCamSrc;
typedef struct _GstRpiCamSrcClass GstRpiCamSrcClass;
struct _GstRpiCamSrc
{
GstPushSrc parent;
GstPad *video_srcpad;
RASPIVID_CONFIG capture_config;
RASPIVID_STATE *capture_state;
gboolean started;
GMutex config_lock;
/* channels for interface */
GList *channels;
#ifdef GST_RPI_CAM_SRC_ENABLE_VIDEO_DIRECTION
GstVideoOrientationMethod orientation;
#endif
GstClockTime duration;
};
struct _GstRpiCamSrcClass
{
GstPushSrcClass parent_class;
};
GType gst_rpi_cam_src_get_type (void);
typedef enum {
GST_RPI_CAM_SRC_SENSOR_MODE_AUTOMATIC = 0,
GST_RPI_CAM_SRC_SENSOR_MODE_1920x1080 = 1,
GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_FAST = 2,
GST_RPI_CAM_SRC_SENSOR_MODE_2592x1944_SLOW = 3,
GST_RPI_CAM_SRC_SENSOR_MODE_1296x972 = 4,
GST_RPI_CAM_SRC_SENSOR_MODE_1296x730 = 5,
GST_RPI_CAM_SRC_SENSOR_MODE_640x480_SLOW = 6,
GST_RPI_CAM_SRC_SENSOR_MODE_640x480_FAST = 7
} GstRpiCamSrcSensorMode;
G_END_DECLS
#endif /* __GST_RPICAMSRC_H__ */

View file

@ -0,0 +1,149 @@
/* GStreamer Raspberry Pi Camera Source Device Provider
* Copyright (C) 2014 Tim-Philipp Müller <tim@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstrpicamsrcdeviceprovider.h"
#include <string.h>
#if GST_CHECK_VERSION (1,4,0)
#include "RaspiCapture.h"
/* FIXME: translations */
#define _(s) s
static GstRpiCamSrcDevice * gst_rpi_cam_src_device_new (void);
G_DEFINE_TYPE (GstRpiCamSrcDeviceProvider, gst_rpi_cam_src_device_provider,
GST_TYPE_DEVICE_PROVIDER);
static GList *gst_rpi_cam_src_device_provider_probe (GstDeviceProvider * provider);
static void
gst_rpi_cam_src_device_provider_class_init (GstRpiCamSrcDeviceProviderClass * klass)
{
GstDeviceProviderClass *dprovider_class = GST_DEVICE_PROVIDER_CLASS (klass);
dprovider_class->probe = gst_rpi_cam_src_device_provider_probe;
gst_device_provider_class_set_static_metadata (dprovider_class,
"Raspberry Pi Camera Source Device Provider", "Source/Video",
"Lists Raspberry Pi camera devices",
"Tim-Philipp Müller <tim@centricular.com>");
}
static void
gst_rpi_cam_src_device_provider_init (GstRpiCamSrcDeviceProvider * provider)
{
raspicapture_init ();
}
static GList *
gst_rpi_cam_src_device_provider_probe (GstDeviceProvider * provider)
{
GstRpiCamSrcDevice *device;
int supported = 0, detected = 0;
raspicamcontrol_get_camera (&supported, &detected);
if (!detected) {
GST_INFO ("No Raspberry Pi camera module detected.");
return NULL;
} else if (!supported) {
GST_WARNING ("Raspberry Pi camera module not supported, make sure to enable it.");
return NULL;
}
GST_INFO ("Raspberry Pi camera module detected and supported.");
device = gst_rpi_cam_src_device_new ();
return g_list_append (NULL, device);
}
G_DEFINE_TYPE (GstRpiCamSrcDevice, gst_rpi_cam_src_device, GST_TYPE_DEVICE);
static GstElement *gst_rpi_cam_src_device_create_element (GstDevice * device,
const gchar * name);
static void
gst_rpi_cam_src_device_class_init (GstRpiCamSrcDeviceClass * klass)
{
GstDeviceClass *device_class = GST_DEVICE_CLASS (klass);
device_class->create_element = gst_rpi_cam_src_device_create_element;
}
static void
gst_rpi_cam_src_device_init (GstRpiCamSrcDevice * device)
{
/* nothing to do here */
}
static GstElement *
gst_rpi_cam_src_device_create_element (GstDevice * device, const gchar * name)
{
return gst_element_factory_make ("rpicamsrc", name);
}
static GstRpiCamSrcDevice *
gst_rpi_cam_src_device_new (void)
{
GstRpiCamSrcDevice *device;
GValue profiles = G_VALUE_INIT;
GValue val = G_VALUE_INIT;
GstStructure *s;
GstCaps *caps;
/* FIXME: retrieve limits from the camera module, max width/height/fps etc. */
s = gst_structure_new ("video/x-h264",
"width", GST_TYPE_INT_RANGE, 1, 1920,
"height", GST_TYPE_INT_RANGE, 1, 1080,
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, RPICAMSRC_MAX_FPS, 1,
"stream-format", G_TYPE_STRING, "byte-stream",
"alignment", G_TYPE_STRING, "au",
NULL);
g_value_init (&profiles, GST_TYPE_LIST);
g_value_init (&val, G_TYPE_STRING);
g_value_set_static_string (&val, "high");
gst_value_list_append_value (&profiles, &val);
g_value_set_static_string (&val, "main");
gst_value_list_append_value (&profiles, &val);
g_value_set_static_string (&val, "baseline");
gst_value_list_append_and_take_value (&profiles, &val);
gst_structure_take_value (s, "profiles", &profiles);
caps = gst_caps_new_full (s, NULL);
device = g_object_new (GST_TYPE_RPICAMSRC_DEVICE,
"display-name", _("Raspberry Pi Camera Module"),
"device-class", "Video/Source", "caps", caps, NULL);
gst_caps_unref (caps);
return device;
}
#endif /* GST_CHECK_VERSION (1,4,0) */

View file

@ -0,0 +1,79 @@
/* GStreamer Raspberry Pi Camera Source Device Provider
* Copyright (C) 2014 Tim-Philipp Müller <tim@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_RPICAMSRC_DEVICE_PROVIDER_H__
#define __GST_RPICAMSRC_DEVICE_PROVIDER_H__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#if GST_CHECK_VERSION (1,4,0)
G_BEGIN_DECLS
typedef struct _GstRpiCamSrcDeviceProvider GstRpiCamSrcDeviceProvider;
typedef struct _GstRpiCamSrcDeviceProviderClass GstRpiCamSrcDeviceProviderClass;
#define GST_TYPE_RPICAMSRC_DEVICE_PROVIDER (gst_rpi_cam_src_device_provider_get_type())
#define GST_IS_RPICAMSRC_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER))
#define GST_IS_RPICAMSRC_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER))
#define GST_RPICAMSRC_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER, GstRpiCamSrcDeviceProviderClass))
#define GST_RPICAMSRC_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RPICAMSRC_DEVICE_PROVIDER, GstRpiCamSrcDeviceProvider))
#define GST_RPICAMSRC_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstRpiCamSrcDeviceProviderClass))
#define GST_RPICAMSRC_DEVICE_PROVIDER_CAST(obj) ((GstRpiCamSrcDeviceProvider *)(obj))
struct _GstRpiCamSrcDeviceProvider {
GstDeviceProvider parent;
};
struct _GstRpiCamSrcDeviceProviderClass {
GstDeviceProviderClass parent_class;
};
GType gst_rpi_cam_src_device_provider_get_type (void);
typedef struct _GstRpiCamSrcDevice GstRpiCamSrcDevice;
typedef struct _GstRpiCamSrcDeviceClass GstRpiCamSrcDeviceClass;
#define GST_TYPE_RPICAMSRC_DEVICE (gst_rpi_cam_src_device_get_type())
#define GST_IS_RPICAMSRC_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RPICAMSRC_DEVICE))
#define GST_IS_RPICAMSRC_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RPICAMSRC_DEVICE))
#define GST_RPICAMSRC_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RPICAMSRC_DEVICE, GstRpiCamSrcDeviceClass))
#define GST_RPICAMSRC_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RPICAMSRC_DEVICE, GstRpiCamSrcDevice))
#define GST_RPICAMSRC_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstRpiCamSrcDeviceClass))
#define GST_RPICAMSRC_DEVICE_CAST(obj) ((GstRpiCamSrcDevice *)(obj))
struct _GstRpiCamSrcDevice {
GstDevice parent;
};
struct _GstRpiCamSrcDeviceClass {
GstDeviceClass parent_class;
};
GType gst_rpi_cam_src_device_get_type (void);
G_END_DECLS
#endif /* GST_CHECK_VERSION (1,4,0) */
#endif /* __GST_RPICAMSRC_DEVICE_PROVIDER_H__ */

24
sys/rpicamsrc/meson.build Normal file
View file

@ -0,0 +1,24 @@
rpicamsrc_sources = [
'gstrpicamsrc.c',
'gstrpicamsrcdeviceprovider.c',
'RaspiCapture.c',
'RaspiCamControl.c',
'RaspiPreview.c',
'RaspiCLI.c',
]
# glib-mkenums
gnome = import('gnome')
enums = gnome.mkenums_simple('gstrpicam-enum-types',
sources: 'gstrpicam_types.h',
identifier_prefix: 'GstRpiCamSrc',
symbol_prefix: 'gst_rpi_cam_src')
library('gstrpicamsrc',
rpicamsrc_sources, enums,
c_args : gst_rpicamsrc_args,
include_directories : config_inc,
dependencies : [gst_dep, gstbase_dep, gstvideo_dep] + mmal_deps,
install : true,
install_dir : plugins_install_dir)

View file

@ -0,0 +1,68 @@
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst
def bus_call(bus, msg, *args):
# print("BUSCALL", msg, msg.type, *args)
if msg.type == Gst.MessageType.EOS:
print("End-of-stream")
loop.quit()
return
elif msg.type == Gst.MessageType.ERROR:
print("GST ERROR", msg.parse_error())
loop.quit()
return
return True
saturation = -100
def set_saturation(pipeline):
global saturation
if saturation <= 100:
print("Setting saturation to {0}".format(saturation))
videosrc.set_property("saturation", saturation)
videosrc.set_property("annotation-text", "Saturation %d" % (saturation))
else:
pipeline.send_event (Gst.Event.new_eos())
return False
saturation += 10
return True
if __name__ == "__main__":
GObject.threads_init()
# initialization
loop = GObject.MainLoop()
Gst.init(None)
pipeline = Gst.parse_launch ("rpicamsrc name=src ! video/x-h264,width=320,height=240 ! h264parse ! mp4mux ! filesink name=s")
if pipeline == None:
print ("Failed to create pipeline")
sys.exit(0)
# watch for messages on the pipeline's bus (note that this will only
# work like this when a GLib main loop is running)
bus = pipeline.get_bus()
bus.add_watch(0, bus_call, loop)
videosrc = pipeline.get_by_name ("src")
videosrc.set_property("saturation", saturation)
videosrc.set_property("annotation-mode", 1)
sink = pipeline.get_by_name ("s")
sink.set_property ("location", "test.mp4")
# this will call set_saturation every 1s
GObject.timeout_add(1000, set_saturation, pipeline)
# run
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run()
except Exception as e:
print(e)
# cleanup
pipeline.set_state(Gst.State.NULL)

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2015, Igalia S.L
* Author: Philippe Normand <philn@igalia.com>
* Licence: LGPL. (See COPYING.LGPL)
*/
#include <glib.h>
#include <gst/gst.h>
#include <gst/video/colorbalance.h>
#define CONTROL_SATURATION 1
#define CONTROL_BRIGHTNESS 1
#define CONTROL_CONTRAST 1
#define PIPELINE "rpicamsrc name=src preview=0 fullscreen=0 ! h264parse ! omxh264dec ! glimagesink sync=0"
#define declare_value(name, value) \
static gint current_##name = value; \
static gboolean incrementing_##name = TRUE;
#if CONTROL_SATURATION
declare_value(SATURATION, 50);
#endif
#if CONTROL_BRIGHTNESS
declare_value(BRIGHTNESS, 50);
#endif
#if CONTROL_CONTRAST
declare_value(CONTRAST, 0);
#endif
#define update(name, channel, current_value) \
if (!g_strcmp0(channel->label, #name)) { \
if (current_value >= channel->max_value) \
incrementing_##name = FALSE; \
else if (current_value <= channel->min_value) \
incrementing_##name = TRUE; \
current_##name += incrementing_##name ? 10 : -10; \
g_print("new " #name ": %d\n", current_##name); \
return current_##name; \
}
gint compute_value(GstColorBalanceChannel* channel, gint current_value)
{
#if CONTROL_SATURATION
update(SATURATION, channel, current_value);
#endif
#if CONTROL_BRIGHTNESS
update(BRIGHTNESS, channel, current_value);
#endif
#if CONTROL_CONTRAST
update(CONTRAST, channel, current_value);
#endif
return current_value;
}
static gboolean process(gpointer data)
{
GstColorBalance* balance = (GstColorBalance*) data;
const GList* controls;
GstColorBalanceChannel* channel;
const GList* item;
gint index, new_value, current_value;
controls = gst_color_balance_list_channels(balance);
if (controls == NULL) {
g_printerr("There is no list of colorbalance controls\n");
return G_SOURCE_REMOVE;
}
for (item = controls, index = 0; item != NULL;
item = item->next, ++index) {
channel = item->data;
current_value = gst_color_balance_get_value(balance, channel);
new_value = compute_value(channel, current_value);
gst_color_balance_set_value(balance, channel, new_value);
}
return G_SOURCE_CONTINUE;
}
int main(int argc, char** argv)
{
GMainLoop* loop;
GstElement* pipeline;
GError* error = NULL;
GstElement* src;
GstColorBalance* balance;
gst_init(&argc, &argv);
loop = g_main_loop_new(NULL, FALSE);
pipeline = gst_parse_launch(PIPELINE, &error);
if (error != NULL) {
g_printerr("Error parsing '%s': %s", PIPELINE, error->message);
g_error_free(error);
return -1;
}
gst_element_set_state(pipeline, GST_STATE_PLAYING);
src = gst_bin_get_by_name(GST_BIN(pipeline), "src");
if (!src) {
g_printerr("Source element not found\n");
return -2;
}
balance = GST_COLOR_BALANCE(src);
g_timeout_add_seconds(1, process, balance);
g_main_loop_run(loop);
gst_object_unref(src);
gst_element_set_state(pipeline, GST_STATE_NULL);
return 0;
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2015, Igalia S.L
* Author: Philippe Normand <philn@igalia.com>
* Licence: LGPL. (See COPYING.LGPL)
*/
#include <glib.h>
#include <gst/gst.h>
#include <gst/video/videoorientation.h>
#define PIPELINE "rpicamsrc name=src preview=0 fullscreen=0 ! h264parse ! omxh264dec ! glimagesink sync=0"
void configure_orientation(GstVideoOrientation* orientation)
{
gboolean flip;
if (gst_video_orientation_get_hflip(orientation, &flip)) {
g_print("current hflip: %s\n", flip ? "enabled" : "disabled");
if (g_getenv("HFLIP"))
gst_video_orientation_set_hflip(orientation, TRUE);
gst_video_orientation_get_hflip(orientation, &flip);
g_print("new hflip: %s\n", flip ? "enabled" : "disabled");
}
if (gst_video_orientation_get_vflip(orientation, &flip)) {
g_print("current vflip: %s\n", flip ? "enabled" : "disabled");
if (g_getenv("VFLIP"))
gst_video_orientation_set_vflip(orientation, TRUE);
gst_video_orientation_get_vflip(orientation, &flip);
g_print("new vflip: %s\n", flip ? "enabled" : "disabled");
}
}
int main(int argc, char** argv)
{
GMainLoop* loop;
GstElement* pipeline;
GError* error = NULL;
GstElement* src;
GstVideoOrientation* orientation;
gst_init(&argc, &argv);
loop = g_main_loop_new(NULL, FALSE);
pipeline = gst_parse_launch(PIPELINE, &error);
if (error != NULL) {
g_printerr("Error parsing '%s': %s", PIPELINE, error->message);
g_error_free(error);
return -1;
}
gst_element_set_state(pipeline, GST_STATE_PLAYING);
src = gst_bin_get_by_name(GST_BIN(pipeline), "src");
if (!src) {
g_printerr("Source element not found\n");
return -2;
}
orientation = GST_VIDEO_ORIENTATION(src);
configure_orientation(orientation);
g_main_loop_run(loop);
gst_object_unref(src);
gst_element_set_state(pipeline, GST_STATE_NULL);
return 0;
}

View file

@ -0,0 +1,583 @@
#include <locale.h>
#include <glib.h>
#include <glib-unix.h>
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#define GST_USE_UNSTABLE_API
#include <gst/webrtc/webrtc.h>
#include <libsoup/soup.h>
#include <json-glib/json-glib.h>
#include <string.h>
#define RTP_PAYLOAD_TYPE "96"
#define SOUP_HTTP_PORT 57778
#define STUN_SERVER "stun.l.google.com:19302"
typedef struct _ReceiverEntry ReceiverEntry;
ReceiverEntry *create_receiver_entry (SoupWebsocketConnection * connection);
void destroy_receiver_entry (gpointer receiver_entry_ptr);
GstPadProbeReturn payloader_caps_event_probe_cb (GstPad * pad,
GstPadProbeInfo * info, gpointer user_data);
void on_offer_created_cb (GstPromise * promise, gpointer user_data);
void on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data);
void on_ice_candidate_cb (GstElement * webrtcbin, guint mline_index,
gchar * candidate, gpointer user_data);
void soup_websocket_message_cb (SoupWebsocketConnection * connection,
SoupWebsocketDataType data_type, GBytes * message, gpointer user_data);
void soup_websocket_closed_cb (SoupWebsocketConnection * connection,
gpointer user_data);
void soup_http_handler (SoupServer * soup_server, SoupMessage * message,
const char *path, GHashTable * query, SoupClientContext * client_context,
gpointer user_data);
void soup_websocket_handler (G_GNUC_UNUSED SoupServer * server,
SoupWebsocketConnection * connection, const char *path,
SoupClientContext * client_context, gpointer user_data);
static gchar *get_string_from_json_object (JsonObject * object);
gboolean exit_sighandler (gpointer user_data);
struct _ReceiverEntry
{
SoupWebsocketConnection *connection;
GstElement *pipeline;
GstElement *webrtcbin;
};
const gchar *html_source = " \n \
<html> \n \
<head> \n \
<script type=\"text/javascript\" src=\"https://webrtc.github.io/adapter/adapter-latest.js\"></script> \n \
<script type=\"text/javascript\"> \n \
var html5VideoElement; \n \
var websocketConnection; \n \
var webrtcPeerConnection; \n \
var webrtcConfiguration; \n \
var reportError; \n \
\n \
\n \
function onLocalDescription(desc) { \n \
console.log(\"Local description: \" + JSON.stringify(desc)); \n \
webrtcPeerConnection.setLocalDescription(desc).then(function() { \n \
websocketConnection.send(JSON.stringify({ type: \"sdp\", \"data\": webrtcPeerConnection.localDescription })); \n \
}).catch(reportError); \n \
} \n \
\n \
\n \
function onIncomingSDP(sdp) { \n \
console.log(\"Incoming SDP: \" + JSON.stringify(sdp)); \n \
webrtcPeerConnection.setRemoteDescription(sdp).catch(reportError); \n \
webrtcPeerConnection.createAnswer().then(onLocalDescription).catch(reportError); \n \
} \n \
\n \
\n \
function onIncomingICE(ice) { \n \
var candidate = new RTCIceCandidate(ice); \n \
console.log(\"Incoming ICE: \" + JSON.stringify(ice)); \n \
webrtcPeerConnection.addIceCandidate(candidate).catch(reportError); \n \
} \n \
\n \
\n \
function onAddRemoteStream(event) { \n \
html5VideoElement.srcObject = event.streams[0]; \n \
} \n \
\n \
\n \
function onIceCandidate(event) { \n \
if (event.candidate == null) \n \
return; \n \
\n \
console.log(\"Sending ICE candidate out: \" + JSON.stringify(event.candidate)); \n \
websocketConnection.send(JSON.stringify({ \"type\": \"ice\", \"data\": event.candidate })); \n \
} \n \
\n \
\n \
function onServerMessage(event) { \n \
var msg; \n \
\n \
try { \n \
msg = JSON.parse(event.data); \n \
} catch (e) { \n \
return; \n \
} \n \
\n \
if (!webrtcPeerConnection) { \n \
webrtcPeerConnection = new RTCPeerConnection(webrtcConfiguration); \n \
webrtcPeerConnection.ontrack = onAddRemoteStream; \n \
webrtcPeerConnection.onicecandidate = onIceCandidate; \n \
} \n \
\n \
switch (msg.type) { \n \
case \"sdp\": onIncomingSDP(msg.data); break; \n \
case \"ice\": onIncomingICE(msg.data); break; \n \
default: break; \n \
} \n \
} \n \
\n \
\n \
function playStream(videoElement, hostname, port, path, configuration, reportErrorCB) { \n \
var l = window.location;\n \
var wsHost = (hostname != undefined) ? hostname : l.hostname; \n \
var wsPort = (port != undefined) ? port : l.port; \n \
var wsPath = (path != undefined) ? path : \"ws\"; \n \
if (wsPort) \n\
wsPort = \":\" + wsPort; \n\
var wsUrl = \"ws://\" + wsHost + wsPort + \"/\" + wsPath; \n \
\n \
html5VideoElement = videoElement; \n \
webrtcConfiguration = configuration; \n \
reportError = (reportErrorCB != undefined) ? reportErrorCB : function(text) {}; \n \
\n \
websocketConnection = new WebSocket(wsUrl); \n \
websocketConnection.addEventListener(\"message\", onServerMessage); \n \
} \n \
\n \
window.onload = function() { \n \
var vidstream = document.getElementById(\"stream\"); \n \
var config = { 'iceServers': [{ 'urls': 'stun:" STUN_SERVER "' }] }; \n\
playStream(vidstream, null, null, null, config, function (errmsg) { console.error(errmsg); }); \n \
}; \n \
\n \
</script> \n \
</head> \n \
\n \
<body> \n \
<div> \n \
<video id=\"stream\" autoplay>Your browser does not support video</video> \n \
</div> \n \
</body> \n \
</html> \n \
";
ReceiverEntry *
create_receiver_entry (SoupWebsocketConnection * connection)
{
GError *error;
ReceiverEntry *receiver_entry;
receiver_entry = g_slice_alloc0 (sizeof (ReceiverEntry));
receiver_entry->connection = connection;
g_object_ref (G_OBJECT (connection));
g_signal_connect (G_OBJECT (connection), "message",
G_CALLBACK (soup_websocket_message_cb), (gpointer) receiver_entry);
error = NULL;
receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin stun-server=stun://" STUN_SERVER " "
"rpicamsrc bitrate=600000 annotation-mode=12 preview=false ! video/x-h264,profile=constrained-baseline,width=640,height=360,level=3.0 ! queue max-size-time=100000000 ! h264parse ! "
"rtph264pay config-interval=-1 name=payloader ! "
"application/x-rtp,media=video,encoding-name=H264,payload="
RTP_PAYLOAD_TYPE " ! webrtcbin. ", &error);
if (error != NULL) {
g_error ("Could not create WebRTC pipeline: %s\n", error->message);
g_error_free (error);
goto cleanup;
}
receiver_entry->webrtcbin =
gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "webrtcbin");
g_assert (receiver_entry->webrtcbin != NULL);
g_signal_connect (receiver_entry->webrtcbin, "on-negotiation-needed",
G_CALLBACK (on_negotiation_needed_cb), (gpointer) receiver_entry);
g_signal_connect (receiver_entry->webrtcbin, "on-ice-candidate",
G_CALLBACK (on_ice_candidate_cb), (gpointer) receiver_entry);
gst_element_set_state (receiver_entry->pipeline, GST_STATE_PLAYING);
return receiver_entry;
cleanup:
destroy_receiver_entry ((gpointer) receiver_entry);
return NULL;
}
void
destroy_receiver_entry (gpointer receiver_entry_ptr)
{
ReceiverEntry *receiver_entry = (ReceiverEntry *) receiver_entry_ptr;
g_assert (receiver_entry != NULL);
if (receiver_entry->pipeline != NULL) {
gst_element_set_state (GST_ELEMENT (receiver_entry->pipeline),
GST_STATE_NULL);
gst_object_unref (GST_OBJECT (receiver_entry->webrtcbin));
gst_object_unref (GST_OBJECT (receiver_entry->pipeline));
}
if (receiver_entry->connection != NULL)
g_object_unref (G_OBJECT (receiver_entry->connection));
g_slice_free1 (sizeof (ReceiverEntry), receiver_entry);
}
void
on_offer_created_cb (GstPromise * promise, gpointer user_data)
{
gchar *sdp_string;
gchar *json_string;
JsonObject *sdp_json;
JsonObject *sdp_data_json;
GstStructure const *reply;
GstPromise *local_desc_promise;
GstWebRTCSessionDescription *offer = NULL;
ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data;
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "offer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
&offer, NULL);
gst_promise_unref (promise);
local_desc_promise = gst_promise_new ();
g_signal_emit_by_name (receiver_entry->webrtcbin, "set-local-description",
offer, local_desc_promise);
gst_promise_interrupt (local_desc_promise);
gst_promise_unref (local_desc_promise);
sdp_string = gst_sdp_message_as_text (offer->sdp);
g_print ("Negotiation offer created:\n%s\n", sdp_string);
sdp_json = json_object_new ();
json_object_set_string_member (sdp_json, "type", "sdp");
sdp_data_json = json_object_new ();
json_object_set_string_member (sdp_data_json, "type", "offer");
json_object_set_string_member (sdp_data_json, "sdp", sdp_string);
json_object_set_object_member (sdp_json, "data", sdp_data_json);
json_string = get_string_from_json_object (sdp_json);
json_object_unref (sdp_json);
soup_websocket_connection_send_text (receiver_entry->connection, json_string);
g_free (json_string);
gst_webrtc_session_description_free (offer);
}
void
on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data)
{
GstPromise *promise;
ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data;
g_print ("Creating negotiation offer\n");
promise = gst_promise_new_with_change_func (on_offer_created_cb,
(gpointer) receiver_entry, NULL);
g_signal_emit_by_name (G_OBJECT (webrtcbin), "create-offer", NULL, promise);
}
void
on_ice_candidate_cb (G_GNUC_UNUSED GstElement * webrtcbin, guint mline_index,
gchar * candidate, gpointer user_data)
{
JsonObject *ice_json;
JsonObject *ice_data_json;
gchar *json_string;
ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data;
ice_json = json_object_new ();
json_object_set_string_member (ice_json, "type", "ice");
ice_data_json = json_object_new ();
json_object_set_int_member (ice_data_json, "sdpMLineIndex", mline_index);
json_object_set_string_member (ice_data_json, "candidate", candidate);
json_object_set_object_member (ice_json, "data", ice_data_json);
json_string = get_string_from_json_object (ice_json);
json_object_unref (ice_json);
soup_websocket_connection_send_text (receiver_entry->connection, json_string);
g_free (json_string);
}
void
soup_websocket_message_cb (G_GNUC_UNUSED SoupWebsocketConnection * connection,
SoupWebsocketDataType data_type, GBytes * message, gpointer user_data)
{
gsize size;
gchar *data;
gchar *data_string;
const gchar *type_string;
JsonNode *root_json;
JsonObject *root_json_object;
JsonObject *data_json_object;
JsonParser *json_parser = NULL;
ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data;
switch (data_type) {
case SOUP_WEBSOCKET_DATA_BINARY:
g_error ("Received unknown binary message, ignoring\n");
g_bytes_unref (message);
return;
case SOUP_WEBSOCKET_DATA_TEXT:
data = g_bytes_unref_to_data (message, &size);
/* Convert to NULL-terminated string */
data_string = g_strndup (data, size);
g_free (data);
break;
default:
g_assert_not_reached ();
}
json_parser = json_parser_new ();
if (!json_parser_load_from_data (json_parser, data_string, -1, NULL))
goto unknown_message;
root_json = json_parser_get_root (json_parser);
if (!JSON_NODE_HOLDS_OBJECT (root_json))
goto unknown_message;
root_json_object = json_node_get_object (root_json);
if (!json_object_has_member (root_json_object, "type")) {
g_error ("Received message without type field\n");
goto cleanup;
}
type_string = json_object_get_string_member (root_json_object, "type");
if (!json_object_has_member (root_json_object, "data")) {
g_error ("Received message without data field\n");
goto cleanup;
}
data_json_object = json_object_get_object_member (root_json_object, "data");
if (g_strcmp0 (type_string, "sdp") == 0) {
const gchar *sdp_type_string;
const gchar *sdp_string;
GstPromise *promise;
GstSDPMessage *sdp;
GstWebRTCSessionDescription *answer;
int ret;
if (!json_object_has_member (data_json_object, "type")) {
g_error ("Received SDP message without type field\n");
goto cleanup;
}
sdp_type_string = json_object_get_string_member (data_json_object, "type");
if (g_strcmp0 (sdp_type_string, "answer") != 0) {
g_error ("Expected SDP message type \"answer\", got \"%s\"\n",
sdp_type_string);
goto cleanup;
}
if (!json_object_has_member (data_json_object, "sdp")) {
g_error ("Received SDP message without SDP string\n");
goto cleanup;
}
sdp_string = json_object_get_string_member (data_json_object, "sdp");
g_print ("Received SDP:\n%s\n", sdp_string);
ret = gst_sdp_message_new (&sdp);
g_assert_cmphex (ret, ==, GST_SDP_OK);
ret =
gst_sdp_message_parse_buffer ((guint8 *) sdp_string,
strlen (sdp_string), sdp);
if (ret != GST_SDP_OK) {
g_error ("Could not parse SDP string\n");
goto cleanup;
}
answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER,
sdp);
g_assert_nonnull (answer);
promise = gst_promise_new ();
g_signal_emit_by_name (receiver_entry->webrtcbin, "set-remote-description",
answer, promise);
gst_promise_interrupt (promise);
gst_promise_unref (promise);
} else if (g_strcmp0 (type_string, "ice") == 0) {
guint mline_index;
const gchar *candidate_string;
if (!json_object_has_member (data_json_object, "sdpMLineIndex")) {
g_error ("Received ICE message without mline index\n");
goto cleanup;
}
mline_index =
json_object_get_int_member (data_json_object, "sdpMLineIndex");
if (!json_object_has_member (data_json_object, "candidate")) {
g_error ("Received ICE message without ICE candidate string\n");
goto cleanup;
}
candidate_string = json_object_get_string_member (data_json_object,
"candidate");
g_print ("Received ICE candidate with mline index %u; candidate: %s\n",
mline_index, candidate_string);
g_signal_emit_by_name (receiver_entry->webrtcbin, "add-ice-candidate",
mline_index, candidate_string);
} else
goto unknown_message;
cleanup:
if (json_parser != NULL)
g_object_unref (G_OBJECT (json_parser));
g_free (data_string);
return;
unknown_message:
g_error ("Unknown message \"%s\", ignoring", data_string);
goto cleanup;
}
void
soup_websocket_closed_cb (SoupWebsocketConnection * connection,
gpointer user_data)
{
GHashTable *receiver_entry_table = (GHashTable *) user_data;
g_hash_table_remove (receiver_entry_table, connection);
g_print ("Closed websocket connection %p\n", (gpointer) connection);
}
void
soup_http_handler (G_GNUC_UNUSED SoupServer * soup_server,
SoupMessage * message, const char *path, G_GNUC_UNUSED GHashTable * query,
G_GNUC_UNUSED SoupClientContext * client_context,
G_GNUC_UNUSED gpointer user_data)
{
SoupBuffer *soup_buffer;
if ((g_strcmp0 (path, "/") != 0) && (g_strcmp0 (path, "/index.html") != 0)) {
soup_message_set_status (message, SOUP_STATUS_NOT_FOUND);
return;
}
soup_buffer =
soup_buffer_new (SOUP_MEMORY_STATIC, html_source, strlen (html_source));
soup_message_headers_set_content_type (message->response_headers, "text/html",
NULL);
soup_message_body_append_buffer (message->response_body, soup_buffer);
soup_buffer_free (soup_buffer);
soup_message_set_status (message, SOUP_STATUS_OK);
}
void
soup_websocket_handler (G_GNUC_UNUSED SoupServer * server,
SoupWebsocketConnection * connection, G_GNUC_UNUSED const char *path,
G_GNUC_UNUSED SoupClientContext * client_context, gpointer user_data)
{
ReceiverEntry *receiver_entry;
GHashTable *receiver_entry_table = (GHashTable *) user_data;
g_print ("Processing new websocket connection %p", (gpointer) connection);
g_signal_connect (G_OBJECT (connection), "closed",
G_CALLBACK (soup_websocket_closed_cb), (gpointer) receiver_entry_table);
receiver_entry = create_receiver_entry (connection);
g_hash_table_replace (receiver_entry_table, connection, receiver_entry);
}
static gchar *
get_string_from_json_object (JsonObject * object)
{
JsonNode *root;
JsonGenerator *generator;
gchar *text;
/* Make it the root node */
root = json_node_init_object (json_node_alloc (), object);
generator = json_generator_new ();
json_generator_set_root (generator, root);
text = json_generator_to_data (generator, NULL);
/* Release everything */
g_object_unref (generator);
json_node_free (root);
return text;
}
gboolean
exit_sighandler (gpointer user_data)
{
g_print ("Caught signal, stopping mainloop\n");
GMainLoop *mainloop = (GMainLoop *) user_data;
g_main_loop_quit (mainloop);
return TRUE;
}
int
main (int argc, char *argv[])
{
GMainLoop *mainloop;
SoupServer *soup_server;
GHashTable *receiver_entry_table;
setlocale (LC_ALL, "");
gst_init (&argc, &argv);
receiver_entry_table =
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
destroy_receiver_entry);
mainloop = g_main_loop_new (NULL, FALSE);
g_assert (mainloop != NULL);
g_unix_signal_add (SIGINT, exit_sighandler, mainloop);
g_unix_signal_add (SIGTERM, exit_sighandler, mainloop);
soup_server =
soup_server_new (SOUP_SERVER_SERVER_HEADER, "webrtc-soup-server", NULL);
soup_server_add_handler (soup_server, "/", soup_http_handler, NULL, NULL);
soup_server_add_websocket_handler (soup_server, "/ws", NULL, NULL,
soup_websocket_handler, (gpointer) receiver_entry_table, NULL);
soup_server_listen_all (soup_server, SOUP_HTTP_PORT,
(SoupServerListenOptions) 0, NULL);
g_print ("WebRTC page link: http://127.0.0.1:%d/\n", (gint) SOUP_HTTP_PORT);
g_main_loop_run (mainloop);
g_object_unref (G_OBJECT (soup_server));
g_hash_table_destroy (receiver_entry_table);
g_main_loop_unref (mainloop);
gst_deinit ();
return 0;
}