3 * Copyright (c) 2013-2016 Jan Schmidt <jan@centricular.com>
5 Copyright (c) 2013, Broadcom Europe Ltd
6 Copyright (c) 2013, James Hughes
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 * Neither the name of the copyright holder nor the
17 names of its contributors may be used to endorse or promote products
18 derived from this software without specific prior written permission.
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
24 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * \file RaspiCapture.c
35 * Modification of the RaspiVid command line capture program for GStreamer
38 * \date 28th Feb 2013, 11 Oct 2013, 5 Mar 2015
39 * \Author: James Hughes, Jan Schmidt
43 * 3 components are created; camera, preview and video encoder.
44 * Camera component has three ports, preview, video and stills.
45 * This program connects preview and stills to the preview and video
46 * encoder. Using mmal we don't need to worry about buffers between these
47 * components, but we do need to handle buffers from the encoder, which
48 * are simply written straight to the file in the requisite buffer callback.
50 * We use the RaspiCamControl code to handle the specific camera settings.
51 * We use the RaspiPreview code to handle the (generic) preview window
54 // We use some GNU extensions (basename, asprintf)
66 #include "interface/vcos/vcos.h"
68 #include "interface/mmal/mmal.h"
69 #include "interface/mmal/mmal_logging.h"
70 #include "interface/mmal/mmal_buffer.h"
71 #include "interface/mmal/util/mmal_util.h"
72 #include "interface/mmal/util/mmal_util_params.h"
73 #include "interface/mmal/util/mmal_default_components.h"
74 #include "interface/mmal/util/mmal_connection.h"
76 #include "RaspiCapture.h"
77 #include "RaspiCamControl.h"
78 #include "RaspiPreview.h"
81 #include <semaphore.h>
83 // Standard port setting for the camera component
84 #define MMAL_CAMERA_PREVIEW_PORT 0
85 #define MMAL_CAMERA_VIDEO_PORT 1
86 #define MMAL_CAMERA_CAPTURE_PORT 2
88 // Video format information
90 #define VIDEO_FRAME_RATE_NUM 30
91 #define VIDEO_FRAME_RATE_DEN 1
93 /// Video render needs at least 2 buffers.
94 #define VIDEO_OUTPUT_BUFFERS_NUM 3
96 // Max bitrate we allow for recording
97 const int MAX_BITRATE = 25000000; // 25Mbits/s
99 /// Interval at which we check for an failure abort during capture
100 const int ABORT_INTERVAL = 100; // ms
103 int mmal_status_to_int(MMAL_STATUS_T status);
105 /** Struct used to pass information in encoder port userdata to callback
109 RASPIVID_STATE *state; /// pointer to our state in case required in callback
110 int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture
113 struct RASPIVID_STATE_T
115 RASPIVID_CONFIG config;
119 MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
120 MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component
121 MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
122 MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder
124 MMAL_PORT_T *camera_video_port;
125 MMAL_PORT_T *camera_still_port;
126 MMAL_PORT_T *encoder_output_port;
128 MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
130 PORT_USERDATA callback_data;
132 MMAL_QUEUE_T *encoded_buffer_q;
137 RASPIPREVIEW_STATE preview_state;
141 /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
142 static XREF_T profile_map[] =
144 {"baseline", MMAL_VIDEO_PROFILE_H264_BASELINE},
145 {"main", MMAL_VIDEO_PROFILE_H264_MAIN},
146 {"high", MMAL_VIDEO_PROFILE_H264_HIGH},
147 // {"constrained", MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this?
150 static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]);
153 static XREF_T initial_map[] =
159 static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]);
162 static XREF_T intra_refresh_map[] =
164 {"cyclic", MMAL_VIDEO_INTRA_REFRESH_CYCLIC},
165 {"adaptive", MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE},
166 {"both", MMAL_VIDEO_INTRA_REFRESH_BOTH},
167 {"cyclicrows", MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS},
168 // {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why.
171 static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]);
175 static void display_valid_parameters(char *app_name);
177 /// Command ID's and Structure defining our command line options
178 #define CommandHelp 0
179 #define CommandWidth 1
180 #define CommandHeight 2
181 #define CommandBitrate 3
182 #define CommandOutput 4
183 #define CommandVerbose 5
184 #define CommandTimeout 6
185 #define CommandDemoMode 7
186 #define CommandFramerate 8
187 #define CommandPreviewEnc 9
188 #define CommandIntraPeriod 10
189 #define CommandProfile 11
190 #define CommandTimed 12
191 #define CommandSignal 13
192 #define CommandKeypress 14
193 #define CommandInitialState 15
195 #define CommandInlineHeaders 17
196 #define CommandSegmentFile 18
197 #define CommandSegmentWrap 19
198 #define CommandSegmentStart 20
199 #define CommandSplitWait 21
200 #define CommandCircular 22
201 #define CommandIMV 23
202 #define CommandCamSelect 24
203 #define CommandSettings 25
204 #define CommandSensorMode 26
205 #define CommandIntraRefreshType 27
207 static COMMAND_LIST cmdline_commands[] =
209 { CommandHelp, "-help", "?", "This help information", 0 },
210 { CommandWidth, "-width", "w", "Set image width <size>. Default 1920", 1 },
211 { CommandHeight, "-height", "h", "Set image height <size>. Default 1080", 1 },
212 { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 },
213 { CommandOutput, "-output", "o", "Output filename <filename> (to write to stdout, use '-o -')", 1 },
214 { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 },
215 { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
216 { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1},
217 { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1},
218 { CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0},
219 { 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},
220 { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1},
221 { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0},
222 { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0},
223 { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0},
224 { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1},
225 { CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1},
226 { CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0},
227 { CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval <ms>", 1},
228 { CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1},
229 { CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1},
230 { CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0},
231 { CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0},
232 { CommandIMV, "-vectors", "x", "Output filename <filename> for inline motion vectors", 1 },
233 { CommandCamSelect, "-camselect", "cs", "Select camera <number>. Default 0", 1 },
234 { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0},
235 { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1},
236 { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1},
239 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
242 static void dump_state(RASPIVID_STATE *state);
245 * Assign a default set of parameters to the state passed in
247 * @param state Pointer to state structure to assign defaults to
249 void raspicapture_default_config(RASPIVID_CONFIG *config)
257 // Default everything to zero
258 memset(config, 0, sizeof(RASPIVID_CONFIG));
260 // Now set anything non-zero
261 config->timeout = 5000; // 5s delay before take image
262 config->width = 1920; // Default to 1080p
263 config->height = 1080;
264 config->bitrate = 17000000; // This is a decent default bitrate for 1080p
265 config->fps_n = VIDEO_FRAME_RATE_NUM;
266 config->fps_d = VIDEO_FRAME_RATE_DEN;
267 config->intraperiod = -1; // Not set
268 config->quantisationParameter = 0;
269 config->demoMode = 0;
270 config->demoInterval = 250; // ms
271 config->immutableInput = 1;
272 config->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
273 config->encoding = MMAL_ENCODING_H264;
275 config->bInlineHeaders = 0;
277 config->inlineMotionVectors = 0;
279 config->cameraNum = 0;
280 config->settings = 0;
281 config->sensor_mode = 0;
283 config->intra_refresh_type = -1;
285 // Setup preview window defaults
286 raspipreview_set_defaults(&config->preview_parameters);
288 // Set up the camera_parameters to default
289 raspicamcontrol_set_defaults(&config->camera_parameters);
295 * Dump image state parameters to printf. Used for debugging
297 * @param state Pointer to state structure to assign defaults to
299 static void dump_state(RASPIVID_STATE *state)
301 RASPIVID_CONFIG *config;
309 config = &state->config;
311 fprintf(stderr, "Width %d, Height %d\n", config->width, config->height);
312 fprintf(stderr, "bitrate %d, framerate %d/%d, time delay %d\n",
313 config->bitrate, config->fps_n, config->fps_d, config->timeout);
314 //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
316 raspipreview_dump_parameters(&config->preview_parameters);
317 raspicamcontrol_dump_parameters(&config->camera_parameters);
322 * Parse the incoming command line and put resulting parameters in to the state
324 * @param argc Number of arguments in command line
325 * @param argv Array of pointers to strings from command line
326 * @param state Pointer to state structure to assign any discovered parameters to
327 * @return Non-0 if failed for some reason, 0 otherwise
329 static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
331 // Parse the command line arguments.
332 // We are looking for --<something> or -<abreviation of something>
337 for (i = 1; i < argc && valid; i++)
339 int command_id, num_parameters;
344 if (argv[i][0] != '-')
350 // Assume parameter is valid until proven otherwise
353 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
355 // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
356 if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
359 // We are now dealing with a command line option
363 display_valid_parameters(basename(argv[0]));
366 case CommandWidth: // Width > 0
367 if (sscanf(argv[i + 1], "%u", &state->width) != 1)
373 case CommandHeight: // Height > 0
374 if (sscanf(argv[i + 1], "%u", &state->height) != 1)
380 case CommandBitrate: // 1-100
381 if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1)
383 if (state->bitrate > MAX_BITRATE)
385 state->bitrate = MAX_BITRATE;
394 case CommandOutput: // output filename
396 int len = strlen(argv[i + 1]);
399 state->filename = malloc(len + 1);
400 vcos_assert(state->filename);
402 strncpy(state->filename, argv[i + 1], len+1);
410 case CommandVerbose: // display lots of data during run
414 case CommandTimeout: // Time to run viewfinder/capture
416 if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
418 // Ensure that if previously selected a waitMethod we dont overwrite it
419 if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE)
420 state->waitMethod = WAIT_METHOD_FOREVER;
429 case CommandDemoMode: // Run in demo mode - no capture
431 // Demo mode might have a timing parameter
432 // so check if a) we have another parameter, b) its not the start of the next option
433 if (i + 1 < argc && argv[i+1][0] != '-')
435 if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
437 // TODO : What limits do we need for timeout?
438 if (state->demoInterval == 0)
439 state->demoInterval = 250; // ms
455 case CommandFramerate: // fps to record
457 if (sscanf(argv[i + 1], "%u", &state->framerate) == 1)
459 // TODO : What limits do we need for fps 1 - 30 - 120??
467 case CommandPreviewEnc:
468 state->immutableInput = 0;
471 case CommandIntraPeriod: // key frame rate
473 if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1)
480 case CommandQP: // quantisation parameter
482 if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1)
489 case CommandProfile: // H264 profile
491 state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size);
493 if( state->profile == -1)
494 state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
500 case CommandInlineHeaders: // H264 inline headers
502 state->bInlineHeaders = 1;
508 if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2)
512 if (state->onTime < 1000)
513 state->onTime = 1000;
515 if (state->offTime < 1000)
516 state->offTime = 1000;
518 state->waitMethod = WAIT_METHOD_TIMED;
525 case CommandKeypress:
526 state->waitMethod = WAIT_METHOD_KEYPRESS;
530 state->waitMethod = WAIT_METHOD_SIGNAL;
531 // Reenable the signal
532 signal(SIGUSR1, signal_handler);
535 case CommandInitialState:
537 state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size);
539 if( state->bCapturing == -1)
540 state->bCapturing = 0;
546 case CommandSegmentFile: // Segment file in to chunks of specified time
548 if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1)
550 // Must enable inline headers for this to work
551 state->bInlineHeaders = 1;
559 case CommandSegmentWrap: // segment wrap value
561 if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1)
568 case CommandSegmentStart: // initial segment number
570 if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap)))
577 case CommandSplitWait: // split files on restart
579 // Must enable inline headers for this to work
580 state->bInlineHeaders = 1;
581 state->splitWait = 1;
585 case CommandCircular:
587 state->bCircularBuffer = 1;
591 case CommandIMV: // output filename
593 state->inlineMotionVectors = 1;
594 int len = strlen(argv[i + 1]);
597 state->imv_filename = malloc(len + 1);
598 vcos_assert(state->imv_filename);
599 if (state->imv_filename)
600 strncpy(state->imv_filename, argv[i + 1], len+1);
607 case CommandCamSelect: //Select camera input port
609 if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1)
618 case CommandSettings:
622 case CommandSensorMode:
624 if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1)
633 case CommandIntraRefreshType:
635 state->config.intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size);
642 // Try parsing for any image specific parameters
643 // result indicates how many parameters were used up, 0,1,2
644 // but we adjust by -1 as we have used one already
645 const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
646 int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
648 // Still unused, try preview options
650 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
653 // If no parms were used, this must be a bad parameters
666 fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
670 // Always disable verbose if output going to stdout
671 if (state->filename && state->filename[0] == '-')
680 * Display usage information for the application to stdout
682 * @param app_name String to display as the application name
684 static void display_valid_parameters(char *app_name)
688 fprintf(stderr, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n");
689 fprintf(stderr, "\nusage: %s [options]\n\n", app_name);
691 fprintf(stderr, "Image parameter commands\n\n");
693 raspicli_display_help(cmdline_commands, cmdline_commands_size);
696 fprintf(stderr, "\n\nH264 Profile options :\n%s", profile_map[0].mode );
698 for (i=1;i<profile_map_size;i++)
700 fprintf(stderr, ",%s", profile_map[i].mode);
703 fprintf(stderr, "\n");
705 // Intra refresh options
706 fprintf(stderr, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode );
708 for (i=1;i<intra_refresh_map_size;i++)
710 fprintf(stderr, ",%s", intra_refresh_map[i].mode);
713 fprintf(stderr, "\n");
715 // Help for preview options
716 raspipreview_display_help();
718 // Now display any help information from the camcontrol code
719 raspicamcontrol_display_help();
721 fprintf(stderr, "\n");
728 * buffer header callback function for camera control
730 * Callback will dump buffer data to the specific file
732 * @param port Pointer to port from which callback originated
733 * @param buffer mmal buffer header pointer
735 static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
737 if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
739 MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data;
740 switch (param->hdr.id) {
741 case MMAL_PARAMETER_CAMERA_SETTINGS:
743 MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param;
744 vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u",
746 settings->analog_gain.num, settings->analog_gain.den,
747 settings->digital_gain.num, settings->digital_gain.den);
748 vcos_log_error("AWB R=%u/%u, B=%u/%u",
749 settings->awb_red_gain.num, settings->awb_red_gain.den,
750 settings->awb_blue_gain.num, settings->awb_blue_gain.den
756 else if (buffer->cmd == MMAL_EVENT_ERROR) {
757 vcos_log_error("Camera control callback got an error");
759 vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
762 mmal_buffer_header_release(buffer);
767 * Open a file based on the settings in state
769 * @param state Pointer to state
771 static FILE *open_filename(RASPIVID_STATE *pState)
773 FILE *new_handle = NULL;
774 char *tempname = NULL, *filename = NULL;
776 if (pState->segmentSize || pState->splitWait)
778 // Create a new filename string
779 asprintf(&tempname, pState->filename, pState->segmentNumber);
784 filename = pState->filename;
788 new_handle = fopen(filename, "wb");
793 fprintf(stderr, "Opening output file \"%s\"\n", filename);
795 fprintf(stderr, "Failed to open new file \"%s\"\n", filename);
805 * Open a file based on the settings in state
807 * This time for the imv output file
809 * @param state Pointer to state
811 static FILE *open_imv_filename(RASPIVID_STATE *pState)
813 FILE *new_handle = NULL;
814 char *tempname = NULL, *filename = NULL;
816 if (pState->segmentSize || pState->splitWait)
818 // Create a new filename string
819 asprintf(&tempname, pState->imv_filename, pState->segmentNumber);
824 filename = pState->imv_filename;
828 new_handle = fopen(filename, "wb");
833 fprintf(stderr, "Opening imv output file \"%s\"\n", filename);
835 fprintf(stderr, "Failed to open new imv file \"%s\"\n", filename);
846 * Update any annotation data specific to the video.
847 * This simply passes on the setting from cli, or
848 * if application defined annotate requested, updates
849 * with the H264 parameters
851 * @param state Pointer to state control struct
854 static void update_annotation_data(RASPIVID_STATE *state)
856 RASPIVID_CONFIG *config = &state->config;
858 // So, if we have asked for a application supplied string, set it to the H264 parameters
859 if (config->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT)
862 const char *refresh = raspicli_unmap_xref(config->intra_refresh_type, intra_refresh_map, intra_refresh_map_size);
864 asprintf(&text, "%dk,%ff,%s,%d,%s",
865 config->bitrate / 1000, ((float)(config->fps_n) / config->fps_d),
866 refresh ? refresh : "(none)",
868 raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
870 raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text,
871 config->camera_parameters.annotate_text_size,
872 config->camera_parameters.annotate_text_colour,
873 config->camera_parameters.annotate_bg_colour);
879 raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate,
880 config->camera_parameters.annotate_string,
881 config->camera_parameters.annotate_text_size,
882 config->camera_parameters.annotate_text_colour,
883 config->camera_parameters.annotate_bg_colour);
890 * buffer header callback function for encoder
892 * Callback will dump buffer data to the specific file
894 * @param port Pointer to port from which callback originated
895 * @param buffer mmal buffer header pointer
897 static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
899 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
900 RASPIVID_STATE *state = pData->state;
901 int64_t current_time;
903 // All our segment times based on the receipt of the first encoder callback
904 if (state->base_time == -1)
905 state->base_time = vcos_getmicrosecs64()/1000;
909 vcos_log_error("Received a encoder buffer callback with no state");
910 // release buffer back to the pool
911 mmal_buffer_header_release(buffer);
915 current_time = vcos_getmicrosecs64()/1000;
916 if (state->base_time == -1)
917 state->base_time = current_time;
919 // See if the second count has changed and we need to update any annotation
920 if (current_time/1000 != state->last_second)
922 update_annotation_data(state);
923 state->last_second = current_time/1000;
926 /* Send buffer to GStreamer element for pushing to the pipeline */
927 mmal_queue_put(state->encoded_buffer_q, buffer);
931 raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp,
932 GstClock *clock, GstClockTime base_time)
934 RASPIVID_CONFIG *config = &state->config;
936 MMAL_BUFFER_HEADER_T *buffer;
937 GstFlowReturn ret = GST_FLOW_ERROR;
938 /* No timestamps if no clockm or invalid PTS */
939 GstClockTime gst_pts = GST_CLOCK_TIME_NONE;
942 buffer = mmal_queue_timedwait(state->encoded_buffer_q, 500);
943 // Work around a bug where mmal_queue_timedwait() might return
944 // immediately if the internal timeout time aligns exactly
945 // with a 1 second rollover boundary by checking errno.
946 if (errno == EINVAL) {
947 GST_WARNING ("Retrying mmal_queue_timedwait() due to spurious failure.");
952 if (G_UNLIKELY(buffer == NULL)) {
953 return GST_FLOW_ERROR_TIMEOUT;
956 if (G_LIKELY (config->useSTC && clock)) {
957 MMAL_PARAMETER_INT64_T param;
958 GstClockTime runtime;
960 runtime = gst_clock_get_time (clock) - base_time;
962 param.hdr.id = MMAL_PARAMETER_SYSTEM_TIME;
963 param.hdr.size = sizeof(param);
966 mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr);
968 if (buffer->pts != -1 && param.value != -1 && param.value >= buffer->pts) {
969 /* Convert microsecond RPi TS to GStreamer clock: */
970 GstClockTime offset = (param.value - buffer->pts) * 1000;
971 if (runtime >= offset)
972 gst_pts = runtime - offset;
974 GST_LOG ("Buf %05u bytes FLAGS 0x%05x (uS) PTS %" G_GINT64_FORMAT
975 " DTS %" G_GINT64_FORMAT " STC %" G_GINT64_FORMAT
976 " (latency %" G_GINT64_FORMAT "uS) TS %" GST_TIME_FORMAT,
977 buffer->length, buffer->flags, buffer->pts, buffer->dts, param.value,
978 param.value - buffer->pts, GST_TIME_ARGS (gst_pts));
981 GST_LOG ("use-stc=false. Not applying STC to buffer");
984 mmal_buffer_header_mem_lock(buffer);
985 buf = gst_buffer_new_allocate(NULL, buffer->length, NULL);
988 GST_BUFFER_DTS(buf) = GST_BUFFER_PTS(buf) = gst_pts;
989 /* FIXME: Can we avoid copies and give MMAL our own buffers to fill? */
990 gst_buffer_fill(buf, 0, buffer->data, buffer->length);
992 if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG))
993 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
994 else if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME))
995 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
997 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
999 /* NAL_END is bogus and can't be trusted */
1000 if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END))
1003 ret = GST_FLOW_KEEP_ACCUMULATING;
1006 mmal_buffer_header_mem_unlock(buffer);
1009 // release buffer back to the pool
1010 mmal_buffer_header_release(buffer);
1012 // and send one back to the port (if still open)
1013 if (state->encoder_output_port->is_enabled)
1015 MMAL_STATUS_T status = MMAL_SUCCESS;
1017 buffer = mmal_queue_get(state->encoder_pool->queue);
1019 status = mmal_port_send_buffer(state->encoder_output_port, buffer);
1021 if (!buffer || status != MMAL_SUCCESS) {
1022 vcos_log_error("Unable to return a buffer to the encoder port");
1023 ret = GST_FLOW_ERROR;
1031 * Create the camera component, set up its ports
1033 * @param state Pointer to state control struct
1035 * @return MMAL_SUCCESS if all OK, something else otherwise
1038 static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
1040 MMAL_COMPONENT_T *camera = NULL;
1041 MMAL_STATUS_T status;
1042 RASPIVID_CONFIG *config = &state->config;
1044 /* Create the component */
1045 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
1047 if (status != MMAL_SUCCESS)
1049 vcos_log_error("Failed to create camera component");
1053 MMAL_PARAMETER_INT32_T camera_num =
1054 {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum};
1056 status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
1058 if (status != MMAL_SUCCESS)
1060 vcos_log_error("Could not select camera : error %d", status);
1064 if (!camera->output_num)
1066 status = MMAL_ENOSYS;
1067 vcos_log_error("Camera doesn't have output ports");
1071 status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
1073 if (status != MMAL_SUCCESS)
1075 vcos_log_error("Could not set sensor mode : error %d", status);
1079 if (config->settings)
1081 MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request =
1082 {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)},
1083 MMAL_PARAMETER_CAMERA_SETTINGS, 1};
1085 status = mmal_port_parameter_set(camera->control, &change_event_request.hdr);
1086 if ( status != MMAL_SUCCESS )
1088 vcos_log_error("No camera settings events");
1092 // Enable the camera, and tell it its control callback function
1093 status = mmal_port_enable(camera->control, camera_control_callback);
1095 if (status != MMAL_SUCCESS)
1097 vcos_log_error("Unable to enable control port : error %d", status);
1101 state->camera_component = camera;
1107 mmal_component_destroy(camera);
1113 raspi_capture_set_format_and_start(RASPIVID_STATE *state)
1115 MMAL_COMPONENT_T *camera = NULL;
1116 MMAL_STATUS_T status;
1117 MMAL_ES_FORMAT_T *format;
1118 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
1119 RASPIVID_CONFIG *config = &state->config;
1121 // set up the camera configuration
1123 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
1125 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
1126 .max_stills_w = config->width,
1127 .max_stills_h = config->height,
1129 .one_shot_stills = 0,
1130 .max_preview_video_w = config->width,
1131 .max_preview_video_h = config->height,
1132 .num_preview_video_frames = 3,
1133 .stills_capture_circular_buffer_height = 0,
1134 .fast_preview_resume = 0,
1135 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC
1138 camera = state->camera_component;
1139 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
1140 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
1141 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
1143 mmal_port_parameter_set(camera->control, &cam_config.hdr);
1145 // Now set up the port formats
1147 // Set the encode format on the Preview port
1148 // HW limitations mean we need the preview to be the same size as the required recorded output
1150 format = preview_port->format;
1152 if(config->camera_parameters.shutter_speed > 6000000)
1154 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1155 { 50, 1000 }, {166, 1000}};
1156 mmal_port_parameter_set(preview_port, &fps_range.hdr);
1158 else if(config->camera_parameters.shutter_speed > 1000000)
1160 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1161 { 166, 1000 }, {999, 1000}};
1162 mmal_port_parameter_set(preview_port, &fps_range.hdr);
1165 //enable dynamic framerate if necessary
1166 if (config->camera_parameters.shutter_speed)
1168 if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed)
1172 GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement");
1176 format->encoding = MMAL_ENCODING_OPAQUE;
1177 format->encoding_variant = MMAL_ENCODING_I420;
1179 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1180 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1181 format->es->video.crop.x = 0;
1182 format->es->video.crop.y = 0;
1183 format->es->video.crop.width = config->width;
1184 format->es->video.crop.height = config->height;
1185 format->es->video.frame_rate.num = config->fps_n;
1186 format->es->video.frame_rate.den = config->fps_d;
1188 status = mmal_port_format_commit(preview_port);
1190 if (status != MMAL_SUCCESS)
1192 vcos_log_error("camera viewfinder format couldn't be set");
1196 // Set the encode format on the video port
1197 format = video_port->format;
1199 if(config->camera_parameters.shutter_speed > 6000000)
1201 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1202 { 50, 1000 }, {166, 1000}};
1203 mmal_port_parameter_set(video_port, &fps_range.hdr);
1205 else if(config->camera_parameters.shutter_speed > 1000000)
1207 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1208 { 167, 1000 }, {999, 1000}};
1209 mmal_port_parameter_set(video_port, &fps_range.hdr);
1212 /* If encoding, set opaque tunneling format */
1213 if (state->encoder_component) {
1214 format->encoding = MMAL_ENCODING_OPAQUE;
1215 format->encoding_variant = MMAL_ENCODING_I420;
1218 format->encoding = config->encoding;
1219 format->encoding_variant = config->encoding;
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 = config->fps_n;
1229 format->es->video.frame_rate.den = config->fps_d;
1231 status = mmal_port_format_commit(video_port);
1233 if (status != MMAL_SUCCESS)
1235 vcos_log_error("camera video format couldn't be set");
1239 // Ensure there are enough buffers to avoid dropping frames
1240 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1241 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1244 // Set the encode format on the still port
1246 format = still_port->format;
1248 format->encoding = MMAL_ENCODING_OPAQUE;
1249 format->encoding_variant = MMAL_ENCODING_I420;
1251 format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
1252 format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
1253 format->es->video.crop.x = 0;
1254 format->es->video.crop.y = 0;
1255 format->es->video.crop.width = config->width;
1256 format->es->video.crop.height = config->height;
1257 format->es->video.frame_rate.num = 0;
1258 format->es->video.frame_rate.den = 1;
1260 status = mmal_port_format_commit(still_port);
1262 if (status != MMAL_SUCCESS)
1264 vcos_log_error("camera still format couldn't be set");
1268 /* Ensure there are enough buffers to avoid dropping frames */
1269 if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1270 still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1272 /* Enable component */
1273 status = mmal_component_enable(camera);
1275 if (status != MMAL_SUCCESS)
1277 vcos_log_error("camera component couldn't be enabled");
1281 raspicamcontrol_set_all_parameters(camera, &config->camera_parameters);
1283 update_annotation_data(state);
1285 if (config->verbose)
1286 fprintf(stderr, "Camera component done\n");
1292 mmal_component_disable(camera);
1298 * Destroy the camera component
1300 * @param state Pointer to state control struct
1303 static void destroy_camera_component(RASPIVID_STATE *state)
1305 if (state->camera_component)
1307 mmal_component_destroy(state->camera_component);
1308 state->camera_component = NULL;
1312 gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state)
1314 MMAL_PORT_T *encoder_output = NULL;
1315 MMAL_STATUS_T status;
1316 MMAL_PARAMETER_BOOLEAN_T param = {{ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, sizeof(param)}, 1};
1318 if (state->encoder_component)
1321 encoder_output = state->encoder_component->output[0];
1322 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1323 if (status != MMAL_SUCCESS)
1325 vcos_log_error("Unable to request I-frame");
1332 * Create the encoder component, set up its ports
1334 * @param state Pointer to state control struct
1336 * @return MMAL_SUCCESS if all OK, something else otherwise
1339 static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
1341 MMAL_COMPONENT_T *encoder = 0;
1342 MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
1343 MMAL_STATUS_T status;
1344 RASPIVID_CONFIG *config = &state->config;
1346 gboolean encoded_format =
1347 (config->encoding == MMAL_ENCODING_H264 ||
1348 config->encoding == MMAL_ENCODING_MJPEG ||
1349 config->encoding == MMAL_ENCODING_JPEG);
1351 if (!encoded_format)
1352 return MMAL_SUCCESS;
1354 if (config->encoding == MMAL_ENCODING_JPEG)
1355 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
1357 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
1359 if (status != MMAL_SUCCESS) {
1360 vcos_log_error("Unable to create video encoder component");
1364 if (!encoder->input_num || !encoder->output_num)
1366 status = MMAL_ENOSYS;
1367 vcos_log_error("Video encoder doesn't have input/output ports");
1371 encoder_input = encoder->input[0];
1372 encoder_output = encoder->output[0];
1374 // We want same format on input and output
1375 mmal_format_copy(encoder_output->format, encoder_input->format);
1377 // Configure desired encoding
1378 encoder_output->format->encoding = config->encoding;
1380 encoder_output->format->bitrate = config->bitrate;
1382 if (config->encoding == MMAL_ENCODING_H264)
1383 encoder_output->buffer_size = encoder_output->buffer_size_recommended;
1385 encoder_output->buffer_size = 256<<10;
1387 if (encoder_output->buffer_size < encoder_output->buffer_size_min)
1388 encoder_output->buffer_size = encoder_output->buffer_size_min;
1390 encoder_output->buffer_num = encoder_output->buffer_num_recommended;
1392 if (encoder_output->buffer_num < encoder_output->buffer_num_min)
1393 encoder_output->buffer_num = encoder_output->buffer_num_min;
1395 GST_DEBUG ("encoder wants %d buffers of size %u",
1396 (guint)encoder_output->buffer_num, (guint)encoder_output->buffer_size);
1398 // We need to set the frame rate on output to 0, to ensure it gets
1399 // updated correctly from the input framerate when port connected
1400 encoder_output->format->es->video.frame_rate.num = 0;
1401 encoder_output->format->es->video.frame_rate.den = 1;
1403 // Commit the port changes to the output port
1404 status = mmal_port_format_commit(encoder_output);
1405 if (status != MMAL_SUCCESS) {
1406 vcos_log_error("Unable to set format on video encoder output port");
1410 // Set the rate control parameter
1413 MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT};
1414 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1415 if (status != MMAL_SUCCESS)
1417 vcos_log_error("Unable to set ratecontrol");
1423 if (config->encoding == MMAL_ENCODING_H264 && config->intraperiod != -1)
1425 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
1426 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1427 if (status != MMAL_SUCCESS)
1429 vcos_log_error("Unable to set intraperiod");
1434 if (config->encoding == MMAL_ENCODING_H264 && config->quantisationParameter)
1436 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
1437 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1438 if (status != MMAL_SUCCESS)
1440 vcos_log_error("Unable to set initial QP");
1444 MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
1445 status = mmal_port_parameter_set(encoder_output, ¶m2.hdr);
1446 if (status != MMAL_SUCCESS)
1448 vcos_log_error("Unable to set min QP");
1452 MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
1453 status = mmal_port_parameter_set(encoder_output, ¶m3.hdr);
1454 if (status != MMAL_SUCCESS)
1456 vcos_log_error("Unable to set max QP");
1461 if (config->encoding == MMAL_ENCODING_H264)
1463 MMAL_PARAMETER_VIDEO_PROFILE_T param;
1464 param.hdr.id = MMAL_PARAMETER_PROFILE;
1465 param.hdr.size = sizeof(param);
1467 param.profile[0].profile = config->profile;
1468 param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
1470 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1471 if (status != MMAL_SUCCESS)
1473 vcos_log_error("Unable to set H264 profile");
1478 if (config->encoding != MMAL_ENCODING_JPEG)
1480 if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS)
1482 vcos_log_error("Unable to set immutable input flag");
1483 // Continue rather than abort..
1486 //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested
1487 if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS)
1489 vcos_log_error("failed to set INLINE HEADER FLAG parameters");
1490 // Continue rather than abort..
1494 if (config->encoding == MMAL_ENCODING_H264)
1496 //set INLINE VECTORS flag to request motion vector estimates
1497 if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS)
1499 vcos_log_error("failed to set INLINE VECTORS parameters");
1500 // Continue rather than abort..
1503 // Adaptive intra refresh settings
1504 if (config->intra_refresh_type != -1)
1506 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1508 /* Need to memset, apparently mmal_port_parameter_get()
1509 * doesn't retrieve all parameters, causing random failures
1512 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1514 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1515 param.hdr.size = sizeof(param);
1517 // Get first so we don't overwrite anything unexpectedly
1518 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1520 param.refresh_mode = config->intra_refresh_type;
1522 //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
1523 // param.cir_mbs = 10;
1525 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1526 if (status != MMAL_SUCCESS)
1528 vcos_log_error("Unable to set H264 intra-refresh values");
1534 if (config->encoding == MMAL_ENCODING_JPEG)
1536 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, config->jpegQuality);
1537 if (status != MMAL_SUCCESS) {
1538 vcos_log_error("Unable to set JPEG quality");
1539 // Continue after warning
1542 #ifdef MMAL_PARAMETER_JPEG_RESTART_INTERVAL
1543 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, config->jpegRestartInterval);
1544 if (status != MMAL_SUCCESS) {
1545 vcos_log_error("Unable to set JPEG restart interval");
1546 // Continue after warning
1552 status = mmal_component_enable(encoder);
1554 if (status != MMAL_SUCCESS)
1556 vcos_log_error("Unable to enable video encoder component");
1560 state->encoder_component = encoder;
1562 if (config->verbose)
1563 fprintf(stderr, "Encoder component done\n");
1569 mmal_component_destroy(encoder);
1571 state->encoder_component = NULL;
1577 * Destroy the encoder component
1579 * @param state Pointer to state control struct
1582 static void destroy_encoder_component(RASPIVID_STATE *state)
1584 /* Empty the buffer header q */
1585 if (state->encoded_buffer_q) {
1586 while (mmal_queue_length(state->encoded_buffer_q)) {
1587 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q);
1588 mmal_buffer_header_release(buffer);
1592 // Get rid of any port buffers first
1593 if (state->encoder_pool)
1595 mmal_port_pool_destroy(state->encoder_output_port, state->encoder_pool);
1596 state->encoder_pool = NULL;
1599 if (state->encoder_component) {
1601 mmal_component_destroy(state->encoder_component);
1602 state->encoder_component = NULL;
1607 * Connect two specific ports together
1609 * @param output_port Pointer the output port
1610 * @param input_port Pointer the input port
1611 * @param Pointer to a mmal connection pointer, reassigned if function successful
1612 * @return Returns a MMAL_STATUS_T giving result of operation
1615 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
1617 MMAL_STATUS_T status;
1619 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
1621 if (status == MMAL_SUCCESS)
1623 status = mmal_connection_enable(*connection);
1624 if (status != MMAL_SUCCESS)
1625 mmal_connection_destroy(*connection);
1632 * Checks if specified port is valid and enabled, then disables it
1634 * @param port Pointer the port
1637 static void check_disable_port(MMAL_PORT_T *port)
1639 if (port && port->is_enabled)
1640 mmal_port_disable(port);
1643 void raspicapture_init(void)
1647 // Register our application with the logging system
1648 vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
1652 raspi_capture_setup(RASPIVID_CONFIG *config)
1654 // Our main data storage vessel..
1655 RASPIVID_STATE *state;
1657 MMAL_STATUS_T status = MMAL_SUCCESS;
1659 /* Default everything to zero */
1660 state = calloc(1, sizeof(RASPIVID_STATE));
1662 /* Apply passed in config */
1663 state->config = *config;
1665 /* Initialize timestamping */
1666 state->base_time = state->last_second = -1;
1668 /* So far, all we can do is create the camera component. Actual
1669 * config and connection of encoders etc happens in _start()
1671 // OK, we have a nice set of parameters. Now set up our components
1672 // We have three components. Camera, Preview and encoder.
1674 if ((status = create_camera_component(state)) != MMAL_SUCCESS)
1676 vcos_log_error("%s: Failed to create camera component", __func__);
1680 if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS)
1682 vcos_log_error("%s: Failed to create preview component", __func__);
1683 destroy_camera_component(state);
1687 state->encoded_buffer_q = mmal_queue_create();
1693 raspi_capture_start(RASPIVID_STATE *state)
1695 MMAL_STATUS_T status = MMAL_SUCCESS;
1696 RASPIVID_CONFIG *config = &state->config;
1698 MMAL_PORT_T *camera_preview_port = NULL;
1699 MMAL_PORT_T *preview_input_port = NULL;
1700 MMAL_PORT_T *encoder_input_port = NULL;
1704 if ((status = create_encoder_component(state)) != MMAL_SUCCESS) {
1705 vcos_log_error("%s: Failed to create encode component", __func__);
1709 if (config->verbose)
1714 state->camera_video_port = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1715 state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1716 camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1717 preview_input_port = state->preview_state.preview_component->input[0];
1719 if (state->encoder_component) {
1720 encoder_input_port = state->encoder_component->input[0];
1721 state->encoder_output_port = state->encoder_component->output[0];
1723 state->encoder_output_port = state->camera_video_port;
1726 if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) {
1730 GST_DEBUG ("Creating pool of %d buffers of size %d",
1731 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1732 /* Create pool of buffer headers for the output port to consume */
1733 pool = mmal_port_pool_create(state->encoder_output_port,
1734 state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
1737 vcos_log_error("Failed to create buffer header pool for encoder output port %s",
1738 state->encoder_output_port->name);
1741 state->encoder_pool = pool;
1743 if (state->config.verbose)
1744 fprintf(stderr, "Starting component connection stage\n");
1746 if (config->preview_parameters.wantPreview )
1748 if (config->verbose)
1750 fprintf(stderr, "Connecting camera preview port to preview input port\n");
1751 fprintf(stderr, "Starting video preview\n");
1754 // Connect camera to preview
1755 status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection);
1756 if (status != MMAL_SUCCESS)
1758 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1763 if (state->encoder_component) {
1764 if (config->verbose)
1765 fprintf(stderr, "Connecting camera video port to encoder input port\n");
1767 // Now connect the camera to the encoder
1768 status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection);
1769 if (status != MMAL_SUCCESS)
1771 if (config->preview_parameters.wantPreview )
1772 mmal_connection_destroy(state->preview_connection);
1773 vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
1778 // Set up our userdata - this is passed though to the callback where we need the information.
1779 state->callback_data.state = state;
1780 state->callback_data.abort = 0;
1782 state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
1784 if (config->verbose)
1785 fprintf(stderr, "Enabling encoder output port\n");
1787 // Enable the encoder output port and tell it its callback function
1788 status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback);
1789 if (status != MMAL_SUCCESS)
1791 vcos_log_error("Failed to setup encoder output");
1795 if (config->demoMode)
1797 // Run for the user specific time..
1798 int num_iterations = config->timeout / config->demoInterval;
1801 if (config->verbose)
1802 fprintf(stderr, "Running in demo mode\n");
1804 for (i=0;config->timeout == 0 || i<num_iterations;i++)
1806 raspicamcontrol_cycle_test(state->camera_component);
1807 vcos_sleep(state->config.demoInterval);
1811 if (config->verbose)
1812 fprintf(stderr, "Starting video capture\n");
1814 if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1819 // Send all the buffers to the encoder output port
1821 int num = mmal_queue_length(state->encoder_pool->queue);
1825 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
1828 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1830 if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS)
1831 vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
1836 // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
1837 // out of storage space)
1838 // Going to check every ABORT_INTERVAL milliseconds
1841 for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
1843 vcos_sleep(ABORT_INTERVAL);
1844 if (state->callback_data.abort)
1848 if (config->verbose)
1849 fprintf(stderr, "Finished capture\n");
1852 return (status == MMAL_SUCCESS);
1855 raspi_capture_stop(state);
1857 if (status != MMAL_SUCCESS) {
1858 mmal_status_to_int(status);
1859 raspicamcontrol_check_configuration(128);
1866 raspi_capture_stop(RASPIVID_STATE *state)
1868 RASPIVID_CONFIG *config = &state->config;
1870 if (config->verbose)
1871 fprintf(stderr, "Closing down\n");
1873 if (config->preview_parameters.wantPreview )
1874 mmal_connection_destroy(state->preview_connection);
1876 // Disable all our ports that are not handled by connections
1877 check_disable_port(state->camera_still_port);
1878 check_disable_port(state->encoder_output_port);
1880 if (state->encoder_component) {
1881 mmal_connection_destroy(state->encoder_connection);
1882 mmal_component_disable(state->encoder_component);
1883 destroy_encoder_component(state);
1888 raspi_capture_free(RASPIVID_STATE *state)
1890 RASPIVID_CONFIG *config = &state->config;
1892 // Can now close our file. Note disabling ports may flush buffers which causes
1893 // problems if we have already closed the file!
1894 if (state->output_file && state->output_file != stdout)
1895 fclose(state->output_file);
1897 /* Disable components */
1898 if (state->encoder_component)
1899 mmal_component_disable(state->encoder_component);
1901 if (state->preview_state.preview_component)
1902 mmal_component_disable(state->preview_state.preview_component);
1904 if (state->camera_component)
1905 mmal_component_disable(state->camera_component);
1907 destroy_encoder_component(state);
1908 raspipreview_destroy(&state->preview_state);
1909 destroy_camera_component(state);
1911 if (state->encoded_buffer_q) {
1912 mmal_queue_destroy(state->encoded_buffer_q);
1913 state->encoded_buffer_q = NULL;
1916 if (config->verbose)
1917 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1923 raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic)
1925 MMAL_STATUS_T status;
1926 RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters;
1927 MMAL_COMPONENT_T *camera = state->camera_component;
1929 /* Store the new config */
1930 state->config = *config;
1934 if (state->encoder_component && config->change_flags & PROP_CHANGE_ENCODING) {
1935 /* BITRATE or QUANT or KEY Interval, intra refresh */
1936 MMAL_COMPONENT_T *encoder = state->encoder_component;
1937 MMAL_PORT_T *encoder_output = encoder->output[0];
1939 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_VIDEO_BIT_RATE, config->bitrate);
1940 if (status != MMAL_SUCCESS)
1941 vcos_log_warn("Unable to change bitrate dynamically");
1944 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
1945 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1946 if (status != MMAL_SUCCESS)
1947 vcos_log_warn("Unable to change intraperiod dynamically");
1950 #if 0 /* not dynamically change-able */
1952 MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
1953 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1954 if (status != MMAL_SUCCESS)
1955 vcos_log_warn("Unable to change Initial Quantisation Parameter dynamically");
1957 MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
1958 status = mmal_port_parameter_set(encoder_output, ¶m2.hdr);
1959 if (status != MMAL_SUCCESS)
1960 vcos_log_warn("Unable to change Minimum Quantisation Parameter dynamically");
1962 MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
1963 status = mmal_port_parameter_set(encoder_output, ¶m3.hdr);
1964 if (status != MMAL_SUCCESS)
1965 vcos_log_warn("Unable to change Maximum Quantisation Parameter dynamically");
1969 // Adaptive intra refresh settings
1970 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1971 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1972 param.hdr.size = sizeof(param);
1974 // Get first so we don't overwrite anything unexpectedly
1975 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1976 if (state != MMAL_SUCCESS) {
1977 /* Need to memset, apparently mmal_port_parameter_get()
1978 * doesn't retrieve all parameters, causing random failures
1979 * when we set it. On older firmware the get fails.
1981 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1983 param.refresh_mode = config->intra_refresh_type;
1985 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1986 if (status != MMAL_SUCCESS)
1987 vcos_log_warn("Unable to set H264 intra-refresh values dynamically");
1991 if (config->change_flags & PROP_CHANGE_PREVIEW) {
1992 /* Preview settings or fullscreen */
1993 status = raspipreview_update_config (&state->preview_state,
1994 &config->preview_parameters);
1995 if (status != MMAL_SUCCESS)
1996 vcos_log_warn("Unable to change preview config dynamically");
1998 if (config->change_flags & PROP_CHANGE_COLOURBALANCE) {
1999 raspicamcontrol_set_saturation(camera, params->saturation);
2000 raspicamcontrol_set_sharpness(camera, params->sharpness);
2001 raspicamcontrol_set_contrast(camera, params->contrast);
2002 raspicamcontrol_set_brightness(camera, params->brightness);
2004 if (config->change_flags & PROP_CHANGE_SENSOR_SETTINGS) {
2005 /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */
2006 raspicamcontrol_set_ISO(camera, params->ISO);
2007 raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation);
2008 raspicamcontrol_set_exposure_mode(camera, params->exposureMode);
2009 raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode);
2010 raspicamcontrol_set_shutter_speed(camera, params->shutter_speed);
2011 raspicamcontrol_set_DRC(camera, params->drc_level);
2013 /* Can we change sensor mode on the fly? Disable if not */
2014 status = mmal_port_parameter_set_uint32(camera->control,
2015 MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
2016 if (status != MMAL_SUCCESS)
2017 vcos_log_warn("Unable to change sensor mode dynamically");
2019 if (config->change_flags & PROP_CHANGE_VIDEO_STABILISATION) {
2020 raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation);
2022 if (config->change_flags & PROP_CHANGE_AWB) {
2023 raspicamcontrol_set_awb_mode(camera, params->awbMode);
2024 raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b);
2026 if (config->change_flags & PROP_CHANGE_IMAGE_COLOUR_EFFECT) {
2027 raspicamcontrol_set_imageFX(camera, params->imageEffect);
2028 raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects);
2030 if (config->change_flags & PROP_CHANGE_ORIENTATION) {
2031 raspicamcontrol_set_rotation(camera, params->rotation);
2032 raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
2034 if (config->change_flags & PROP_CHANGE_ROI) {
2035 raspicamcontrol_set_ROI(camera, params->roi);
2037 if (config->change_flags & PROP_CHANGE_ANNOTATION)
2038 update_annotation_data(state);