mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
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:
commit
84dbf94313
22 changed files with 9764 additions and 0 deletions
155
sys/rpicamsrc/RaspiCLI.c
Normal file
155
sys/rpicamsrc/RaspiCLI.c
Normal 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
56
sys/rpicamsrc/RaspiCLI.h
Normal 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
|
1467
sys/rpicamsrc/RaspiCamControl.c
Normal file
1467
sys/rpicamsrc/RaspiCamControl.c
Normal file
File diff suppressed because it is too large
Load diff
220
sys/rpicamsrc/RaspiCamControl.h
Normal file
220
sys/rpicamsrc/RaspiCamControl.h
Normal 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
2026
sys/rpicamsrc/RaspiCapture.c
Normal file
File diff suppressed because it is too large
Load diff
143
sys/rpicamsrc/RaspiCapture.h
Normal file
143
sys/rpicamsrc/RaspiCapture.h
Normal 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
|
307
sys/rpicamsrc/RaspiPreview.c
Normal file
307
sys/rpicamsrc/RaspiPreview.c
Normal 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, ¶m.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",
|
||||
¶ms->previewWindow.x, ¶ms->previewWindow.y,
|
||||
¶ms->previewWindow.width, ¶ms->previewWindow.height);
|
||||
|
||||
// Failed to get any window parameters, so revert to full screen
|
||||
if (tmp == 0)
|
||||
params->wantFullScreenPreview = 1;
|
||||
else
|
||||
params->wantFullScreenPreview = 0;
|
||||
|
||||
used = 2;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandFullScreen: // Want full screen preview mode (overrides display rect)
|
||||
params->wantPreview = 1;
|
||||
params->wantFullScreenPreview = 1;
|
||||
|
||||
used = 1;
|
||||
break;
|
||||
|
||||
case CommandOpacity: // Define preview window opacity
|
||||
if (sscanf(arg2, "%u", ¶ms->opacity) != 1)
|
||||
params->opacity = 255;
|
||||
else
|
||||
used = 2;
|
||||
break;
|
||||
|
||||
case CommandDisablePreview: // Turn off preview output
|
||||
params->wantPreview = 0;
|
||||
used = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display help for command line options
|
||||
*/
|
||||
void raspipreview_display_help()
|
||||
{
|
||||
fprintf(stderr, "\nPreview parameter commands\n\n");
|
||||
raspicli_display_help(cmdline_commands, cmdline_commands_size);
|
||||
}
|
||||
#endif
|
75
sys/rpicamsrc/RaspiPreview.h
Normal file
75
sys/rpicamsrc/RaspiPreview.h
Normal 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
1516
sys/rpicamsrc/RaspiStill.c
Normal file
File diff suppressed because it is too large
Load diff
957
sys/rpicamsrc/RaspiStillYUV.c
Normal file
957
sys/rpicamsrc/RaspiStillYUV.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
40
sys/rpicamsrc/gstrpicam-enums-template.c
Normal file
40
sys/rpicamsrc/gstrpicam-enums-template.c
Normal 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 (>ype_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 (>ype_id, new_type);
|
||||
}
|
||||
return (GType) gtype_id;
|
||||
}
|
||||
|
||||
/*** END value-tail ***/
|
||||
|
24
sys/rpicamsrc/gstrpicam-enums-template.h
Normal file
24
sys/rpicamsrc/gstrpicam-enums-template.h
Normal 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 ***/
|
101
sys/rpicamsrc/gstrpicam_types.h
Normal file
101
sys/rpicamsrc/gstrpicam_types.h
Normal 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
1477
sys/rpicamsrc/gstrpicamsrc.c
Normal file
File diff suppressed because it is too large
Load diff
113
sys/rpicamsrc/gstrpicamsrc.h
Normal file
113
sys/rpicamsrc/gstrpicamsrc.h
Normal 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__ */
|
149
sys/rpicamsrc/gstrpicamsrcdeviceprovider.c
Normal file
149
sys/rpicamsrc/gstrpicamsrcdeviceprovider.c
Normal 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) */
|
79
sys/rpicamsrc/gstrpicamsrcdeviceprovider.h
Normal file
79
sys/rpicamsrc/gstrpicamsrcdeviceprovider.h
Normal 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
24
sys/rpicamsrc/meson.build
Normal 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)
|
68
tests/examples/rpicamsrc/dynamicprops.py
Executable file
68
tests/examples/rpicamsrc/dynamicprops.py
Executable 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)
|
||||
|
114
tests/examples/rpicamsrc/test_color_balance.c
Normal file
114
tests/examples/rpicamsrc/test_color_balance.c
Normal 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;
|
||||
}
|
70
tests/examples/rpicamsrc/test_orientation.c
Normal file
70
tests/examples/rpicamsrc/test_orientation.c
Normal 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;
|
||||
}
|
583
tests/examples/rpicamsrc/webrtc-unidirectional-h264.c
Normal file
583
tests/examples/rpicamsrc/webrtc-unidirectional-h264.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue