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)
932 RASPIVID_CONFIG *config = &state->config;
934 MMAL_BUFFER_HEADER_T *buffer;
935 GstFlowReturn ret = GST_FLOW_ERROR;
936 /* No timestamps if no clockm or invalid PTS */
937 GstClockTime gst_pts = GST_CLOCK_TIME_NONE;
939 /* FIXME: Use our own interruptible cond wait: */
940 buffer = mmal_queue_wait(state->encoded_buffer_q);
943 if (G_LIKELY (config->useSTC && clock)) {
944 MMAL_PARAMETER_INT64_T param;
945 GstClockTime runtime;
947 runtime = gst_clock_get_time (clock) - base_time;
949 param.hdr.id = MMAL_PARAMETER_SYSTEM_TIME;
950 param.hdr.size = sizeof(param);
953 mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr);
955 if (buffer->pts != -1 && param.value != -1 && param.value >= buffer->pts) {
956 /* Convert microsecond RPi TS to GStreamer clock: */
957 GstClockTime offset = (param.value - buffer->pts) * 1000;
958 if (runtime >= offset)
959 gst_pts = runtime - offset;
961 GST_LOG ("Buf (uS) PTS %" G_GINT64_FORMAT " DTS %" G_GINT64_FORMAT
962 " STC %" G_GINT64_FORMAT " (latency %" G_GINT64_FORMAT
963 "uS) TS %" GST_TIME_FORMAT,
964 buffer->pts, buffer->dts, param.value, param.value - buffer->pts,
965 GST_TIME_ARGS (gst_pts));
968 GST_LOG ("use-stc=false. Not applying STC to buffer");
971 mmal_buffer_header_mem_lock(buffer);
972 buf = gst_buffer_new_allocate(NULL, buffer->length, NULL);
975 GST_BUFFER_DTS(buf) = GST_BUFFER_PTS(buf) = gst_pts;
976 /* FIXME: Can we avoid copies and give MMAL our own buffers to fill? */
977 gst_buffer_fill(buf, 0, buffer->data, buffer->length);
981 mmal_buffer_header_mem_unlock(buffer);
984 // release buffer back to the pool
985 mmal_buffer_header_release(buffer);
987 // and send one back to the port (if still open)
988 if (state->encoder_output_port->is_enabled)
990 MMAL_STATUS_T status = MMAL_SUCCESS;
992 buffer = mmal_queue_get(state->encoder_pool->queue);
994 status = mmal_port_send_buffer(state->encoder_output_port, buffer);
996 if (!buffer || status != MMAL_SUCCESS) {
997 vcos_log_error("Unable to return a buffer to the encoder port");
998 ret = GST_FLOW_ERROR;
1006 * Create the camera component, set up its ports
1008 * @param state Pointer to state control struct
1010 * @return MMAL_SUCCESS if all OK, something else otherwise
1013 static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
1015 MMAL_COMPONENT_T *camera = NULL;
1016 MMAL_STATUS_T status;
1017 RASPIVID_CONFIG *config = &state->config;
1019 /* Create the component */
1020 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
1022 if (status != MMAL_SUCCESS)
1024 vcos_log_error("Failed to create camera component");
1028 MMAL_PARAMETER_INT32_T camera_num =
1029 {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum};
1031 status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
1033 if (status != MMAL_SUCCESS)
1035 vcos_log_error("Could not select camera : error %d", status);
1039 if (!camera->output_num)
1041 status = MMAL_ENOSYS;
1042 vcos_log_error("Camera doesn't have output ports");
1046 status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
1048 if (status != MMAL_SUCCESS)
1050 vcos_log_error("Could not set sensor mode : error %d", status);
1054 if (config->settings)
1056 MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request =
1057 {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)},
1058 MMAL_PARAMETER_CAMERA_SETTINGS, 1};
1060 status = mmal_port_parameter_set(camera->control, &change_event_request.hdr);
1061 if ( status != MMAL_SUCCESS )
1063 vcos_log_error("No camera settings events");
1067 // Enable the camera, and tell it its control callback function
1068 status = mmal_port_enable(camera->control, camera_control_callback);
1070 if (status != MMAL_SUCCESS)
1072 vcos_log_error("Unable to enable control port : error %d", status);
1076 state->camera_component = camera;
1082 mmal_component_destroy(camera);
1088 raspi_capture_set_format_and_start(RASPIVID_STATE *state)
1090 MMAL_COMPONENT_T *camera = NULL;
1091 MMAL_STATUS_T status;
1092 MMAL_ES_FORMAT_T *format;
1093 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
1094 RASPIVID_CONFIG *config = &state->config;
1096 // set up the camera configuration
1098 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
1100 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
1101 .max_stills_w = config->width,
1102 .max_stills_h = config->height,
1104 .one_shot_stills = 0,
1105 .max_preview_video_w = config->width,
1106 .max_preview_video_h = config->height,
1107 .num_preview_video_frames = 3,
1108 .stills_capture_circular_buffer_height = 0,
1109 .fast_preview_resume = 0,
1110 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC
1113 camera = state->camera_component;
1114 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
1115 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
1116 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
1118 mmal_port_parameter_set(camera->control, &cam_config.hdr);
1120 // Now set up the port formats
1122 // Set the encode format on the Preview port
1123 // HW limitations mean we need the preview to be the same size as the required recorded output
1125 format = preview_port->format;
1127 if(config->camera_parameters.shutter_speed > 6000000)
1129 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1130 { 50, 1000 }, {166, 1000}};
1131 mmal_port_parameter_set(preview_port, &fps_range.hdr);
1133 else if(config->camera_parameters.shutter_speed > 1000000)
1135 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1136 { 166, 1000 }, {999, 1000}};
1137 mmal_port_parameter_set(preview_port, &fps_range.hdr);
1140 //enable dynamic framerate if necessary
1141 if (config->camera_parameters.shutter_speed)
1143 if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed)
1147 GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement");
1151 format->encoding = MMAL_ENCODING_OPAQUE;
1152 format->encoding_variant = MMAL_ENCODING_I420;
1154 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1155 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1156 format->es->video.crop.x = 0;
1157 format->es->video.crop.y = 0;
1158 format->es->video.crop.width = config->width;
1159 format->es->video.crop.height = config->height;
1160 format->es->video.frame_rate.num = config->fps_n;
1161 format->es->video.frame_rate.den = config->fps_d;
1163 status = mmal_port_format_commit(preview_port);
1165 if (status != MMAL_SUCCESS)
1167 vcos_log_error("camera viewfinder format couldn't be set");
1171 // Set the encode format on the video port
1172 format = video_port->format;
1174 if(config->camera_parameters.shutter_speed > 6000000)
1176 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1177 { 50, 1000 }, {166, 1000}};
1178 mmal_port_parameter_set(video_port, &fps_range.hdr);
1180 else if(config->camera_parameters.shutter_speed > 1000000)
1182 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1183 { 167, 1000 }, {999, 1000}};
1184 mmal_port_parameter_set(video_port, &fps_range.hdr);
1187 /* If encoding, set opaque tunneling format */
1188 if (state->encoder_component) {
1189 format->encoding = MMAL_ENCODING_OPAQUE;
1190 format->encoding_variant = MMAL_ENCODING_I420;
1193 format->encoding = config->encoding;
1194 format->encoding_variant = config->encoding;
1197 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1198 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1199 format->es->video.crop.x = 0;
1200 format->es->video.crop.y = 0;
1201 format->es->video.crop.width = config->width;
1202 format->es->video.crop.height = config->height;
1203 format->es->video.frame_rate.num = config->fps_n;
1204 format->es->video.frame_rate.den = config->fps_d;
1206 status = mmal_port_format_commit(video_port);
1208 if (status != MMAL_SUCCESS)
1210 vcos_log_error("camera video format couldn't be set");
1214 // Ensure there are enough buffers to avoid dropping frames
1215 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1216 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1219 // Set the encode format on the still port
1221 format = still_port->format;
1223 format->encoding = MMAL_ENCODING_OPAQUE;
1224 format->encoding_variant = MMAL_ENCODING_I420;
1226 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1227 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1228 format->es->video.crop.x = 0;
1229 format->es->video.crop.y = 0;
1230 format->es->video.crop.width = config->width;
1231 format->es->video.crop.height = config->height;
1232 format->es->video.frame_rate.num = 0;
1233 format->es->video.frame_rate.den = 1;
1235 status = mmal_port_format_commit(still_port);
1237 if (status != MMAL_SUCCESS)
1239 vcos_log_error("camera still format couldn't be set");
1243 /* Ensure there are enough buffers to avoid dropping frames */
1244 if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1245 still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1247 /* Enable component */
1248 status = mmal_component_enable(camera);
1250 if (status != MMAL_SUCCESS)
1252 vcos_log_error("camera component couldn't be enabled");
1256 raspicamcontrol_set_all_parameters(camera, &config->camera_parameters);
1258 update_annotation_data(state);
1260 if (config->verbose)
1261 fprintf(stderr, "Camera component done\n");
1268 mmal_component_destroy(camera);
1274 * Destroy the camera component
1276 * @param state Pointer to state control struct
1279 static void destroy_camera_component(RASPIVID_STATE *state)
1281 if (state->camera_component)
1283 mmal_component_destroy(state->camera_component);
1284 state->camera_component = NULL;
1288 gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state)
1290 MMAL_PORT_T *encoder_output = NULL;
1291 MMAL_STATUS_T status;
1292 MMAL_PARAMETER_BOOLEAN_T param = {{ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, sizeof(param)}, 1};
1294 if (state->encoder_component)
1297 encoder_output = state->encoder_component->output[0];
1298 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1299 if (status != MMAL_SUCCESS)
1301 vcos_log_error("Unable to request I-frame");
1308 * Create the encoder component, set up its ports
1310 * @param state Pointer to state control struct
1312 * @return MMAL_SUCCESS if all OK, something else otherwise
1315 static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
1317 MMAL_COMPONENT_T *encoder = 0;
1318 MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
1319 MMAL_STATUS_T status;
1320 RASPIVID_CONFIG *config = &state->config;
1322 gboolean encoded_format =
1323 (config->encoding == MMAL_ENCODING_H264 ||
1324 config->encoding == MMAL_ENCODING_MJPEG ||
1325 config->encoding == MMAL_ENCODING_JPEG);
1327 if (!encoded_format)
1328 return MMAL_SUCCESS;
1330 if (config->encoding == MMAL_ENCODING_JPEG)
1331 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
1333 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
1335 if (status != MMAL_SUCCESS) {
1336 vcos_log_error("Unable to create video encoder component");
1340 if (!encoder->input_num || !encoder->output_num)
1342 status = MMAL_ENOSYS;
1343 vcos_log_error("Video encoder doesn't have input/output ports");
1347 encoder_input = encoder->input[0];
1348 encoder_output = encoder->output[0];
1350 // We want same format on input and output
1351 mmal_format_copy(encoder_output->format, encoder_input->format);
1353 // Configure desired encoding
1354 encoder_output->format->encoding = config->encoding;
1356 encoder_output->format->bitrate = config->bitrate;
1358 if (config->encoding == MMAL_ENCODING_H264)
1359 encoder_output->buffer_size = encoder_output->buffer_size_recommended;
1361 encoder_output->buffer_size = 256<<10;
1363 if (encoder_output->buffer_size < encoder_output->buffer_size_min)
1364 encoder_output->buffer_size = encoder_output->buffer_size_min;
1366 encoder_output->buffer_num = encoder_output->buffer_num_recommended;
1368 if (encoder_output->buffer_num < encoder_output->buffer_num_min)
1369 encoder_output->buffer_num = encoder_output->buffer_num_min;
1371 GST_DEBUG ("encoder wants %d buffers of size %u",
1372 (guint)encoder_output->buffer_num, (guint)encoder_output->buffer_size);
1374 // We need to set the frame rate on output to 0, to ensure it gets
1375 // updated correctly from the input framerate when port connected
1376 encoder_output->format->es->video.frame_rate.num = 0;
1377 encoder_output->format->es->video.frame_rate.den = 1;
1379 // Commit the port changes to the output port
1380 status = mmal_port_format_commit(encoder_output);
1381 if (status != MMAL_SUCCESS) {
1382 vcos_log_error("Unable to set format on video encoder output port");
1386 // Set the rate control parameter
1389 MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT};
1390 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1391 if (status != MMAL_SUCCESS)
1393 vcos_log_error("Unable to set ratecontrol");
1399 if (config->encoding == MMAL_ENCODING_H264 && config->intraperiod != -1)
1401 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
1402 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1403 if (status != MMAL_SUCCESS)
1405 vcos_log_error("Unable to set intraperiod");
1410 if (config->encoding == MMAL_ENCODING_H264 && config->quantisationParameter)
1412 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
1413 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1414 if (status != MMAL_SUCCESS)
1416 vcos_log_error("Unable to set initial QP");
1420 MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
1421 status = mmal_port_parameter_set(encoder_output, ¶m2.hdr);
1422 if (status != MMAL_SUCCESS)
1424 vcos_log_error("Unable to set min QP");
1428 MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
1429 status = mmal_port_parameter_set(encoder_output, ¶m3.hdr);
1430 if (status != MMAL_SUCCESS)
1432 vcos_log_error("Unable to set max QP");
1437 if (config->encoding == MMAL_ENCODING_H264)
1439 MMAL_PARAMETER_VIDEO_PROFILE_T param;
1440 param.hdr.id = MMAL_PARAMETER_PROFILE;
1441 param.hdr.size = sizeof(param);
1443 param.profile[0].profile = config->profile;
1444 param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
1446 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1447 if (status != MMAL_SUCCESS)
1449 vcos_log_error("Unable to set H264 profile");
1454 if (config->encoding != MMAL_ENCODING_JPEG)
1456 if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS)
1458 vcos_log_error("Unable to set immutable input flag");
1459 // Continue rather than abort..
1462 //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested
1463 if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS)
1465 vcos_log_error("failed to set INLINE HEADER FLAG parameters");
1466 // Continue rather than abort..
1470 if (config->encoding == MMAL_ENCODING_H264)
1472 //set INLINE VECTORS flag to request motion vector estimates
1473 if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS)
1475 vcos_log_error("failed to set INLINE VECTORS parameters");
1476 // Continue rather than abort..
1479 // Adaptive intra refresh settings
1480 if (config->intra_refresh_type != -1)
1482 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1484 /* Need to memset, apparently mmal_port_parameter_get()
1485 * doesn't retrieve all parameters, causing random failures
1488 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1490 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1491 param.hdr.size = sizeof(param);
1493 // Get first so we don't overwrite anything unexpectedly
1494 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1496 param.refresh_mode = config->intra_refresh_type;
1498 //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
1499 // param.cir_mbs = 10;
1501 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1502 if (status != MMAL_SUCCESS)
1504 vcos_log_error("Unable to set H264 intra-refresh values");
1510 if (config->encoding == MMAL_ENCODING_JPEG)
1512 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, config->jpegQuality);
1513 if (status != MMAL_SUCCESS) {
1514 vcos_log_error("Unable to set JPEG quality");
1515 // Continue after warning
1518 #ifdef MMAL_PARAMETER_JPEG_RESTART_INTERVAL
1519 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, config->jpegRestartInterval);
1520 if (status != MMAL_SUCCESS) {
1521 vcos_log_error("Unable to set JPEG restart interval");
1522 // Continue after warning
1528 status = mmal_component_enable(encoder);
1530 if (status != MMAL_SUCCESS)
1532 vcos_log_error("Unable to enable video encoder component");
1536 state->encoder_component = encoder;
1538 if (config->verbose)
1539 fprintf(stderr, "Encoder component done\n");
1545 mmal_component_destroy(encoder);
1547 state->encoder_component = NULL;
1553 * Destroy the encoder component
1555 * @param state Pointer to state control struct
1558 static void destroy_encoder_component(RASPIVID_STATE *state)
1560 /* Empty the buffer header q */
1561 if (state->encoded_buffer_q) {
1562 while (mmal_queue_length(state->encoded_buffer_q)) {
1563 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q);
1564 mmal_buffer_header_release(buffer);
1568 // Get rid of any port buffers first
1569 if (state->encoder_pool)
1571 mmal_port_pool_destroy(state->encoder_output_port, state->encoder_pool);
1572 state->encoder_pool = NULL;
1575 if (state->encoder_component) {
1577 mmal_component_destroy(state->encoder_component);
1578 state->encoder_component = NULL;
1583 * Connect two specific ports together
1585 * @param output_port Pointer the output port
1586 * @param input_port Pointer the input port
1587 * @param Pointer to a mmal connection pointer, reassigned if function successful
1588 * @return Returns a MMAL_STATUS_T giving result of operation
1591 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
1593 MMAL_STATUS_T status;
1595 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
1597 if (status == MMAL_SUCCESS)
1599 status = mmal_connection_enable(*connection);
1600 if (status != MMAL_SUCCESS)
1601 mmal_connection_destroy(*connection);
1608 * Checks if specified port is valid and enabled, then disables it
1610 * @param port Pointer the port
1613 static void check_disable_port(MMAL_PORT_T *port)
1615 if (port && port->is_enabled)
1616 mmal_port_disable(port);
1619 void raspicapture_init()
1623 // Register our application with the logging system
1624 vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
1628 raspi_capture_setup(RASPIVID_CONFIG *config)
1630 // Our main data storage vessel..
1631 RASPIVID_STATE *state;
1633 MMAL_STATUS_T status = MMAL_SUCCESS;
1635 /* Default everything to zero */
1636 state = calloc(1, sizeof(RASPIVID_STATE));
1638 /* Apply passed in config */
1639 state->config = *config;
1641 /* Initialize timestamping */
1642 state->base_time = state->last_second = -1;
1644 /* So far, all we can do is create the camera component. Actual
1645 * config and connection of encoders etc happens in _start()
1647 // OK, we have a nice set of parameters. Now set up our components
1648 // We have three components. Camera, Preview and encoder.
1650 if ((status = create_camera_component(state)) != MMAL_SUCCESS)
1652 vcos_log_error("%s: Failed to create camera component", __func__);
1656 if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS)
1658 vcos_log_error("%s: Failed to create preview component", __func__);
1659 destroy_camera_component(state);
1663 state->encoded_buffer_q = mmal_queue_create();
1669 raspi_capture_start(RASPIVID_STATE *state)
1671 MMAL_STATUS_T status = MMAL_SUCCESS;
1672 RASPIVID_CONFIG *config = &state->config;
1674 MMAL_PORT_T *camera_preview_port = NULL;
1675 MMAL_PORT_T *preview_input_port = NULL;
1676 MMAL_PORT_T *encoder_input_port = NULL;
1680 if ((status = create_encoder_component(state)) != MMAL_SUCCESS) {
1681 vcos_log_error("%s: Failed to create encode component", __func__);
1685 if (config->verbose)
1690 state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1691 state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1692 camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1693 preview_input_port = state->preview_state.preview_component->input[0];
1695 if (state->encoder_component) {
1696 encoder_input_port = state->encoder_component->input[0];
1697 state->encoder_output_port = state->encoder_component->output[0];
1699 state->encoder_output_port = state->camera_video_port;
1702 if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) {
1706 GST_DEBUG ("Creating pool of %d buffers of size %d",
1707 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1708 /* Create pool of buffer headers for the output port to consume */
1709 pool = mmal_port_pool_create(state->encoder_output_port,
1710 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1713 vcos_log_error("Failed to create buffer header pool for encoder output port %s",
1714 state->encoder_output_port->name);
1717 state->encoder_pool = pool;
1719 if (state->config.verbose)
1720 fprintf(stderr, "Starting component connection stage\n");
1722 if (config->preview_parameters.wantPreview )
1724 if (config->verbose)
1726 fprintf(stderr, "Connecting camera preview port to preview input port\n");
1727 fprintf(stderr, "Starting video preview\n");
1730 // Connect camera to preview
1731 status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection);
1732 if (status != MMAL_SUCCESS)
1734 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1739 if (state->encoder_component) {
1740 if (config->verbose)
1741 fprintf(stderr, "Connecting camera video port to encoder input port\n");
1743 // Now connect the camera to the encoder
1744 status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection);
1745 if (status != MMAL_SUCCESS)
1747 if (config->preview_parameters.wantPreview )
1748 mmal_connection_destroy(state->preview_connection);
1749 vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
1754 // Set up our userdata - this is passed though to the callback where we need the information.
1755 state->callback_data.state = state;
1756 state->callback_data.abort = 0;
1758 state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
1760 if (config->verbose)
1761 fprintf(stderr, "Enabling encoder output port\n");
1763 // Enable the encoder output port and tell it its callback function
1764 status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback);
1765 if (status != MMAL_SUCCESS)
1767 vcos_log_error("Failed to setup encoder output");
1771 if (config->demoMode)
1773 // Run for the user specific time..
1774 int num_iterations = config->timeout / config->demoInterval;
1777 if (config->verbose)
1778 fprintf(stderr, "Running in demo mode\n");
1780 for (i=0;config->timeout == 0 || i<num_iterations;i++)
1782 raspicamcontrol_cycle_test(state->camera_component);
1783 vcos_sleep(state->config.demoInterval);
1787 if (config->verbose)
1788 fprintf(stderr, "Starting video capture\n");
1790 if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1795 // Send all the buffers to the encoder output port
1797 int num = mmal_queue_length(state->encoder_pool->queue);
1801 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
1804 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1806 if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS)
1807 vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
1812 // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
1813 // out of storage space)
1814 // Going to check every ABORT_INTERVAL milliseconds
1817 for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
1819 vcos_sleep(ABORT_INTERVAL);
1820 if (state->callback_data.abort)
1824 if (config->verbose)
1825 fprintf(stderr, "Finished capture\n");
1828 return (status == MMAL_SUCCESS);
1831 raspi_capture_stop(state);
1833 if (status != MMAL_SUCCESS) {
1834 mmal_status_to_int(status);
1835 raspicamcontrol_check_configuration(128);
1842 raspi_capture_stop(RASPIVID_STATE *state)
1844 RASPIVID_CONFIG *config = &state->config;
1846 if (config->verbose)
1847 fprintf(stderr, "Closing down\n");
1849 if (config->preview_parameters.wantPreview )
1850 mmal_connection_destroy(state->preview_connection);
1852 // Disable all our ports that are not handled by connections
1853 check_disable_port(state->camera_still_port);
1854 check_disable_port(state->encoder_output_port);
1856 if (state->encoder_component) {
1857 mmal_connection_destroy(state->encoder_connection);
1858 mmal_component_disable(state->encoder_component);
1859 destroy_encoder_component(state);
1864 raspi_capture_free(RASPIVID_STATE *state)
1866 RASPIVID_CONFIG *config = &state->config;
1868 // Can now close our file. Note disabling ports may flush buffers which causes
1869 // problems if we have already closed the file!
1870 if (state->output_file && state->output_file != stdout)
1871 fclose(state->output_file);
1873 /* Disable components */
1874 if (state->encoder_component)
1875 mmal_component_disable(state->encoder_component);
1877 if (state->preview_state.preview_component)
1878 mmal_component_disable(state->preview_state.preview_component);
1880 if (state->camera_component)
1881 mmal_component_disable(state->camera_component);
1883 destroy_encoder_component(state);
1884 raspipreview_destroy(&state->preview_state);
1885 destroy_camera_component(state);
1887 if (state->encoded_buffer_q) {
1888 mmal_queue_destroy(state->encoded_buffer_q);
1889 state->encoded_buffer_q = NULL;
1892 if (config->verbose)
1893 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1899 raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic)
1901 MMAL_STATUS_T status;
1902 RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters;
1903 MMAL_COMPONENT_T *camera = state->camera_component;
1905 /* Store the new config */
1906 state->config = *config;
1910 if (state->encoder_component && config->change_flags & PROP_CHANGE_ENCODING) {
1911 /* BITRATE or QUANT or KEY Interval, intra refresh */
1912 MMAL_COMPONENT_T *encoder = state->encoder_component;
1913 MMAL_PORT_T *encoder_output = encoder->output[0];
1915 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_VIDEO_BIT_RATE, config->bitrate);
1916 if (status != MMAL_SUCCESS)
1917 vcos_log_warn("Unable to 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);