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 if (state->encoder_component) {
1565 // Get rid of any port buffers first
1566 if (state->encoder_pool)
1568 mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
1569 state->encoder_pool = NULL;
1572 mmal_component_destroy(state->encoder_component);
1573 state->encoder_component = NULL;
1578 * Connect two specific ports together
1580 * @param output_port Pointer the output port
1581 * @param input_port Pointer the input port
1582 * @param Pointer to a mmal connection pointer, reassigned if function successful
1583 * @return Returns a MMAL_STATUS_T giving result of operation
1586 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
1588 MMAL_STATUS_T status;
1590 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
1592 if (status == MMAL_SUCCESS)
1594 status = mmal_connection_enable(*connection);
1595 if (status != MMAL_SUCCESS)
1596 mmal_connection_destroy(*connection);
1603 * Checks if specified port is valid and enabled, then disables it
1605 * @param port Pointer the port
1608 static void check_disable_port(MMAL_PORT_T *port)
1610 if (port && port->is_enabled)
1611 mmal_port_disable(port);
1614 void raspicapture_init()
1618 // Register our application with the logging system
1619 vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
1623 raspi_capture_setup(RASPIVID_CONFIG *config)
1625 // Our main data storage vessel..
1626 RASPIVID_STATE *state;
1628 MMAL_STATUS_T status = MMAL_SUCCESS;
1630 /* Default everything to zero */
1631 state = calloc(1, sizeof(RASPIVID_STATE));
1633 /* Apply passed in config */
1634 state->config = *config;
1636 /* Initialize timestamping */
1637 state->base_time = state->last_second = -1;
1639 /* So far, all we can do is create the camera component. Actual
1640 * config and connection of encoders etc happens in _start()
1642 // OK, we have a nice set of parameters. Now set up our components
1643 // We have three components. Camera, Preview and encoder.
1645 if ((status = create_camera_component(state)) != MMAL_SUCCESS)
1647 vcos_log_error("%s: Failed to create camera component", __func__);
1651 if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS)
1653 vcos_log_error("%s: Failed to create preview component", __func__);
1654 destroy_camera_component(state);
1658 state->encoded_buffer_q = mmal_queue_create();
1664 raspi_capture_start(RASPIVID_STATE *state)
1666 MMAL_STATUS_T status = MMAL_SUCCESS;
1667 RASPIVID_CONFIG *config = &state->config;
1669 MMAL_PORT_T *camera_preview_port = NULL;
1670 MMAL_PORT_T *preview_input_port = NULL;
1671 MMAL_PORT_T *encoder_input_port = NULL;
1675 if ((status = create_encoder_component(state)) != MMAL_SUCCESS) {
1676 vcos_log_error("%s: Failed to create encode component", __func__);
1680 if (config->verbose)
1685 state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1686 state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1687 camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1688 preview_input_port = state->preview_state.preview_component->input[0];
1690 if (state->encoder_component) {
1691 encoder_input_port = state->encoder_component->input[0];
1692 state->encoder_output_port = state->encoder_component->output[0];
1694 state->encoder_output_port = state->camera_video_port;
1697 if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) {
1701 GST_DEBUG ("Creating pool of %d buffers of size %d",
1702 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1703 /* Create pool of buffer headers for the output port to consume */
1704 pool = mmal_port_pool_create(state->encoder_output_port,
1705 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1708 vcos_log_error("Failed to create buffer header pool for encoder output port %s",
1709 state->encoder_output_port->name);
1712 state->encoder_pool = pool;
1714 if (state->config.verbose)
1715 fprintf(stderr, "Starting component connection stage\n");
1717 if (config->preview_parameters.wantPreview )
1719 if (config->verbose)
1721 fprintf(stderr, "Connecting camera preview port to preview input port\n");
1722 fprintf(stderr, "Starting video preview\n");
1725 // Connect camera to preview
1726 status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection);
1727 if (status != MMAL_SUCCESS)
1729 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1734 if (state->encoder_component) {
1735 if (config->verbose)
1736 fprintf(stderr, "Connecting camera video port to encoder input port\n");
1738 // Now connect the camera to the encoder
1739 status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection);
1740 if (status != MMAL_SUCCESS)
1742 if (config->preview_parameters.wantPreview )
1743 mmal_connection_destroy(state->preview_connection);
1744 vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
1749 // Set up our userdata - this is passed though to the callback where we need the information.
1750 state->callback_data.state = state;
1751 state->callback_data.abort = 0;
1753 state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
1755 if (config->verbose)
1756 fprintf(stderr, "Enabling encoder output port\n");
1758 // Enable the encoder output port and tell it its callback function
1759 status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback);
1760 if (status != MMAL_SUCCESS)
1762 vcos_log_error("Failed to setup encoder output");
1766 if (config->demoMode)
1768 // Run for the user specific time..
1769 int num_iterations = config->timeout / config->demoInterval;
1772 if (config->verbose)
1773 fprintf(stderr, "Running in demo mode\n");
1775 for (i=0;config->timeout == 0 || i<num_iterations;i++)
1777 raspicamcontrol_cycle_test(state->camera_component);
1778 vcos_sleep(state->config.demoInterval);
1782 if (config->verbose)
1783 fprintf(stderr, "Starting video capture\n");
1785 if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1790 // Send all the buffers to the encoder output port
1792 int num = mmal_queue_length(state->encoder_pool->queue);
1796 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
1799 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1801 if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS)
1802 vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
1807 // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
1808 // out of storage space)
1809 // Going to check every ABORT_INTERVAL milliseconds
1812 for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
1814 vcos_sleep(ABORT_INTERVAL);
1815 if (state->callback_data.abort)
1819 if (config->verbose)
1820 fprintf(stderr, "Finished capture\n");
1823 return (status == MMAL_SUCCESS);
1826 raspi_capture_stop(state);
1828 if (status != MMAL_SUCCESS) {
1829 mmal_status_to_int(status);
1830 raspicamcontrol_check_configuration(128);
1837 raspi_capture_stop(RASPIVID_STATE *state)
1839 RASPIVID_CONFIG *config = &state->config;
1841 if (config->verbose)
1842 fprintf(stderr, "Closing down\n");
1844 if (config->preview_parameters.wantPreview )
1845 mmal_connection_destroy(state->preview_connection);
1847 // Disable all our ports that are not handled by connections
1848 check_disable_port(state->camera_still_port);
1849 check_disable_port(state->encoder_output_port);
1851 if (state->encoder_component) {
1852 mmal_connection_destroy(state->encoder_connection);
1853 mmal_component_disable(state->encoder_component);
1854 destroy_encoder_component(state);
1859 raspi_capture_free(RASPIVID_STATE *state)
1861 RASPIVID_CONFIG *config = &state->config;
1863 // Can now close our file. Note disabling ports may flush buffers which causes
1864 // problems if we have already closed the file!
1865 if (state->output_file && state->output_file != stdout)
1866 fclose(state->output_file);
1868 /* Disable components */
1869 if (state->encoder_component)
1870 mmal_component_disable(state->encoder_component);
1872 if (state->preview_state.preview_component)
1873 mmal_component_disable(state->preview_state.preview_component);
1875 if (state->camera_component)
1876 mmal_component_disable(state->camera_component);
1878 destroy_encoder_component(state);
1879 raspipreview_destroy(&state->preview_state);
1880 destroy_camera_component(state);
1882 if (state->encoded_buffer_q) {
1883 mmal_queue_destroy(state->encoded_buffer_q);
1884 state->encoded_buffer_q = NULL;
1887 if (config->verbose)
1888 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1894 raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic)
1896 MMAL_STATUS_T status;
1897 RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters;
1898 MMAL_COMPONENT_T *camera = state->camera_component;
1900 /* Store the new config */
1901 state->config = *config;
1905 if (state->encoder_component && config->change_flags & PROP_CHANGE_ENCODING) {
1906 /* BITRATE or QUANT or KEY Interval, intra refresh */
1907 MMAL_COMPONENT_T *encoder = state->encoder_component;
1908 MMAL_PORT_T *encoder_output = encoder->output[0];
1910 #if 0 /* not dynamically change-able */
1911 encoder_output->format->bitrate = config->bitrate;
1912 status = mmal_port_format_commit(encoder_output);
1913 if (status != MMAL_SUCCESS) {
1914 vcos_log_warn("Cannot change bitrate dynamically");
1919 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
1920 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1921 if (status != MMAL_SUCCESS)
1922 vcos_log_warn("Unable to change intraperiod dynamically");
1925 #if 0 /* not dynamically change-able */
1927 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
1928 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1929 if (status != MMAL_SUCCESS)
1930 vcos_log_warn("Unable to change Initial Quantisation Parameter dynamically");
1932 MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
1933 status = mmal_port_parameter_set(encoder_output, ¶m2.hdr);
1934 if (status != MMAL_SUCCESS)
1935 vcos_log_warn("Unable to change Minimum Quantisation Parameter dynamically");
1937 MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
1938 status = mmal_port_parameter_set(encoder_output, ¶m3.hdr);
1939 if (status != MMAL_SUCCESS)
1940 vcos_log_warn("Unable to change Maximum Quantisation Parameter dynamically");
1944 // Adaptive intra refresh settings
1945 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1946 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1947 param.hdr.size = sizeof(param);
1949 // Get first so we don't overwrite anything unexpectedly
1950 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1951 if (state != MMAL_SUCCESS) {
1952 /* Need to memset, apparently mmal_port_parameter_get()
1953 * doesn't retrieve all parameters, causing random failures
1954 * when we set it. On older firmware the get fails.
1956 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1958 param.refresh_mode = config->intra_refresh_type;
1960 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1961 if (status != MMAL_SUCCESS)
1962 vcos_log_warn("Unable to set H264 intra-refresh values dynamically");
1966 if (config->change_flags & PROP_CHANGE_PREVIEW) {
1967 /* Preview settings or fullscreen */
1968 status = raspipreview_update_config (&state->preview_state,
1969 &config->preview_parameters);
1970 if (status != MMAL_SUCCESS)
1971 vcos_log_warn("Unable to change preview config dynamically");
1973 if (config->change_flags & PROP_CHANGE_COLOURBALANCE) {
1974 raspicamcontrol_set_saturation(camera, params->saturation);
1975 raspicamcontrol_set_sharpness(camera, params->sharpness);
1976 raspicamcontrol_set_contrast(camera, params->contrast);
1977 raspicamcontrol_set_brightness(camera, params->brightness);
1979 if (config->change_flags & PROP_CHANGE_SENSOR_SETTINGS) {
1980 /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */
1981 raspicamcontrol_set_ISO(camera, params->ISO);
1982 raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation);
1983 raspicamcontrol_set_exposure_mode(camera, params->exposureMode);
1984 raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode);
1985 raspicamcontrol_set_shutter_speed(camera, params->shutter_speed);
1986 raspicamcontrol_set_DRC(camera, params->drc_level);
1988 /* Can we change sensor mode on the fly? Disable if not */
1989 status = mmal_port_parameter_set_uint32(camera->control,
1990 MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
1991 if (status != MMAL_SUCCESS)
1992 vcos_log_warn("Unable to change sensor mode dynamically");
1994 if (config->change_flags & PROP_CHANGE_VIDEO_STABILISATION) {
1995 raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation);
1997 if (config->change_flags & PROP_CHANGE_AWB) {
1998 raspicamcontrol_set_awb_mode(camera, params->awbMode);
1999 raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b);
2001 if (config->change_flags & PROP_CHANGE_IMAGE_COLOUR_EFFECT) {
2002 raspicamcontrol_set_imageFX(camera, params->imageEffect);
2003 raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects);
2005 if (config->change_flags & PROP_CHANGE_ORIENTATION) {
2006 raspicamcontrol_set_rotation(camera, params->rotation);
2007 raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
2009 if (config->change_flags & PROP_CHANGE_ROI) {
2010 raspicamcontrol_set_ROI(camera, params->roi);
2012 if (config->change_flags & PROP_CHANGE_ANNOTATION)
2013 update_annotation_data(state);