rpicamsrc: RaspiCapture: handle MMAL_EVENT_ERROR
[platform/upstream/gstreamer.git] / sys / rpicamsrc / RaspiCapture.c
index 348a5c8..cdc6ce2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
+ * Copyright (c) 2013-2016 Jan Schmidt <jan@centricular.com>
 Portions:
 Copyright (c) 2013, Broadcom Europe Ltd
 Copyright (c) 2013, James Hughes
@@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * Modification of the RaspiVid command line capture program for GStreamer
  * use.
  *
- * \date 28th Feb 2013, 11 Oct 2013
+ * \date 28th Feb 2013, 11 Oct 2013, 5 Mar 2015
  * \Author: James Hughes, Jan Schmidt
  *
  * Description
@@ -50,6 +50,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * We use the RaspiPreview code to handle the (generic) preview window
  */
 
+// We use some GNU extensions (basename, asprintf)
+#define _GNU_SOURCE
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -57,6 +59,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory.h>
 #include <sysexits.h>
 
+#include <gst/gst.h>
 
 #include "bcm_host.h"
 #include "interface/vcos/vcos.h"
@@ -72,18 +75,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "RaspiCapture.h"
 #include "RaspiCamControl.h"
 #include "RaspiPreview.h"
+#include "RaspiCLI.h"
 
 #include <semaphore.h>
 
-/// Camera number to use - we only have one camera, indexed from 0.
-#define CAMERA_NUMBER 0
-
 // Standard port setting for the camera component
 #define MMAL_CAMERA_PREVIEW_PORT 0
 #define MMAL_CAMERA_VIDEO_PORT 1
 #define MMAL_CAMERA_CAPTURE_PORT 2
 
 // Video format information
+// 0 implies variable
 #define VIDEO_FRAME_RATE_NUM 30
 #define VIDEO_FRAME_RATE_DEN 1
 
@@ -91,7 +93,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define VIDEO_OUTPUT_BUFFERS_NUM 3
 
 // Max bitrate we allow for recording
-const int MAX_BITRATE = 30000000; // 30Mbits/s
+const int MAX_BITRATE = 25000000; // 25Mbits/s
 
 /// Interval at which we check for an failure abort during capture
 const int ABORT_INTERVAL = 100; // ms
@@ -118,15 +120,23 @@ struct RASPIVID_STATE_T
    MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
    MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder
 
+   MMAL_PORT_T *camera_video_port;
    MMAL_PORT_T *camera_still_port;
    MMAL_PORT_T *encoder_output_port;
 
    MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
 
    PORT_USERDATA callback_data;
+
+   MMAL_QUEUE_T *encoded_buffer_q;
+
+   int64_t base_time;
+   int64_t last_second;
+
+   RASPIPREVIEW_STATE preview_state;
 };
 
-#if 0
+
 /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
 static XREF_T  profile_map[] =
 {
@@ -138,6 +148,28 @@ static XREF_T  profile_map[] =
 
 static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]);
 
+#if 0
+static XREF_T  initial_map[] =
+{
+   {"record",     0},
+   {"pause",      1},
+};
+
+static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]);
+#endif
+
+static XREF_T  intra_refresh_map[] =
+{
+   {"cyclic",       MMAL_VIDEO_INTRA_REFRESH_CYCLIC},
+   {"adaptive",     MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE},
+   {"both",         MMAL_VIDEO_INTRA_REFRESH_BOTH},
+   {"cyclicrows",   MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS},
+//   {"random",       MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why.
+};
+
+static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]);
+
+#if 0
 
 static void display_valid_parameters(char *app_name);
 
@@ -154,21 +186,53 @@ static void display_valid_parameters(char *app_name);
 #define CommandPreviewEnc   9
 #define CommandIntraPeriod  10
 #define CommandProfile      11
+#define CommandTimed        12
+#define CommandSignal       13
+#define CommandKeypress     14
+#define CommandInitialState 15
+#define CommandQP           16
+#define CommandInlineHeaders 17
+#define CommandSegmentFile  18
+#define CommandSegmentWrap  19
+#define CommandSegmentStart 20
+#define CommandSplitWait    21
+#define CommandCircular     22
+#define CommandIMV          23
+#define CommandCamSelect    24
+#define CommandSettings     25
+#define CommandSensorMode   26
+#define CommandIntraRefreshType 27
 
 static COMMAND_LIST cmdline_commands[] =
 {
-   { CommandHelp,    "-help",       "?",  "This help information", 0 },
-   { CommandWidth,   "-width",      "w",  "Set image width <size>. Default 1920", 1 },
-   { CommandHeight,  "-height",     "h",  "Set image height <size>. Default 1080", 1 },
-   { CommandBitrate, "-bitrate",    "b",  "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 },
-   { CommandOutput,  "-output",     "o",  "Output filename <filename> (to write to stdout, use '-o -')", 1 },
-   { CommandVerbose, "-verbose",    "v",  "Output verbose information during run", 0 },
-   { CommandTimeout, "-timeout",    "t",  "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
-   { CommandDemoMode,"-demo",       "d",  "Run a demo mode (cycle through range of camera options, no capture)", 1},
-   { CommandFramerate,"-framerate", "fps","Specify the frames per second to record", 1},
-   { CommandPreviewEnc,"-penc",     "e",  "Display preview image *after* encoding (shows compression artifacts)", 0},
-   { CommandIntraPeriod,"-intra",   "g",  "Specify the intra refresh period (key frame rate/GoP size)", 1},
-   { CommandProfile,  "-profile",   "pf", "Specify H264 profile to use for encoding", 1},
+   { CommandHelp,          "-help",       "?",  "This help information", 0 },
+   { CommandWidth,         "-width",      "w",  "Set image width <size>. Default 1920", 1 },
+   { CommandHeight,        "-height",     "h",  "Set image height <size>. Default 1080", 1 },
+   { CommandBitrate,       "-bitrate",    "b",  "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 },
+   { CommandOutput,        "-output",     "o",  "Output filename <filename> (to write to stdout, use '-o -')", 1 },
+   { CommandVerbose,       "-verbose",    "v",  "Output verbose information during run", 0 },
+   { CommandTimeout,       "-timeout",    "t",  "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
+   { CommandDemoMode,      "-demo",       "d",  "Run a demo mode (cycle through range of camera options, no capture)", 1},
+   { CommandFramerate,     "-framerate",  "fps","Specify the frames per second to record", 1},
+   { CommandPreviewEnc,    "-penc",       "e",  "Display preview image *after* encoding (shows compression artifacts)", 0},
+   { 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},
+   { CommandProfile,       "-profile",    "pf", "Specify H264 profile to use for encoding", 1},
+   { CommandTimed,         "-timed",      "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0},
+   { CommandSignal,        "-signal",     "s",  "Cycle between capture and pause on Signal", 0},
+   { CommandKeypress,      "-keypress",   "k",  "Cycle between capture and pause on ENTER", 0},
+   { CommandInitialState,  "-initial",    "i",  "Initial state. Use 'record' or 'pause'. Default 'record'", 1},
+   { CommandQP,            "-qp",         "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1},
+   { CommandInlineHeaders, "-inline",     "ih", "Insert inline headers (SPS, PPS) to stream", 0},
+   { CommandSegmentFile,   "-segment",    "sg", "Segment output file in to multiple files at specified interval <ms>", 1},
+   { CommandSegmentWrap,   "-wrap",       "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1},
+   { CommandSegmentStart,  "-start",      "sn", "In segment mode, start with specified segment number", 1},
+   { CommandSplitWait,     "-split",      "sp", "In wait mode, create new output file for each start event", 0},
+   { CommandCircular,      "-circular",   "c",  "Run encoded data through circular buffer until triggered then save", 0},
+   { CommandIMV,           "-vectors",    "x",  "Output filename <filename> for inline motion vectors", 1 },
+   { CommandCamSelect,     "-camselect",  "cs", "Select camera <number>. Default 0", 1 },
+   { CommandSettings,      "-settings",   "set","Retrieve camera settings and write to stdout", 0},
+   { CommandSensorMode,    "-mode",       "md", "Force sensor mode. 0=auto. See docs for other modes available", 1},
+   { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1},
 };
 
 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
@@ -183,18 +247,39 @@ static void dump_state(RASPIVID_STATE *state);
  */
 void raspicapture_default_config(RASPIVID_CONFIG *config)
 {
+   if (!config)
+   {
+      vcos_assert(0);
+      return;
+   }
+
+   // Default everything to zero
+   memset(config, 0, sizeof(RASPIVID_CONFIG));
 
    // Now set anything non-zero
    config->timeout = 5000;     // 5s delay before take image
    config->width = 1920;       // Default to 1080p
    config->height = 1080;
    config->bitrate = 17000000; // This is a decent default bitrate for 1080p
-   config->framerate = VIDEO_FRAME_RATE_NUM;
-   config->intraperiod = 0;    // Not set
+   config->fps_n = VIDEO_FRAME_RATE_NUM;
+   config->fps_d = VIDEO_FRAME_RATE_DEN;
+   config->intraperiod = -1;    // Not set
+   config->quantisationParameter = 0;
    config->demoMode = 0;
    config->demoInterval = 250; // ms
    config->immutableInput = 1;
    config->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
+   config->encoding = MMAL_ENCODING_H264;
+
+   config->bInlineHeaders = 0;
+
+   config->inlineMotionVectors = 0;
+
+   config->cameraNum = 0;
+   config->settings = 0;
+   config->sensor_mode = 0;
+
+   config->intra_refresh_type = -1;
 
    // Setup preview window defaults
    raspipreview_set_defaults(&config->preview_parameters);
@@ -204,6 +289,7 @@ void raspicapture_default_config(RASPIVID_CONFIG *config)
 
 }
 
+
 /**
  * Dump image state parameters to printf. Used for debugging
  *
@@ -211,18 +297,23 @@ void raspicapture_default_config(RASPIVID_CONFIG *config)
  */
 static void dump_state(RASPIVID_STATE *state)
 {
+   RASPIVID_CONFIG *config;
+
    if (!state)
    {
       vcos_assert(0);
       return;
    }
 
-   fprintf(stderr, "Width %d, Height %d, filename %s\n", state->config.width, state->config.height, state->config.filename);
-   fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->config.bitrate, state->config.framerate, state->config.timeout);
-   //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->config.profile, profile_map, profile_map_size));
+   config = &state->config;
 
-   raspipreview_dump_parameters(&state->config.preview_parameters);
-   //raspicamcontrol_dump_parameters(&state->config.camera_parameters);
+   fprintf(stderr, "Width %d, Height %d\n", config->width, config->height);
+   fprintf(stderr, "bitrate %d, framerate %d/%d, time delay %d\n",
+       config->bitrate, config->fps_n, config->fps_d, config->timeout);
+   //fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
+
+   raspipreview_dump_parameters(&config->preview_parameters);
+   raspicamcontrol_dump_parameters(&config->camera_parameters);
 }
 
 #if 0
@@ -307,7 +398,7 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
             state->filename = malloc(len + 1);
             vcos_assert(state->filename);
             if (state->filename)
-               strncpy(state->filename, argv[i + 1], len);
+               strncpy(state->filename, argv[i + 1], len+1);
             i++;
          }
          else
@@ -323,7 +414,10 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
       {
          if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
          {
-            // TODO : What limits do we need for timeout?
+            // Ensure that if previously selected a waitMethod we dont overwrite it
+            if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE)
+               state->waitMethod = WAIT_METHOD_FOREVER;
+
             i++;
          }
          else
@@ -382,6 +476,15 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
          break;
       }
 
+      case CommandQP: // quantisation parameter
+      {
+         if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1)
+            i++;
+         else
+            valid = 0;
+         break;
+      }
+
       case CommandProfile: // H264 profile
       {
          state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size);
@@ -393,6 +496,146 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
          break;
       }
 
+      case CommandInlineHeaders: // H264 inline headers
+      {
+         state->bInlineHeaders = 1;
+         break;
+      }
+
+      case CommandTimed:
+      {
+         if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2)
+         {
+            i++;
+
+            if (state->onTime < 1000)
+               state->onTime = 1000;
+
+            if (state->offTime < 1000)
+               state->offTime = 1000;
+
+            state->waitMethod = WAIT_METHOD_TIMED;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandKeypress:
+         state->waitMethod = WAIT_METHOD_KEYPRESS;
+         break;
+
+      case CommandSignal:
+         state->waitMethod = WAIT_METHOD_SIGNAL;
+         // Reenable the signal
+         signal(SIGUSR1, signal_handler);
+         break;
+
+      case CommandInitialState:
+      {
+         state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size);
+
+         if( state->bCapturing == -1)
+            state->bCapturing = 0;
+
+         i++;
+         break;
+      }
+
+      case CommandSegmentFile: // Segment file in to chunks of specified time
+      {
+         if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1)
+         {
+            // Must enable inline headers for this to work
+            state->bInlineHeaders = 1;
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandSegmentWrap: // segment wrap value
+      {
+         if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1)
+            i++;
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandSegmentStart: // initial segment number
+      {
+         if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap)))
+            i++;
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandSplitWait: // split files on restart
+      {
+         // Must enable inline headers for this to work
+         state->bInlineHeaders = 1;
+         state->splitWait = 1;
+         break;
+      }
+
+      case CommandCircular:
+      {
+         state->bCircularBuffer = 1;
+         break;
+      }
+
+      case CommandIMV:  // output filename
+      {
+         state->inlineMotionVectors = 1;
+         int len = strlen(argv[i + 1]);
+         if (len)
+         {
+            state->imv_filename = malloc(len + 1);
+            vcos_assert(state->imv_filename);
+            if (state->imv_filename)
+               strncpy(state->imv_filename, argv[i + 1], len+1);
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+      case CommandCamSelect:  //Select camera input port
+      {
+         if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1)
+         {
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandSettings:
+         state->settings = 1;
+         break;
+
+      case CommandSensorMode:
+      {
+         if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1)
+         {
+            i++;
+         }
+         else
+            valid = 0;
+         break;
+      }
+
+      case CommandIntraRefreshType:
+      {
+         state->config.intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size);
+         i++;
+         break;
+      }
+
       default:
       {
          // Try parsing for any image specific parameters
@@ -419,7 +662,7 @@ static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
 
    if (!valid)
    {
-      fprintf(stderr, "Invalid command line option (%s)\n", argv[i]);
+      fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
       return 1;
    }
 
@@ -456,6 +699,18 @@ static void display_valid_parameters(char *app_name)
       fprintf(stderr, ",%s", profile_map[i].mode);
    }
 
+   fprintf(stderr, "\n");
+
+   // Intra refresh options
+   fprintf(stderr, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode );
+
+   for (i=1;i<intra_refresh_map_size;i++)
+   {
+      fprintf(stderr, ",%s", intra_refresh_map[i].mode);
+   }
+
+   fprintf(stderr, "\n");
+
    // Help for preview options
    raspipreview_display_help();
 
@@ -480,76 +735,273 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf
 {
    if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
    {
+      MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data;
+      switch (param->hdr.id) {
+         case MMAL_PARAMETER_CAMERA_SETTINGS:
+         {
+            MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param;
+            vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u",
+            settings->exposure,
+                        settings->analog_gain.num, settings->analog_gain.den,
+                        settings->digital_gain.num, settings->digital_gain.den);
+            vcos_log_error("AWB R=%u/%u, B=%u/%u",
+                        settings->awb_red_gain.num, settings->awb_red_gain.den,
+                        settings->awb_blue_gain.num, settings->awb_blue_gain.den
+                        );
+         }
+         break;
+      }
    }
-   else
-   {
+   else if (buffer->cmd == MMAL_EVENT_ERROR) {
+      vcos_log_error("Camera control callback got an error");
+   } else {
       vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
    }
 
    mmal_buffer_header_release(buffer);
 }
 
+#if 0
 /**
- *  buffer header callback function for encoder
+ * Open a file based on the settings in state
  *
- *  Callback will dump buffer data to the specific file
+ * @param state Pointer to state
+ */
+static FILE *open_filename(RASPIVID_STATE *pState)
+{
+   FILE *new_handle = NULL;
+   char *tempname = NULL, *filename = NULL;
+
+   if (pState->segmentSize || pState->splitWait)
+   {
+      // Create a new filename string
+      asprintf(&tempname, pState->filename, pState->segmentNumber);
+      filename = tempname;
+   }
+   else
+   {
+      filename = pState->filename;
+   }
+
+   if (filename)
+      new_handle = fopen(filename, "wb");
+
+   if (pState->verbose)
+   {
+      if (new_handle)
+         fprintf(stderr, "Opening output file \"%s\"\n", filename);
+      else
+         fprintf(stderr, "Failed to open new file \"%s\"\n", filename);
+   }
+
+   if (tempname)
+      free(tempname);
+
+   return new_handle;
+}
+
+/**
+ * Open a file based on the settings in state
  *
- * @param port Pointer to port from which callback originated
- * @param buffer mmal buffer header pointer
+ * This time for the imv output file
+ *
+ * @param state Pointer to state
  */
-static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+static FILE *open_imv_filename(RASPIVID_STATE *pState)
 {
-   MMAL_BUFFER_HEADER_T *new_buffer;
+   FILE *new_handle = NULL;
+   char *tempname = NULL, *filename = NULL;
 
-   // We pass our file handle and other stuff in via the userdata field.
+   if (pState->segmentSize || pState->splitWait)
+   {
+      // Create a new filename string
+      asprintf(&tempname, pState->imv_filename, pState->segmentNumber);
+      filename = tempname;
+   }
+   else
+   {
+      filename = pState->imv_filename;
+   }
 
-   PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
-   RASPIVID_STATE *state  = pData->state;
+   if (filename)
+      new_handle = fopen(filename, "wb");
 
-   if (pData)
+   if (pState->verbose)
    {
-      int bytes_written = buffer->length;
+      if (new_handle)
+         fprintf(stderr, "Opening imv output file \"%s\"\n", filename);
+      else
+         fprintf(stderr, "Failed to open new imv file \"%s\"\n", filename);
+   }
 
-      vcos_assert(state->output_file);
+   if (tempname)
+      free(tempname);
 
-      if (buffer->length)
-      {
-         mmal_buffer_header_mem_lock(buffer);
+   return new_handle;
+}
+#endif
+
+/**
+ * Update any annotation data specific to the video.
+ * This simply passes on the setting from cli, or
+ * if application defined annotate requested, updates
+ * with the H264 parameters
+ *
+ * @param state Pointer to state control struct
+ *
+ */
+static void update_annotation_data(RASPIVID_STATE *state)
+{
+   RASPIVID_CONFIG *config = &state->config;
 
-         bytes_written = fwrite(buffer->data, 1, buffer->length, state->output_file);
+   // So, if we have asked for a application supplied string, set it to the H264 parameters
+   if (config->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT)
+   {
+      char *text;
+      const char *refresh = raspicli_unmap_xref(config->intra_refresh_type, intra_refresh_map, intra_refresh_map_size);
 
-         mmal_buffer_header_mem_unlock(buffer);
-      }
+      asprintf(&text,  "%dk,%ff,%s,%d,%s",
+            config->bitrate / 1000,  ((float)(config->fps_n) / config->fps_d),
+            refresh ? refresh : "(none)",
+            config->intraperiod,
+            raspicli_unmap_xref(config->profile, profile_map, profile_map_size));
 
-      if (bytes_written != buffer->length)
-      {
-         vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length);
-         pData->abort = 1;
-      }
+      raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate, text,
+                       config->camera_parameters.annotate_text_size,
+                       config->camera_parameters.annotate_text_colour,
+                       config->camera_parameters.annotate_bg_colour);
+
+      free(text);
    }
    else
    {
-      vcos_log_error("Received a encoder buffer callback with no state");
+      raspicamcontrol_set_annotate(state->camera_component, config->camera_parameters.enable_annotate,
+                       config->camera_parameters.annotate_string,
+                       config->camera_parameters.annotate_text_size,
+                       config->camera_parameters.annotate_text_colour,
+                       config->camera_parameters.annotate_bg_colour);
    }
+}
 
-   // release buffer back to the pool
-   mmal_buffer_header_release(buffer);
 
-   // and send one back to the port (if still open)
-   if (port->is_enabled)
-   {
-      MMAL_STATUS_T status = MMAL_SUCCESS;
 
-      new_buffer = mmal_queue_get(state->encoder_pool->queue);
+/**
+ *  buffer header callback function for encoder
+ *
+ *  Callback will dump buffer data to the specific file
+ *
+ * @param port Pointer to port from which callback originated
+ * @param buffer mmal buffer header pointer
+ */
+static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+   PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
+   RASPIVID_STATE *state  = pData->state;
+   int64_t current_time;
+
+   // All our segment times based on the receipt of the first encoder callback
+   if (state->base_time == -1)
+      state->base_time = vcos_getmicrosecs64()/1000;
 
-      if (new_buffer)
-         status = mmal_port_send_buffer(port, new_buffer);
+   if (pData == NULL)
+   {
+      vcos_log_error("Received a encoder buffer callback with no state");
+      // release buffer back to the pool
+      mmal_buffer_header_release(buffer);
+      return;
+   }
 
-      if (!new_buffer || status != MMAL_SUCCESS)
-         vcos_log_error("Unable to return a buffer to the encoder port");
+   current_time = vcos_getmicrosecs64()/1000;
+   if (state->base_time == -1)
+     state->base_time = current_time;
+
+   // See if the second count has changed and we need to update any annotation
+   if (current_time/1000 != state->last_second)
+   {
+      update_annotation_data(state);
+      state->last_second = current_time/1000;
    }
+
+   /* Send buffer to GStreamer element for pushing to the pipeline */
+   mmal_queue_put(state->encoded_buffer_q, buffer);
 }
 
+GstFlowReturn
+raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp,
+    GstClock *clock, GstClockTime base_time)
+{
+  RASPIVID_CONFIG *config = &state->config;
+  GstBuffer *buf;
+  MMAL_BUFFER_HEADER_T *buffer;
+  GstFlowReturn ret = GST_FLOW_ERROR;
+  /* No timestamps if no clockm or invalid PTS */
+  GstClockTime gst_pts = GST_CLOCK_TIME_NONE;
+
+  /* FIXME: Use our own interruptible cond wait: */
+  buffer = mmal_queue_wait(state->encoded_buffer_q);
+
+
+  if (G_LIKELY (config->useSTC && clock)) {
+    MMAL_PARAMETER_INT64_T param;
+    GstClockTime runtime;
+
+    runtime = gst_clock_get_time (clock) - base_time;
+
+    param.hdr.id = MMAL_PARAMETER_SYSTEM_TIME;
+    param.hdr.size = sizeof(param);
+    param.value = -1;
+
+    mmal_port_parameter_get(state->encoder_output_port, &param.hdr);
+
+    if (buffer->pts != -1 && param.value != -1 && param.value >= buffer->pts) {
+      /* Convert microsecond RPi TS to GStreamer clock: */
+      GstClockTime offset = (param.value - buffer->pts) * 1000;
+      if (runtime >= offset)
+        gst_pts = runtime - offset;
+    }
+    GST_LOG ("Buf (uS) PTS %" G_GINT64_FORMAT " DTS %" G_GINT64_FORMAT
+        " STC %" G_GINT64_FORMAT " (latency %" G_GINT64_FORMAT
+        "uS) TS %" GST_TIME_FORMAT,
+        buffer->pts, buffer->dts, param.value, param.value - buffer->pts,
+        GST_TIME_ARGS (gst_pts));
+  }
+  else {
+    GST_LOG ("use-stc=false. Not applying STC to buffer");
+  }
+
+  mmal_buffer_header_mem_lock(buffer);
+  buf = gst_buffer_new_allocate(NULL, buffer->length, NULL);
+  if (buf) {
+    if (config->useSTC)
+        GST_BUFFER_DTS(buf) = GST_BUFFER_PTS(buf) = gst_pts;
+    /* FIXME: Can we avoid copies and give MMAL our own buffers to fill? */
+    gst_buffer_fill(buf, 0, buffer->data, buffer->length);
+    ret = GST_FLOW_OK;
+  }
+
+  mmal_buffer_header_mem_unlock(buffer);
+
+  *bufp = buf;
+  // release buffer back to the pool
+  mmal_buffer_header_release(buffer);
+
+  // and send one back to the port (if still open)
+  if (state->encoder_output_port->is_enabled)
+  {
+     MMAL_STATUS_T status = MMAL_SUCCESS;
+
+     buffer = mmal_queue_get(state->encoder_pool->queue);
+     if (buffer)
+        status = mmal_port_send_buffer(state->encoder_output_port, buffer);
+
+     if (!buffer || status != MMAL_SUCCESS) {
+       vcos_log_error("Unable to return a buffer to the encoder port");
+       ret = GST_FLOW_ERROR;
+     }
+  }
+
+  return ret;
+}
 
 /**
  * Create the camera component, set up its ports
@@ -561,10 +1013,9 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf
  */
 static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
 {
-   MMAL_COMPONENT_T *camera = 0;
-   MMAL_ES_FORMAT_T *format;
-   MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
+   MMAL_COMPONENT_T *camera = NULL;
    MMAL_STATUS_T status;
+   RASPIVID_CONFIG *config = &state->config;
 
    /* Create the component */
    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
@@ -575,6 +1026,17 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
       goto error;
    }
 
+   MMAL_PARAMETER_INT32_T camera_num =
+      {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, config->cameraNum};
+
+   status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Could not select camera : error %d", status);
+      goto error;
+   }
+
    if (!camera->output_num)
    {
       status = MMAL_ENOSYS;
@@ -582,9 +1044,26 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
       goto error;
    }
 
-   preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
-   video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
-   still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
+   status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
+
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Could not set sensor mode : error %d", status);
+      goto error;
+   }
+
+   if (config->settings)
+   {
+      MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request =
+         {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)},
+          MMAL_PARAMETER_CAMERA_SETTINGS, 1};
+
+      status = mmal_port_parameter_set(camera->control, &change_event_request.hdr);
+      if ( status != MMAL_SUCCESS )
+      {
+         vcos_log_error("No camera settings events");
+      }
+   }
 
    // Enable the camera, and tell it its control callback function
    status = mmal_port_enable(camera->control, camera_control_callback);
@@ -595,24 +1074,49 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
       goto error;
    }
 
+   state->camera_component = camera;
+
+   return status;
+
+error:
+   if (camera)
+     mmal_component_destroy(camera);
+
+   return status;
+}
+
+MMAL_STATUS_T
+raspi_capture_set_format_and_start(RASPIVID_STATE *state)
+{
+   MMAL_COMPONENT_T *camera = NULL;
+   MMAL_STATUS_T status;
+   MMAL_ES_FORMAT_T *format;
+   MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
+   RASPIVID_CONFIG *config = &state->config;
+
    //  set up the camera configuration
+
+   MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
    {
-      MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
-      {
-         { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
-         .max_stills_w = state->config.width,
-         .max_stills_h = state->config.height,
-         .stills_yuv422 = 0,
-         .one_shot_stills = 0,
-         .max_preview_video_w = state->config.width,
-         .max_preview_video_h = state->config.height,
-         .num_preview_video_frames = 3,
-         .stills_capture_circular_buffer_height = 0,
-         .fast_preview_resume = 0,
-         .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
-      };
-      mmal_port_parameter_set(camera->control, &cam_config.hdr);
-   }
+      { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
+      .max_stills_w = config->width,
+      .max_stills_h = config->height,
+      .stills_yuv422 = 0,
+      .one_shot_stills = 0,
+      .max_preview_video_w = config->width,
+      .max_preview_video_h = config->height,
+      .num_preview_video_frames = 3,
+      .stills_capture_circular_buffer_height = 0,
+      .fast_preview_resume = 0,
+      .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC
+   };
+
+   camera = state->camera_component;
+   preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
+   video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
+   still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
+
+   mmal_port_parameter_set(camera->control, &cam_config.hdr);
 
    // Now set up the port formats
 
@@ -621,18 +1125,41 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
 
    format = preview_port->format;
 
+   if(config->camera_parameters.shutter_speed > 6000000)
+   {
+        MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
+                                                     { 50, 1000 }, {166, 1000}};
+        mmal_port_parameter_set(preview_port, &fps_range.hdr);
+   }
+   else if(config->camera_parameters.shutter_speed > 1000000)
+   {
+        MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
+                                                     { 166, 1000 }, {999, 1000}};
+        mmal_port_parameter_set(preview_port, &fps_range.hdr);
+   }
+
+   //enable dynamic framerate if necessary
+   if (config->camera_parameters.shutter_speed)
+   {
+      if (((float)(config->fps_n) / config->fps_d) > 1000000.0 / config->camera_parameters.shutter_speed)
+      {
+         config->fps_n = 0;
+         config->fps_d = 1;
+         GST_INFO ("Enabling dynamic frame rate to fulfil shutter speed requirement");
+      }
+   }
+
    format->encoding = MMAL_ENCODING_OPAQUE;
    format->encoding_variant = MMAL_ENCODING_I420;
 
-   format->encoding = MMAL_ENCODING_OPAQUE;
-   format->es->video.width = state->config.width;
-   format->es->video.height = state->config.height;
+   format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
+   format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
    format->es->video.crop.x = 0;
    format->es->video.crop.y = 0;
-   format->es->video.crop.width = state->config.width;
-   format->es->video.crop.height = state->config.height;
-   format->es->video.frame_rate.num = state->config.framerate;
-   format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN;
+   format->es->video.crop.width = config->width;
+   format->es->video.crop.height = config->height;
+   format->es->video.frame_rate.num = config->fps_n;
+   format->es->video.frame_rate.den = config->fps_d;
 
    status = mmal_port_format_commit(preview_port);
 
@@ -643,19 +1170,39 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
    }
 
    // Set the encode format on the video  port
-
    format = video_port->format;
-   format->encoding_variant = MMAL_ENCODING_I420;
 
-   format->encoding = MMAL_ENCODING_OPAQUE;
-   format->es->video.width = state->config.width;
-   format->es->video.height = state->config.height;
+   if(config->camera_parameters.shutter_speed > 6000000)
+   {
+        MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
+                                                     { 50, 1000 }, {166, 1000}};
+        mmal_port_parameter_set(video_port, &fps_range.hdr);
+   }
+   else if(config->camera_parameters.shutter_speed > 1000000)
+   {
+        MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
+                                                     { 167, 1000 }, {999, 1000}};
+        mmal_port_parameter_set(video_port, &fps_range.hdr);
+   }
+
+   /* If encoding, set opaque tunneling format */
+   if (state->encoder_component) {
+     format->encoding = MMAL_ENCODING_OPAQUE;
+     format->encoding_variant = MMAL_ENCODING_I420;
+   }
+   else {
+     format->encoding = config->encoding;
+     format->encoding_variant = config->encoding;
+   }
+
+   format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
+   format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
    format->es->video.crop.x = 0;
    format->es->video.crop.y = 0;
-   format->es->video.crop.width = state->config.width;
-   format->es->video.crop.height = state->config.height;
-   format->es->video.frame_rate.num = state->config.framerate;
-   format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN;
+   format->es->video.crop.width = config->width;
+   format->es->video.crop.height = config->height;
+   format->es->video.frame_rate.num = config->fps_n;
+   format->es->video.frame_rate.den = config->fps_d;
 
    status = mmal_port_format_commit(video_port);
 
@@ -677,13 +1224,13 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
    format->encoding = MMAL_ENCODING_OPAQUE;
    format->encoding_variant = MMAL_ENCODING_I420;
 
-   format->es->video.width = state->config.width;
-   format->es->video.height = state->config.height;
+   format->es->video.width = VCOS_ALIGN_UP(config->width, 32);
+   format->es->video.height = VCOS_ALIGN_UP(config->height, 16);
    format->es->video.crop.x = 0;
    format->es->video.crop.y = 0;
-   format->es->video.crop.width = state->config.width;
-   format->es->video.crop.height = state->config.height;
-   format->es->video.frame_rate.num = 1;
+   format->es->video.crop.width = config->width;
+   format->es->video.crop.height = config->height;
+   format->es->video.frame_rate.num = 0;
    format->es->video.frame_rate.den = 1;
 
    status = mmal_port_format_commit(still_port);
@@ -707,11 +1254,11 @@ static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
       goto error;
    }
 
-   raspicamcontrol_set_all_parameters(camera, &state->config.camera_parameters);
+   raspicamcontrol_set_all_parameters(camera, &config->camera_parameters);
 
-   state->camera_component = camera;
+   update_annotation_data(state);
 
-   if (state->config.verbose)
+   if (config->verbose)
       fprintf(stderr, "Camera component done\n");
 
    return status;
@@ -739,6 +1286,25 @@ static void destroy_camera_component(RASPIVID_STATE *state)
    }
 }
 
+gboolean raspi_capture_request_i_frame(RASPIVID_STATE *state)
+{
+   MMAL_PORT_T *encoder_output = NULL;
+   MMAL_STATUS_T status;
+   MMAL_PARAMETER_BOOLEAN_T param = {{  MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, sizeof(param)}, 1};
+
+   if (state->encoder_component)
+     return TRUE;
+
+   encoder_output = state->encoder_component->output[0];
+   status = mmal_port_parameter_set(encoder_output, &param.hdr);
+   if (status != MMAL_SUCCESS)
+   {
+      vcos_log_error("Unable to request I-frame");
+      return FALSE;
+   }
+   return TRUE;
+}
+
 /**
  * Create the encoder component, set up its ports
  *
@@ -752,14 +1318,24 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
    MMAL_COMPONENT_T *encoder = 0;
    MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
    MMAL_STATUS_T status;
-   MMAL_POOL_T *pool;
+   RASPIVID_CONFIG *config = &state->config;
 
-   status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
+   gboolean encoded_format =
+     (config->encoding == MMAL_ENCODING_H264 ||
+      config->encoding == MMAL_ENCODING_MJPEG ||
+      config->encoding == MMAL_ENCODING_JPEG);
 
-   if (status != MMAL_SUCCESS)
-   {
-      vcos_log_error("Unable to create video encoder component");
-      goto error;
+   if (!encoded_format)
+     return MMAL_SUCCESS;
+
+   if (config->encoding == MMAL_ENCODING_JPEG)
+     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
+   else
+     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
+
+   if (status != MMAL_SUCCESS) {
+     vcos_log_error("Unable to create video encoder component");
+     goto error;
    }
 
    if (!encoder->input_num || !encoder->output_num)
@@ -775,12 +1351,15 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
    // We want same format on input and output
    mmal_format_copy(encoder_output->format, encoder_input->format);
 
-   // Only supporting H264 at the moment
-   encoder_output->format->encoding = MMAL_ENCODING_H264;
+   // Configure desired encoding
+   encoder_output->format->encoding = config->encoding;
 
-   encoder_output->format->bitrate = state->config.bitrate;
+   encoder_output->format->bitrate = config->bitrate;
 
-   encoder_output->buffer_size = encoder_output->buffer_size_recommended;
+   if (config->encoding == MMAL_ENCODING_H264)
+     encoder_output->buffer_size = encoder_output->buffer_size_recommended;
+   else
+     encoder_output->buffer_size = 256<<10;
 
    if (encoder_output->buffer_size < encoder_output->buffer_size_min)
       encoder_output->buffer_size = encoder_output->buffer_size_min;
@@ -790,16 +1369,21 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
    if (encoder_output->buffer_num < encoder_output->buffer_num_min)
       encoder_output->buffer_num = encoder_output->buffer_num_min;
 
+   GST_DEBUG ("encoder wants %d buffers of size %u",
+       (guint)encoder_output->buffer_num, (guint)encoder_output->buffer_size);
+
+   // We need to set the frame rate on output to 0, to ensure it gets
+   // updated correctly from the input framerate when port connected
+   encoder_output->format->es->video.frame_rate.num = 0;
+   encoder_output->format->es->video.frame_rate.den = 1;
+
    // Commit the port changes to the output port
    status = mmal_port_format_commit(encoder_output);
-
-   if (status != MMAL_SUCCESS)
-   {
+   if (status != MMAL_SUCCESS) {
       vcos_log_error("Unable to set format on video encoder output port");
       goto error;
    }
 
-
    // Set the rate control parameter
    if (0)
    {
@@ -813,24 +1397,51 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
 
    }
 
-   if (state->config.intraperiod)
+   if (config->encoding == MMAL_ENCODING_H264 && config->intraperiod != -1)
    {
-      MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->config.intraperiod};
+      MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
       status = mmal_port_parameter_set(encoder_output, &param.hdr);
       if (status != MMAL_SUCCESS)
       {
          vcos_log_error("Unable to set intraperiod");
          goto error;
       }
+   }
+
+   if (config->encoding == MMAL_ENCODING_H264 && config->quantisationParameter)
+   {
+      MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to set initial QP");
+         goto error;
+      }
+
+      MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
+      status = mmal_port_parameter_set(encoder_output, &param2.hdr);
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to set min QP");
+         goto error;
+      }
 
+      MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
+      status = mmal_port_parameter_set(encoder_output, &param3.hdr);
+      if (status != MMAL_SUCCESS)
+      {
+         vcos_log_error("Unable to set max QP");
+         goto error;
+      }
    }
 
+   if (config->encoding == MMAL_ENCODING_H264)
    {
       MMAL_PARAMETER_VIDEO_PROFILE_T  param;
       param.hdr.id = MMAL_PARAMETER_PROFILE;
       param.hdr.size = sizeof(param);
 
-      param.profile[0].profile = state->config.profile;
+      param.profile[0].profile = config->profile;
       param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // This is the only value supported
 
       status = mmal_port_parameter_set(encoder_output, &param.hdr);
@@ -841,11 +1452,77 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
       }
    }
 
+   if (config->encoding != MMAL_ENCODING_JPEG)
+   {
+     if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, config->immutableInput) != MMAL_SUCCESS)
+     {
+        vcos_log_error("Unable to set immutable input flag");
+        // Continue rather than abort..
+     }
+
+     //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested
+     if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, config->bInlineHeaders) != MMAL_SUCCESS)
+     {
+        vcos_log_error("failed to set INLINE HEADER FLAG parameters");
+        // Continue rather than abort..
+     }
+  }
+
+  if (config->encoding == MMAL_ENCODING_H264)
+  {
+     //set INLINE VECTORS flag to request motion vector estimates
+     if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, config->inlineMotionVectors) != MMAL_SUCCESS)
+     {
+        vcos_log_error("failed to set INLINE VECTORS parameters");
+        // Continue rather than abort..
+     }
 
-   if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->config.immutableInput) != MMAL_SUCCESS)
+     // Adaptive intra refresh settings
+     if (config->intra_refresh_type != -1)
+     {
+        MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T  param;
+
+        /* Need to memset, apparently mmal_port_parameter_get()
+         * doesn't retrieve all parameters, causing random failures
+         * when we set it
+         */
+        memset (&param, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
+
+        param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
+        param.hdr.size = sizeof(param);
+
+        // Get first so we don't overwrite anything unexpectedly
+        status = mmal_port_parameter_get(encoder_output, &param.hdr);
+
+        param.refresh_mode = config->intra_refresh_type;
+
+        //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
+        //   param.cir_mbs = 10;
+
+        status = mmal_port_parameter_set(encoder_output, &param.hdr);
+        if (status != MMAL_SUCCESS)
+        {
+         vcos_log_error("Unable to set H264 intra-refresh values");
+           goto error;
+        }
+     }
+   }
+
+   if (config->encoding == MMAL_ENCODING_JPEG)
    {
-      vcos_log_error("Unable to set immutable input flag");
-      // Continue rather than abort..
+      status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, config->jpegQuality);
+      if (status != MMAL_SUCCESS) {
+         vcos_log_error("Unable to set JPEG quality");
+         // Continue after warning
+      }
+
+#ifdef MMAL_PARAMETER_JPEG_RESTART_INTERVAL
+      status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, config->jpegRestartInterval);
+      if (status != MMAL_SUCCESS) {
+         vcos_log_error("Unable to set JPEG restart interval");
+         // Continue after warning
+      }
+#endif
    }
 
    //  Enable component
@@ -857,18 +1534,9 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
       goto error;
    }
 
-   /* Create pool of buffer headers for the output port to consume */
-   pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
-
-   if (!pool)
-   {
-      vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
-   }
-
-   state->encoder_pool = pool;
    state->encoder_component = encoder;
 
-   if (state->config.verbose)
+   if (config->verbose)
       fprintf(stderr, "Encoder component done\n");
 
    return status;
@@ -877,6 +1545,8 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
    if (encoder)
       mmal_component_destroy(encoder);
 
+   state->encoder_component = NULL;
+
    return status;
 }
 
@@ -888,14 +1558,23 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
  */
 static void destroy_encoder_component(RASPIVID_STATE *state)
 {
+  /* Empty the buffer header q */
+   if (state->encoded_buffer_q) {
+      while (mmal_queue_length(state->encoded_buffer_q)) {
+        MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoded_buffer_q);
+        mmal_buffer_header_release(buffer);
+      }
+   }
+
    // Get rid of any port buffers first
    if (state->encoder_pool)
    {
-      mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
+      mmal_port_pool_destroy(state->encoder_output_port, state->encoder_pool);
+      state->encoder_pool = NULL;
    }
 
-   if (state->encoder_component)
-   {
+   if (state->encoder_component) {
+
       mmal_component_destroy(state->encoder_component);
       state->encoder_component = NULL;
    }
@@ -947,17 +1626,12 @@ void raspicapture_init()
 }
 
 RASPIVID_STATE *
-raspi_capture_start(RASPIVID_CONFIG *config)
+raspi_capture_setup(RASPIVID_CONFIG *config)
 {
   // Our main data storage vessel..
   RASPIVID_STATE *state;
-  //int exit_code = EX_OK;
 
   MMAL_STATUS_T status = MMAL_SUCCESS;
-  MMAL_PORT_T *camera_preview_port = NULL;
-  MMAL_PORT_T *camera_video_port = NULL;
-  MMAL_PORT_T *preview_input_port = NULL;
-  MMAL_PORT_T *encoder_input_port = NULL;
 
   /* Default everything to zero */
   state = calloc(1, sizeof(RASPIVID_STATE));
@@ -965,11 +1639,12 @@ raspi_capture_start(RASPIVID_CONFIG *config)
   /* Apply passed in config */
   state->config = *config;
 
-  if (state->config.verbose)
-  {
-     dump_state(state);
-  }
+  /* Initialize timestamping */
+  state->base_time = state->last_second = -1;
 
+  /* So far, all we can do is create the camera component. Actual
+   * config and connection of encoders etc happens in _start()
+   */
   // OK, we have a nice set of parameters. Now set up our components
   // We have three components. Camera, Preview and encoder.
 
@@ -979,34 +1654,75 @@ raspi_capture_start(RASPIVID_CONFIG *config)
      return NULL;
   }
 
-  if ((status = raspipreview_create(&state->config.preview_parameters)) != MMAL_SUCCESS)
+  if ((status = raspipreview_create(&state->preview_state, &config->preview_parameters)) != MMAL_SUCCESS)
   {
      vcos_log_error("%s: Failed to create preview component", __func__);
      destroy_camera_component(state);
      return NULL;
   }
 
-  if ((status = create_encoder_component(state)) != MMAL_SUCCESS)
+  state->encoded_buffer_q = mmal_queue_create();
+
+  return state;
+}
+
+gboolean
+raspi_capture_start(RASPIVID_STATE *state)
+{
+  MMAL_STATUS_T status = MMAL_SUCCESS;
+  RASPIVID_CONFIG *config = &state->config;
+
+  MMAL_PORT_T *camera_preview_port = NULL;
+  MMAL_PORT_T *preview_input_port = NULL;
+  MMAL_PORT_T *encoder_input_port = NULL;
+
+  MMAL_POOL_T *pool;
+
+  if ((status = create_encoder_component(state)) != MMAL_SUCCESS) {
+    vcos_log_error("%s: Failed to create encode component", __func__);
+    return FALSE;
+  }
+
+  if (config->verbose)
   {
-     vcos_log_error("%s: Failed to create encode component", __func__);
-     raspipreview_destroy(&state->config.preview_parameters);
-     destroy_camera_component(state);
-     return NULL;
+     dump_state(state);
+  }
+
+  state->camera_video_port   = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT];
+  state->camera_still_port   = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
+  camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
+  preview_input_port  = state->preview_state.preview_component->input[0];
+
+  if (state->encoder_component) {
+    encoder_input_port  = state->encoder_component->input[0];
+    state->encoder_output_port = state->encoder_component->output[0];
+  } else {
+    state->encoder_output_port = state->camera_video_port;
+  }
+
+  if ((status = raspi_capture_set_format_and_start(state)) != MMAL_SUCCESS) {
+     return FALSE;
+  }
+
+  GST_DEBUG ("Creating pool of %d buffers of size %d",
+      state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
+  /* Create pool of buffer headers for the output port to consume */
+  pool = mmal_port_pool_create(state->encoder_output_port,
+             state->encoder_output_port->buffer_num, state->encoder_output_port->buffer_size);
+  if (!pool)
+  {
+    vcos_log_error("Failed to create buffer header pool for encoder output port %s",
+         state->encoder_output_port->name);
+    return FALSE;
   }
+  state->encoder_pool = pool;
 
   if (state->config.verbose)
      fprintf(stderr, "Starting component connection stage\n");
 
-  camera_preview_port = state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
-  camera_video_port   = state->camera_component->output[MMAL_CAMERA_VIDEO_PORT];
-  preview_input_port  = state->config.preview_parameters.preview_component->input[0];
-  encoder_input_port  = state->encoder_component->input[0];
-  state->camera_still_port   = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
-  state->encoder_output_port = state->encoder_component->output[0];
-
-  if (state->config.preview_parameters.wantPreview )
+  if (config->preview_parameters.wantPreview )
   {
-     if (state->config.verbose)
+     if (config->verbose)
      {
         fprintf(stderr, "Connecting camera preview port to preview input port\n");
         fprintf(stderr, "Starting video preview\n");
@@ -1016,46 +1732,24 @@ raspi_capture_start(RASPIVID_CONFIG *config)
      status = connect_ports(camera_preview_port, preview_input_port, &state->preview_connection);
      if (status != MMAL_SUCCESS)
      {
-        mmal_status_to_int(status);
         vcos_log_error("%s: Failed to connect camera to preview", __func__);
-        goto error;
+        return FALSE;
      }
   }
 
-  if (state->config.verbose)
-     fprintf(stderr, "Connecting camera stills port to encoder input port\n");
-
-  // Now connect the camera to the encoder
-  status = connect_ports(camera_video_port, encoder_input_port, &state->encoder_connection);
-
-  if (status != MMAL_SUCCESS)
-  {
-     vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
-     goto error;
-  }
-
-  if (state->config.filename)
-  {
-     if (state->config.filename[0] == '-')
-     {
-        state->output_file = stdout;
-
-        // Ensure we don't upset the output stream with diagnostics/info
-        state->config.verbose = 0;
-     }
-     else
-     {
-        if (state->config.verbose)
-           fprintf(stderr, "Opening output file \"%s\"\n", state->config.filename);
-
-        state->output_file = fopen(state->config.filename, "wb");
-     }
-
-     if (!state->output_file)
-     {
-        // Notify user, carry on but discarding encoded output buffers
-        vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state->config.filename);
-     }
+  if (state->encoder_component) {
+    if (config->verbose)
+       fprintf(stderr, "Connecting camera video port to encoder input port\n");
+
+    // Now connect the camera to the encoder
+    status = connect_ports(state->camera_video_port, encoder_input_port, &state->encoder_connection);
+    if (status != MMAL_SUCCESS)
+    {
+      if (config->preview_parameters.wantPreview )
+        mmal_connection_destroy(state->preview_connection);
+      vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
+      return FALSE;
+    }
   }
 
   // Set up our userdata - this is passed though to the callback where we need the information.
@@ -1064,89 +1758,75 @@ raspi_capture_start(RASPIVID_CONFIG *config)
 
   state->encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state->callback_data;
 
-  if (state->config.verbose)
+  if (config->verbose)
      fprintf(stderr, "Enabling encoder output port\n");
 
   // Enable the encoder output port and tell it its callback function
   status = mmal_port_enable(state->encoder_output_port, encoder_buffer_callback);
-
   if (status != MMAL_SUCCESS)
   {
      vcos_log_error("Failed to setup encoder output");
      goto error;
   }
 
-  if (state->config.demoMode)
+  if (config->demoMode)
   {
      // Run for the user specific time..
-     int num_iterations = state->config.timeout / state->config.demoInterval;
+     int num_iterations = config->timeout / config->demoInterval;
      int i;
 
-     if (state->config.verbose)
+     if (config->verbose)
         fprintf(stderr, "Running in demo mode\n");
 
-     for (i=0;state->config.timeout == 0 || i<num_iterations;i++)
+     for (i=0;config->timeout == 0 || i<num_iterations;i++)
      {
         raspicamcontrol_cycle_test(state->camera_component);
         vcos_sleep(state->config.demoInterval);
      }
   }
-  else
-  {
-     // Only encode stuff if we have a filename and it opened
-     if (state->output_file)
-     {
-        int wait;
-
-        if (state->config.verbose)
-           fprintf(stderr, "Starting video capture\n");
 
-        if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
-        {
-           goto error;
-        }
+  if (config->verbose)
+    fprintf(stderr, "Starting video capture\n");
 
-        // Send all the buffers to the encoder output port
-        {
-           int num = mmal_queue_length(state->encoder_pool->queue);
-           int q;
-           for (q=0;q<num;q++)
-           {
-              MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
+  if (mmal_port_parameter_set_boolean(state->camera_video_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
+  {
+     goto error;
+  }
 
-              if (!buffer)
-                 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
+  // Send all the buffers to the encoder output port
+  {
+     int num = mmal_queue_length(state->encoder_pool->queue);
+     int q;
+     for (q=0;q<num;q++)
+     {
+        MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state->encoder_pool->queue);
 
-              if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS)
-                 vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
+        if (!buffer)
+           vcos_log_error("Unable to get a required buffer %d from pool queue", q);
 
-           }
-        }
+        if (mmal_port_send_buffer(state->encoder_output_port, buffer)!= MMAL_SUCCESS)
+           vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
 
-        // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
-        // out of storage space)
-        // Going to check every ABORT_INTERVAL milliseconds
+     }
+  }
 
-        for (wait = 0; state->config.timeout == 0 || wait < state->config.timeout; wait+= ABORT_INTERVAL)
-        {
-           vcos_sleep(ABORT_INTERVAL);
-           if (state->callback_data.abort)
-              break;
-        }
+  // Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
+  // out of storage space)
+  // Going to check every ABORT_INTERVAL milliseconds
 
-        if (state->config.verbose)
-           fprintf(stderr, "Finished capture\n");
-     }
-     else
-     {
-        if (state->config.timeout)
-           vcos_sleep(state->config.timeout);
-        else
-           for (;;) vcos_sleep(ABORT_INTERVAL);
-     }
+#if 0
+  for (wait = 0; config->timeout == 0 || wait < config->timeout; wait+= ABORT_INTERVAL)
+  {
+     vcos_sleep(ABORT_INTERVAL);
+     if (state->callback_data.abort)
+        break;
   }
 
-  return state;
+  if (config->verbose)
+     fprintf(stderr, "Finished capture\n");
+#endif
+
+  return (status == MMAL_SUCCESS);
 
 error:
   raspi_capture_stop(state);
@@ -1156,22 +1836,35 @@ error:
     raspicamcontrol_check_configuration(128);
   }
 
-  return NULL;
+  return FALSE;
 }
 
 void
 raspi_capture_stop(RASPIVID_STATE *state)
 {
-  if (state->config.verbose)
+   RASPIVID_CONFIG *config = &state->config;
+
+  if (config->verbose)
      fprintf(stderr, "Closing down\n");
 
+  if (config->preview_parameters.wantPreview )
+     mmal_connection_destroy(state->preview_connection);
+
   // Disable all our ports that are not handled by connections
   check_disable_port(state->camera_still_port);
   check_disable_port(state->encoder_output_port);
 
-  if (state->config.preview_parameters.wantPreview )
-     mmal_connection_destroy(state->preview_connection);
-  mmal_connection_destroy(state->encoder_connection);
+  if (state->encoder_component) {
+     mmal_connection_destroy(state->encoder_connection);
+     mmal_component_disable(state->encoder_component);
+     destroy_encoder_component(state);
+  }
+}
+
+void
+raspi_capture_free(RASPIVID_STATE *state)
+{
+   RASPIVID_CONFIG *config = &state->config;
 
   // Can now close our file. Note disabling ports may flush buffers which causes
   // problems if we have already closed the file!
@@ -1182,18 +1875,142 @@ raspi_capture_stop(RASPIVID_STATE *state)
   if (state->encoder_component)
      mmal_component_disable(state->encoder_component);
 
-  if (state->config.preview_parameters.preview_component)
-     mmal_component_disable(state->config.preview_parameters.preview_component);
+  if (state->preview_state.preview_component)
+     mmal_component_disable(state->preview_state.preview_component);
 
   if (state->camera_component)
      mmal_component_disable(state->camera_component);
 
   destroy_encoder_component(state);
-  raspipreview_destroy(&state->config.preview_parameters);
+  raspipreview_destroy(&state->preview_state);
   destroy_camera_component(state);
 
-  if (state->config.verbose)
+  if (state->encoded_buffer_q) {
+    mmal_queue_destroy(state->encoded_buffer_q);
+    state->encoded_buffer_q = NULL;
+  }
+
+  if (config->verbose)
      fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
 
   free(state);
 }
+
+void
+raspi_capture_update_config (RASPIVID_STATE *state, RASPIVID_CONFIG *config, gboolean dynamic)
+{
+  MMAL_STATUS_T status;
+  RASPICAM_CAMERA_PARAMETERS *params = &config->camera_parameters;
+  MMAL_COMPONENT_T *camera = state->camera_component;
+
+  /* Store the new config */
+  state->config = *config;
+  if (!dynamic)
+    return;
+
+  if (state->encoder_component && config->change_flags & PROP_CHANGE_ENCODING) {
+    /* BITRATE or QUANT or KEY Interval, intra refresh */
+    MMAL_COMPONENT_T *encoder = state->encoder_component;
+    MMAL_PORT_T *encoder_output = encoder->output[0];
+
+    status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_VIDEO_BIT_RATE, config->bitrate);
+    if (status != MMAL_SUCCESS)
+      vcos_log_warn("Unable to change bitrate dynamically");
+
+    {
+      MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, config->intraperiod};
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+        vcos_log_warn("Unable to change intraperiod dynamically");
+    }
+
+#if 0 /* not dynamically change-able */
+    {
+      MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, config->quantisationParameter};
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+        vcos_log_warn("Unable to change Initial Quantisation Parameter dynamically");
+
+      MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, config->quantisationParameter};
+      status = mmal_port_parameter_set(encoder_output, &param2.hdr);
+      if (status != MMAL_SUCCESS)
+        vcos_log_warn("Unable to change Minimum Quantisation Parameter dynamically");
+
+      MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, config->quantisationParameter};
+      status = mmal_port_parameter_set(encoder_output, &param3.hdr);
+      if (status != MMAL_SUCCESS)
+        vcos_log_warn("Unable to change Maximum Quantisation Parameter dynamically");
+    }
+
+    {
+      // Adaptive intra refresh settings
+      MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T  param;
+      param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
+      param.hdr.size = sizeof(param);
+
+      // Get first so we don't overwrite anything unexpectedly
+      status = mmal_port_parameter_get(encoder_output, &param.hdr);
+      if (state != MMAL_SUCCESS) {
+        /* Need to memset, apparently mmal_port_parameter_get()
+         * doesn't retrieve all parameters, causing random failures
+         * when we set it. On older firmware the get fails.
+         */
+        memset (&param, 0, sizeof (MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T));
+      }
+      param.refresh_mode = config->intra_refresh_type;
+
+      status = mmal_port_parameter_set(encoder_output, &param.hdr);
+      if (status != MMAL_SUCCESS)
+         vcos_log_warn("Unable to set H264 intra-refresh values dynamically");
+    }
+#endif
+  }
+  if (config->change_flags & PROP_CHANGE_PREVIEW) {
+    /* Preview settings or fullscreen */
+    status = raspipreview_update_config (&state->preview_state,
+        &config->preview_parameters);
+    if (status != MMAL_SUCCESS)
+      vcos_log_warn("Unable to change preview config dynamically");
+  }
+  if (config->change_flags & PROP_CHANGE_COLOURBALANCE) {
+    raspicamcontrol_set_saturation(camera, params->saturation);
+    raspicamcontrol_set_sharpness(camera, params->sharpness);
+    raspicamcontrol_set_contrast(camera, params->contrast);
+    raspicamcontrol_set_brightness(camera, params->brightness);
+  }
+  if (config->change_flags & PROP_CHANGE_SENSOR_SETTINGS) {
+    /* ISO, EXPOSURE, SHUTTER, DRC, Sensor Mode */
+    raspicamcontrol_set_ISO(camera, params->ISO);
+    raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation);
+    raspicamcontrol_set_exposure_mode(camera, params->exposureMode);
+    raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode);
+    raspicamcontrol_set_shutter_speed(camera, params->shutter_speed);
+    raspicamcontrol_set_DRC(camera, params->drc_level);
+
+    /* Can we change sensor mode on the fly? Disable if not */
+    status = mmal_port_parameter_set_uint32(camera->control,
+       MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, config->sensor_mode);
+    if (status != MMAL_SUCCESS)
+      vcos_log_warn("Unable to change sensor mode dynamically");
+  }
+  if (config->change_flags & PROP_CHANGE_VIDEO_STABILISATION) {
+    raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation);
+  }
+  if (config->change_flags & PROP_CHANGE_AWB) {
+    raspicamcontrol_set_awb_mode(camera, params->awbMode);
+    raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b);
+  }
+  if (config->change_flags & PROP_CHANGE_IMAGE_COLOUR_EFFECT) {
+    raspicamcontrol_set_imageFX(camera, params->imageEffect);
+    raspicamcontrol_set_colourFX(camera, &params->colourEffects);
+  }
+  if (config->change_flags & PROP_CHANGE_ORIENTATION) {
+    raspicamcontrol_set_rotation(camera, params->rotation);
+    raspicamcontrol_set_flips(camera, params->hflip, params->vflip);
+  }
+  if (config->change_flags & PROP_CHANGE_ROI) {
+    raspicamcontrol_set_ROI(camera, params->roi);
+  }
+  if (config->change_flags & PROP_CHANGE_ANNOTATION)
+    update_annotation_data(state);
+}