2 * Copyright (c) 2013-2016 Jan Schmidt <jan@centricular.com>
4 Copyright (c) 2013, Broadcom Europe Ltd
5 Copyright (c) 2013, James Hughes
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 * Neither the name of the copyright holder nor the
16 names of its contributors may be used to endorse or promote products
17 derived from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
23 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * \file RaspiCapture.c
34 * Modification of the RaspiVid command line capture program for GStreamer
37 * \date 28th Feb 2013, 11 Oct 2013, 5 Mar 2015
38 * \Author: James Hughes, Jan Schmidt
42 * 3 components are created; camera, preview and video encoder.
43 * Camera component has three ports, preview, video and stills.
44 * This program connects preview and stills to the preview and video
45 * encoder. Using mmal we don't need to worry about buffers between these
46 * components, but we do need to handle buffers from the encoder, which
47 * are simply written straight to the file in the requisite buffer callback.
49 * We use the RaspiCamControl code to handle the specific camera settings.
50 * We use the RaspiPreview code to handle the (generic) preview window
53 // We use some GNU extensions (basename, asprintf)
65 #include "interface/vcos/vcos.h"
67 #include "interface/mmal/mmal.h"
68 #include "interface/mmal/mmal_logging.h"
69 #include "interface/mmal/mmal_buffer.h"
70 #include "interface/mmal/util/mmal_util.h"
71 #include "interface/mmal/util/mmal_util_params.h"
72 #include "interface/mmal/util/mmal_default_components.h"
73 #include "interface/mmal/util/mmal_connection.h"
75 #include "RaspiCapture.h"
76 #include "RaspiCamControl.h"
77 #include "RaspiPreview.h"
80 #include <semaphore.h>
82 // Standard port setting for the camera component
83 #define MMAL_CAMERA_PREVIEW_PORT 0
84 #define MMAL_CAMERA_VIDEO_PORT 1
85 #define MMAL_CAMERA_CAPTURE_PORT 2
87 // Video format information
89 #define VIDEO_FRAME_RATE_NUM 30
90 #define VIDEO_FRAME_RATE_DEN 1
92 /// Video render needs at least 2 buffers.
93 #define VIDEO_OUTPUT_BUFFERS_NUM 3
95 // Max bitrate we allow for recording
96 const int MAX_BITRATE = 25000000; // 25Mbits/s
98 /// Interval at which we check for an failure abort during capture
99 const int ABORT_INTERVAL = 100; // ms
102 int mmal_status_to_int(MMAL_STATUS_T status);
104 /** Struct used to pass information in encoder port userdata to callback
108 RASPIVID_STATE *state; /// pointer to our state in case required in callback
109 int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture
112 struct RASPIVID_STATE_T
114 RASPIVID_CONFIG config;
118 MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
119 MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component
120 MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
121 MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder
123 MMAL_PORT_T *camera_video_port;
124 MMAL_PORT_T *camera_still_port;
125 MMAL_PORT_T *encoder_output_port;
127 MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
129 PORT_USERDATA callback_data;
131 MMAL_QUEUE_T *encoded_buffer_q;
136 RASPIPREVIEW_STATE preview_state;
140 /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
141 static XREF_T profile_map[] =
143 {"baseline", MMAL_VIDEO_PROFILE_H264_BASELINE},
144 {"main", MMAL_VIDEO_PROFILE_H264_MAIN},
145 {"high", MMAL_VIDEO_PROFILE_H264_HIGH},
146 // {"constrained", MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this?
149 static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]);
152 static XREF_T initial_map[] =
158 static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]);
161 static XREF_T intra_refresh_map[] =
163 {"cyclic", MMAL_VIDEO_INTRA_REFRESH_CYCLIC},
164 {"adaptive", MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE},
165 {"both", MMAL_VIDEO_INTRA_REFRESH_BOTH},
166 {"cyclicrows", MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS},
167 // {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why.
170 static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]);
174 static void display_valid_parameters(char *app_name);
176 /// Command ID's and Structure defining our command line options
177 #define CommandHelp 0
178 #define CommandWidth 1
179 #define CommandHeight 2
180 #define CommandBitrate 3
181 #define CommandOutput 4
182 #define CommandVerbose 5
183 #define CommandTimeout 6
184 #define CommandDemoMode 7
185 #define CommandFramerate 8
186 #define CommandPreviewEnc 9
187 #define CommandIntraPeriod 10
188 #define CommandProfile 11
189 #define CommandTimed 12
190 #define CommandSignal 13
191 #define CommandKeypress 14
192 #define CommandInitialState 15
194 #define CommandInlineHeaders 17
195 #define CommandSegmentFile 18
196 #define CommandSegmentWrap 19
197 #define CommandSegmentStart 20
198 #define CommandSplitWait 21
199 #define CommandCircular 22
200 #define CommandIMV 23
201 #define CommandCamSelect 24
202 #define CommandSettings 25
203 #define CommandSensorMode 26
204 #define CommandIntraRefreshType 27
206 static COMMAND_LIST cmdline_commands[] =
208 { CommandHelp, "-help", "?", "This help information", 0 },
209 { CommandWidth, "-width", "w", "Set image width <size>. Default 1920", 1 },
210 { CommandHeight, "-height", "h", "Set image height <size>. Default 1080", 1 },
211 { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 },
212 { CommandOutput, "-output", "o", "Output filename <filename> (to write to stdout, use '-o -')", 1 },
213 { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 },
214 { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
215 { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1},
216 { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1},
217 { CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0},
218 { CommandIntraPeriod, "-intra", "g", "Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames.", 1},
219 { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1},
220 { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0},
221 { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0},
222 { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0},
223 { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1},
224 { CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1},
225 { CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0},
226 { CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval <ms>", 1},
227 { CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1},
228 { CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1},
229 { CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0},
230 { CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0},
231 { CommandIMV, "-vectors", "x", "Output filename <filename> for inline motion vectors", 1 },
232 { CommandCamSelect, "-camselect", "cs", "Select camera <number>. Default 0", 1 },
233 { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0},
234 { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1},
235 { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1},
238 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
241 static void dump_state(RASPIVID_STATE *state);
244 * Assign a default set of parameters to the state passed in
246 * @param state Pointer to state structure to assign defaults to
248 void raspicapture_default_config(RASPIVID_CONFIG *config)
256 // Default everything to zero
257 memset(config, 0, sizeof(RASPIVID_CONFIG));
259 // Now set anything non-zero
260 config->timeout = 5000; // 5s delay before take image
261 config->width = 1920; // Default to 1080p
262 config->height = 1080;
263 config->bitrate = 17000000; // This is a decent default bitrate for 1080p
264 config->fps_n = VIDEO_FRAME_RATE_NUM;
265 config->fps_d = VIDEO_FRAME_RATE_DEN;
266 config->intraperiod = -1; // Not set
267 config->quantisationParameter = 0;
268 config->demoMode = 0;
269 config->demoInterval = 250; // ms
270 config->immutableInput = 1;
271 config->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
272 config->encoding = MMAL_ENCODING_H264;
274 config->bInlineHeaders = 0;
276 config->inlineMotionVectors = 0;
278 config->cameraNum = 0;
279 config->settings = 0;
280 config->sensor_mode = 0;
282 config->intra_refresh_type = -1;
284 // Setup preview window defaults
285 raspipreview_set_defaults(&config->preview_parameters);
287 // Set up the camera_parameters to default
288 raspicamcontrol_set_defaults(&config->camera_parameters);
294 * Dump image state parameters to printf. Used for debugging
296 * @param state Pointer to state structure to assign defaults to
298 static void dump_state(RASPIVID_STATE *state)
300 RASPIVID_CONFIG *config;
308 config = &state->config;
310 fprintf(stderr, "Width %d, Height %d\n", config->width, config->height);
311 fprintf(stderr, "bitrate %d, framerate %d/%d, time delay %d\n",
312 config->bitrate, config->fps_n, config->fps_d, config->timeout);
313 //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
315 raspipreview_dump_parameters(&config->preview_parameters);
316 raspicamcontrol_dump_parameters(&config->camera_parameters);
321 * Parse the incoming command line and put resulting parameters in to the state
323 * @param argc Number of arguments in command line
324 * @param argv Array of pointers to strings from command line
325 * @param state Pointer to state structure to assign any discovered parameters to
326 * @return Non-0 if failed for some reason, 0 otherwise
328 static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
330 // Parse the command line arguments.
331 // We are looking for --<something> or -<abreviation of something>
336 for (i = 1; i < argc && valid; i++)
338 int command_id, num_parameters;
343 if (argv[i][0] != '-')
349 // Assume parameter is valid until proven otherwise
352 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
354 // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
355 if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
358 // We are now dealing with a command line option
362 display_valid_parameters(basename(argv[0]));
365 case CommandWidth: // Width > 0
366 if (sscanf(argv[i + 1], "%u", &state->width) != 1)
372 case CommandHeight: // Height > 0
373 if (sscanf(argv[i + 1], "%u", &state->height) != 1)
379 case CommandBitrate: // 1-100
380 if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1)
382 if (state->bitrate > MAX_BITRATE)
384 state->bitrate = MAX_BITRATE;
393 case CommandOutput: // output filename
395 int len = strlen(argv[i + 1]);
398 state->filename = malloc(len + 1);
399 vcos_assert(state->filename);
401 strncpy(state->filename, argv[i + 1], len+1);
409 case CommandVerbose: // display lots of data during run
413 case CommandTimeout: // Time to run viewfinder/capture
415 if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
417 // Ensure that if previously selected a waitMethod we dont overwrite it
418 if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE)
419 state->waitMethod = WAIT_METHOD_FOREVER;
428 case CommandDemoMode: // Run in demo mode - no capture
430 // Demo mode might have a timing parameter
431 // so check if a) we have another parameter, b) its not the start of the next option
432 if (i + 1 < argc && argv[i+1][0] != '-')
434 if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
436 // TODO : What limits do we need for timeout?
437 if (state->demoInterval == 0)
438 state->demoInterval = 250; // ms
454 case CommandFramerate: // fps to record
456 if (sscanf(argv[i + 1], "%u", &state->framerate) == 1)
458 // TODO : What limits do we need for fps 1 - 30 - 120??
466 case CommandPreviewEnc:
467 state->immutableInput = 0;
470 case CommandIntraPeriod: // key frame rate
472 if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1)
479 case CommandQP: // quantisation parameter
481 if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1)
488 case CommandProfile: // H264 profile
490 state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size);
492 if( state->profile == -1)
493 state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
499 case CommandInlineHeaders: // H264 inline headers
501 state->bInlineHeaders = 1;
507 if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2)
511 if (state->onTime < 1000)
512 state->onTime = 1000;
514 if (state->offTime < 1000)
515 state->offTime = 1000;
517 state->waitMethod = WAIT_METHOD_TIMED;
524 case CommandKeypress:
525 state->waitMethod = WAIT_METHOD_KEYPRESS;
529 state->waitMethod = WAIT_METHOD_SIGNAL;
530 // Reenable the signal
531 signal(SIGUSR1, signal_handler);
534 case CommandInitialState:
536 state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size);
538 if( state->bCapturing == -1)
539 state->bCapturing = 0;
545 case CommandSegmentFile: // Segment file in to chunks of specified time
547 if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1)
549 // Must enable inline headers for this to work
550 state->bInlineHeaders = 1;
558 case CommandSegmentWrap: // segment wrap value
560 if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1)
567 case CommandSegmentStart: // initial segment number
569 if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap)))
576 case CommandSplitWait: // split files on restart
578 // Must enable inline headers for this to work
579 state->bInlineHeaders = 1;
580 state->splitWait = 1;
584 case CommandCircular:
586 state->bCircularBuffer = 1;
590 case CommandIMV: // output filename
592 state->inlineMotionVectors = 1;
593 int len = strlen(argv[i + 1]);
596 state->imv_filename = malloc(len + 1);
597 vcos_assert(state->imv_filename);
598 if (state->imv_filename)
599 strncpy(state->imv_filename, argv[i + 1], len+1);
606 case CommandCamSelect: //Select camera input port
608 if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1)
617 case CommandSettings:
621 case CommandSensorMode:
623 if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1)
632 case CommandIntraRefreshType:
634 state->config.intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size);
641 // Try parsing for any image specific parameters
642 // result indicates how many parameters were used up, 0,1,2
643 // but we adjust by -1 as we have used one already
644 const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
645 int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
647 // Still unused, try preview options
649 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
652 // If no parms were used, this must be a bad parameters
665 fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
669 // Always disable verbose if output going to stdout
670 if (state->filename && state->filename[0] == '-')
679 * Display usage information for the application to stdout
681 * @param app_name String to display as the application name
683 static void display_valid_parameters(char *app_name)
687 fprintf(stderr, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n");
688 fprintf(stderr, "\nusage: %s [options]\n\n", app_name);
690 fprintf(stderr, "Image parameter commands\n\n");
692 raspicli_display_help(cmdline_commands, cmdline_commands_size);
695 fprintf(stderr, "\n\nH264 Profile options :\n%s", profile_map[0].mode );
697 for (i=1;i<profile_map_size;i++)
699 fprintf(stderr, ",%s", profile_map[i].mode);
702 fprintf(stderr, "\n");
704 // Intra refresh options
705 fprintf(stderr, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode );
707 for (i=1;i<intra_refresh_map_size;i++)
709 fprintf(stderr, ",%s", intra_refresh_map[i].mode);
712 fprintf(stderr, "\n");
714 // Help for preview options
715 raspipreview_display_help();
717 // Now display any help information from the camcontrol code
718 raspicamcontrol_display_help();
720 fprintf(stderr, "\n");
727 * buffer header callback function for camera control
729 * Callback will dump buffer data to the specific file
731 * @param port Pointer to port from which callback originated
732 * @param buffer mmal buffer header pointer
734 static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
736 if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
738 MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data;
739 switch (param->hdr.id) {
740 case MMAL_PARAMETER_CAMERA_SETTINGS:
742 MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param;
743 vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u",
745 settings->analog_gain.num, settings->analog_gain.den,
746 settings->digital_gain.num, settings->digital_gain.den);
747 vcos_log_error("AWB R=%u/%u, B=%u/%u",
748 settings->awb_red_gain.num, settings->awb_red_gain.den,
749 settings->awb_blue_gain.num, settings->awb_blue_gain.den
757 vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
760 mmal_buffer_header_release(buffer);
765 * Open a file based on the settings in state
767 * @param state Pointer to state
769 static FILE *open_filename(RASPIVID_STATE *pState)
771 FILE *new_handle = NULL;
772 char *tempname = NULL, *filename = NULL;
774 if (pState->segmentSize || pState->splitWait)
776 // Create a new filename string
777 asprintf(&tempname, pState->filename, pState->segmentNumber);
782 filename = pState->filename;
786 new_handle = fopen(filename, "wb");
791 fprintf(stderr, "Opening output file \"%s\"\n", filename);
793 fprintf(stderr, "Failed to open new file \"%s\"\n", filename);
803 * Open a file based on the settings in state
805 * This time for the imv output file
807 * @param state Pointer to state
809 static FILE *open_imv_filename(RASPIVID_STATE *pState)
811 FILE *new_handle = NULL;
812 char *tempname = NULL, *filename = NULL;
814 if (pState->segmentSize || pState->splitWait)
816 // Create a new filename string
817 asprintf(&tempname, pState->imv_filename, pState->segmentNumber);
822 filename = pState->imv_filename;
826 new_handle = fopen(filename, "wb");
831 fprintf(stderr, "Opening imv output file \"%s\"\n", filename);
833 fprintf(stderr, "Failed to open new imv file \"%s\"\n", filename);
844 * Update any annotation data specific to the video.
845 * This simply passes on the setting from cli, or
846 * if application defined annotate requested, updates
847 * with the H264 parameters
849 * @param state Pointer to state control struct
852 static void update_annotation_data(RASPIVID_STATE *state)
854 RASPIVID_CONFIG *config = &state->config;
856 // So, if we have asked for a application supplied string, set it to the H264 parameters
857 if (config->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT)
860 const char *refresh = raspicli_unmap_xref(config->intra_refresh_type, intra_refresh_map, intra_refresh_map_size);
862 asprintf(&text, "%dk,%ff,%s,%d,%s",
863 config->bitrate / 1000, ((float)(config->fps_n) / config->fps_d),
864 refresh ? refresh : "(none)",
866 raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
868 raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text,
869 config->camera_parameters.annotate_text_size,
870 config->camera_parameters.annotate_text_colour,
871 config->camera_parameters.annotate_bg_colour);
877 raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate,
878 config->camera_parameters.annotate_string,
879 config->camera_parameters.annotate_text_size,
880 config->camera_parameters.annotate_text_colour,
881 config->camera_parameters.annotate_bg_colour);
888 * buffer header callback function for encoder
890 * Callback will dump buffer data to the specific file
892 * @param port Pointer to port from which callback originated
893 * @param buffer mmal buffer header pointer
895 static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
897 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
898 RASPIVID_STATE *state = pData->state;
899 int64_t current_time;
901 // All our segment times based on the receipt of the first encoder callback
902 if (state->base_time == -1)
903 state->base_time = vcos_getmicrosecs64()/1000;
907 vcos_log_error("Received a encoder buffer callback with no state");
908 // release buffer back to the pool
909 mmal_buffer_header_release(buffer);
913 current_time = vcos_getmicrosecs64()/1000;
914 if (state->base_time == -1)
915 state->base_time = current_time;
917 // See if the second count has changed and we need to update any annotation
918 if (current_time/1000 != state->last_second)
920 update_annotation_data(state);
921 state->last_second = current_time/1000;
924 /* Send buffer to GStreamer element for pushing to the pipeline */
925 mmal_queue_put(state->encoded_buffer_q, buffer);
929 raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp,
930 GstClock *clock, GstClockTime base_time)
933 MMAL_BUFFER_HEADER_T *buffer;
934 GstFlowReturn ret = GST_FLOW_ERROR;
935 /* No timestamps if no clockm or invalid PTS */
936 GstClockTime gst_pts = GST_CLOCK_TIME_NONE;
938 /* FIXME: Use our own interruptible cond wait: */
939 buffer = mmal_queue_wait(state->encoded_buffer_q);
942 if (G_LIKELY (clock)) {
943 MMAL_PARAMETER_INT64_T param;
944 GstClockTime runtime;
946 runtime = gst_clock_get_time (clock) - base_time;
948 param.hdr.id = MMAL_PARAMETER_SYSTEM_TIME;
949 param.hdr.size = sizeof(param);
952 mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr);
954 if (param.value != -1 && param.value >= buffer->pts) {
955 /* Convert microsecond RPi TS to GStreamer clock: */
956 GstClockTime offset = (param.value - buffer->pts) * 1000;
957 if (runtime >= offset)
958 gst_pts = runtime - offset;
960 GST_LOG ("Buf (uS) PTS %" G_GINT64_FORMAT " DTS %" G_GINT64_FORMAT
961 " STC %" G_GINT64_FORMAT " (latency %" G_GINT64_FORMAT
962 "uS) TS %" GST_TIME_FORMAT,
963 buffer->pts, buffer->dts, param.value, param.value - buffer->pts,
964 GST_TIME_ARGS (gst_pts));
968 mmal_buffer_header_mem_lock(buffer);
969 buf = gst_buffer_new_allocate(NULL, buffer->length, NULL);
971 /* FIXME: Can we avoid copies and give MMAL our own buffers to fill? */
972 GST_BUFFER_PTS(buf) = gst_pts;
973 gst_buffer_fill(buf, 0, buffer->data, buffer->length);
977 mmal_buffer_header_mem_unlock(buffer);
980 // release buffer back to the pool
981 mmal_buffer_header_release(buffer);
983 // and send one back to the port (if still open)
984 if (state->encoder_output_port->is_enabled)
986 MMAL_STATUS_T status = MMAL_SUCCESS;
988 buffer = mmal_queue_get(state->encoder_pool->queue);
990 status = mmal_port_send_buffer(state->encoder_output_port, buffer);
992 if (!buffer || status != MMAL_SUCCESS) {
993 vcos_log_error("Unable to return a buffer to the encoder port");
994 ret = GST_FLOW_ERROR;
1002 * Create the camera component, set up its ports
1004 * @param state Pointer to state control struct
1006 * @return MMAL_SUCCESS if all OK, something else otherwise
1009 static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
1011 MMAL_COMPONENT_T *camera = NULL;
1012 MMAL_STATUS_T status;
1013 RASPIVID_CONFIG *config = &state->config;
1015 /* Create the component */
1016 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
1018 if (status != MMAL_SUCCESS)
1020 vcos_log_error("Failed to create camera component");
1024 MMAL_PARAMETER_INT32_T camera_num =
1025 {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum};
1027 status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
1029 if (status != MMAL_SUCCESS)
1031 vcos_log_error("Could not select camera : error %d", status);
1035 if (!camera->output_num)
1037 status = MMAL_ENOSYS;
1038 vcos_log_error("Camera doesn't have output ports");
1042 status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
1044 if (status != MMAL_SUCCESS)
1046 vcos_log_error("Could not set sensor mode : error %d", status);
1050 if (config->settings)
1052 MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request =
1053 {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)},
1054 MMAL_PARAMETER_CAMERA_SETTINGS, 1};
1056 status = mmal_port_parameter_set(camera->control, &change_event_request.hdr);
1057 if ( status != MMAL_SUCCESS )
1059 vcos_log_error("No camera settings events");
1063 // Enable the camera, and tell it its control callback function
1064 status = mmal_port_enable(camera->control, camera_control_callback);
1066 if (status != MMAL_SUCCESS)
1068 vcos_log_error("Unable to enable control port : error %d", status);
1072 state->camera_component = camera;
1078 mmal_component_destroy(camera);
1084 raspi_capture_set_format_and_start(RASPIVID_STATE *state)
1086 MMAL_COMPONENT_T *camera = NULL;
1087 MMAL_STATUS_T status;
1088 MMAL_ES_FORMAT_T *format;
1089 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
1090 RASPIVID_CONFIG *config = &state->config;
1092 // set up the camera configuration
1094 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
1096 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
1097 .max_stills_w = config->width,
1098 .max_stills_h = config->height,
1100 .one_shot_stills = 0,
1101 .max_preview_video_w = config->width,
1102 .max_preview_video_h = config->height,
1103 .num_preview_video_frames = 3,
1104 .stills_capture_circular_buffer_height = 0,
1105 .fast_preview_resume = 0,
1106 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC
1109 camera = state->camera_component;
1110 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
1111 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
1112 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
1114 mmal_port_parameter_set(camera->control, &cam_config.hdr);
1116 // Now set up the port formats
1118 // Set the encode format on the Preview port
1119 // HW limitations mean we need the preview to be the same size as the required recorded output
1121 format = preview_port->format;
1123 if(config->camera_parameters.shutter_speed > 6000000)
1125 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1126 { 50, 1000 }, {166, 1000}};
1127 mmal_port_parameter_set(preview_port, &fps_range.hdr);
1129 else if(config->camera_parameters.shutter_speed > 1000000)
1131 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1132 { 166, 1000 }, {999, 1000}};
1133 mmal_port_parameter_set(preview_port, &fps_range.hdr);
1136 //enable dynamic framerate if necessary
1137 if (config->camera_parameters.shutter_speed)
1139 if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed)
1143 GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement");
1147 format->encoding = MMAL_ENCODING_OPAQUE;
1148 format->encoding_variant = MMAL_ENCODING_I420;
1150 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1151 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1152 format->es->video.crop.x = 0;
1153 format->es->video.crop.y = 0;
1154 format->es->video.crop.width = config->width;
1155 format->es->video.crop.height = config->height;
1156 format->es->video.frame_rate.num = config->fps_n;
1157 format->es->video.frame_rate.den = config->fps_d;
1159 status = mmal_port_format_commit(preview_port);
1161 if (status != MMAL_SUCCESS)
1163 vcos_log_error("camera viewfinder format couldn't be set");
1167 // Set the encode format on the video port
1168 format = video_port->format;
1170 if(config->camera_parameters.shutter_speed > 6000000)
1172 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1173 { 50, 1000 }, {166, 1000}};
1174 mmal_port_parameter_set(video_port, &fps_range.hdr);
1176 else if(config->camera_parameters.shutter_speed > 1000000)
1178 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1179 { 167, 1000 }, {999, 1000}};
1180 mmal_port_parameter_set(video_port, &fps_range.hdr);
1183 /* If encoding, set opaque tunneling format */
1184 if (state->encoder_component) {
1185 format->encoding = MMAL_ENCODING_OPAQUE;
1186 format->encoding_variant = MMAL_ENCODING_I420;
1189 format->encoding = config->encoding;
1190 format->encoding_variant = config->encoding;
1193 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1194 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1195 format->es->video.crop.x = 0;
1196 format->es->video.crop.y = 0;
1197 format->es->video.crop.width = config->width;
1198 format->es->video.crop.height = config->height;
1199 format->es->video.frame_rate.num = config->fps_n;
1200 format->es->video.frame_rate.den = config->fps_d;
1202 status = mmal_port_format_commit(video_port);
1204 if (status != MMAL_SUCCESS)
1206 vcos_log_error("camera video format couldn't be set");
1210 // Ensure there are enough buffers to avoid dropping frames
1211 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1212 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1215 // Set the encode format on the still port
1217 format = still_port->format;
1219 format->encoding = MMAL_ENCODING_OPAQUE;
1220 format->encoding_variant = MMAL_ENCODING_I420;
1222 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1223 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1224 format->es->video.crop.x = 0;
1225 format->es->video.crop.y = 0;
1226 format->es->video.crop.width = config->width;
1227 format->es->video.crop.height = config->height;
1228 format->es->video.frame_rate.num = 0;
1229 format->es->video.frame_rate.den = 1;
1231 status = mmal_port_format_commit(still_port);
1233 if (status != MMAL_SUCCESS)
1235 vcos_log_error("camera still format couldn't be set");
1239 /* Ensure there are enough buffers to avoid dropping frames */
1240 if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1241 still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1243 /* Enable component */
1244 status = mmal_component_enable(camera);
1246 if (status != MMAL_SUCCESS)
1248 vcos_log_error("camera component couldn't be enabled");
1252 raspicamcontrol_set_all_parameters(camera, &config->camera_parameters);
1254 update_annotation_data(state);
1256 if (config->verbose)
1257 fprintf(stderr, "Camera component done\n");
1264 mmal_component_destroy(camera);
1270 * Destroy the camera component
1272 * @param state Pointer to state control struct
1275 static void destroy_camera_component(RASPIVID_STATE *state)
1277 if (state->camera_component)
1279 mmal_component_destroy(state->camera_component);
1280 state->camera_component = NULL;
1284 gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state)
1286 MMAL_PORT_T *encoder_output = NULL;
1287 MMAL_STATUS_T status;
1288 MMAL_PARAMETER_BOOLEAN_T param = {{ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, sizeof(param)}, 1};
1290 if (state->encoder_component)
1293 encoder_output = state->encoder_component->output[0];
1294 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1295 if (status != MMAL_SUCCESS)
1297 vcos_log_error("Unable to request I-frame");
1304 * Create the encoder component, set up its ports
1306 * @param state Pointer to state control struct
1308 * @return MMAL_SUCCESS if all OK, something else otherwise
1311 static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
1313 MMAL_COMPONENT_T *encoder = 0;
1314 MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
1315 MMAL_STATUS_T status;
1316 RASPIVID_CONFIG *config = &state->config;
1318 gboolean encoded_format =
1319 (config->encoding == MMAL_ENCODING_H264 ||
1320 config->encoding == MMAL_ENCODING_MJPEG ||
1321 config->encoding == MMAL_ENCODING_JPEG);
1323 if (!encoded_format)
1324 return MMAL_SUCCESS;
1326 if (config->encoding == MMAL_ENCODING_JPEG)
1327 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
1329 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
1331 if (status != MMAL_SUCCESS) {
1332 vcos_log_error("Unable to create video encoder component");
1336 if (!encoder->input_num || !encoder->output_num)
1338 status = MMAL_ENOSYS;
1339 vcos_log_error("Video encoder doesn't have input/output ports");
1343 encoder_input = encoder->input[0];
1344 encoder_output = encoder->output[0];
1346 // We want same format on input and output
1347 mmal_format_copy(encoder_output->format, encoder_input->format);
1349 // Configure desired encoding
1350 encoder_output->format->encoding = config->encoding;
1352 encoder_output->format->bitrate = config->bitrate;
1354 if (config->encoding == MMAL_ENCODING_H264)
1355 encoder_output->buffer_size = encoder_output->buffer_size_recommended;
1357 encoder_output->buffer_size = 256<<10;
1359 if (encoder_output->buffer_size < encoder_output->buffer_size_min)
1360 encoder_output->buffer_size = encoder_output->buffer_size_min;
1362 encoder_output->buffer_num = encoder_output->buffer_num_recommended;
1364 if (encoder_output->buffer_num < encoder_output->buffer_num_min)
1365 encoder_output->buffer_num = encoder_output->buffer_num_min;
1367 GST_DEBUG ("encoder wants %d buffers of size %u",
1368 (guint)encoder_output->buffer_num, (guint)encoder_output->buffer_size);
1370 // We need to set the frame rate on output to 0, to ensure it gets
1371 // updated correctly from the input framerate when port connected
1372 encoder_output->format->es->video.frame_rate.num = 0;
1373 encoder_output->format->es->video.frame_rate.den = 1;
1375 // Commit the port changes to the output port
1376 status = mmal_port_format_commit(encoder_output);
1377 if (status != MMAL_SUCCESS) {
1378 vcos_log_error("Unable to set format on video encoder output port");
1382 // Set the rate control parameter
1385 MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT};
1386 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1387 if (status != MMAL_SUCCESS)
1389 vcos_log_error("Unable to set ratecontrol");
1395 if (config->encoding == MMAL_ENCODING_H264 && config->intraperiod != -1)
1397 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
1398 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1399 if (status != MMAL_SUCCESS)
1401 vcos_log_error("Unable to set intraperiod");
1406 if (config->encoding == MMAL_ENCODING_H264 && config->quantisationParameter)
1408 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
1409 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1410 if (status != MMAL_SUCCESS)
1412 vcos_log_error("Unable to set initial QP");
1416 MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
1417 status = mmal_port_parameter_set(encoder_output, ¶m2.hdr);
1418 if (status != MMAL_SUCCESS)
1420 vcos_log_error("Unable to set min QP");
1424 MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
1425 status = mmal_port_parameter_set(encoder_output, ¶m3.hdr);
1426 if (status != MMAL_SUCCESS)
1428 vcos_log_error("Unable to set max QP");
1433 if (config->encoding == MMAL_ENCODING_H264)
1435 MMAL_PARAMETER_VIDEO_PROFILE_T param;
1436 param.hdr.id = MMAL_PARAMETER_PROFILE;
1437 param.hdr.size = sizeof(param);
1439 param.profile[0].profile = config->profile;
1440 param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
1442 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1443 if (status != MMAL_SUCCESS)
1445 vcos_log_error("Unable to set H264 profile");
1450 if (config->encoding != MMAL_ENCODING_JPEG)
1452 if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS)
1454 vcos_log_error("Unable to set immutable input flag");
1455 // Continue rather than abort..
1458 //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested
1459 if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS)
1461 vcos_log_error("failed to set INLINE HEADER FLAG parameters");
1462 // Continue rather than abort..
1466 if (config->encoding == MMAL_ENCODING_H264)
1468 //set INLINE VECTORS flag to request motion vector estimates
1469 if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS)
1471 vcos_log_error("failed to set INLINE VECTORS parameters");
1472 // Continue rather than abort..
1475 // Adaptive intra refresh settings
1476 if (config->intra_refresh_type != -1)
1478 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1480 /* Need to memset, apparently mmal_port_parameter_get()
1481 * doesn't retrieve all parameters, causing random failures
1484 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1486 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1487 param.hdr.size = sizeof(param);
1489 // Get first so we don't overwrite anything unexpectedly
1490 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1492 param.refresh_mode = config->intra_refresh_type;
1494 //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
1495 // param.cir_mbs = 10;
1497 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1498 if (status != MMAL_SUCCESS)
1500 vcos_log_error("Unable to set H264 intra-refresh values");
1506 if (config->encoding == MMAL_ENCODING_JPEG)
1508 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, config->jpegQuality);
1509 if (status != MMAL_SUCCESS) {
1510 vcos_log_error("Unable to set JPEG quality");
1511 // Continue after warning
1514 #ifdef MMAL_PARAMETER_JPEG_RESTART_INTERVAL
1515 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, config->jpegRestartInterval);
1516 if (status != MMAL_SUCCESS) {
1517 vcos_log_error("Unable to set JPEG restart interval");
1518 // Continue after warning
1524 status = mmal_component_enable(encoder);
1526 if (status != MMAL_SUCCESS)
1528 vcos_log_error("Unable to enable video encoder component");
1532 state->encoder_component = encoder;
1534 if (config->verbose)
1535 fprintf(stderr, "Encoder component done\n");
1541 mmal_component_destroy(encoder);
1543 state->encoder_component = NULL;
1549 * Destroy the encoder component
1551 * @param state Pointer to state control struct
1554 static void destroy_encoder_component(RASPIVID_STATE *state)
1556 /* Empty the buffer header q */
1557 if (state->encoded_buffer_q) {
1558 while (mmal_queue_length(state->encoded_buffer_q)) {
1559 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q);
1560 mmal_buffer_header_release(buffer);
1564 // Get rid of any port buffers first
1565 if (state->encoder_pool)
1567 mmal_port_pool_destroy(state->encoder_output_port, state->encoder_pool);
1568 state->encoder_pool = NULL;
1571 if (state->encoder_component) {
1573 mmal_component_destroy(state->encoder_component);
1574 state->encoder_component = NULL;
1579 * Connect two specific ports together
1581 * @param output_port Pointer the output port
1582 * @param input_port Pointer the input port
1583 * @param Pointer to a mmal connection pointer, reassigned if function successful
1584 * @return Returns a MMAL_STATUS_T giving result of operation
1587 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
1589 MMAL_STATUS_T status;
1591 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
1593 if (status == MMAL_SUCCESS)
1595 status = mmal_connection_enable(*connection);
1596 if (status != MMAL_SUCCESS)
1597 mmal_connection_destroy(*connection);
1604 * Checks if specified port is valid and enabled, then disables it
1606 * @param port Pointer the port
1609 static void check_disable_port(MMAL_PORT_T *port)
1611 if (port && port->is_enabled)
1612 mmal_port_disable(port);
1615 void raspicapture_init()
1619 // Register our application with the logging system
1620 vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
1624 raspi_capture_setup(RASPIVID_CONFIG *config)
1626 // Our main data storage vessel..
1627 RASPIVID_STATE *state;
1629 MMAL_STATUS_T status = MMAL_SUCCESS;
1631 /* Default everything to zero */
1632 state = calloc(1, sizeof(RASPIVID_STATE));
1634 /* Apply passed in config */
1635 state->config = *config;
1637 /* Initialize timestamping */
1638 state->base_time = state->last_second = -1;
1640 /* So far, all we can do is create the camera component. Actual
1641 * config and connection of encoders etc happens in _start()
1643 // OK, we have a nice set of parameters. Now set up our components
1644 // We have three components. Camera, Preview and encoder.
1646 if ((status = create_camera_component(state)) != MMAL_SUCCESS)
1648 vcos_log_error("%s: Failed to create camera component", __func__);
1652 if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS)
1654 vcos_log_error("%s: Failed to create preview component", __func__);
1655 destroy_camera_component(state);
1659 state->encoded_buffer_q = mmal_queue_create();
1665 raspi_capture_start(RASPIVID_STATE *state)
1667 MMAL_STATUS_T status = MMAL_SUCCESS;
1668 RASPIVID_CONFIG *config = &state->config;
1670 MMAL_PORT_T *camera_preview_port = NULL;
1671 MMAL_PORT_T *preview_input_port = NULL;
1672 MMAL_PORT_T *encoder_input_port = NULL;
1676 if ((status = create_encoder_component(state)) != MMAL_SUCCESS) {
1677 vcos_log_error("%s: Failed to create encode component", __func__);
1681 if (config->verbose)
1686 state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1687 state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1688 camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1689 preview_input_port = state->preview_state.preview_component->input[0];
1691 if (state->encoder_component) {
1692 encoder_input_port = state->encoder_component->input[0];
1693 state->encoder_output_port = state->encoder_component->output[0];
1695 state->encoder_output_port = state->camera_video_port;
1698 if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) {
1702 GST_DEBUG ("Creating pool of %d buffers of size %d",
1703 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1704 /* Create pool of buffer headers for the output port to consume */
1705 pool = mmal_port_pool_create(state->encoder_output_port,
1706 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1709 vcos_log_error("Failed to create buffer header pool for encoder output port %s",
1710 state->encoder_output_port->name);
1713 state->encoder_pool = pool;
1715 if (state->config.verbose)
1716 fprintf(stderr, "Starting component connection stage\n");
1718 if (config->preview_parameters.wantPreview )
1720 if (config->verbose)
1722 fprintf(stderr, "Connecting camera preview port to preview input port\n");
1723 fprintf(stderr, "Starting video preview\n");
1726 // Connect camera to preview
1727 status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection);
1728 if (status != MMAL_SUCCESS)
1730 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1735 if (state->encoder_component) {
1736 if (config->verbose)
1737 fprintf(stderr, "Connecting camera video port to encoder input port\n");
1739 // Now connect the camera to the encoder
1740 status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection);
1741 if (status != MMAL_SUCCESS)
1743 if (config->preview_parameters.wantPreview )
1744 mmal_connection_destroy(state->preview_connection);
1745 vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
1750 // Set up our userdata - this is passed though to the callback where we need the information.
1751 state->callback_data.state = state;
1752 state->callback_data.abort = 0;
1754 state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
1756 if (config->verbose)
1757 fprintf(stderr, "Enabling encoder output port\n");
1759 // Enable the encoder output port and tell it its callback function
1760 status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback);
1761 if (status != MMAL_SUCCESS)
1763 vcos_log_error("Failed to setup encoder output");
1767 if (config->demoMode)
1769 // Run for the user specific time..
1770 int num_iterations = config->timeout / config->demoInterval;
1773 if (config->verbose)
1774 fprintf(stderr, "Running in demo mode\n");
1776 for (i=0;config->timeout == 0 || i<num_iterations;i++)
1778 raspicamcontrol_cycle_test(state->camera_component);
1779 vcos_sleep(state->config.demoInterval);
1783 if (config->verbose)
1784 fprintf(stderr, "Starting video capture\n");
1786 if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1791 // Send all the buffers to the encoder output port
1793 int num = mmal_queue_length(state->encoder_pool->queue);
1797 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
1800 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1802 if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS)
1803 vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
1808 // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
1809 // out of storage space)
1810 // Going to check every ABORT_INTERVAL milliseconds
1813 for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
1815 vcos_sleep(ABORT_INTERVAL);
1816 if (state->callback_data.abort)
1820 if (config->verbose)
1821 fprintf(stderr, "Finished capture\n");
1824 return (status == MMAL_SUCCESS);
1827 raspi_capture_stop(state);
1829 if (status != MMAL_SUCCESS) {
1830 mmal_status_to_int(status);
1831 raspicamcontrol_check_configuration(128);
1838 raspi_capture_stop(RASPIVID_STATE *state)
1840 RASPIVID_CONFIG *config = &state->config;
1842 if (config->verbose)
1843 fprintf(stderr, "Closing down\n");
1845 if (config->preview_parameters.wantPreview )
1846 mmal_connection_destroy(state->preview_connection);
1848 // Disable all our ports that are not handled by connections
1849 check_disable_port(state->camera_still_port);
1850 check_disable_port(state->encoder_output_port);
1852 if (state->encoder_component) {
1853 mmal_connection_destroy(state->encoder_connection);
1854 mmal_component_disable(state->encoder_component);
1855 destroy_encoder_component(state);
1860 raspi_capture_free(RASPIVID_STATE *state)
1862 RASPIVID_CONFIG *config = &state->config;
1864 // Can now close our file. Note disabling ports may flush buffers which causes
1865 // problems if we have already closed the file!
1866 if (state->output_file && state->output_file != stdout)
1867 fclose(state->output_file);
1869 /* Disable components */
1870 if (state->encoder_component)
1871 mmal_component_disable(state->encoder_component);
1873 if (state->preview_state.preview_component)
1874 mmal_component_disable(state->preview_state.preview_component);
1876 if (state->camera_component)
1877 mmal_component_disable(state->camera_component);
1879 destroy_encoder_component(state);
1880 raspipreview_destroy(&state->preview_state);
1881 destroy_camera_component(state);
1883 if (state->encoded_buffer_q) {
1884 mmal_queue_destroy(state->encoded_buffer_q);
1885 state->encoded_buffer_q = NULL;
1888 if (config->verbose)
1889 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1895 raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic)
1897 MMAL_STATUS_T status;
1898 RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters;
1899 MMAL_COMPONENT_T *camera = state->camera_component;
1901 /* Store the new config */
1902 state->config = *config;
1906 if (state->encoder_component && config->change_flags & PROP_CHANGE_ENCODING) {
1907 /* BITRATE or QUANT or KEY Interval, intra refresh */
1908 MMAL_COMPONENT_T *encoder = state->encoder_component;
1909 MMAL_PORT_T *encoder_output = encoder->output[0];
1911 #if 0 /* not dynamically change-able */
1912 encoder_output->format->bitrate = config->bitrate;
1913 status = mmal_port_format_commit(encoder_output);
1914 if (status != MMAL_SUCCESS) {
1915 vcos_log_warn("Cannot change bitrate dynamically");
1920 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
1921 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1922 if (status != MMAL_SUCCESS)
1923 vcos_log_warn("Unable to change intraperiod dynamically");
1926 #if 0 /* not dynamically change-able */
1928 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
1929 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1930 if (status != MMAL_SUCCESS)
1931 vcos_log_warn("Unable to change Initial Quantisation Parameter dynamically");
1933 MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
1934 status = mmal_port_parameter_set(encoder_output, ¶m2.hdr);
1935 if (status != MMAL_SUCCESS)
1936 vcos_log_warn("Unable to change Minimum Quantisation Parameter dynamically");
1938 MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
1939 status = mmal_port_parameter_set(encoder_output, ¶m3.hdr);
1940 if (status != MMAL_SUCCESS)
1941 vcos_log_warn("Unable to change Maximum Quantisation Parameter dynamically");
1945 // Adaptive intra refresh settings
1946 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1947 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1948 param.hdr.size = sizeof(param);
1950 // Get first so we don't overwrite anything unexpectedly
1951 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1952 if (state != MMAL_SUCCESS) {
1953 /* Need to memset, apparently mmal_port_parameter_get()
1954 * doesn't retrieve all parameters, causing random failures
1955 * when we set it. On older firmware the get fails.
1957 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1959 param.refresh_mode = config->intra_refresh_type;
1961 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1962 if (status != MMAL_SUCCESS)
1963 vcos_log_warn("Unable to set H264 intra-refresh values dynamically");
1967 if (config->change_flags & PROP_CHANGE_PREVIEW) {
1968 /* Preview settings or fullscreen */
1969 status = raspipreview_update_config (&state->preview_state,
1970 &config->preview_parameters);
1971 if (status != MMAL_SUCCESS)
1972 vcos_log_warn("Unable to change preview config dynamically");
1974 if (config->change_flags & PROP_CHANGE_COLOURBALANCE) {
1975 raspicamcontrol_set_saturation(camera, params->saturation);
1976 raspicamcontrol_set_sharpness(camera, params->sharpness);
1977 raspicamcontrol_set_contrast(camera, params->contrast);
1978 raspicamcontrol_set_brightness(camera, params->brightness);
1980 if (config->change_flags & PROP_CHANGE_SENSOR_SETTINGS) {
1981 /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */
1982 raspicamcontrol_set_ISO(camera, params->ISO);
1983 raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation);
1984 raspicamcontrol_set_exposure_mode(camera, params->exposureMode);
1985 raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode);
1986 raspicamcontrol_set_shutter_speed(camera, params->shutter_speed);
1987 raspicamcontrol_set_DRC(camera, params->drc_level);
1989 /* Can we change sensor mode on the fly? Disable if not */
1990 status = mmal_port_parameter_set_uint32(camera->control,
1991 MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
1992 if (status != MMAL_SUCCESS)
1993 vcos_log_warn("Unable to change sensor mode dynamically");
1995 if (config->change_flags & PROP_CHANGE_VIDEO_STABILISATION) {
1996 raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation);
1998 if (config->change_flags & PROP_CHANGE_AWB) {
1999 raspicamcontrol_set_awb_mode(camera, params->awbMode);
2000 raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b);
2002 if (config->change_flags & PROP_CHANGE_IMAGE_COLOUR_EFFECT) {
2003 raspicamcontrol_set_imageFX(camera, params->imageEffect);
2004 raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects);
2006 if (config->change_flags & PROP_CHANGE_ORIENTATION) {
2007 raspicamcontrol_set_rotation(camera, params->rotation);
2008 raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
2010 if (config->change_flags & PROP_CHANGE_ROI) {
2011 raspicamcontrol_set_ROI(camera, params->roi);
2013 if (config->change_flags & PROP_CHANGE_ANNOTATION)
2014 update_annotation_data(state);