├── examples └── motiontest.py ├── setup.py ├── src ├── picam.h ├── RaspiCamControl.h ├── picammodule.c ├── RaspiCamControl.c └── picam.c ├── picam └── __init__.py ├── LICENSE └── README.md /examples/motiontest.py: -------------------------------------------------------------------------------- 1 | import picam 2 | from PIL import Image 3 | from array import array 4 | import time 5 | width = 100 6 | height = 100 7 | 8 | THRESHOLD = 15 9 | QUANITY_MIN = 50 10 | frame1 = picam.takeRGBPhotoWithDetails(width,height) 11 | while True: 12 | frame2 = picam.takeRGBPhotoWithDetails(width,height) 13 | (_,q) = picam.difference(frame1,frame2,THRESHOLD) 14 | print q 15 | if q > QUANITY_MIN: 16 | picam.LEDOn() 17 | else: 18 | picam.LEDOff() 19 | frame1 = frame2 20 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | import os 3 | 4 | module1 = Extension('picam._picam', 5 | define_macros = [('MAJOR_VERSION', '1'), 6 | ('MINOR_VERSION', '0')], 7 | include_dirs = ['/usr/local/include','/opt/vc/include','/opt/vc/include/interface/vcos/pthreads','/opt/vc/include/interface/vmcs_host/linux/'], 8 | libraries = ['mmal','vcos','bcm_host'], 9 | library_dirs = ['/usr/local/lib','/opt/vc/lib'], 10 | sources = ['./src/picammodule.c','./src/picam.c','./src/RaspiCamControl.c']) 11 | 12 | setup (name = 'picam', 13 | version = '1.0', 14 | author='Sean Ashton', 15 | author_email='sean.ashton@schimera.com', 16 | description = 'Raspberry Pi Camera Module Python Library', 17 | license='MIT', 18 | download_url='https://github.com/ashtons/picam', 19 | packages=['picam'], 20 | package_data={'picam':['_picam.so']}, 21 | ext_modules = [module1] 22 | ) 23 | -------------------------------------------------------------------------------- /src/picam.h: -------------------------------------------------------------------------------- 1 | #ifndef _PICAM_H 2 | #define _PICAM_H 3 | 4 | #include 5 | #include "interface/mmal/mmal.h" 6 | typedef struct { 7 | int exposure; 8 | int meterMode; 9 | int imageFX; 10 | int awbMode; 11 | int ISO; 12 | int sharpness; /// -100 to 100 13 | int contrast; /// -100 to 100 14 | int brightness; /// 0 to 100 15 | int saturation; /// -100 to 100 16 | int videoStabilisation; /// 0 or 1 (false or true) 17 | int exposureCompensation; /// -10 to +10 ? 18 | int rotation; 19 | int hflip; /// 0 or 1 20 | int vflip; /// 0 or 1 21 | int shutter_speed; //0 for auto, otherwise the shutter speed in ms 22 | int videoProfile; 23 | int videoBitrate; //17000000 24 | int videoFramerate; //30 25 | int quantisationParameter; //0 26 | int inlineHeaders; /// Insert inline headers to stream (SPS, PPS) 27 | double roi[4]; 28 | } PicamParams; 29 | 30 | uint8_t *takePhoto(PicamParams *parms, long *sizeread); 31 | uint8_t *takePhotoWithDetails(int width, int height, int quality, PicamParams *parms, long *sizeread); 32 | uint8_t *takeRGBPhotoWithDetails(int width, int height, PicamParams *parms,long *sizeread); 33 | uint8_t *internelPhotoWithDetails(int width, int height, int quality,MMAL_FOURCC_T encoding,PicamParams *parms, long *sizeread); 34 | void internelVideoWithDetails(char *filename, int width, int height, int duration, PicamParams *parms); 35 | #endif // _PICAM_H 36 | -------------------------------------------------------------------------------- /picam/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Sean Ashton 2 | # Licensed under the terms of the MIT License (see LICENSE.txt) 3 | from _picam import * 4 | import StringIO 5 | from PIL import Image 6 | import ImageDraw 7 | import RPi.GPIO as GPIO 8 | import os 9 | GPIO_AVAILABLE = True 10 | 11 | config = _picam.config 12 | 13 | try: 14 | #add disable_camera_led=1 to config.txt to have control over the LED 15 | GPIO.setwarnings(False) 16 | GPIO.setmode(GPIO.BCM) 17 | GPIO.setup(5, GPIO.OUT, initial=False) 18 | except: 19 | GPIO_AVAILABLE = False 20 | 21 | def LEDOn(): 22 | if GPIO_AVAILABLE: 23 | GPIO.output(5,True) 24 | 25 | def LEDOff(): 26 | if GPIO_AVAILABLE: 27 | GPIO.output(5,False) 28 | 29 | 30 | def difference(list1,list2, tolerance): 31 | return _picam.difference(list1,list2, tolerance) 32 | 33 | def takeRGBPhotoWithDetails(width, height): 34 | return _picam.takeRGBPhotoWithDetails(width, height) 35 | 36 | def takePhoto(): 37 | s = _picam.takePhoto() 38 | ss = StringIO.StringIO(s) 39 | i = Image.open(ss) 40 | return i 41 | 42 | def takePhotoWithDetails(width, height, quality): 43 | s = _picam.takePhotoWithDetails(width, height, quality) 44 | ss = StringIO.StringIO(s) 45 | i = Image.open(ss) 46 | return i 47 | 48 | def recordVideoWithDetails(filename, width, height, duration): 49 | directory = os.path.dirname(filename) 50 | if os.path.exists(directory): 51 | _picam.recordVideoWithDetails(filename, width, height, duration) 52 | else: 53 | raise Exception("Path does not exist!") 54 | 55 | def saveRGBToImage(rgb_list, filename, width, height): 56 | im = Image.new("RGB", (width, height), "white") 57 | draw = ImageDraw.Draw(im) 58 | for i in range(0, len(rgb_list)): 59 | rgb = rgb_list[i] 60 | r = (rgb>>16) & 0x0ff 61 | g = (rgb>>8) & 0x0ff 62 | b = (rgb) & 0x0ff 63 | x = i % width 64 | y = i / height 65 | color = (r,g,b) 66 | draw.point((x,(height-1)-y),color) 67 | im.save(filename) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2013, Broadcom Europe Ltd 3 | Copyright (c) 2013, James Hughes 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the copyright holder nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2013 Sean Ashton 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of 34 | this software and associated documentation files (the "Software"), to deal in 35 | the Software without restriction, including without limitation the rights to 36 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 37 | the Software, and to permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 45 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 46 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 47 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | picam 2 | ===== 3 | 4 | Raspberry Pi (RPi) Camera Module Python Library 5 | 6 | 7 | This module will allow some basic functionality to use the camera module from within Python without starting a new process to take the photo. 8 | 9 | Based largely on a stripped down version of the raspistill and raspivid code, with a python wrapper. 10 | 11 | Returns a PIL Image object 12 | 13 | 14 | import picam 15 | import time 16 | 17 | i = picam.takePhoto() 18 | i.save('/tmp/test.jpg') 19 | 20 | # (width, height, jpg quality) 21 | ii = picam.takePhotoWithDetails(640,480, 85) 22 | 23 | filename = "/tmp/picam-%s.h264" % time.strftime("%Y%m%d-%H%M%S") 24 | 25 | # (width, height, duration = 5s) 26 | picam.recordVideoWithDetails(filename,640,480,5000) 27 | 28 | #RGB pixel info 29 | frame1 = picam.takeRGBPhotoWithDetails(width,height) 30 | frame2 = picam.takeRGBPhotoWithDetails(width,height) 31 | 32 | #returns RGB pixel list with modified pixels, and the quantity of changed pixels 33 | (modified,q) = picam.difference(frame1,frame2,THRESHOLD) 34 | 35 | #add disable_camera_led=1 to config.txt to have control over the LED 36 | picam.LEDOn() 37 | picam.LEDOff() 38 | 39 | picam.config.imageFX = picam.MMAL_PARAM_IMAGEFX_WATERCOLOUR 40 | picam.config.exposure = picam.MMAL_PARAM_EXPOSUREMODE_AUTO 41 | picam.config.meterMode = picam.MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE 42 | picam.config.awbMode = picam.MMAL_PARAM_AWBMODE_SHADE 43 | picam.config.ISO = 0 #auto 44 | picam.config.ISO = 400 45 | picam.config.ISO = 800 46 | 47 | picam.config.sharpness = 0 # -100 to 100 48 | picam.config.contrast = 0 # -100 to 100 49 | picam.config.brightness = 50 # 0 to 100 50 | picam.config.saturation = 0 # -100 to 100 51 | picam.config.videoStabilisation = 0 # 0 or 1 (false or true) 52 | picam.config.exposureCompensation = 0 # -10 to +10 ? 53 | picam.config.rotation = 90 # 0-359 54 | picam.config.hflip = 1 # 0 or 1 55 | picam.config.vflip = 0 # 0 or 1 56 | picam.config.shutterSpeed = 20000 # 0 = auto, otherwise the shutter speed in ms 57 | 58 | picam.config.videoProfile = picam.MMAL_VIDEO_PROFILE_H264_HIGH 59 | picam.config.videoFramerate = 15 60 | picam.config.videoBitrate = 17000000 61 | 62 | picam.config.inlineHeaders = 0 # Insert inline headers to stream (SPS, PPS), 0 or 1 63 | picam.config.quantisationParameter = 0 # Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate 64 | 65 | picam.config.roi = [0.0,0.0,0.5,0.5] # Region of interest, normalised coordinates (0.0 - 1.0). 66 | picam.config.roi = [0.5,0.5,0.25,0.25] 67 | 68 | Installation 69 | ------------ 70 | 71 | This extension depends on the Userland libraries, so they need to be installed first 72 | 73 | sudo apt-get install cmake 74 | sudo apt-get install python-dev 75 | sudo apt-get install python-imaging 76 | 77 | git clone https://github.com/raspberrypi/userland.git 78 | cd userland 79 | ./buildme 80 | 81 | Download the folder and run the setup command to install the script 82 | 83 | sudo python setup.py install 84 | 85 | or 86 | 87 | sudo pip install https://github.com/ashtons/picam/zipball/master#egg=picam 88 | 89 | 90 | Upgrade Firmware 91 | ---------------- 92 | Update the firmware (some functions are only available in the newer firmware versions) 93 | 94 | sudo apt-get install rpi-update 95 | 96 | sudo rpi-update 97 | 98 | 99 | Make sure the necessary dependencies are installed and the latest versions 100 | 101 | sudo apt-get update 102 | 103 | sudo apt-get upgrade 104 | 105 | sudo raspi-config #to enable the camera 106 | 107 | raspistill -o /tmp/test.jpg 108 | 109 | sudo apt-get install python-imaging-tk 110 | 111 | Troubleshooting 112 | --------------- 113 | If you see an error message similar to the following 114 | 115 | mmal: mmal_vc_port_parameter_set: failed to set port parameter 64:0:ENOSYS 116 | 117 | mmal: Function not implemented 118 | 119 | then you need to update your firmware. 120 | 121 | mmal: main: Failed to create camera component 122 | 123 | this error probably indicates that the camera hasn't been plugged in correctly. 124 | 125 | Constants 126 | ------------ 127 | MMAL_PARAM_EXPOSUREMODE_OFF 128 | MMAL_PARAM_EXPOSUREMODE_AUTO 129 | MMAL_PARAM_EXPOSUREMODE_NIGHT 130 | MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW 131 | MMAL_PARAM_EXPOSUREMODE_BACKLIGHT 132 | MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT 133 | MMAL_PARAM_EXPOSUREMODE_SPORTS 134 | MMAL_PARAM_EXPOSUREMODE_SNOW 135 | MMAL_PARAM_EXPOSUREMODE_BEACH 136 | MMAL_PARAM_EXPOSUREMODE_VERYLONG 137 | MMAL_PARAM_EXPOSUREMODE_FIXEDFPS 138 | MMAL_PARAM_EXPOSUREMODE_ANTISHAKE 139 | MMAL_PARAM_EXPOSUREMODE_FIREWORKS 140 | 141 | MMAL_PARAM_AWBMODE_OFF 142 | MMAL_PARAM_AWBMODE_AUTO 143 | MMAL_PARAM_AWBMODE_SUNLIGHT 144 | MMAL_PARAM_AWBMODE_CLOUDY 145 | MMAL_PARAM_AWBMODE_SHADE 146 | MMAL_PARAM_AWBMODE_TUNGSTEN 147 | MMAL_PARAM_AWBMODE_FLUORESCENT 148 | MMAL_PARAM_AWBMODE_INCANDESCENT 149 | MMAL_PARAM_AWBMODE_FLASH 150 | MMAL_PARAM_AWBMODE_HORIZON 151 | 152 | MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE 153 | MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT 154 | MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT 155 | MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX 156 | 157 | MMAL_PARAM_IMAGEFX_NONE 158 | MMAL_PARAM_IMAGEFX_NEGATIVE 159 | MMAL_PARAM_IMAGEFX_SOLARIZE 160 | MMAL_PARAM_IMAGEFX_SKETCH 161 | MMAL_PARAM_IMAGEFX_DENOISE 162 | MMAL_PARAM_IMAGEFX_EMBOSS 163 | MMAL_PARAM_IMAGEFX_OILPAINT 164 | MMAL_PARAM_IMAGEFX_HATCH 165 | MMAL_PARAM_IMAGEFX_GPEN 166 | MMAL_PARAM_IMAGEFX_PASTEL 167 | MMAL_PARAM_IMAGEFX_WATERCOLOUR 168 | MMAL_PARAM_IMAGEFX_BLUR 169 | MMAL_PARAM_IMAGEFX_SATURATION 170 | MMAL_PARAM_IMAGEFX_COLOURSWAP 171 | MMAL_PARAM_IMAGEFX_WASHEDOUT 172 | MMAL_PARAM_IMAGEFX_POSTERISE 173 | MMAL_PARAM_IMAGEFX_COLOURPOINT 174 | MMAL_PARAM_IMAGEFX_COLOURBALANCE 175 | MMAL_PARAM_IMAGEFX_CARTOON 176 | 177 | MMAL_VIDEO_PROFILE_H264_BASELINE 178 | MMAL_VIDEO_PROFILE_H264_MAIN 179 | MMAL_VIDEO_PROFILE_H264_HIGH 180 | 181 | -------------------------------------------------------------------------------- /src/RaspiCamControl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Broadcom Europe Ltd 3 | Copyright (c) 2013, James Hughes 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the copyright holder nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef RASPICAMCONTROL_H_ 30 | #define RASPICAMCONTROL_H_ 31 | 32 | /* Various parameters 33 | * 34 | * Exposure Mode 35 | * MMAL_PARAM_EXPOSUREMODE_OFF, 36 | MMAL_PARAM_EXPOSUREMODE_AUTO, 37 | MMAL_PARAM_EXPOSUREMODE_NIGHT, 38 | MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, 39 | MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, 40 | MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, 41 | MMAL_PARAM_EXPOSUREMODE_SPORTS, 42 | MMAL_PARAM_EXPOSUREMODE_SNOW, 43 | MMAL_PARAM_EXPOSUREMODE_BEACH, 44 | MMAL_PARAM_EXPOSUREMODE_VERYLONG, 45 | MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, 46 | MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, 47 | MMAL_PARAM_EXPOSUREMODE_FIREWORKS, 48 | * 49 | * AWB Mode 50 | * MMAL_PARAM_AWBMODE_OFF, 51 | MMAL_PARAM_AWBMODE_AUTO, 52 | MMAL_PARAM_AWBMODE_SUNLIGHT, 53 | MMAL_PARAM_AWBMODE_CLOUDY, 54 | MMAL_PARAM_AWBMODE_SHADE, 55 | MMAL_PARAM_AWBMODE_TUNGSTEN, 56 | MMAL_PARAM_AWBMODE_FLUORESCENT, 57 | MMAL_PARAM_AWBMODE_INCANDESCENT, 58 | MMAL_PARAM_AWBMODE_FLASH, 59 | MMAL_PARAM_AWBMODE_HORIZON, 60 | * 61 | * Image FX 62 | MMAL_PARAM_IMAGEFX_NONE, 63 | MMAL_PARAM_IMAGEFX_NEGATIVE, 64 | MMAL_PARAM_IMAGEFX_SOLARIZE, 65 | MMAL_PARAM_IMAGEFX_POSTERIZE, 66 | MMAL_PARAM_IMAGEFX_WHITEBOARD, 67 | MMAL_PARAM_IMAGEFX_BLACKBOARD, 68 | MMAL_PARAM_IMAGEFX_SKETCH, 69 | MMAL_PARAM_IMAGEFX_DENOISE, 70 | MMAL_PARAM_IMAGEFX_EMBOSS, 71 | MMAL_PARAM_IMAGEFX_OILPAINT, 72 | MMAL_PARAM_IMAGEFX_HATCH, 73 | MMAL_PARAM_IMAGEFX_GPEN, 74 | MMAL_PARAM_IMAGEFX_PASTEL, 75 | MMAL_PARAM_IMAGEFX_WATERCOLOUR, 76 | MMAL_PARAM_IMAGEFX_FILM, 77 | MMAL_PARAM_IMAGEFX_BLUR, 78 | MMAL_PARAM_IMAGEFX_SATURATION, 79 | MMAL_PARAM_IMAGEFX_COLOURSWAP, 80 | MMAL_PARAM_IMAGEFX_WASHEDOUT, 81 | MMAL_PARAM_IMAGEFX_POSTERISE, 82 | MMAL_PARAM_IMAGEFX_COLOURPOINT, 83 | MMAL_PARAM_IMAGEFX_COLOURBALANCE, 84 | MMAL_PARAM_IMAGEFX_CARTOON, 85 | 86 | */ 87 | 88 | 89 | 90 | // There isn't actually a MMAL structure for the following, so make one 91 | typedef struct 92 | { 93 | int enable; /// Turn colourFX on or off 94 | int u,v; /// U and V to use 95 | } MMAL_PARAM_COLOURFX_T; 96 | 97 | typedef struct 98 | { 99 | int enable; 100 | int width,height; 101 | int quality; 102 | } MMAL_PARAM_THUMBNAIL_CONFIG_T; 103 | 104 | typedef struct 105 | { 106 | double x; 107 | double y; 108 | double w; 109 | double h; 110 | } PARAM_FLOAT_RECT_T; 111 | 112 | /// struct contain camera settings 113 | typedef struct 114 | { 115 | int sharpness; /// -100 to 100 116 | int contrast; /// -100 to 100 117 | int brightness; /// 0 to 100 118 | int saturation; /// -100 to 100 119 | int ISO; /// TODO : what range? 120 | int videoStabilisation; /// 0 or 1 (false or true) 121 | int exposureCompensation; /// -10 to +10 ? 122 | MMAL_PARAM_EXPOSUREMODE_T exposureMode; 123 | MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode; 124 | MMAL_PARAM_AWBMODE_T awbMode; 125 | MMAL_PARAM_IMAGEFX_T imageEffect; 126 | MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters; 127 | MMAL_PARAM_COLOURFX_T colourEffects; 128 | int rotation; /// 0-359 129 | int hflip; /// 0 or 1 130 | int vflip; /// 0 or 1 131 | PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect 132 | int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms 133 | } RASPICAM_CAMERA_PARAMETERS; 134 | 135 | 136 | void raspicamcontrol_check_configuration(int min_gpu_mem); 137 | 138 | int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2); 139 | void raspicamcontrol_display_help(); 140 | int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera); 141 | 142 | int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params); 143 | int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params); 144 | void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params); 145 | 146 | void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params); 147 | 148 | void raspicamcontrol_check_configuration(int min_gpu_mem); 149 | 150 | // Individual setting functions 151 | int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation); 152 | int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness); 153 | int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast); 154 | int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness); 155 | int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO); 156 | int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode); 157 | int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation); 158 | int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); 159 | int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); 160 | int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); 161 | int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); 162 | int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); 163 | int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); 164 | int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); 165 | int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); 166 | int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); 167 | 168 | //Individual getting functions 169 | int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); 170 | int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera); 171 | int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera); 172 | int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera); 173 | int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera); 174 | MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera); 175 | int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera); 176 | int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera); 177 | MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera); 178 | MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera); 179 | MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera); 180 | MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera); 181 | MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera); 182 | 183 | 184 | #endif /* RASPICAMCONTROL_H_ */ 185 | -------------------------------------------------------------------------------- /src/picammodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "structmember.h" 3 | #include 4 | #include 5 | #include "picam.h" 6 | #include "interface/mmal/mmal.h" 7 | 8 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 9 | #define DICT_SET(dict,val) PyModule_AddIntConstant(dict, #val, val); 10 | 11 | 12 | typedef struct { 13 | PyObject_HEAD 14 | /* Type-specific fields go here. */ 15 | int exposure; 16 | int meterMode; 17 | int imageFX; 18 | int awbMode; 19 | int ISO; 20 | int sharpness; /// -100 to 100 21 | int contrast; /// -100 to 100 22 | int brightness; /// 0 to 100 23 | int saturation; /// -100 to 100 24 | int videoStabilisation; /// 0 or 1 (false or true) 25 | int exposureCompensation; /// -10 to +10 ? 26 | int rotation; /// 0-359 27 | int hflip; /// 0 or 1 28 | int vflip; /// 0 or 1 29 | int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms 30 | int videoProfile; // MMAL_VIDEO_PROFILE_H264_HIGH 31 | int videoBitrate; 32 | int videoFramerate; //30 33 | int quantisationParameter; // Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate 34 | int inlineHeaders; // Insert inline headers to stream (SPS, PPS) 35 | PyObject *roi; 36 | } _PicamConfig; 37 | 38 | static void PicamConfig_dealloc(_PicamConfig* self) { 39 | self->ob_type->tp_free((PyObject*)self); 40 | } 41 | 42 | static int Picam_init(_PicamConfig *self, PyObject *args, PyObject *kwds) { 43 | return 0; 44 | } 45 | 46 | static PyObject *Picam_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { 47 | _PicamConfig *self; 48 | 49 | self = (_PicamConfig *)type->tp_alloc(type, 0); 50 | if (self != NULL) { 51 | self->exposure = MMAL_PARAM_EXPOSUREMODE_AUTO; 52 | self->meterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; 53 | self->imageFX = MMAL_PARAM_IMAGEFX_NONE; 54 | self->awbMode = MMAL_PARAM_AWBMODE_AUTO; 55 | self->ISO = 0; 56 | self->sharpness = 0; 57 | self->contrast = 0; 58 | self->brightness= 50; 59 | self->saturation = 0; 60 | self->videoStabilisation = 0; /// 0 or 1 (false or true) 61 | self->exposureCompensation = 0 ; 62 | self->rotation = 0; 63 | self->hflip = 0; 64 | self->vflip = 0; 65 | self->shutter_speed = 0; 66 | self->videoProfile = MMAL_VIDEO_PROFILE_H264_HIGH; 67 | self->videoBitrate = 17000000; 68 | self->videoFramerate = 30; 69 | self->quantisationParameter = 0; 70 | self->inlineHeaders = 0; 71 | self->roi = PyList_New(4); 72 | PyList_SetItem(self->roi, 0,PyFloat_FromDouble(0)); 73 | PyList_SetItem(self->roi, 1, PyFloat_FromDouble(0)); 74 | PyList_SetItem(self->roi, 2, PyFloat_FromDouble(1.0)); 75 | PyList_SetItem(self->roi, 3, PyFloat_FromDouble(1.0)); 76 | 77 | 78 | } 79 | return (PyObject *)self; 80 | } 81 | static PyMemberDef PicamConfig_members[] = { 82 | {"exposure", T_INT, offsetof(_PicamConfig, exposure), 0, "exposure"}, 83 | {"meterMode",T_INT, offsetof(_PicamConfig, meterMode), 0, "meterMode"}, 84 | {"imageFX", T_INT, offsetof(_PicamConfig, imageFX), 0, "imageFX"}, 85 | {"awbMode", T_INT, offsetof(_PicamConfig, awbMode), 0, "awbMode"}, 86 | {"ISO", T_INT, offsetof(_PicamConfig, ISO), 0, "ISO"}, 87 | {"sharpness", T_INT, offsetof(_PicamConfig, sharpness), 0, "sharpness"}, 88 | {"contrast", T_INT, offsetof(_PicamConfig, contrast), 0, "contrast"}, 89 | {"brightness", T_INT, offsetof(_PicamConfig, brightness), 0, "brightness"}, 90 | {"saturation", T_INT, offsetof(_PicamConfig, saturation), 0, "saturation"}, 91 | {"videoStabilisation", T_INT, offsetof(_PicamConfig, videoStabilisation), 0, "videoStabilisation"}, 92 | {"exposureCompensation", T_INT, offsetof(_PicamConfig, exposureCompensation), 0, "exposureCompensation"}, 93 | {"rotation", T_INT, offsetof(_PicamConfig, rotation), 0, "rotation"}, 94 | {"hflip", T_INT, offsetof(_PicamConfig, hflip), 0, "hflip"}, 95 | {"vflip", T_INT, offsetof(_PicamConfig, vflip), 0, "vflip"}, 96 | {"shutterSpeed", T_INT, offsetof(_PicamConfig, shutter_speed), 0, "0 = auto, otherwise the shutter speed in ms"}, 97 | {"videoProfile", T_INT, offsetof(_PicamConfig, videoProfile), 0, "videoProfile"}, 98 | {"videoBitrate", T_INT, offsetof(_PicamConfig, videoBitrate), 0, "videoBitrate"}, 99 | {"videoFramerate", T_INT, offsetof(_PicamConfig, videoFramerate), 0, "videoFramerate"}, 100 | {"quantisationParameter", T_INT, offsetof(_PicamConfig, quantisationParameter), 0, "quantisationParameter"}, 101 | {"inlineHeaders", T_INT, offsetof(_PicamConfig, inlineHeaders), 0, "inlineHeaders"}, 102 | {"roi", T_OBJECT, offsetof(_PicamConfig, roi), 0, "roi"}, 103 | {NULL} /* Sentinel */ 104 | }; 105 | static PyTypeObject PicamConfigType = { 106 | PyObject_HEAD_INIT(NULL) 107 | 0, /*ob_size*/ 108 | "picam.Config", /*tp_name*/ 109 | sizeof(_PicamConfig), /*tp_basicsize*/ 110 | 0, /*tp_itemsize*/ 111 | (destructor)PicamConfig_dealloc, /*tp_dealloc*/ 112 | 0, /*tp_print*/ 113 | 0, /*tp_getattr*/ 114 | 0, /*tp_setattr*/ 115 | 0, /*tp_compare*/ 116 | 0, /*tp_repr*/ 117 | 0, /*tp_as_number*/ 118 | 0, /*tp_as_sequence*/ 119 | 0, /*tp_as_mapping*/ 120 | 0, /*tp_hash */ 121 | 0, /*tp_call*/ 122 | 0, /*tp_str*/ 123 | 0, /*tp_getattro*/ 124 | 0, /*tp_setattro*/ 125 | 0, /*tp_as_buffer*/ 126 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 127 | "PicamConfig objects", /* tp_doc */ 128 | 0, /* tp_traverse */ 129 | 0, /* tp_clear */ 130 | 0, /* tp_richcompare */ 131 | 0, /* tp_weaklistoffset */ 132 | 0, /* tp_iter */ 133 | 0, /* tp_iternext */ 134 | 0, /* tp_methods */ 135 | PicamConfig_members, /* tp_members */ 136 | 0, /* tp_getset */ 137 | 0, /* tp_base */ 138 | 0, /* tp_dict */ 139 | 0, /* tp_descr_get */ 140 | 0, /* tp_descr_set */ 141 | 0, /* tp_dictoffset */ 142 | (initproc)Picam_init, /* tp_init */ 143 | 0, /* tp_alloc */ 144 | Picam_new, /* tp_new */ 145 | }; 146 | static _PicamConfig* picam_newconfig() 147 | { 148 | _PicamConfig* o = PyObject_New(_PicamConfig, &PicamConfigType); 149 | 150 | if (o != 0) { 151 | o->exposure = MMAL_PARAM_EXPOSUREMODE_AUTO; 152 | o->meterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; 153 | o->imageFX = MMAL_PARAM_IMAGEFX_NONE; 154 | o->awbMode = MMAL_PARAM_AWBMODE_AUTO; 155 | o->ISO = 0; 156 | o->sharpness = 0; 157 | o->contrast = 0; 158 | o->brightness= 50; 159 | o->saturation = 0; 160 | o->videoStabilisation = 0; /// 0 or 1 (false or true) 161 | o->exposureCompensation = 0 ; 162 | o->rotation = 0; 163 | o->hflip = 0; 164 | o->vflip = 0; 165 | o->shutter_speed = 0; 166 | o->videoProfile = MMAL_VIDEO_PROFILE_H264_HIGH; 167 | o->videoBitrate = 17000000; 168 | o->videoFramerate = 30; 169 | o->quantisationParameter = 0; 170 | o->inlineHeaders = 0; 171 | o->roi = PyList_New(4); 172 | PyList_SetItem(o->roi, 0,PyFloat_FromDouble(0)); 173 | PyList_SetItem(o->roi, 1, PyFloat_FromDouble(0)); 174 | PyList_SetItem(o->roi, 2, PyFloat_FromDouble(1.0)); 175 | PyList_SetItem(o->roi, 3, PyFloat_FromDouble(1.0)); 176 | } 177 | return o; 178 | } 179 | 180 | static _PicamConfig *picamConfig = NULL; 181 | 182 | static PyObject * picam_listtest(PyObject *self, PyObject *args) { 183 | PyObject *V = PyList_New(3); 184 | PyList_SetItem(V, 0,PyInt_FromLong(1L)); 185 | PyList_SetItem(V, 1, PyInt_FromLong(2L)); 186 | PyList_SetItem(V, 2, PyInt_FromLong(3L)); 187 | return Py_BuildValue("N", V); 188 | } 189 | 190 | static void fillParms(PicamParams *parms) { 191 | parms->exposure = picamConfig->exposure; 192 | parms->meterMode = picamConfig->meterMode; 193 | parms->imageFX = picamConfig->imageFX; 194 | parms->awbMode = picamConfig->awbMode; 195 | parms->ISO = picamConfig->ISO; 196 | parms->sharpness = picamConfig->sharpness; /// -100 to 100 197 | parms->contrast = picamConfig->contrast; /// -100 to 100 198 | parms->brightness = picamConfig->brightness; /// 0 to 100 199 | parms->saturation = picamConfig->saturation; /// -100 to 100 200 | parms->videoStabilisation = picamConfig->videoStabilisation; /// 0 or 1 (false or true) 201 | parms->exposureCompensation = picamConfig->exposureCompensation; /// -10 to +10 ? 202 | parms->rotation = picamConfig->rotation; 203 | parms->hflip = picamConfig->hflip; 204 | parms->vflip = picamConfig->vflip; 205 | parms->shutter_speed = picamConfig->shutter_speed; 206 | parms->videoProfile = picamConfig->videoProfile; 207 | parms->videoBitrate = picamConfig->videoBitrate; 208 | parms->videoFramerate = picamConfig->videoFramerate; 209 | parms->quantisationParameter = picamConfig->quantisationParameter; 210 | parms->inlineHeaders = picamConfig->inlineHeaders; 211 | parms->roi[0] = PyFloat_AsDouble(PyList_GetItem(picamConfig->roi, 0)); 212 | parms->roi[1] = PyFloat_AsDouble(PyList_GetItem(picamConfig->roi, 1));; 213 | parms->roi[2] = PyFloat_AsDouble(PyList_GetItem(picamConfig->roi, 2));; 214 | parms->roi[3] = PyFloat_AsDouble(PyList_GetItem(picamConfig->roi, 3));; 215 | if (parms->roi[0] > 1.0) { 216 | parms->roi[0] = 0.0; 217 | } 218 | if (parms->roi[1] > 1.0) { 219 | parms->roi[1] = 0.0; 220 | } 221 | if (parms->roi[2] > 1.0) { 222 | parms->roi[2] = 1.0; 223 | } 224 | if (parms->roi[3] > 1.0) { 225 | parms->roi[3] = 1.0; 226 | } 227 | if (parms->roi[0] + parms->roi[2] > 1.0) 228 | parms->roi[2] = 1 - parms->roi[0]; 229 | 230 | if (parms->roi[1] + parms->roi[3] > 1.0) 231 | parms->roi[3] = 1 - parms->roi[1]; 232 | } 233 | 234 | static PyObject * picam_takephoto(PyObject *self, PyObject *args) { 235 | PyObject *result = Py_None; 236 | long bufsize = 0l; 237 | //printf("%d %d %d %d %d\n",picamConfig->exposure, picamConfig->meterMode, picamConfig->imageFX, picamConfig->awbMode, picamConfig->ISO); 238 | PicamParams parms; 239 | fillParms(&parms); 240 | char *buffer = (char *)takePhoto(&parms, &bufsize); 241 | result = Py_BuildValue("s#", buffer, bufsize); 242 | free(buffer); 243 | return result; 244 | } 245 | 246 | static PyObject *picam_difference(PyObject *self, PyObject *args) { 247 | PyObject *list1 = Py_None; 248 | PyObject *list2 = Py_None; 249 | int threshold; 250 | if (!PyArg_ParseTuple(args,"OOi",&list1,&list2,&threshold)) { 251 | return NULL; 252 | } 253 | Py_ssize_t size1 = PyList_Size(list1); 254 | Py_ssize_t size2 = PyList_Size(list2); 255 | long quantity = 0l; 256 | if (size1 == size2) { 257 | PyObject *listResult = PyList_New(size1); 258 | int loop = 0; 259 | long rgb1 = 0; 260 | long rgb2 = 0; 261 | int r1,g1,b1; 262 | int r2,g2,b2; 263 | long val = 0; 264 | for (loop=0;loop>16) & 0x0ff; 270 | g1 = (rgb1>>8) & 0x0ff; 271 | b1 = (rgb1) & 0x0ff; 272 | 273 | r2 = (rgb2>>16) & 0x0ff; 274 | g2 = (rgb2>>8) & 0x0ff; 275 | b2 = (rgb2) & 0x0ff; 276 | 277 | int i1 = abs(r1-r2); 278 | int i2 = abs(g1-g2); 279 | int i3 = abs(b1-b2); 280 | 281 | if (threshold != 0) { 282 | if ((i1 > threshold) || (i2 > threshold) || (i3 > threshold) ) { 283 | i1 = 255; 284 | i2 = 255; 285 | i3 = 255; 286 | quantity++; 287 | } 288 | } 289 | 290 | int valR = CLAMP(i1,0,255); 291 | int valG = CLAMP(i2,0,255); 292 | int valB = CLAMP(i3,0,255); 293 | val = (valR << 16) + (valG << 8) + valB; 294 | PyList_SetItem(listResult, loop, PyInt_FromLong(val)); 295 | 296 | } 297 | 298 | PyObject *result = Py_BuildValue("NN", listResult, PyInt_FromLong(quantity)); 299 | return result; 300 | 301 | } 302 | return NULL; 303 | } 304 | 305 | 306 | static PyObject *picam_takergbphotowithdetails(PyObject *self, PyObject *args) { 307 | int width; 308 | int height; 309 | if (!PyArg_ParseTuple(args,"ii",&width,&height)) { 310 | return NULL; 311 | } 312 | long bufsize = 0l; 313 | PicamParams parms; 314 | fillParms(&parms); 315 | char *buffer = (char *)takeRGBPhotoWithDetails(width, height,&parms, &bufsize); 316 | 317 | long bufMinusHeader = bufsize-54; 318 | long listSize = bufMinusHeader / 3; 319 | PyObject *listResult = PyList_New(listSize); 320 | int i; 321 | int ii = 0; 322 | for (i=54;i 30 | #include 31 | 32 | #include "interface/vcos/vcos.h" 33 | 34 | #include "interface/vmcs_host/vc_vchi_gencmd.h" 35 | #include "interface/mmal/mmal.h" 36 | #include "interface/mmal/mmal_logging.h" 37 | #include "interface/mmal/util/mmal_util.h" 38 | #include "interface/mmal/util/mmal_util_params.h" 39 | #include "interface/mmal/util/mmal_default_components.h" 40 | #include "RaspiCamControl.h" 41 | 42 | 43 | /// Cross reference structure, mode string against mode id 44 | typedef struct xref_t 45 | { 46 | char *mode; 47 | int mmal_mode; 48 | } XREF_T; 49 | 50 | /// Structure to cross reference exposure strings against the MMAL parameter equivalent 51 | static XREF_T exposure_map[] = 52 | { 53 | {"off", MMAL_PARAM_EXPOSUREMODE_OFF}, 54 | {"auto", MMAL_PARAM_EXPOSUREMODE_AUTO}, 55 | {"night", MMAL_PARAM_EXPOSUREMODE_NIGHT}, 56 | {"nightpreview", MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW}, 57 | {"backlight", MMAL_PARAM_EXPOSUREMODE_BACKLIGHT}, 58 | {"spotlight", MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT}, 59 | {"sports", MMAL_PARAM_EXPOSUREMODE_SPORTS}, 60 | {"snow", MMAL_PARAM_EXPOSUREMODE_SNOW}, 61 | {"beach", MMAL_PARAM_EXPOSUREMODE_BEACH}, 62 | {"verylong", MMAL_PARAM_EXPOSUREMODE_VERYLONG}, 63 | {"fixedfps", MMAL_PARAM_EXPOSUREMODE_FIXEDFPS}, 64 | {"antishake", MMAL_PARAM_EXPOSUREMODE_ANTISHAKE}, 65 | {"fireworks", MMAL_PARAM_EXPOSUREMODE_FIREWORKS} 66 | }; 67 | 68 | static const int exposure_map_size = sizeof(exposure_map) / sizeof(exposure_map[0]); 69 | 70 | /// Structure to cross reference awb strings against the MMAL parameter equivalent 71 | static XREF_T awb_map[] = 72 | { 73 | {"off", MMAL_PARAM_AWBMODE_OFF}, 74 | {"auto", MMAL_PARAM_AWBMODE_AUTO}, 75 | {"sun", MMAL_PARAM_AWBMODE_SUNLIGHT}, 76 | {"cloud", MMAL_PARAM_AWBMODE_CLOUDY}, 77 | {"shade", MMAL_PARAM_AWBMODE_SHADE}, 78 | {"tungsten", MMAL_PARAM_AWBMODE_TUNGSTEN}, 79 | {"fluorescent", MMAL_PARAM_AWBMODE_FLUORESCENT}, 80 | {"incandescent", MMAL_PARAM_AWBMODE_INCANDESCENT}, 81 | {"flash", MMAL_PARAM_AWBMODE_FLASH}, 82 | {"horizon", MMAL_PARAM_AWBMODE_HORIZON} 83 | }; 84 | 85 | static const int awb_map_size = sizeof(awb_map) / sizeof(awb_map[0]); 86 | 87 | /// Structure to cross reference image effect against the MMAL parameter equivalent 88 | static XREF_T imagefx_map[] = 89 | { 90 | {"none", MMAL_PARAM_IMAGEFX_NONE}, 91 | {"negative", MMAL_PARAM_IMAGEFX_NEGATIVE}, 92 | {"solarise", MMAL_PARAM_IMAGEFX_SOLARIZE}, 93 | {"sketch", MMAL_PARAM_IMAGEFX_SKETCH}, 94 | {"denoise", MMAL_PARAM_IMAGEFX_DENOISE}, 95 | {"emboss", MMAL_PARAM_IMAGEFX_EMBOSS}, 96 | {"oilpaint", MMAL_PARAM_IMAGEFX_OILPAINT}, 97 | {"hatch", MMAL_PARAM_IMAGEFX_HATCH}, 98 | {"gpen", MMAL_PARAM_IMAGEFX_GPEN}, 99 | {"pastel", MMAL_PARAM_IMAGEFX_PASTEL}, 100 | {"watercolour", MMAL_PARAM_IMAGEFX_WATERCOLOUR}, 101 | {"film", MMAL_PARAM_IMAGEFX_FILM}, 102 | {"blur", MMAL_PARAM_IMAGEFX_BLUR}, 103 | {"saturation", MMAL_PARAM_IMAGEFX_SATURATION}, 104 | {"colourswap", MMAL_PARAM_IMAGEFX_COLOURSWAP}, 105 | {"washedout", MMAL_PARAM_IMAGEFX_WASHEDOUT}, 106 | {"posterise", MMAL_PARAM_IMAGEFX_POSTERISE}, 107 | {"colourpoint", MMAL_PARAM_IMAGEFX_COLOURPOINT}, 108 | {"colourbalance", MMAL_PARAM_IMAGEFX_COLOURBALANCE}, 109 | {"cartoon", MMAL_PARAM_IMAGEFX_CARTOON} 110 | }; 111 | 112 | static const int imagefx_map_size = sizeof(imagefx_map) / sizeof(imagefx_map[0]); 113 | 114 | static XREF_T metering_mode_map[] = 115 | { 116 | {"average", MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE}, 117 | {"spot", MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT}, 118 | {"backlit", MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT}, 119 | {"matrix", MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX} 120 | }; 121 | 122 | static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]); 123 | 124 | 125 | #define CommandSharpness 0 126 | #define CommandContrast 1 127 | #define CommandBrightness 2 128 | #define CommandSaturation 3 129 | #define CommandISO 4 130 | #define CommandVideoStab 5 131 | #define CommandEVComp 6 132 | #define CommandExposure 7 133 | #define CommandAWB 8 134 | #define CommandImageFX 9 135 | #define CommandColourFX 10 136 | #define CommandMeterMode 11 137 | #define CommandRotation 12 138 | #define CommandHFlip 13 139 | #define CommandVFlip 14 140 | #define CommandROI 15 141 | #define CommandShutterSpeed 16 142 | 143 | #define parameter_reset -99999 144 | 145 | 146 | /** 147 | * Function to take a string, a mapping, and return the int equivalent 148 | * @param str Incoming string to match 149 | * @param map Mapping data 150 | * @param num_refs The number of items in the mapping data 151 | * @return The integer match for the string, or -1 if no match 152 | */ 153 | static int map_xref(const char *str, const XREF_T *map, int num_refs) 154 | { 155 | int i; 156 | 157 | for (i=0;iexposureMode, exposure_map, exposure_map_size); 263 | const char *awb_mode = unmap_xref(params->awbMode, awb_map, awb_map_size); 264 | const char *image_effect = unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); 265 | const char *metering_mode = unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); 266 | 267 | fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); 268 | fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); 269 | fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); 270 | fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); 271 | fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); 272 | } 273 | 274 | /** 275 | * Convert a MMAL status return value to a simple boolean of success 276 | * ALso displays a fault if code is not success 277 | * 278 | * @param status The error code to convert 279 | * @return 0 if status is sucess, 1 otherwise 280 | */ 281 | int mmal_status_to_int(MMAL_STATUS_T status) 282 | { 283 | if (status == MMAL_SUCCESS) 284 | return 0; 285 | else 286 | { 287 | switch (status) 288 | { 289 | case MMAL_ENOMEM : vcos_log_error("Out of memory"); break; 290 | case MMAL_ENOSPC : vcos_log_error("Out of resources (other than memory)"); break; 291 | case MMAL_EINVAL: vcos_log_error("Argument is invalid"); break; 292 | case MMAL_ENOSYS : vcos_log_error("Function not implemented"); break; 293 | case MMAL_ENOENT : vcos_log_error("No such file or directory"); break; 294 | case MMAL_ENXIO : vcos_log_error("No such device or address"); break; 295 | case MMAL_EIO : vcos_log_error("I/O error"); break; 296 | case MMAL_ESPIPE : vcos_log_error("Illegal seek"); break; 297 | case MMAL_ECORRUPT : vcos_log_error("Data is corrupt \attention FIXME: not POSIX"); break; 298 | case MMAL_ENOTREADY :vcos_log_error("Component is not ready \attention FIXME: not POSIX"); break; 299 | case MMAL_ECONFIG : vcos_log_error("Component is not configured \attention FIXME: not POSIX"); break; 300 | case MMAL_EISCONN : vcos_log_error("Port is already connected "); break; 301 | case MMAL_ENOTCONN : vcos_log_error("Port is disconnected"); break; 302 | case MMAL_EAGAIN : vcos_log_error("Resource temporarily unavailable. Try again later"); break; 303 | case MMAL_EFAULT : vcos_log_error("Bad address"); break; 304 | default : vcos_log_error("Unknown status error"); break; 305 | } 306 | 307 | return 1; 308 | } 309 | } 310 | 311 | /** 312 | * Give the supplied parameter block a set of default values 313 | * @params Pointer to parameter block 314 | */ 315 | void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params) 316 | { 317 | vcos_assert(params); 318 | 319 | params->sharpness = 0; 320 | params->contrast = 0; 321 | params->brightness = 50; 322 | params->saturation = 0; 323 | params->ISO = 0; // 0 = auto 324 | params->videoStabilisation = 0; 325 | params->exposureCompensation = 0; 326 | params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; 327 | params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; 328 | params->awbMode = MMAL_PARAM_AWBMODE_AUTO; 329 | params->imageEffect = MMAL_PARAM_IMAGEFX_NONE; 330 | params->colourEffects.enable = 0; 331 | params->colourEffects.u = 128; 332 | params->colourEffects.v = 128; 333 | params->rotation = 0; 334 | params->hflip = params->vflip = 0; 335 | params->roi.x = params->roi.y = 0.0; 336 | params->roi.w = params->roi.h = 1.0; 337 | params->shutter_speed = 0; // 0 = auto 338 | } 339 | 340 | /** 341 | * Get all the current camera parameters from specified camera component 342 | * @param camera Pointer to camera component 343 | * @param params Pointer to parameter block to accept settings 344 | * @return 0 if successful, non-zero if unsuccessful 345 | */ 346 | int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params) 347 | { 348 | vcos_assert(camera); 349 | vcos_assert(params); 350 | 351 | if (!camera || !params) 352 | return 1; 353 | 354 | /* TODO : Write these get functions 355 | params->sharpness = raspicamcontrol_get_sharpness(camera); 356 | params->contrast = raspicamcontrol_get_contrast(camera); 357 | params->brightness = raspicamcontrol_get_brightness(camera); 358 | params->saturation = raspicamcontrol_get_saturation(camera); 359 | params->ISO = raspicamcontrol_get_ISO(camera); 360 | params->videoStabilisation = raspicamcontrol_get_video_stabilisation(camera); 361 | params->exposureCompensation = raspicamcontrol_get_exposure_compensation(camera); 362 | params->exposureMode = raspicamcontrol_get_exposure_mode(camera); 363 | params->awbMode = raspicamcontrol_get_awb_mode(camera); 364 | params->imageEffect = raspicamcontrol_get_image_effect(camera); 365 | params->colourEffects = raspicamcontrol_get_colour_effect(camera); 366 | params->thumbnailConfig = raspicamcontrol_get_thumbnail_config(camera); 367 | */ 368 | return 0; 369 | } 370 | 371 | /** 372 | * Set the specified camera to all the specified settings 373 | * @param camera Pointer to camera component 374 | * @param params Pointer to parameter block containing parameters 375 | * @return 0 if successful, none-zero if unsuccessful. 376 | */ 377 | int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params) 378 | { 379 | int result; 380 | 381 | result = raspicamcontrol_set_saturation(camera, params->saturation); 382 | result += raspicamcontrol_set_sharpness(camera, params->sharpness); 383 | result += raspicamcontrol_set_contrast(camera, params->contrast); 384 | result += raspicamcontrol_set_brightness(camera, params->brightness); 385 | result += raspicamcontrol_set_ISO(camera, params->ISO); 386 | result += raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation); 387 | result += raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation); 388 | result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); 389 | result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); 390 | result += raspicamcontrol_set_awb_mode(camera, params->awbMode); 391 | result += raspicamcontrol_set_imageFX(camera, params->imageEffect); 392 | result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); 393 | //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason 394 | result += raspicamcontrol_set_rotation(camera, params->rotation); 395 | result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); 396 | result += raspicamcontrol_set_ROI(camera, params->roi); 397 | result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); 398 | return result; 399 | } 400 | 401 | /** 402 | * Adjust the saturation level for images 403 | * @param camera Pointer to camera component 404 | * @param saturation Value to adjust, -100 to 100 405 | * @return 0 if successful, non-zero if any parameters out of range 406 | */ 407 | int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation) 408 | { 409 | int ret = 0; 410 | 411 | if (!camera) 412 | return 1; 413 | 414 | if (saturation >= -100 && saturation <= 100) 415 | { 416 | MMAL_RATIONAL_T value = {saturation, 100}; 417 | ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SATURATION, value)); 418 | } 419 | else 420 | { 421 | vcos_log_error("Invalid saturation value"); 422 | ret = 1; 423 | } 424 | 425 | return ret; 426 | } 427 | 428 | /** 429 | * Set the sharpness of the image 430 | * @param camera Pointer to camera component 431 | * @param sharpness Sharpness adjustment -100 to 100 432 | */ 433 | int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness) 434 | { 435 | int ret = 0; 436 | 437 | if (!camera) 438 | return 1; 439 | 440 | if (sharpness >= -100 && sharpness <= 100) 441 | { 442 | MMAL_RATIONAL_T value = {sharpness, 100}; 443 | ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SHARPNESS, value)); 444 | } 445 | else 446 | { 447 | vcos_log_error("Invalid sharpness value"); 448 | ret = 1; 449 | } 450 | 451 | return ret; 452 | } 453 | 454 | /** 455 | * Set the contrast adjustment for the image 456 | * @param camera Pointer to camera component 457 | * @param contrast Contrast adjustment -100 to 100 458 | * @return 459 | */ 460 | int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast) 461 | { 462 | int ret = 0; 463 | 464 | if (!camera) 465 | return 1; 466 | 467 | if (contrast >= -100 && contrast <= 100) 468 | { 469 | MMAL_RATIONAL_T value = {contrast, 100}; 470 | ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_CONTRAST, value)); 471 | } 472 | else 473 | { 474 | vcos_log_error("Invalid contrast value"); 475 | ret = 1; 476 | } 477 | 478 | return ret; 479 | } 480 | 481 | /** 482 | * Adjust the brightness level for images 483 | * @param camera Pointer to camera component 484 | * @param brightness Value to adjust, 0 to 100 485 | * @return 0 if successful, non-zero if any parameters out of range 486 | */ 487 | int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness) 488 | { 489 | int ret = 0; 490 | 491 | if (!camera) 492 | return 1; 493 | 494 | if (brightness >= 0 && brightness <= 100) 495 | { 496 | MMAL_RATIONAL_T value = {brightness, 100}; 497 | ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_BRIGHTNESS, value)); 498 | } 499 | else 500 | { 501 | vcos_log_error("Invalid brightness value"); 502 | ret = 1; 503 | } 504 | 505 | return ret; 506 | } 507 | 508 | /** 509 | * Adjust the ISO used for images 510 | * @param camera Pointer to camera component 511 | * @param ISO Value to set TODO : 512 | * @return 0 if successful, non-zero if any parameters out of range 513 | */ 514 | int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO) 515 | { 516 | if (!camera) 517 | return 1; 518 | 519 | return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_ISO, ISO)); 520 | } 521 | 522 | /** 523 | * Adjust the metering mode for images 524 | * @param camera Pointer to camera component 525 | * @param saturation Value from following 526 | * - MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, 527 | * - MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, 528 | * - MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, 529 | * - MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX 530 | * @return 0 if successful, non-zero if any parameters out of range 531 | */ 532 | int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T m_mode ) 533 | { 534 | MMAL_PARAMETER_EXPOSUREMETERINGMODE_T meter_mode = {{MMAL_PARAMETER_EXP_METERING_MODE,sizeof(meter_mode)}, 535 | m_mode}; 536 | if (!camera) 537 | return 1; 538 | 539 | return mmal_status_to_int(mmal_port_parameter_set(camera->control, &meter_mode.hdr)); 540 | } 541 | 542 | 543 | /** 544 | * Set the video stabilisation flag. Only used in video mode 545 | * @param camera Pointer to camera component 546 | * @param saturation Flag 0 off 1 on 547 | * @return 0 if successful, non-zero if any parameters out of range 548 | */ 549 | int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation) 550 | { 551 | if (!camera) 552 | return 1; 553 | 554 | return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_VIDEO_STABILISATION, vstabilisation)); 555 | } 556 | 557 | /** 558 | * Adjust the exposure compensation for images (EV) 559 | * @param camera Pointer to camera component 560 | * @param exp_comp Value to adjust, -10 to +10 561 | * @return 0 if successful, non-zero if any parameters out of range 562 | */ 563 | int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp) 564 | { 565 | if (!camera) 566 | return 1; 567 | 568 | return mmal_status_to_int(mmal_port_parameter_set_int32(camera->control, MMAL_PARAMETER_EXPOSURE_COMP , exp_comp)); 569 | } 570 | 571 | 572 | /** 573 | * Set exposure mode for images 574 | * @param camera Pointer to camera component 575 | * @param mode Exposure mode to set from 576 | * - MMAL_PARAM_EXPOSUREMODE_OFF, 577 | * - MMAL_PARAM_EXPOSUREMODE_AUTO, 578 | * - MMAL_PARAM_EXPOSUREMODE_NIGHT, 579 | * - MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, 580 | * - MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, 581 | * - MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, 582 | * - MMAL_PARAM_EXPOSUREMODE_SPORTS, 583 | * - MMAL_PARAM_EXPOSUREMODE_SNOW, 584 | * - MMAL_PARAM_EXPOSUREMODE_BEACH, 585 | * - MMAL_PARAM_EXPOSUREMODE_VERYLONG, 586 | * - MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, 587 | * - MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, 588 | * - MMAL_PARAM_EXPOSUREMODE_FIREWORKS, 589 | * 590 | * @return 0 if successful, non-zero if any parameters out of range 591 | */ 592 | int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode) 593 | { 594 | MMAL_PARAMETER_EXPOSUREMODE_T exp_mode = {{MMAL_PARAMETER_EXPOSURE_MODE,sizeof(exp_mode)}, mode}; 595 | 596 | if (!camera) 597 | return 1; 598 | 599 | return mmal_status_to_int(mmal_port_parameter_set(camera->control, &exp_mode.hdr)); 600 | } 601 | 602 | 603 | /** 604 | * Set the aWB (auto white balance) mode for images 605 | * @param camera Pointer to camera component 606 | * @param awb_mode Value to set from 607 | * - MMAL_PARAM_AWBMODE_OFF, 608 | * - MMAL_PARAM_AWBMODE_AUTO, 609 | * - MMAL_PARAM_AWBMODE_SUNLIGHT, 610 | * - MMAL_PARAM_AWBMODE_CLOUDY, 611 | * - MMAL_PARAM_AWBMODE_SHADE, 612 | * - MMAL_PARAM_AWBMODE_TUNGSTEN, 613 | * - MMAL_PARAM_AWBMODE_FLUORESCENT, 614 | * - MMAL_PARAM_AWBMODE_INCANDESCENT, 615 | * - MMAL_PARAM_AWBMODE_FLASH, 616 | * - MMAL_PARAM_AWBMODE_HORIZON, 617 | * @return 0 if successful, non-zero if any parameters out of range 618 | */ 619 | int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode) 620 | { 621 | MMAL_PARAMETER_AWBMODE_T param = {{MMAL_PARAMETER_AWB_MODE,sizeof(param)}, awb_mode}; 622 | 623 | if (!camera) 624 | return 1; 625 | 626 | return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); 627 | } 628 | 629 | /** 630 | * Set the image effect for the images 631 | * @param camera Pointer to camera component 632 | * @param imageFX Value from 633 | * - MMAL_PARAM_IMAGEFX_NONE, 634 | * - MMAL_PARAM_IMAGEFX_NEGATIVE, 635 | * - MMAL_PARAM_IMAGEFX_SOLARIZE, 636 | * - MMAL_PARAM_IMAGEFX_POSTERIZE, 637 | * - MMAL_PARAM_IMAGEFX_WHITEBOARD, 638 | * - MMAL_PARAM_IMAGEFX_BLACKBOARD, 639 | * - MMAL_PARAM_IMAGEFX_SKETCH, 640 | * - MMAL_PARAM_IMAGEFX_DENOISE, 641 | * - MMAL_PARAM_IMAGEFX_EMBOSS, 642 | * - MMAL_PARAM_IMAGEFX_OILPAINT, 643 | * - MMAL_PARAM_IMAGEFX_HATCH, 644 | * - MMAL_PARAM_IMAGEFX_GPEN, 645 | * - MMAL_PARAM_IMAGEFX_PASTEL, 646 | * - MMAL_PARAM_IMAGEFX_WATERCOLOUR, 647 | * - MMAL_PARAM_IMAGEFX_FILM, 648 | * - MMAL_PARAM_IMAGEFX_BLUR, 649 | * - MMAL_PARAM_IMAGEFX_SATURATION, 650 | * - MMAL_PARAM_IMAGEFX_COLOURSWAP, 651 | * - MMAL_PARAM_IMAGEFX_WASHEDOUT, 652 | * - MMAL_PARAM_IMAGEFX_POSTERISE, 653 | * - MMAL_PARAM_IMAGEFX_COLOURPOINT, 654 | * - MMAL_PARAM_IMAGEFX_COLOURBALANCE, 655 | * - MMAL_PARAM_IMAGEFX_CARTOON, 656 | * @return 0 if successful, non-zero if any parameters out of range 657 | */ 658 | int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX) 659 | { 660 | MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX}; 661 | 662 | if (!camera) 663 | return 1; 664 | 665 | return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr)); 666 | } 667 | 668 | /* TODO :what to do with the image effects parameters? 669 | MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)}, 670 | imageFX, 0, {0}}; 671 | mmal_port_parameter_set(camera->control, &imfx_param.hdr); 672 | */ 673 | 674 | /** 675 | * Set the colour effect for images (Set UV component) 676 | * @param camera Pointer to camera component 677 | * @param colourFX Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white) 678 | * @return 0 if successful, non-zero if any parameters out of range 679 | */ 680 | int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX) 681 | { 682 | MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0}; 683 | 684 | if (!camera) 685 | return 1; 686 | 687 | colfx.enable = colourFX->enable; 688 | colfx.u = colourFX->u; 689 | colfx.v = colourFX->v; 690 | 691 | return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr)); 692 | 693 | } 694 | 695 | 696 | /** 697 | * Set the rotation of the image 698 | * @param camera Pointer to camera component 699 | * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only) 700 | * @return 0 if successful, non-zero if any parameters out of range 701 | */ 702 | int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation) 703 | { 704 | int ret; 705 | int my_rotation = ((rotation % 360 ) / 90) * 90; 706 | 707 | ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation); 708 | mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation); 709 | mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation); 710 | 711 | return ret; 712 | } 713 | 714 | /** 715 | * Set the flips state of the image 716 | * @param camera Pointer to camera component 717 | * @param hflip If true, horizontally flip the image 718 | * @param vflip If true, vertically flip the image 719 | * 720 | * @return 0 if successful, non-zero if any parameters out of range 721 | */ 722 | int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip) 723 | { 724 | MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE}; 725 | 726 | if (hflip && vflip) 727 | mirror.value = MMAL_PARAM_MIRROR_BOTH; 728 | else 729 | if (hflip) 730 | mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL; 731 | else 732 | if (vflip) 733 | mirror.value = MMAL_PARAM_MIRROR_VERTICAL; 734 | 735 | mmal_port_parameter_set(camera->output[0], &mirror.hdr); 736 | mmal_port_parameter_set(camera->output[1], &mirror.hdr); 737 | return mmal_port_parameter_set(camera->output[2], &mirror.hdr); 738 | } 739 | 740 | /** 741 | * Set the ROI of the sensor to use for captures/preview 742 | * @param camera Pointer to camera component 743 | * @param rect Normalised coordinates of ROI rectangle 744 | * 745 | * @return 0 if successful, non-zero if any parameters out of range 746 | */ 747 | int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) 748 | { 749 | MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}}; 750 | 751 | crop.rect.x = (65536 * rect.x); 752 | crop.rect.y = (65536 * rect.y); 753 | crop.rect.width = (65536 * rect.w); 754 | crop.rect.height = (65536 * rect.h); 755 | 756 | return mmal_port_parameter_set(camera->control, &crop.hdr); 757 | } 758 | 759 | /** 760 | * Adjust the exposure time used for images 761 | * @param camera Pointer to camera component 762 | * @param shutter speed in microseconds 763 | * @return 0 if successful, non-zero if any parameters out of range 764 | */ 765 | int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed) 766 | { 767 | if (!camera) 768 | return 1; 769 | 770 | return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_SHUTTER_SPEED, speed)); 771 | } 772 | 773 | /** 774 | * Asked GPU how much memory it has allocated 775 | * 776 | * @return amount of memory in MB 777 | */ 778 | static int raspicamcontrol_get_mem_gpu(void) 779 | { 780 | char response[80] = ""; 781 | int gpu_mem = 0; 782 | if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0) 783 | vc_gencmd_number_property(response, "gpu", &gpu_mem); 784 | return gpu_mem; 785 | } 786 | 787 | /** 788 | * Ask GPU about its camera abilities 789 | * @param supported None-zero if software supports the camera 790 | * @param detected None-zero if a camera has been detected 791 | */ 792 | static void raspicamcontrol_get_camera(int *supported, int *detected) 793 | { 794 | char response[80] = ""; 795 | if (vc_gencmd(response, sizeof response, "get_camera") == 0) 796 | { 797 | if (supported) 798 | vc_gencmd_number_property(response, "supported", supported); 799 | if (detected) 800 | vc_gencmd_number_property(response, "detected", detected); 801 | } 802 | } 803 | 804 | /** 805 | * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities 806 | * @param supported None-zero if software supports the camera 807 | * @param detected None-zero if a camera has been detected 808 | */ 809 | void raspicamcontrol_check_configuration(int min_gpu_mem) 810 | { 811 | int gpu_mem = raspicamcontrol_get_mem_gpu(); 812 | int supported = 0, detected = 0; 813 | raspicamcontrol_get_camera(&supported, &detected); 814 | if (!supported) 815 | vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n"); 816 | else if (gpu_mem < min_gpu_mem) 817 | vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem); 818 | else if (!detected) 819 | vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n"); 820 | else 821 | vcos_log_error("Failed to run camera app. Please check for firmware updates\n"); 822 | } 823 | 824 | -------------------------------------------------------------------------------- /src/picam.c: -------------------------------------------------------------------------------- 1 | #include "picam.h" 2 | #ifndef _GNU_SOURCE 3 | #define _GNU_SOURCE 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define VERSION_STRING "v1.2" 12 | 13 | #include "bcm_host.h" 14 | #include "interface/vcos/vcos.h" 15 | 16 | #include "interface/mmal/mmal.h" 17 | #include "interface/mmal/mmal_logging.h" 18 | #include "interface/mmal/mmal_buffer.h" 19 | #include "interface/mmal/util/mmal_util.h" 20 | #include "interface/mmal/util/mmal_util_params.h" 21 | #include "interface/mmal/util/mmal_default_components.h" 22 | #include "interface/mmal/util/mmal_connection.h" 23 | 24 | #include "RaspiCamControl.h" 25 | #include 26 | 27 | /// Camera number to use - we only have one camera, indexed from 0. 28 | #define CAMERA_NUMBER 0 29 | #define MMAL_CAMERA_PREVIEW_PORT 0 30 | #define MMAL_CAMERA_VIDEO_PORT 1 31 | #define MMAL_CAMERA_CAPTURE_PORT 2 32 | 33 | // Stills format information 34 | #define STILLS_FRAME_RATE_NUM 15 35 | #define STILLS_FRAME_RATE_DEN 1 36 | 37 | /// Video render needs at least 2 buffers. 38 | #define VIDEO_OUTPUT_BUFFERS_NUM 3 39 | // Video format information 40 | #define VIDEO_FRAME_RATE_NUM 30 41 | #define VIDEO_FRAME_RATE_DEN 1 42 | 43 | // Max bitrate we allow for recording 44 | const int MAX_BITRATE = 25000000; // 25Mbits/s 45 | 46 | /// Interval at which we check for an failure abort during capture 47 | const int ABORT_INTERVAL = 100; // ms 48 | 49 | int mmal_status_to_int(MMAL_STATUS_T status); 50 | 51 | 52 | /** Structure containing all state information for the current run 53 | */ 54 | typedef struct 55 | { 56 | int width; /// Requested width of image 57 | int height; /// requested height of image 58 | int quality; /// JPEG quality setting (1-100) 59 | uint8_t *filedata; 60 | long bytesStored; 61 | 62 | int videoEncode; 63 | /* Video */ 64 | int bitrate; /// Requested bitrate 65 | int framerate; /// Requested frame rate (fps) 66 | int intraperiod; /// Intra-refresh period (key frame rate) 67 | int quantisationParameter; /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate 68 | int inlineHeaders; /// Insert inline headers to stream (SPS, PPS) 69 | int profile; /// H264 profile to use for encoding 70 | char *filename; /// filename of output file 71 | int immutableInput; 72 | 73 | /* End Video */ 74 | MMAL_FOURCC_T encoding; /// Encoding to use for the output file. 75 | 76 | 77 | RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters 78 | 79 | MMAL_COMPONENT_T *preview_component; 80 | MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component 81 | MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component 82 | MMAL_COMPONENT_T *null_sink_component; /// Pointer to the null sink component 83 | MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview 84 | MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder 85 | 86 | MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port 87 | 88 | } RASPISTILL_STATE; 89 | 90 | 91 | 92 | /** Struct used to pass information in encoder port userdata to callback 93 | */ 94 | typedef struct 95 | { 96 | VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) 97 | RASPISTILL_STATE *pstate; /// pointer to our state in case required in callback 98 | FILE *file_handle; /// File handle to write buffer data to. 99 | int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture 100 | 101 | } PORT_USERDATA; 102 | 103 | 104 | /** 105 | * Assign a default set of parameters to the state passed in 106 | * 107 | * @param state Pointer to state structure to assign defaults to 108 | */ 109 | static void default_status(RASPISTILL_STATE *state) 110 | { 111 | if (!state) { 112 | vcos_assert(0); 113 | return; 114 | } 115 | state->width = 2592; 116 | state->height = 1944; 117 | state->quality = 85; 118 | state->bytesStored = 0l; 119 | /*Video*/ 120 | 121 | state->bitrate = 17000000; 122 | state->framerate = VIDEO_FRAME_RATE_NUM; 123 | state->immutableInput = 0; 124 | state->intraperiod = 0; // Not set 125 | state->quantisationParameter = 0; 126 | state->inlineHeaders = 0; 127 | state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; 128 | 129 | state->camera_component = NULL; 130 | state->encoder_component = NULL; 131 | state->encoder_connection = NULL; 132 | state->encoder_pool = NULL; 133 | state->encoding = MMAL_ENCODING_JPEG; //MMAL_ENCODING_BMP 134 | raspicamcontrol_set_defaults(&state->camera_parameters); 135 | //state->camera_parameters.exposureMode = MMAL_PARAM_EXPOSUREMODE_NIGHT; 136 | //state->camera_parameters.exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; 137 | } 138 | 139 | 140 | 141 | 142 | /** 143 | * buffer header callback function for camera control 144 | * 145 | * No actions taken in current version 146 | * 147 | * @param port Pointer to port from which callback originated 148 | * @param buffer mmal buffer header pointer 149 | */ 150 | static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 151 | { 152 | if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) { 153 | } else { 154 | vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); 155 | } 156 | 157 | mmal_buffer_header_release(buffer); 158 | } 159 | 160 | /** 161 | * buffer header callback function for encoder 162 | * 163 | * Callback will dump buffer data to the specific file 164 | * 165 | * @param port Pointer to port from which callback originated 166 | * @param buffer mmal buffer header pointer 167 | */ 168 | static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 169 | { 170 | int complete = 0; 171 | 172 | // We pass our file handle and other stuff in via the userdata field. 173 | 174 | PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; 175 | 176 | if (pData) { 177 | 178 | RASPISTILL_STATE *state = pData->pstate; 179 | int bytes_written = buffer->length; 180 | if (state->videoEncode == 1) { 181 | vcos_assert(pData->file_handle); 182 | if (buffer->length) { 183 | mmal_buffer_header_mem_lock(buffer); 184 | bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); 185 | mmal_buffer_header_mem_unlock(buffer); 186 | } 187 | if (bytes_written != buffer->length) { 188 | vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); 189 | pData->abort = 1; 190 | } 191 | } else { 192 | if (buffer->length) { 193 | mmal_buffer_header_mem_lock(buffer); 194 | if ((state->bytesStored > 0) && (bytes_written > 0)) { 195 | long newLen = state->bytesStored + bytes_written; 196 | uint8_t *new_buffer = malloc(newLen); 197 | memcpy(new_buffer, state->filedata, state->bytesStored); 198 | memcpy(new_buffer + state->bytesStored, buffer->data, bytes_written); 199 | 200 | free(state->filedata); //Free previous buffer 201 | 202 | state->filedata = new_buffer; 203 | state->bytesStored = state->bytesStored + bytes_written; 204 | } else { 205 | state->filedata = malloc(bytes_written); 206 | memcpy(state->filedata, buffer->data, bytes_written); 207 | state->bytesStored = bytes_written; 208 | } 209 | mmal_buffer_header_mem_unlock(buffer); 210 | } 211 | } 212 | // Now flag if we have completed 213 | if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) 214 | complete = 1; 215 | } else { 216 | vcos_log_error("Received a encoder buffer callback with no state"); 217 | } 218 | 219 | // release buffer back to the pool 220 | mmal_buffer_header_release(buffer); 221 | 222 | // and send one back to the port (if still open) 223 | if (port->is_enabled) { 224 | MMAL_STATUS_T status = MMAL_SUCCESS; 225 | MMAL_BUFFER_HEADER_T *new_buffer; 226 | 227 | new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); 228 | 229 | if (new_buffer) { 230 | status = mmal_port_send_buffer(port, new_buffer); 231 | } 232 | if (!new_buffer || status != MMAL_SUCCESS) 233 | vcos_log_error("Unable to return a buffer to the encoder port"); 234 | } 235 | 236 | if (complete) 237 | vcos_semaphore_post(&(pData->complete_semaphore)); 238 | 239 | } 240 | 241 | 242 | /** 243 | * Create the camera component, set up its ports 244 | * 245 | * @param state Pointer to state control struct 246 | * 247 | * @return MMAL_SUCCESS if all OK, something else otherwise 248 | * 249 | */ 250 | static MMAL_STATUS_T create_video_camera_component(RASPISTILL_STATE *state) 251 | { 252 | MMAL_COMPONENT_T *camera = 0; 253 | MMAL_ES_FORMAT_T *format; 254 | MMAL_PORT_T *video_port = NULL, *still_port = NULL; 255 | MMAL_STATUS_T status; 256 | 257 | /* Create the component */ 258 | status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); 259 | 260 | if (status != MMAL_SUCCESS) { 261 | vcos_log_error("Failed to create camera component"); 262 | goto error; 263 | } 264 | 265 | if (!camera->output_num) { 266 | status = MMAL_ENOSYS; 267 | vcos_log_error("Camera doesn't have output ports"); 268 | goto error; 269 | } 270 | 271 | 272 | video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; 273 | still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; 274 | 275 | // Enable the camera, and tell it its control callback function 276 | status = mmal_port_enable(camera->control, camera_control_callback); 277 | 278 | if (status != MMAL_SUCCESS) { 279 | vcos_log_error("Unable to enable control port : error %d", status); 280 | goto error; 281 | } 282 | 283 | // set up the camera configuration 284 | 285 | MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = 286 | { 287 | { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, 288 | .max_stills_w = state->width, 289 | .max_stills_h = state->height, 290 | .stills_yuv422 = 0, 291 | .one_shot_stills = 1, 292 | .max_preview_video_w = 640, 293 | .max_preview_video_h = 480, 294 | .num_preview_video_frames = 3, 295 | .stills_capture_circular_buffer_height = 0, 296 | .fast_preview_resume = 0, 297 | .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC 298 | }; 299 | mmal_port_parameter_set(camera->control, &cam_config.hdr); 300 | 301 | raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); 302 | // Now set up the port formats 303 | 304 | // Set the encode format on the video port 305 | 306 | format = video_port->format; 307 | format->encoding_variant = MMAL_ENCODING_I420; 308 | 309 | format->encoding = MMAL_ENCODING_OPAQUE; 310 | format->es->video.width = state->width; 311 | format->es->video.height = state->height; 312 | format->es->video.crop.x = 0; 313 | format->es->video.crop.y = 0; 314 | format->es->video.crop.width = state->width; 315 | format->es->video.crop.height = state->height; 316 | format->es->video.frame_rate.num = state->framerate; 317 | format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; 318 | 319 | status = mmal_port_format_commit(video_port); 320 | 321 | if (status != MMAL_SUCCESS) { 322 | vcos_log_error("camera video format couldn't be set"); 323 | goto error; 324 | } 325 | 326 | // Ensure there are enough buffers to avoid dropping frames 327 | if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) 328 | video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; 329 | 330 | 331 | // Set the encode format on the still port 332 | 333 | format = still_port->format; 334 | 335 | format->encoding = MMAL_ENCODING_OPAQUE; 336 | if (state->videoEncode == 1) { 337 | format->encoding_variant = MMAL_ENCODING_I420; 338 | } 339 | format->es->video.width = state->width; 340 | format->es->video.height = state->height; 341 | format->es->video.crop.x = 0; 342 | format->es->video.crop.y = 0; 343 | format->es->video.crop.width = state->width; 344 | format->es->video.crop.height = state->height; 345 | format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; 346 | format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; 347 | 348 | status = mmal_port_format_commit(still_port); 349 | 350 | if (status != MMAL_SUCCESS) 351 | { 352 | vcos_log_error("camera still format couldn't be set"); 353 | goto error; 354 | } 355 | 356 | /* Ensure there are enough buffers to avoid dropping frames */ 357 | if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) 358 | still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; 359 | 360 | /* Enable component */ 361 | status = mmal_component_enable(camera); 362 | 363 | if (status != MMAL_SUCCESS) 364 | { 365 | vcos_log_error("camera component couldn't be enabled"); 366 | goto error; 367 | } 368 | 369 | 370 | 371 | state->camera_component = camera; 372 | 373 | 374 | 375 | return status; 376 | 377 | error: 378 | 379 | if (camera) 380 | mmal_component_destroy(camera); 381 | 382 | return status; 383 | } 384 | /** 385 | * Destroy the camera component 386 | * 387 | * @param state Pointer to state control struct 388 | * 389 | */ 390 | static void destroy_camera_component(RASPISTILL_STATE *state) 391 | { 392 | if (state->camera_component) { 393 | mmal_component_destroy(state->camera_component); 394 | state->camera_component = NULL; 395 | } 396 | } 397 | 398 | 399 | 400 | /** 401 | * Create the encoder component, set up its ports 402 | * 403 | * @param state Pointer to state control struct. encoder_component member set to the created camera_component if successfull. 404 | * 405 | * @return a MMAL_STATUS, MMAL_SUCCESS if all OK, something else otherwise 406 | */ 407 | static MMAL_STATUS_T create_encoder_component(RASPISTILL_STATE *state) 408 | { 409 | MMAL_COMPONENT_T *encoder = 0; 410 | MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; 411 | MMAL_STATUS_T status; 412 | MMAL_POOL_T *pool; 413 | 414 | status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder); 415 | 416 | if (status != MMAL_SUCCESS) { 417 | vcos_log_error("Unable to create JPEG encoder component"); 418 | goto error; 419 | } 420 | 421 | if (!encoder->input_num || !encoder->output_num) { 422 | status = MMAL_ENOSYS; 423 | vcos_log_error("JPEG encoder doesn't have input/output ports"); 424 | goto error; 425 | } 426 | 427 | encoder_input = encoder->input[0]; 428 | encoder_output = encoder->output[0]; 429 | 430 | // We want same format on input and output 431 | mmal_format_copy(encoder_output->format, encoder_input->format); 432 | 433 | // Specify out output format 434 | encoder_output->format->encoding = state->encoding; 435 | 436 | encoder_output->buffer_size = encoder_output->buffer_size_recommended; 437 | 438 | if (encoder_output->buffer_size < encoder_output->buffer_size_min) 439 | encoder_output->buffer_size = encoder_output->buffer_size_min; 440 | 441 | encoder_output->buffer_num = encoder_output->buffer_num_recommended; 442 | 443 | if (encoder_output->buffer_num < encoder_output->buffer_num_min) 444 | encoder_output->buffer_num = encoder_output->buffer_num_min; 445 | 446 | // Commit the port changes to the output port 447 | status = mmal_port_format_commit(encoder_output); 448 | 449 | if (status != MMAL_SUCCESS) { 450 | vcos_log_error("Unable to set format on video encoder output port"); 451 | goto error; 452 | } 453 | 454 | // Set the JPEG quality level 455 | status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, state->quality); 456 | 457 | if (status != MMAL_SUCCESS) { 458 | vcos_log_error("Unable to set JPEG quality"); 459 | goto error; 460 | } 461 | 462 | 463 | // Enable component 464 | status = mmal_component_enable(encoder); 465 | 466 | if (status != MMAL_SUCCESS) { 467 | vcos_log_error("Unable to enable video encoder component"); 468 | goto error; 469 | } 470 | 471 | /* Create pool of buffer headers for the output port to consume */ 472 | pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); 473 | 474 | if (!pool) { 475 | vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); 476 | } 477 | 478 | state->encoder_pool = pool; 479 | state->encoder_component = encoder; 480 | 481 | return status; 482 | 483 | error: 484 | 485 | if (encoder) 486 | mmal_component_destroy(encoder); 487 | 488 | return status; 489 | } 490 | /** 491 | * Create the encoder component, set up its ports 492 | * 493 | * @param state Pointer to state control struct 494 | * 495 | * @return MMAL_SUCCESS if all OK, something else otherwise 496 | * 497 | */ 498 | static MMAL_STATUS_T create_video_encoder_component(RASPISTILL_STATE *state) 499 | { 500 | MMAL_COMPONENT_T *encoder = 0; 501 | MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; 502 | MMAL_STATUS_T status; 503 | MMAL_POOL_T *pool; 504 | 505 | status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); 506 | 507 | if (status != MMAL_SUCCESS) 508 | { 509 | vcos_log_error("Unable to create video encoder component"); 510 | goto error; 511 | } 512 | 513 | if (!encoder->input_num || !encoder->output_num) 514 | { 515 | status = MMAL_ENOSYS; 516 | vcos_log_error("Video encoder doesn't have input/output ports"); 517 | goto error; 518 | } 519 | 520 | encoder_input = encoder->input[0]; 521 | encoder_output = encoder->output[0]; 522 | 523 | // We want same format on input and output 524 | mmal_format_copy(encoder_output->format, encoder_input->format); 525 | 526 | // Only supporting H264 at the moment 527 | 528 | encoder_output->format->encoding = MMAL_ENCODING_H264; 529 | 530 | encoder_output->format->bitrate = state->bitrate; 531 | encoder_output->buffer_size = encoder_output->buffer_size_recommended; 532 | 533 | if (encoder_output->buffer_size < encoder_output->buffer_size_min) 534 | encoder_output->buffer_size = encoder_output->buffer_size_min; 535 | 536 | encoder_output->buffer_num = encoder_output->buffer_num_recommended; 537 | 538 | if (encoder_output->buffer_num < encoder_output->buffer_num_min) 539 | encoder_output->buffer_num = encoder_output->buffer_num_min; 540 | 541 | // Commit the port changes to the output port 542 | status = mmal_port_format_commit(encoder_output); 543 | 544 | if (status != MMAL_SUCCESS) 545 | { 546 | vcos_log_error("Unable to set format on video encoder output port"); 547 | goto error; 548 | } 549 | 550 | 551 | // Set the rate control parameter 552 | if (0) 553 | { 554 | MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT}; 555 | status = mmal_port_parameter_set(encoder_output, ¶m.hdr); 556 | if (status != MMAL_SUCCESS) 557 | { 558 | vcos_log_error("Unable to set ratecontrol"); 559 | goto error; 560 | } 561 | 562 | } 563 | 564 | if (state->intraperiod) 565 | { 566 | MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod}; 567 | status = mmal_port_parameter_set(encoder_output, ¶m.hdr); 568 | if (status != MMAL_SUCCESS) 569 | { 570 | vcos_log_error("Unable to set intraperiod"); 571 | goto error; 572 | } 573 | 574 | } 575 | if (state->quantisationParameter) 576 | { 577 | MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->quantisationParameter}; 578 | status = mmal_port_parameter_set(encoder_output, ¶m.hdr); 579 | if (status != MMAL_SUCCESS) 580 | { 581 | vcos_log_error("Unable to set QP"); 582 | goto error; 583 | } 584 | 585 | MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, sizeof(param)}, state->quantisationParameter+6}; 586 | status = mmal_port_parameter_set(encoder_output, ¶m2.hdr); 587 | if (status != MMAL_SUCCESS) 588 | { 589 | vcos_log_error("Unable to set QP"); 590 | goto error; 591 | } 592 | } 593 | { 594 | MMAL_PARAMETER_VIDEO_PROFILE_T param; 595 | param.hdr.id = MMAL_PARAMETER_PROFILE; 596 | param.hdr.size = sizeof(param); 597 | 598 | param.profile[0].profile = state->profile; 599 | param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported 600 | status = mmal_port_parameter_set(encoder_output, ¶m.hdr); 601 | if (status != MMAL_SUCCESS) 602 | { 603 | vcos_log_error("Unable to set H264 profile"); 604 | goto error; 605 | } 606 | } 607 | if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS) 608 | { 609 | vcos_log_error("Unable to set immutable input flag"); 610 | // Continue rather than abort.. 611 | } 612 | 613 | //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested 614 | if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, state->inlineHeaders) != MMAL_SUCCESS) 615 | { 616 | vcos_log_error("failed to set INLINE HEADER FLAG parameters"); 617 | // Continue rather than abort.. 618 | } 619 | // Enable component 620 | status = mmal_component_enable(encoder); 621 | 622 | if (status != MMAL_SUCCESS) 623 | { 624 | vcos_log_error("Unable to enable video encoder component"); 625 | goto error; 626 | } 627 | 628 | /* Create pool of buffer headers for the output port to consume */ 629 | pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); 630 | 631 | if (!pool) 632 | { 633 | vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); 634 | } 635 | 636 | state->encoder_pool = pool; 637 | state->encoder_component = encoder; 638 | 639 | 640 | 641 | return status; 642 | 643 | error: 644 | if (encoder) 645 | mmal_component_destroy(encoder); 646 | 647 | return status; 648 | } 649 | /** 650 | * Destroy the encoder component 651 | * 652 | * @param state Pointer to state control struct 653 | * 654 | */ 655 | static void destroy_encoder_component(RASPISTILL_STATE *state) 656 | { 657 | // Get rid of any port buffers first 658 | if (state->encoder_pool) { 659 | mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); 660 | } 661 | 662 | if (state->encoder_component) { 663 | mmal_component_destroy(state->encoder_component); 664 | state->encoder_component = NULL; 665 | } 666 | } 667 | 668 | 669 | /** 670 | * Connect two specific ports together 671 | * 672 | * @param output_port Pointer the output port 673 | * @param input_port Pointer the input port 674 | * @param Pointer to a mmal connection pointer, reassigned if function successful 675 | * @return Returns a MMAL_STATUS_T giving result of operation 676 | * 677 | */ 678 | static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) 679 | { 680 | MMAL_STATUS_T status; 681 | 682 | status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); 683 | 684 | if (status == MMAL_SUCCESS) { 685 | status = mmal_connection_enable(*connection); 686 | if (status != MMAL_SUCCESS) 687 | mmal_connection_destroy(*connection); 688 | } 689 | 690 | return status; 691 | } 692 | 693 | /** 694 | * Checks if specified port is valid and enabled, then disables it 695 | * 696 | * @param port Pointer the port 697 | * 698 | */ 699 | static void check_disable_port(MMAL_PORT_T *port) 700 | { 701 | if (port && port->is_enabled) 702 | mmal_port_disable(port); 703 | } 704 | 705 | uint8_t *takePhoto(PicamParams *parms, long *sizeread) { 706 | long test = 0l; 707 | uint8_t *tmp = internelPhotoWithDetails(2592,1944,85, MMAL_ENCODING_JPEG, parms, &test); 708 | *sizeread = test; 709 | return tmp; 710 | } 711 | 712 | uint8_t *takePhotoWithDetails(int width, int height, int quality, PicamParams *parms, long *sizeread) { 713 | long test = 0l; 714 | uint8_t *tmp = internelPhotoWithDetails(width,height,quality,MMAL_ENCODING_JPEG,parms, &test); 715 | *sizeread = test; 716 | return tmp; 717 | } 718 | 719 | uint8_t *takeRGBPhotoWithDetails(int width, int height, PicamParams *parms, long *sizeread) { 720 | long test = 0l; 721 | uint8_t *tmp = internelPhotoWithDetails(width,height,100, MMAL_ENCODING_BMP, parms, &test); 722 | *sizeread = test; 723 | return tmp; 724 | } 725 | 726 | uint8_t *internelPhotoWithDetails(int width, int height, int quality,MMAL_FOURCC_T encoding, PicamParams *parms, long *sizeread) { 727 | RASPISTILL_STATE state; 728 | MMAL_STATUS_T status = MMAL_SUCCESS; 729 | 730 | MMAL_PORT_T *preview_input_port = NULL; 731 | MMAL_PORT_T *camera_preview_port = NULL; 732 | MMAL_PORT_T *camera_still_port = NULL; 733 | MMAL_PORT_T *encoder_input_port = NULL; 734 | MMAL_PORT_T *encoder_output_port = NULL; 735 | if (width > 2592) { 736 | width = 2592; 737 | } else if (width < 20) { 738 | width = 20; 739 | } 740 | if (height > 1944) { 741 | height = 1944; 742 | } else if (height < 20) { 743 | height = 20; 744 | } 745 | if (quality > 100) { 746 | quality = 100; 747 | } else if (quality < 0) { 748 | quality = 85; 749 | } 750 | bcm_host_init(); 751 | default_status(&state); 752 | state.width = width; 753 | state.height = height; 754 | state.quality = quality; 755 | state.encoding = encoding; 756 | state.videoEncode = 0; 757 | state.bitrate = parms->videoBitrate; 758 | state.framerate = parms->videoFramerate; 759 | state.profile = parms->videoProfile; 760 | 761 | state.camera_parameters.exposureMode = parms->exposure; 762 | state.camera_parameters.exposureMeterMode = parms->meterMode; 763 | state.camera_parameters.awbMode = parms->awbMode; 764 | state.camera_parameters.imageEffect = parms->imageFX; 765 | state.camera_parameters.ISO = parms->ISO; 766 | state.camera_parameters.sharpness = parms->sharpness; 767 | state.camera_parameters.contrast = parms->contrast; 768 | state.camera_parameters.brightness= parms->brightness; 769 | state.camera_parameters.saturation = parms->saturation; 770 | state.camera_parameters.videoStabilisation = parms->videoStabilisation; /// 0 or 1 (false or true) 771 | state.camera_parameters.exposureCompensation = parms->exposureCompensation; 772 | state.camera_parameters.rotation = parms->rotation; 773 | state.camera_parameters.hflip = parms->hflip; 774 | state.camera_parameters.vflip = parms->vflip; 775 | state.camera_parameters.shutter_speed = parms->shutter_speed; 776 | state.camera_parameters.roi.x = parms->roi[0]; 777 | state.camera_parameters.roi.y = parms->roi[1]; 778 | state.camera_parameters.roi.w = parms->roi[2]; 779 | state.camera_parameters.roi.h = parms->roi[3]; 780 | 781 | MMAL_COMPONENT_T *preview = 0; 782 | 783 | if ((status = create_video_camera_component(&state)) != MMAL_SUCCESS) { 784 | vcos_log_error("%s: Failed to create camera component", __func__); 785 | } else if ((status = mmal_component_create("vc.null_sink", &preview)) != MMAL_SUCCESS) { 786 | vcos_log_error("%s: Failed to create preview component", __func__); 787 | destroy_camera_component(&state); 788 | } else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) { 789 | vcos_log_error("%s: Failed to create encode component", __func__); 790 | destroy_camera_component(&state); 791 | } else { 792 | status = mmal_component_enable(preview); 793 | state.preview_component = preview; 794 | PORT_USERDATA callback_data; 795 | camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; 796 | camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; 797 | encoder_input_port = state.encoder_component->input[0]; 798 | encoder_output_port = state.encoder_component->output[0]; 799 | preview_input_port = state.preview_component->input[0]; 800 | 801 | status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); 802 | 803 | VCOS_STATUS_T vcos_status; 804 | 805 | 806 | // Now connect the camera to the encoder 807 | status = connect_ports(camera_still_port, encoder_input_port, &state.encoder_connection); 808 | if (status != MMAL_SUCCESS) { 809 | vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); 810 | goto error; 811 | } 812 | 813 | // Set up our userdata - this is passed though to the callback where we need the information. 814 | // Null until we open our filename 815 | callback_data.pstate = &state; 816 | vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "picam-sem", 0); 817 | 818 | vcos_assert(vcos_status == VCOS_SUCCESS); 819 | 820 | if (status != MMAL_SUCCESS) { 821 | vcos_log_error("Failed to setup encoder output"); 822 | goto error; 823 | } 824 | 825 | 826 | int num, q; 827 | // Enable the encoder output port 828 | encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; 829 | 830 | // Enable the encoder output port and tell it its callback function 831 | status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); 832 | 833 | // Send all the buffers to the encoder output port 834 | num = mmal_queue_length(state.encoder_pool->queue); 835 | 836 | for (q=0;qqueue); 838 | 839 | if (!buffer) 840 | vcos_log_error("Unable to get a required buffer %d from pool queue", q); 841 | 842 | if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) 843 | vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); 844 | } 845 | 846 | 847 | 848 | if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) { 849 | vcos_log_error("%s: Failed to start capture", __func__); 850 | } else { 851 | // Wait for capture to complete 852 | // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error 853 | // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic 854 | vcos_semaphore_wait(&callback_data.complete_semaphore); 855 | } 856 | // Disable encoder output port 857 | status = mmal_port_disable(encoder_output_port); 858 | 859 | *sizeread = state.bytesStored; 860 | 861 | vcos_semaphore_delete(&callback_data.complete_semaphore); 862 | } 863 | error: 864 | mmal_status_to_int(status); 865 | 866 | // Disable all our ports that are not handled by connections 867 | check_disable_port(encoder_output_port); 868 | mmal_connection_destroy(state.encoder_connection); 869 | 870 | if (state.encoder_component) 871 | mmal_component_disable(state.encoder_component); 872 | 873 | if (state.preview_component) { 874 | mmal_component_disable(state.preview_component); 875 | mmal_component_destroy(state.preview_component); 876 | state.preview_component = NULL; 877 | } 878 | if (state.camera_component) 879 | mmal_component_disable(state.camera_component); 880 | 881 | 882 | destroy_encoder_component(&state); 883 | destroy_camera_component(&state); 884 | 885 | 886 | if (status != MMAL_SUCCESS) 887 | raspicamcontrol_check_configuration(128); 888 | 889 | return state.filedata; 890 | } 891 | 892 | void internelVideoWithDetails(char *filename, int width, int height, int duration, PicamParams *parms) { 893 | RASPISTILL_STATE state; 894 | MMAL_STATUS_T status = MMAL_SUCCESS; 895 | 896 | 897 | MMAL_PORT_T *camera_video_port = NULL; 898 | MMAL_PORT_T *encoder_input_port = NULL; 899 | MMAL_PORT_T *encoder_output_port = NULL; 900 | FILE *output_file = NULL; 901 | 902 | if (width > 1920) { 903 | width = 1920; 904 | } else if (width < 20) { 905 | width = 20; 906 | } 907 | if (height > 1080) { 908 | height = 1080; 909 | } else if (height < 20) { 910 | height = 20; 911 | } 912 | 913 | bcm_host_init(); 914 | 915 | default_status(&state); 916 | state.width = width; 917 | state.height = height; 918 | state.quality = 0; 919 | state.videoEncode = 1; 920 | state.filename = filename; 921 | 922 | state.bitrate = parms->videoBitrate; 923 | state.framerate = parms->videoFramerate; 924 | state.profile = parms->videoProfile; 925 | state.quantisationParameter = parms->quantisationParameter; 926 | state.inlineHeaders = parms->inlineHeaders; 927 | 928 | state.camera_parameters.exposureMode = parms->exposure; 929 | state.camera_parameters.exposureMeterMode = parms->meterMode; 930 | state.camera_parameters.awbMode = parms->awbMode; 931 | state.camera_parameters.imageEffect = parms->imageFX; 932 | state.camera_parameters.ISO = parms->ISO; 933 | state.camera_parameters.sharpness = parms->sharpness; 934 | state.camera_parameters.contrast = parms->contrast; 935 | state.camera_parameters.brightness= parms->brightness; 936 | state.camera_parameters.saturation = parms->saturation; 937 | state.camera_parameters.videoStabilisation = parms->videoStabilisation; /// 0 or 1 (false or true) 938 | state.camera_parameters.exposureCompensation = parms->exposureCompensation; 939 | state.camera_parameters.rotation = parms->rotation; 940 | state.camera_parameters.hflip = parms->hflip; 941 | state.camera_parameters.vflip = parms->vflip; 942 | state.camera_parameters.shutter_speed = parms->shutter_speed; 943 | state.camera_parameters.roi.x = parms->roi[0]; 944 | state.camera_parameters.roi.y = parms->roi[1]; 945 | state.camera_parameters.roi.w = parms->roi[2]; 946 | state.camera_parameters.roi.h = parms->roi[3]; 947 | 948 | if ((status = create_video_camera_component(&state)) != MMAL_SUCCESS) { 949 | vcos_log_error("%s: Failed to create camera component", __func__); 950 | } else if ((status = create_video_encoder_component(&state)) != MMAL_SUCCESS) { 951 | vcos_log_error("%s: Failed to create encode component", __func__); 952 | destroy_camera_component(&state); 953 | } else { 954 | PORT_USERDATA callback_data; 955 | camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; 956 | encoder_input_port = state.encoder_component->input[0]; 957 | encoder_output_port = state.encoder_component->output[0]; 958 | 959 | // Now connect the camera to the encoder 960 | status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection); 961 | 962 | if (status != MMAL_SUCCESS) { 963 | vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); 964 | goto error; 965 | } 966 | 967 | // Set up our userdata - this is passed though to the callback where we need the information. 968 | // Null until we open our filename 969 | callback_data.pstate = &state; 970 | 971 | 972 | if (status != MMAL_SUCCESS) { 973 | vcos_log_error("Failed to setup encoder output"); 974 | goto error; 975 | } 976 | 977 | if (state.filename) { 978 | output_file = fopen(state.filename, "wb"); 979 | callback_data.file_handle = output_file; 980 | callback_data.pstate = &state; 981 | callback_data.abort = 0; 982 | } 983 | 984 | encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; 985 | 986 | int wait; 987 | 988 | // Enable the encoder output port and tell it its callback function 989 | status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); 990 | if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) { 991 | goto error; 992 | } 993 | // Send all the buffers to the encoder output port 994 | { 995 | int num = mmal_queue_length(state.encoder_pool->queue); 996 | int q; 997 | for (q=0;qqueue); 999 | 1000 | if (!buffer) 1001 | vcos_log_error("Unable to get a required buffer %d from pool queue", q); 1002 | 1003 | if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) 1004 | vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); 1005 | 1006 | } 1007 | } 1008 | for (wait = 0; wait < duration; wait+= ABORT_INTERVAL) { 1009 | vcos_sleep(ABORT_INTERVAL); 1010 | if (callback_data.abort) 1011 | break; 1012 | } 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | // Disable encoder output port 1019 | status = mmal_port_disable(encoder_output_port); 1020 | 1021 | 1022 | 1023 | vcos_semaphore_delete(&callback_data.complete_semaphore); 1024 | } 1025 | error: 1026 | mmal_status_to_int(status); 1027 | 1028 | // Disable all our ports that are not handled by connections 1029 | check_disable_port(encoder_output_port); 1030 | if (output_file && output_file != stdout) 1031 | fclose(output_file); 1032 | 1033 | mmal_connection_destroy(state.encoder_connection); 1034 | 1035 | if (state.encoder_component) 1036 | mmal_component_disable(state.encoder_component); 1037 | 1038 | 1039 | if (state.camera_component) 1040 | mmal_component_disable(state.camera_component); 1041 | 1042 | destroy_encoder_component(&state); 1043 | destroy_camera_component(&state); 1044 | 1045 | 1046 | if (status != MMAL_SUCCESS) 1047 | raspicamcontrol_check_configuration(128); 1048 | 1049 | } 1050 | --------------------------------------------------------------------------------