• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param.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, &param.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, &param.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, &param.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, &param.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, &param2.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, &param3.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, &param.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 (&param, 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, &param.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, &param.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, &param.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, &param.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, &param2.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, &param3.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, &param.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 (&param, 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, &param.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, &params->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