1 /* *INDENT-OFF* */
2 /*
3 * Copyright (c) 2013-2016 Jan Schmidt <jan@centricular.com>
4 Portions:
5 Copyright (c) 2013, Broadcom Europe Ltd
6 Copyright (c) 2013, James Hughes
7 All rights reserved.
8
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.
19
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.
30 */
31
32 /**
33 * \file RaspiCapture.c
34 *
35 * Modification of the RaspiVid command line capture program for GStreamer
36 * use.
37 *
38 * \date 28th Feb 2013, 11 Oct 2013, 5 Mar 2015
39 * \Author: James Hughes, Jan Schmidt
40 *
41 * Description
42 *
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.
49 *
50 * We use the RaspiCamControl code to handle the specific camera settings.
51 * We use the RaspiPreview code to handle the (generic) preview window
52 */
53
54 // We use some GNU extensions (basename, asprintf)
55 #define _GNU_SOURCE
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <memory.h>
61 #include <sysexits.h>
62
63 #include <gst/gst.h>
64
65 #include "bcm_host.h"
66 #include "interface/vcos/vcos.h"
67
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"
75
76 #include "RaspiCapture.h"
77 #include "RaspiCamControl.h"
78 #include "RaspiPreview.h"
79 #include "RaspiCLI.h"
80
81 #include <semaphore.h>
82
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
87
88 // Video format information
89 // 0 implies variable
90 #define VIDEO_FRAME_RATE_NUM 30
91 #define VIDEO_FRAME_RATE_DEN 1
92
93 /// Video render needs at least 2 buffers.
94 #define VIDEO_OUTPUT_BUFFERS_NUM 3
95
96 // Max bitrate we allow for recording
97 const int MAX_BITRATE = 25000000; // 25Mbits/s
98
99 /// Interval at which we check for an failure abort during capture
100 const int ABORT_INTERVAL = 100; // ms
101
102
103 int mmal_status_to_int(MMAL_STATUS_T status);
104
105 /** Struct used to pass information in encoder port userdata to callback
106 */
107 typedef struct
108 {
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
111 } PORT_USERDATA;
112
113 struct RASPIVID_STATE_T
114 {
115 RASPIVID_CONFIG config;
116
117 FILE *output_file;
118
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
123
124 MMAL_PORT_T *camera_video_port;
125 MMAL_PORT_T *camera_still_port;
126 MMAL_PORT_T *encoder_output_port;
127
128 MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
129
130 PORT_USERDATA callback_data;
131
132 MMAL_QUEUE_T *encoded_buffer_q;
133
134 int64_t base_time;
135 int64_t last_second;
136
137 RASPIPREVIEW_STATE preview_state;
138 };
139
140
141 /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
142 static XREF_T profile_map[] =
143 {
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?
148 };
149
150 static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]);
151
152 #if 0
153 static XREF_T initial_map[] =
154 {
155 {"record", 0},
156 {"pause", 1},
157 };
158
159 static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]);
160 #endif
161
162 static XREF_T intra_refresh_map[] =
163 {
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.
169 };
170
171 static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]);
172
173 #if 0
174
175 static void display_valid_parameters(char *app_name);
176
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
194 #define CommandQP 16
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
206
207 static COMMAND_LIST cmdline_commands[] =
208 {
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},
237 };
238
239 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
240 #endif
241
242 static void dump_state(RASPIVID_STATE *state);
243
244 /**
245 * Assign a default set of parameters to the state passed in
246 *
247 * @param state Pointer to state structure to assign defaults to
248 */
raspicapture_default_config(RASPIVID_CONFIG * config)249 void raspicapture_default_config(RASPIVID_CONFIG *config)
250 {
251 if (!config)
252 {
253 vcos_assert(0);
254 return;
255 }
256
257 // Default everything to zero
258 memset(config, 0, sizeof(RASPIVID_CONFIG));
259
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;
274
275 config->bInlineHeaders = 0;
276
277 config->inlineMotionVectors = 0;
278
279 config->cameraNum = 0;
280 config->settings = 0;
281 config->sensor_mode = 0;
282
283 config->intra_refresh_type = -1;
284
285 // Setup preview window defaults
286 raspipreview_set_defaults(&config->preview_parameters);
287
288 // Set up the camera_parameters to default
289 raspicamcontrol_set_defaults(&config->camera_parameters);
290
291 }
292
293
294 /**
295 * Dump image state parameters to printf. Used for debugging
296 *
297 * @param state Pointer to state structure to assign defaults to
298 */
dump_state(RASPIVID_STATE * state)299 static void dump_state(RASPIVID_STATE *state)
300 {
301 RASPIVID_CONFIG *config;
302
303 if (!state)
304 {
305 vcos_assert(0);
306 return;
307 }
308
309 config = &state->config;
310
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));
315
316 raspipreview_dump_parameters(&config->preview_parameters);
317 raspicamcontrol_dump_parameters(&config->camera_parameters);
318 }
319
320 #if 0
321 /**
322 * Parse the incoming command line and put resulting parameters in to the state
323 *
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
328 */
329 static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
330 {
331 // Parse the command line arguments.
332 // We are looking for --<something> or -<abreviation of something>
333
334 int valid = 1;
335 int i;
336
337 for (i = 1; i < argc && valid; i++)
338 {
339 int command_id, num_parameters;
340
341 if (!argv[i])
342 continue;
343
344 if (argv[i][0] != '-')
345 {
346 valid = 0;
347 continue;
348 }
349
350 // Assume parameter is valid until proven otherwise
351 valid = 1;
352
353 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
354
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) )
357 continue;
358
359 // We are now dealing with a command line option
360 switch (command_id)
361 {
362 case CommandHelp:
363 display_valid_parameters(basename(argv[0]));
364 return -1;
365
366 case CommandWidth: // Width > 0
367 if (sscanf(argv[i + 1], "%u", &state->width) != 1)
368 valid = 0;
369 else
370 i++;
371 break;
372
373 case CommandHeight: // Height > 0
374 if (sscanf(argv[i + 1], "%u", &state->height) != 1)
375 valid = 0;
376 else
377 i++;
378 break;
379
380 case CommandBitrate: // 1-100
381 if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1)
382 {
383 if (state->bitrate > MAX_BITRATE)
384 {
385 state->bitrate = MAX_BITRATE;
386 }
387 i++;
388 }
389 else
390 valid = 0;
391
392 break;
393
394 case CommandOutput: // output filename
395 {
396 int len = strlen(argv[i + 1]);
397 if (len)
398 {
399 state->filename = malloc(len + 1);
400 vcos_assert(state->filename);
401 if (state->filename)
402 strncpy(state->filename, argv[i + 1], len+1);
403 i++;
404 }
405 else
406 valid = 0;
407 break;
408 }
409
410 case CommandVerbose: // display lots of data during run
411 state->verbose = 1;
412 break;
413
414 case CommandTimeout: // Time to run viewfinder/capture
415 {
416 if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
417 {
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;
421
422 i++;
423 }
424 else
425 valid = 0;
426 break;
427 }
428
429 case CommandDemoMode: // Run in demo mode - no capture
430 {
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] != '-')
434 {
435 if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
436 {
437 // TODO : What limits do we need for timeout?
438 if (state->demoInterval == 0)
439 state->demoInterval = 250; // ms
440
441 state->demoMode = 1;
442 i++;
443 }
444 else
445 valid = 0;
446 }
447 else
448 {
449 state->demoMode = 1;
450 }
451
452 break;
453 }
454
455 case CommandFramerate: // fps to record
456 {
457 if (sscanf(argv[i + 1], "%u", &state->framerate) == 1)
458 {
459 // TODO : What limits do we need for fps 1 - 30 - 120??
460 i++;
461 }
462 else
463 valid = 0;
464 break;
465 }
466
467 case CommandPreviewEnc:
468 state->immutableInput = 0;
469 break;
470
471 case CommandIntraPeriod: // key frame rate
472 {
473 if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1)
474 i++;
475 else
476 valid = 0;
477 break;
478 }
479
480 case CommandQP: // quantisation parameter
481 {
482 if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1)
483 i++;
484 else
485 valid = 0;
486 break;
487 }
488
489 case CommandProfile: // H264 profile
490 {
491 state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size);
492
493 if( state->profile == -1)
494 state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
495
496 i++;
497 break;
498 }
499
500 case CommandInlineHeaders: // H264 inline headers
501 {
502 state->bInlineHeaders = 1;
503 break;
504 }
505
506 case CommandTimed:
507 {
508 if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2)
509 {
510 i++;
511
512 if (state->onTime < 1000)
513 state->onTime = 1000;
514
515 if (state->offTime < 1000)
516 state->offTime = 1000;
517
518 state->waitMethod = WAIT_METHOD_TIMED;
519 }
520 else
521 valid = 0;
522 break;
523 }
524
525 case CommandKeypress:
526 state->waitMethod = WAIT_METHOD_KEYPRESS;
527 break;
528
529 case CommandSignal:
530 state->waitMethod = WAIT_METHOD_SIGNAL;
531 // Reenable the signal
532 signal(SIGUSR1, signal_handler);
533 break;
534
535 case CommandInitialState:
536 {
537 state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size);
538
539 if( state->bCapturing == -1)
540 state->bCapturing = 0;
541
542 i++;
543 break;
544 }
545
546 case CommandSegmentFile: // Segment file in to chunks of specified time
547 {
548 if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1)
549 {
550 // Must enable inline headers for this to work
551 state->bInlineHeaders = 1;
552 i++;
553 }
554 else
555 valid = 0;
556 break;
557 }
558
559 case CommandSegmentWrap: // segment wrap value
560 {
561 if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1)
562 i++;
563 else
564 valid = 0;
565 break;
566 }
567
568 case CommandSegmentStart: // initial segment number
569 {
570 if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap)))
571 i++;
572 else
573 valid = 0;
574 break;
575 }
576
577 case CommandSplitWait: // split files on restart
578 {
579 // Must enable inline headers for this to work
580 state->bInlineHeaders = 1;
581 state->splitWait = 1;
582 break;
583 }
584
585 case CommandCircular:
586 {
587 state->bCircularBuffer = 1;
588 break;
589 }
590
591 case CommandIMV: // output filename
592 {
593 state->inlineMotionVectors = 1;
594 int len = strlen(argv[i + 1]);
595 if (len)
596 {
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);
601 i++;
602 }
603 else
604 valid = 0;
605 break;
606 }
607 case CommandCamSelect: //Select camera input port
608 {
609 if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1)
610 {
611 i++;
612 }
613 else
614 valid = 0;
615 break;
616 }
617
618 case CommandSettings:
619 state->settings = 1;
620 break;
621
622 case CommandSensorMode:
623 {
624 if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1)
625 {
626 i++;
627 }
628 else
629 valid = 0;
630 break;
631 }
632
633 case CommandIntraRefreshType:
634 {
635 state->config.intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size);
636 i++;
637 break;
638 }
639
640 default:
641 {
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));
647
648 // Still unused, try preview options
649 if (!parms_used)
650 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
651
652
653 // If no parms were used, this must be a bad parameters
654 if (!parms_used)
655 valid = 0;
656 else
657 i += parms_used - 1;
658
659 break;
660 }
661 }
662 }
663
664 if (!valid)
665 {
666 fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
667 return 1;
668 }
669
670 // Always disable verbose if output going to stdout
671 if (state->filename && state->filename[0] == '-')
672 {
673 state->verbose = 0;
674 }
675
676 return 0;
677 }
678
679 /**
680 * Display usage information for the application to stdout
681 *
682 * @param app_name String to display as the application name
683 */
684 static void display_valid_parameters(char *app_name)
685 {
686 int i;
687
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);
690
691 fprintf(stderr, "Image parameter commands\n\n");
692
693 raspicli_display_help(cmdline_commands, cmdline_commands_size);
694
695 // Profile options
696 fprintf(stderr, "\n\nH264 Profile options :\n%s", profile_map[0].mode );
697
698 for (i=1;i<profile_map_size;i++)
699 {
700 fprintf(stderr, ",%s", profile_map[i].mode);
701 }
702
703 fprintf(stderr, "\n");
704
705 // Intra refresh options
706 fprintf(stderr, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode );
707
708 for (i=1;i<intra_refresh_map_size;i++)
709 {
710 fprintf(stderr, ",%s", intra_refresh_map[i].mode);
711 }
712
713 fprintf(stderr, "\n");
714
715 // Help for preview options
716 raspipreview_display_help();
717
718 // Now display any help information from the camcontrol code
719 raspicamcontrol_display_help();
720
721 fprintf(stderr, "\n");
722
723 return;
724 }
725 #endif
726
727 /**
728 * buffer header callback function for camera control
729 *
730 * Callback will dump buffer data to the specific file
731 *
732 * @param port Pointer to port from which callback originated
733 * @param buffer mmal buffer header pointer
734 */
camera_control_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)735 static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
736 {
737 if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
738 {
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:
742 {
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",
745 settings->exposure,
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
751 );
752 }
753 break;
754 }
755 }
756 else if (buffer->cmd == MMAL_EVENT_ERROR) {
757 vcos_log_error("Camera control callback got an error");
758 } else {
759 vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
760 }
761
762 mmal_buffer_header_release(buffer);
763 }
764
765 #if 0
766 /**
767 * Open a file based on the settings in state
768 *
769 * @param state Pointer to state
770 */
771 static FILE *open_filename(RASPIVID_STATE *pState)
772 {
773 FILE *new_handle = NULL;
774 char *tempname = NULL, *filename = NULL;
775
776 if (pState->segmentSize || pState->splitWait)
777 {
778 // Create a new filename string
779 asprintf(&tempname, pState->filename, pState->segmentNumber);
780 filename = tempname;
781 }
782 else
783 {
784 filename = pState->filename;
785 }
786
787 if (filename)
788 new_handle = fopen(filename, "wb");
789
790 if (pState->verbose)
791 {
792 if (new_handle)
793 fprintf(stderr, "Opening output file \"%s\"\n", filename);
794 else
795 fprintf(stderr, "Failed to open new file \"%s\"\n", filename);
796 }
797
798 if (tempname)
799 free(tempname);
800
801 return new_handle;
802 }
803
804 /**
805 * Open a file based on the settings in state
806 *
807 * This time for the imv output file
808 *
809 * @param state Pointer to state
810 */
811 static FILE *open_imv_filename(RASPIVID_STATE *pState)
812 {
813 FILE *new_handle = NULL;
814 char *tempname = NULL, *filename = NULL;
815
816 if (pState->segmentSize || pState->splitWait)
817 {
818 // Create a new filename string
819 asprintf(&tempname, pState->imv_filename, pState->segmentNumber);
820 filename = tempname;
821 }
822 else
823 {
824 filename = pState->imv_filename;
825 }
826
827 if (filename)
828 new_handle = fopen(filename, "wb");
829
830 if (pState->verbose)
831 {
832 if (new_handle)
833 fprintf(stderr, "Opening imv output file \"%s\"\n", filename);
834 else
835 fprintf(stderr, "Failed to open new imv file \"%s\"\n", filename);
836 }
837
838 if (tempname)
839 free(tempname);
840
841 return new_handle;
842 }
843 #endif
844
845 /**
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
850 *
851 * @param state Pointer to state control struct
852 *
853 */
update_annotation_data(RASPIVID_STATE * state)854 static void update_annotation_data(RASPIVID_STATE *state)
855 {
856 RASPIVID_CONFIG *config = &state->config;
857
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)
860 {
861 char *text;
862 const char *refresh = raspicli_unmap_xref(config->intra_refresh_type, intra_refresh_map, intra_refresh_map_size);
863
864 asprintf(&text, "%dk,%ff,%s,%d,%s",
865 config->bitrate / 1000, ((float)(config->fps_n) / config->fps_d),
866 refresh ? refresh : "(none)",
867 config->intraperiod,
868 raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
869
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);
874
875 free(text);
876 }
877 else
878 {
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);
884 }
885 }
886
887
888
889 /**
890 * buffer header callback function for encoder
891 *
892 * Callback will dump buffer data to the specific file
893 *
894 * @param port Pointer to port from which callback originated
895 * @param buffer mmal buffer header pointer
896 */
encoder_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)897 static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
898 {
899 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
900 RASPIVID_STATE *state = pData->state;
901 int64_t current_time;
902
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;
906
907 if (pData == NULL)
908 {
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);
912 return;
913 }
914
915 current_time = vcos_getmicrosecs64()/1000;
916 if (state->base_time == -1)
917 state->base_time = current_time;
918
919 // See if the second count has changed and we need to update any annotation
920 if (current_time/1000 != state->last_second)
921 {
922 update_annotation_data(state);
923 state->last_second = current_time/1000;
924 }
925
926 /* Send buffer to GStreamer element for pushing to the pipeline */
927 mmal_queue_put(state->encoded_buffer_q, buffer);
928 }
929
930 GstFlowReturn
raspi_capture_fill_buffer(RASPIVID_STATE * state,GstBuffer ** bufp,GstClock * clock,GstClockTime base_time)931 raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp,
932 GstClock *clock, GstClockTime base_time)
933 {
934 RASPIVID_CONFIG *config = &state->config;
935 GstBuffer *buf;
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;
940
941 do {
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.");
948 continue;
949 }
950 } while (0);
951
952 if (G_UNLIKELY(buffer == NULL)) {
953 return GST_FLOW_ERROR_TIMEOUT;
954 }
955
956 if (G_LIKELY (config->useSTC && clock)) {
957 MMAL_PARAMETER_INT64_T param;
958 GstClockTime runtime;
959
960 runtime = gst_clock_get_time (clock) - base_time;
961
962 param.hdr.id = MMAL_PARAMETER_SYSTEM_TIME;
963 param.hdr.size = sizeof(param);
964 param.value = -1;
965
966 mmal_port_parameter_get(state->encoder_output_port, ¶m.hdr);
967
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;
973 }
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));
979 }
980 else {
981 GST_LOG ("use-stc=false. Not applying STC to buffer");
982 }
983
984 mmal_buffer_header_mem_lock(buffer);
985 buf = gst_buffer_new_allocate(NULL, buffer->length, NULL);
986 if (buf) {
987 if (config->useSTC)
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->offset, buffer->length);
991
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);
996 else
997 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
998
999 /* NAL_END is bogus and can't be trusted */
1000 if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END))
1001 ret = GST_FLOW_OK;
1002 else
1003 ret = GST_FLOW_KEEP_ACCUMULATING;
1004 }
1005
1006 mmal_buffer_header_mem_unlock(buffer);
1007
1008 *bufp = buf;
1009 // release buffer back to the pool
1010 mmal_buffer_header_release(buffer);
1011
1012 // and send one back to the port (if still open)
1013 if (state->encoder_output_port->is_enabled)
1014 {
1015 MMAL_STATUS_T status = MMAL_SUCCESS;
1016
1017 buffer = mmal_queue_get(state->encoder_pool->queue);
1018 if (buffer)
1019 status = mmal_port_send_buffer(state->encoder_output_port, buffer);
1020
1021 if (!buffer || status != MMAL_SUCCESS) {
1022 vcos_log_error("Unable to return a buffer to the encoder port");
1023 ret = GST_FLOW_ERROR;
1024 }
1025 }
1026
1027 return ret;
1028 }
1029
1030 /**
1031 * Create the camera component, set up its ports
1032 *
1033 * @param state Pointer to state control struct
1034 *
1035 * @return MMAL_SUCCESS if all OK, something else otherwise
1036 *
1037 */
create_camera_component(RASPIVID_STATE * state)1038 static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
1039 {
1040 MMAL_COMPONENT_T *camera = NULL;
1041 MMAL_STATUS_T status;
1042 RASPIVID_CONFIG *config = &state->config;
1043
1044 /* Create the component */
1045 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
1046
1047 if (status != MMAL_SUCCESS)
1048 {
1049 vcos_log_error("Failed to create camera component");
1050 goto error;
1051 }
1052
1053 MMAL_PARAMETER_INT32_T camera_num =
1054 {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum};
1055
1056 status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
1057
1058 if (status != MMAL_SUCCESS)
1059 {
1060 vcos_log_error("Could not select camera : error %d", status);
1061 goto error;
1062 }
1063
1064 if (!camera->output_num)
1065 {
1066 status = MMAL_ENOSYS;
1067 vcos_log_error("Camera doesn't have output ports");
1068 goto error;
1069 }
1070
1071 status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
1072
1073 if (status != MMAL_SUCCESS)
1074 {
1075 vcos_log_error("Could not set sensor mode : error %d", status);
1076 goto error;
1077 }
1078
1079 if (config->settings)
1080 {
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};
1084
1085 status = mmal_port_parameter_set(camera->control, &change_event_request.hdr);
1086 if ( status != MMAL_SUCCESS )
1087 {
1088 vcos_log_error("No camera settings events");
1089 }
1090 }
1091
1092 // Enable the camera, and tell it its control callback function
1093 status = mmal_port_enable(camera->control, camera_control_callback);
1094
1095 if (status != MMAL_SUCCESS)
1096 {
1097 vcos_log_error("Unable to enable control port : error %d", status);
1098 goto error;
1099 }
1100
1101 state->camera_component = camera;
1102
1103 return status;
1104
1105 error:
1106 if (camera)
1107 mmal_component_destroy(camera);
1108
1109 return status;
1110 }
1111
1112 MMAL_STATUS_T
raspi_capture_set_format_and_start(RASPIVID_STATE * state)1113 raspi_capture_set_format_and_start(RASPIVID_STATE *state)
1114 {
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;
1120
1121 // set up the camera configuration
1122
1123 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
1124 {
1125 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
1126 .max_stills_w = config->width,
1127 .max_stills_h = config->height,
1128 .stills_yuv422 = 0,
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
1136 };
1137
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];
1142
1143 mmal_port_parameter_set(camera->control, &cam_config.hdr);
1144
1145 // Now set up the port formats
1146
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
1149
1150 format = preview_port->format;
1151
1152 if(config->camera_parameters.shutter_speed > 6000000)
1153 {
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);
1157 }
1158 else if(config->camera_parameters.shutter_speed > 1000000)
1159 {
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);
1163 }
1164
1165 //enable dynamic framerate if necessary
1166 if (config->camera_parameters.shutter_speed)
1167 {
1168 if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed)
1169 {
1170 config->fps_n = 0;
1171 config->fps_d = 1;
1172 GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement");
1173 }
1174 }
1175
1176 format->encoding = MMAL_ENCODING_OPAQUE;
1177 format->encoding_variant = MMAL_ENCODING_I420;
1178
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;
1187
1188 status = mmal_port_format_commit(preview_port);
1189
1190 if (status != MMAL_SUCCESS)
1191 {
1192 vcos_log_error("camera viewfinder format couldn't be set");
1193 goto error;
1194 }
1195
1196 // Set the encode format on the video port
1197 format = video_port->format;
1198
1199 if(config->camera_parameters.shutter_speed > 6000000)
1200 {
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);
1204 }
1205 else if(config->camera_parameters.shutter_speed > 1000000)
1206 {
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);
1210 }
1211
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;
1216 }
1217 else {
1218 format->encoding = config->encoding;
1219 format->encoding_variant = config->encoding;
1220 }
1221
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;
1230
1231 status = mmal_port_format_commit(video_port);
1232
1233 if (status != MMAL_SUCCESS)
1234 {
1235 vcos_log_error("camera video format couldn't be set");
1236 goto error;
1237 }
1238
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;
1242
1243
1244 // Set the encode format on the still port
1245
1246 format = still_port->format;
1247
1248 format->encoding = MMAL_ENCODING_OPAQUE;
1249 format->encoding_variant = MMAL_ENCODING_I420;
1250
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;
1259
1260 status = mmal_port_format_commit(still_port);
1261
1262 if (status != MMAL_SUCCESS)
1263 {
1264 vcos_log_error("camera still format couldn't be set");
1265 goto error;
1266 }
1267
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;
1271
1272 /* Enable component */
1273 status = mmal_component_enable(camera);
1274
1275 if (status != MMAL_SUCCESS)
1276 {
1277 vcos_log_error("camera component couldn't be enabled");
1278 goto error;
1279 }
1280
1281 raspicamcontrol_set_all_parameters(camera, &config->camera_parameters);
1282
1283 update_annotation_data(state);
1284
1285 if (config->verbose)
1286 fprintf(stderr, "Camera component done\n");
1287
1288 return status;
1289
1290 error:
1291 if (camera)
1292 mmal_component_disable(camera);
1293
1294 return status;
1295 }
1296
1297 /**
1298 * Destroy the camera component
1299 *
1300 * @param state Pointer to state control struct
1301 *
1302 */
destroy_camera_component(RASPIVID_STATE * state)1303 static void destroy_camera_component(RASPIVID_STATE *state)
1304 {
1305 if (state->camera_component)
1306 {
1307 mmal_component_destroy(state->camera_component);
1308 state->camera_component = NULL;
1309 }
1310 }
1311
raspi_capture_request_i_frame(RASPIVID_STATE * state)1312 gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state)
1313 {
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};
1317
1318 if (state->encoder_component)
1319 return TRUE;
1320
1321 encoder_output = state->encoder_component->output[0];
1322 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1323 if (status != MMAL_SUCCESS)
1324 {
1325 vcos_log_error("Unable to request I-frame");
1326 return FALSE;
1327 }
1328 return TRUE;
1329 }
1330
1331 /**
1332 * Create the encoder component, set up its ports
1333 *
1334 * @param state Pointer to state control struct
1335 *
1336 * @return MMAL_SUCCESS if all OK, something else otherwise
1337 *
1338 */
create_encoder_component(RASPIVID_STATE * state)1339 static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
1340 {
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;
1345
1346 gboolean encoded_format =
1347 (config->encoding == MMAL_ENCODING_H264 ||
1348 config->encoding == MMAL_ENCODING_MJPEG ||
1349 config->encoding == MMAL_ENCODING_JPEG);
1350
1351 if (!encoded_format)
1352 return MMAL_SUCCESS;
1353
1354 if (config->encoding == MMAL_ENCODING_JPEG)
1355 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
1356 else
1357 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
1358
1359 if (status != MMAL_SUCCESS) {
1360 vcos_log_error("Unable to create video encoder component");
1361 goto error;
1362 }
1363
1364 if (!encoder->input_num || !encoder->output_num)
1365 {
1366 status = MMAL_ENOSYS;
1367 vcos_log_error("Video encoder doesn't have input/output ports");
1368 goto error;
1369 }
1370
1371 encoder_input = encoder->input[0];
1372 encoder_output = encoder->output[0];
1373
1374 // We want same format on input and output
1375 mmal_format_copy(encoder_output->format, encoder_input->format);
1376
1377 // Configure desired encoding
1378 encoder_output->format->encoding = config->encoding;
1379
1380 encoder_output->format->bitrate = config->bitrate;
1381
1382 if (config->encoding == MMAL_ENCODING_H264)
1383 encoder_output->buffer_size = encoder_output->buffer_size_recommended;
1384 else
1385 encoder_output->buffer_size = 256<<10;
1386
1387 if (encoder_output->buffer_size < encoder_output->buffer_size_min)
1388 encoder_output->buffer_size = encoder_output->buffer_size_min;
1389
1390 encoder_output->buffer_num = encoder_output->buffer_num_recommended;
1391
1392 if (encoder_output->buffer_num < encoder_output->buffer_num_min)
1393 encoder_output->buffer_num = encoder_output->buffer_num_min;
1394
1395 GST_DEBUG ("encoder wants %d buffers of size %u",
1396 (guint)encoder_output->buffer_num, (guint)encoder_output->buffer_size);
1397
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;
1402
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");
1407 goto error;
1408 }
1409
1410 // Set the rate control parameter
1411 if (0)
1412 {
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)
1416 {
1417 vcos_log_error("Unable to set ratecontrol");
1418 goto error;
1419 }
1420
1421 }
1422
1423 if (config->encoding == MMAL_ENCODING_H264 && config->intraperiod != -1)
1424 {
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)
1428 {
1429 vcos_log_error("Unable to set intraperiod");
1430 goto error;
1431 }
1432 }
1433
1434 if (config->encoding == MMAL_ENCODING_H264 && config->quantisationParameter)
1435 {
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)
1439 {
1440 vcos_log_error("Unable to set initial QP");
1441 goto error;
1442 }
1443
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)
1447 {
1448 vcos_log_error("Unable to set min QP");
1449 goto error;
1450 }
1451
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)
1455 {
1456 vcos_log_error("Unable to set max QP");
1457 goto error;
1458 }
1459 }
1460
1461 if (config->encoding == MMAL_ENCODING_H264)
1462 {
1463 MMAL_PARAMETER_VIDEO_PROFILE_T param;
1464 param.hdr.id = MMAL_PARAMETER_PROFILE;
1465 param.hdr.size = sizeof(param);
1466
1467 param.profile[0].profile = config->profile;
1468 param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
1469
1470 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1471 if (status != MMAL_SUCCESS)
1472 {
1473 vcos_log_error("Unable to set H264 profile");
1474 goto error;
1475 }
1476 }
1477
1478 if (config->encoding != MMAL_ENCODING_JPEG)
1479 {
1480 if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS)
1481 {
1482 vcos_log_error("Unable to set immutable input flag");
1483 // Continue rather than abort..
1484 }
1485
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)
1488 {
1489 vcos_log_error("failed to set INLINE HEADER FLAG parameters");
1490 // Continue rather than abort..
1491 }
1492 }
1493
1494 if (config->encoding == MMAL_ENCODING_H264)
1495 {
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)
1498 {
1499 vcos_log_error("failed to set INLINE VECTORS parameters");
1500 // Continue rather than abort..
1501 }
1502
1503 // Adaptive intra refresh settings
1504 if (config->intra_refresh_type != -1)
1505 {
1506 MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param;
1507
1508 /* Need to memset, apparently mmal_port_parameter_get()
1509 * doesn't retrieve all parameters, causing random failures
1510 * when we set it
1511 */
1512 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1513
1514 param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
1515 param.hdr.size = sizeof(param);
1516
1517 // Get first so we don't overwrite anything unexpectedly
1518 status = mmal_port_parameter_get(encoder_output, ¶m.hdr);
1519
1520 param.refresh_mode = config->intra_refresh_type;
1521
1522 //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
1523 // param.cir_mbs = 10;
1524
1525 status = mmal_port_parameter_set(encoder_output, ¶m.hdr);
1526 if (status != MMAL_SUCCESS)
1527 {
1528 vcos_log_error("Unable to set H264 intra-refresh values");
1529 goto error;
1530 }
1531 }
1532 }
1533
1534 if (config->encoding == MMAL_ENCODING_JPEG)
1535 {
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
1540 }
1541
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
1547 }
1548 #endif
1549 }
1550
1551 // Enable component
1552 status = mmal_component_enable(encoder);
1553
1554 if (status != MMAL_SUCCESS)
1555 {
1556 vcos_log_error("Unable to enable video encoder component");
1557 goto error;
1558 }
1559
1560 state->encoder_component = encoder;
1561
1562 if (config->verbose)
1563 fprintf(stderr, "Encoder component done\n");
1564
1565 return status;
1566
1567 error:
1568 if (encoder)
1569 mmal_component_destroy(encoder);
1570
1571 state->encoder_component = NULL;
1572
1573 return status;
1574 }
1575
1576 /**
1577 * Destroy the encoder component
1578 *
1579 * @param state Pointer to state control struct
1580 *
1581 */
destroy_encoder_component(RASPIVID_STATE * state)1582 static void destroy_encoder_component(RASPIVID_STATE *state)
1583 {
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);
1589 }
1590 }
1591
1592 // Get rid of any port buffers first
1593 if (state->encoder_pool)
1594 {
1595 mmal_port_pool_destroy(state->encoder_output_port, state->encoder_pool);
1596 state->encoder_pool = NULL;
1597 }
1598
1599 if (state->encoder_component) {
1600
1601 mmal_component_destroy(state->encoder_component);
1602 state->encoder_component = NULL;
1603 }
1604 }
1605
1606 /**
1607 * Connect two specific ports together
1608 *
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
1613 *
1614 */
connect_ports(MMAL_PORT_T * output_port,MMAL_PORT_T * input_port,MMAL_CONNECTION_T ** connection)1615 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
1616 {
1617 MMAL_STATUS_T status;
1618
1619 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
1620
1621 if (status == MMAL_SUCCESS)
1622 {
1623 status = mmal_connection_enable(*connection);
1624 if (status != MMAL_SUCCESS)
1625 mmal_connection_destroy(*connection);
1626 }
1627
1628 return status;
1629 }
1630
1631 /**
1632 * Checks if specified port is valid and enabled, then disables it
1633 *
1634 * @param port Pointer the port
1635 *
1636 */
check_disable_port(MMAL_PORT_T * port)1637 static void check_disable_port(MMAL_PORT_T *port)
1638 {
1639 if (port && port->is_enabled)
1640 mmal_port_disable(port);
1641 }
1642
raspicapture_init(void)1643 void raspicapture_init(void)
1644 {
1645 bcm_host_init();
1646
1647 // Register our application with the logging system
1648 vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
1649 }
1650
1651 RASPIVID_STATE *
raspi_capture_setup(RASPIVID_CONFIG * config)1652 raspi_capture_setup(RASPIVID_CONFIG *config)
1653 {
1654 // Our main data storage vessel..
1655 RASPIVID_STATE *state;
1656
1657 MMAL_STATUS_T status = MMAL_SUCCESS;
1658
1659 /* Default everything to zero */
1660 state = calloc(1, sizeof(RASPIVID_STATE));
1661
1662 /* Apply passed in config */
1663 state->config = *config;
1664
1665 /* Initialize timestamping */
1666 state->base_time = state->last_second = -1;
1667
1668 /* So far, all we can do is create the camera component. Actual
1669 * config and connection of encoders etc happens in _start()
1670 */
1671 // OK, we have a nice set of parameters. Now set up our components
1672 // We have three components. Camera, Preview and encoder.
1673
1674 if ((status = create_camera_component(state)) != MMAL_SUCCESS)
1675 {
1676 vcos_log_error("%s: Failed to create camera component", __func__);
1677 return NULL;
1678 }
1679
1680 if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS)
1681 {
1682 vcos_log_error("%s: Failed to create preview component", __func__);
1683 destroy_camera_component(state);
1684 return NULL;
1685 }
1686
1687 state->encoded_buffer_q = mmal_queue_create();
1688
1689 return state;
1690 }
1691
1692 gboolean
raspi_capture_start(RASPIVID_STATE * state)1693 raspi_capture_start(RASPIVID_STATE *state)
1694 {
1695 MMAL_STATUS_T status = MMAL_SUCCESS;
1696 RASPIVID_CONFIG *config = &state->config;
1697
1698 MMAL_PORT_T *camera_preview_port = NULL;
1699 MMAL_PORT_T *preview_input_port = NULL;
1700 MMAL_PORT_T *encoder_input_port = NULL;
1701
1702 MMAL_POOL_T *pool;
1703
1704 if ((status = create_encoder_component(state)) != MMAL_SUCCESS) {
1705 vcos_log_error("%s: Failed to create encode component", __func__);
1706 return FALSE;
1707 }
1708
1709 if (config->verbose)
1710 {
1711 dump_state(state);
1712 }
1713
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];
1718
1719 if (state->encoder_component) {
1720 encoder_input_port = state->encoder_component->input[0];
1721 state->encoder_output_port = state->encoder_component->output[0];
1722 } else {
1723 state->encoder_output_port = state->camera_video_port;
1724 }
1725
1726 if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) {
1727 return FALSE;
1728 }
1729
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);
1735 if (!pool)
1736 {
1737 vcos_log_error("Failed to create buffer header pool for encoder output port %s",
1738 state->encoder_output_port->name);
1739 return FALSE;
1740 }
1741 state->encoder_pool = pool;
1742
1743 if (state->config.verbose)
1744 fprintf(stderr, "Starting component connection stage\n");
1745
1746 if (config->preview_parameters.wantPreview )
1747 {
1748 if (config->verbose)
1749 {
1750 fprintf(stderr, "Connecting camera preview port to preview input port\n");
1751 fprintf(stderr, "Starting video preview\n");
1752 }
1753
1754 // Connect camera to preview
1755 status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection);
1756 if (status != MMAL_SUCCESS)
1757 {
1758 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1759 return FALSE;
1760 }
1761 }
1762
1763 if (state->encoder_component) {
1764 if (config->verbose)
1765 fprintf(stderr, "Connecting camera video port to encoder input port\n");
1766
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)
1770 {
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__);
1774 return FALSE;
1775 }
1776 }
1777
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;
1781
1782 state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
1783
1784 if (config->verbose)
1785 fprintf(stderr, "Enabling encoder output port\n");
1786
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)
1790 {
1791 vcos_log_error("Failed to setup encoder output");
1792 goto error;
1793 }
1794
1795 if (config->demoMode)
1796 {
1797 // Run for the user specific time..
1798 int num_iterations = config->timeout / config->demoInterval;
1799 int i;
1800
1801 if (config->verbose)
1802 fprintf(stderr, "Running in demo mode\n");
1803
1804 for (i=0;config->timeout == 0 || i<num_iterations;i++)
1805 {
1806 raspicamcontrol_cycle_test(state->camera_component);
1807 vcos_sleep(state->config.demoInterval);
1808 }
1809 }
1810
1811 if (config->verbose)
1812 fprintf(stderr, "Starting video capture\n");
1813
1814 if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1815 {
1816 goto error;
1817 }
1818
1819 // Send all the buffers to the encoder output port
1820 {
1821 int num = mmal_queue_length(state->encoder_pool->queue);
1822 int q;
1823 for (q=0;q<num;q++)
1824 {
1825 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
1826
1827 if (!buffer)
1828 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1829
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);
1832
1833 }
1834 }
1835
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
1839
1840 #if 0
1841 for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
1842 {
1843 vcos_sleep(ABORT_INTERVAL);
1844 if (state->callback_data.abort)
1845 break;
1846 }
1847
1848 if (config->verbose)
1849 fprintf(stderr, "Finished capture\n");
1850 #endif
1851
1852 return (status == MMAL_SUCCESS);
1853
1854 error:
1855 raspi_capture_stop(state);
1856
1857 if (status != MMAL_SUCCESS) {
1858 mmal_status_to_int(status);
1859 raspicamcontrol_check_configuration(128);
1860 }
1861
1862 return FALSE;
1863 }
1864
1865 void
raspi_capture_stop(RASPIVID_STATE * state)1866 raspi_capture_stop(RASPIVID_STATE *state)
1867 {
1868 RASPIVID_CONFIG *config = &state->config;
1869
1870 if (config->verbose)
1871 fprintf(stderr, "Closing down\n");
1872
1873 if (config->preview_parameters.wantPreview )
1874 mmal_connection_destroy(state->preview_connection);
1875
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);
1879
1880 if (state->encoder_component) {
1881 mmal_connection_destroy(state->encoder_connection);
1882 mmal_component_disable(state->encoder_component);
1883 destroy_encoder_component(state);
1884 }
1885 }
1886
1887 void
raspi_capture_free(RASPIVID_STATE * state)1888 raspi_capture_free(RASPIVID_STATE *state)
1889 {
1890 RASPIVID_CONFIG *config = &state->config;
1891
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);
1896
1897 /* Disable components */
1898 if (state->encoder_component)
1899 mmal_component_disable(state->encoder_component);
1900
1901 if (state->preview_state.preview_component)
1902 mmal_component_disable(state->preview_state.preview_component);
1903
1904 if (state->camera_component)
1905 mmal_component_disable(state->camera_component);
1906
1907 destroy_encoder_component(state);
1908 raspipreview_destroy(&state->preview_state);
1909 destroy_camera_component(state);
1910
1911 if (state->encoded_buffer_q) {
1912 mmal_queue_destroy(state->encoded_buffer_q);
1913 state->encoded_buffer_q = NULL;
1914 }
1915
1916 if (config->verbose)
1917 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1918
1919 free(state);
1920 }
1921
1922 void
raspi_capture_update_config(RASPIVID_STATE * state,RASPIVID_CONFIG * config,gboolean dynamic)1923 raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic)
1924 {
1925 MMAL_STATUS_T status;
1926 RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters;
1927 MMAL_COMPONENT_T *camera = state->camera_component;
1928
1929 /* Store the new config */
1930 state->config = *config;
1931 if (!dynamic)
1932 return;
1933
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];
1938
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");
1942
1943 {
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");
1948 }
1949
1950 #if 0 /* not dynamically change-able */
1951 {
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");
1956
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");
1961
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");
1966 }
1967
1968 {
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);
1973
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.
1980 */
1981 memset (¶m, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
1982 }
1983 param.refresh_mode = config->intra_refresh_type;
1984
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");
1988 }
1989 #endif
1990 }
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");
1997 }
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);
2003 }
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);
2012
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");
2018 }
2019 if (config->change_flags & PROP_CHANGE_VIDEO_STABILISATION) {
2020 raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation);
2021 }
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);
2025 }
2026 if (config->change_flags & PROP_CHANGE_IMAGE_COLOUR_EFFECT) {
2027 raspicamcontrol_set_imageFX(camera, params->imageEffect);
2028 raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects);
2029 }
2030 if (config->change_flags & PROP_CHANGE_ORIENTATION) {
2031 raspicamcontrol_set_rotation(camera, params->rotation);
2032 raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
2033 }
2034 if (config->change_flags & PROP_CHANGE_ROI) {
2035 raspicamcontrol_set_ROI(camera, params->roi);
2036 }
2037 if (config->change_flags & PROP_CHANGE_ANNOTATION)
2038 update_annotation_data(state);
2039 }
2040 /* *INDENT-ON* */
2041