1 /*
2 Copyright (c) 2018, Raspberry Pi (Trading) Ltd.
3 Copyright (c) 2013, Broadcom Europe Ltd.
4 Copyright (c) 2013, James Hughes
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9     * Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11     * Redistributions in binary form must reproduce the above copyright
12       notice, this list of conditions and the following disclaimer in the
13       documentation and/or other materials provided with the distribution.
14     * Neither the name of the copyright holder nor the
15       names of its contributors may be used to endorse or promote products
16       derived from this software without specific prior written permission.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 /**
31  * \file RaspiVid.c
32  * Command line program to capture a camera video stream and encode it to file.
33  * Also optionally display a preview/viewfinder of current camera input.
34  *
35  * Description
36  *
37  * 3 components are created; camera, preview and video encoder.
38  * Camera component has three ports, preview, video and stills.
39  * This program connects preview and video to the preview and video
40  * encoder. Using mmal we don't need to worry about buffers between these
41  * components, but we do need to handle buffers from the encoder, which
42  * are simply written straight to the file in the requisite buffer callback.
43  *
44  * If raw option is selected, a video splitter component is connected between
45  * camera and preview. This allows us to set up callback for raw camera data
46  * (in YUV420 or RGB format) which might be useful for further image processing.
47  *
48  * We use the RaspiCamControl code to handle the specific camera settings.
49  * We use the RaspiPreview code to handle the (generic) preview window
50  */
51 
52 // We use some GNU extensions (basename)
53 #ifndef _GNU_SOURCE
54 #define _GNU_SOURCE
55 #endif
56 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <ctype.h>
61 #include <memory.h>
62 #include <sysexits.h>
63 
64 #include <sys/types.h>
65 #include <sys/socket.h>
66 #include <netinet/in.h>
67 #include <arpa/inet.h>
68 #include <time.h>
69 
70 #include "bcm_host.h"
71 #include "interface/vcos/vcos.h"
72 
73 #include "interface/mmal/mmal.h"
74 #include "interface/mmal/mmal_logging.h"
75 #include "interface/mmal/mmal_buffer.h"
76 #include "interface/mmal/util/mmal_util.h"
77 #include "interface/mmal/util/mmal_util_params.h"
78 #include "interface/mmal/util/mmal_default_components.h"
79 #include "interface/mmal/util/mmal_connection.h"
80 #include "interface/mmal/mmal_parameters_camera.h"
81 
82 #include "RaspiCommonSettings.h"
83 #include "RaspiCamControl.h"
84 #include "RaspiPreview.h"
85 #include "RaspiCLI.h"
86 #include "RaspiHelpers.h"
87 #include "RaspiGPS.h"
88 
89 #include <semaphore.h>
90 
91 #include <stdbool.h>
92 
93 // Standard port setting for the camera component
94 #define MMAL_CAMERA_PREVIEW_PORT 0
95 #define MMAL_CAMERA_VIDEO_PORT 1
96 #define MMAL_CAMERA_CAPTURE_PORT 2
97 
98 // Port configuration for the splitter component
99 #define SPLITTER_OUTPUT_PORT 0
100 #define SPLITTER_PREVIEW_PORT 1
101 
102 // Video format information
103 // 0 implies variable
104 #define VIDEO_FRAME_RATE_NUM 30
105 #define VIDEO_FRAME_RATE_DEN 1
106 
107 /// Video render needs at least 2 buffers.
108 #define VIDEO_OUTPUT_BUFFERS_NUM 3
109 
110 // Max bitrate we allow for recording
111 const int MAX_BITRATE_MJPEG = 25000000; // 25Mbits/s
112 const int MAX_BITRATE_LEVEL4 = 25000000; // 25Mbits/s
113 const int MAX_BITRATE_LEVEL42 = 62500000; // 62.5Mbits/s
114 
115 /// Interval at which we check for an failure abort during capture
116 const int ABORT_INTERVAL = 100; // ms
117 
118 /// Capture/Pause switch method
119 /// Simply capture for time specified
120 enum
121 {
122    WAIT_METHOD_NONE,       /// Simply capture for time specified
123    WAIT_METHOD_TIMED,      /// Cycle between capture and pause for times specified
124    WAIT_METHOD_KEYPRESS,   /// Switch between capture and pause on keypress
125    WAIT_METHOD_SIGNAL,     /// Switch between capture and pause on signal
126    WAIT_METHOD_FOREVER     /// Run/record forever
127 };
128 
129 // Forward
130 typedef struct RASPIVID_STATE_S RASPIVID_STATE;
131 
132 /** Struct used to pass information in encoder port userdata to callback
133  */
134 typedef struct
135 {
136    FILE *file_handle;                   /// File handle to write buffer data to.
137    RASPIVID_STATE *pstate;              /// pointer to our state in case required in callback
138    int abort;                           /// Set to 1 in callback if an error occurs to attempt to abort the capture
139    char *cb_buff;                       /// Circular buffer
140    int   cb_len;                        /// Length of buffer
141    int   cb_wptr;                       /// Current write pointer
142    int   cb_wrap;                       /// Has buffer wrapped at least once?
143    int   cb_data;                       /// Valid bytes in buffer
144 #define IFRAME_BUFSIZE (60*1000)
145    int   iframe_buff[IFRAME_BUFSIZE];          /// buffer of iframe pointers
146    int   iframe_buff_wpos;
147    int   iframe_buff_rpos;
148    char  header_bytes[29];
149    int  header_wptr;
150    FILE *imv_file_handle;               /// File handle to write inline motion vectors to.
151    FILE *raw_file_handle;               /// File handle to write raw data to.
152    int  flush_buffers;
153    FILE *pts_file_handle;               /// File timestamps
154 } PORT_USERDATA;
155 
156 /** Possible raw output formats
157  */
158 typedef enum
159 {
160    RAW_OUTPUT_FMT_YUV = 1,
161    RAW_OUTPUT_FMT_RGB,
162    RAW_OUTPUT_FMT_GRAY,
163 } RAW_OUTPUT_FMT;
164 
165 /** Structure containing all state information for the current run
166  */
167 struct RASPIVID_STATE_S
168 {
169    RASPICOMMONSETTINGS_PARAMETERS common_settings;     /// Common settings
170    int timeout;                        /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
171    MMAL_FOURCC_T encoding;             /// Requested codec video encoding (MJPEG or H264)
172    int bitrate;                        /// Requested bitrate
173    int framerate;                      /// Requested frame rate (fps)
174    int intraperiod;                    /// Intra-refresh period (key frame rate)
175    int quantisationParameter;          /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate
176    int bInlineHeaders;                  /// Insert inline headers to stream (SPS, PPS)
177    int demoMode;                       /// Run app in demo mode
178    int demoInterval;                   /// Interval between camera settings changes
179    int immutableInput;                 /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either
180    /// the camera output or the encoder output (with compression artifacts)
181    int profile;                        /// H264 profile to use for encoding
182    int level;                          /// H264 level to use for encoding
183    int waitMethod;                     /// Method for switching between pause and capture
184 
185    int onTime;                         /// In timed cycle mode, the amount of time the capture is on per cycle
186    int offTime;                        /// In timed cycle mode, the amount of time the capture is off per cycle
187 
188    int segmentSize;                    /// Segment mode In timed cycle mode, the amount of time the capture is off per cycle
189    int segmentWrap;                    /// Point at which to wrap segment counter
190    int segmentNumber;                  /// Current segment counter
191    int splitNow;                       /// Split at next possible i-frame if set to 1.
192    int splitWait;                      /// Switch if user wants splited files
193 
194    RASPIPREVIEW_PARAMETERS preview_parameters;   /// Preview setup parameters
195    RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
196 
197    MMAL_COMPONENT_T *camera_component;    /// Pointer to the camera component
198    MMAL_COMPONENT_T *splitter_component;  /// Pointer to the splitter component
199    MMAL_COMPONENT_T *encoder_component;   /// Pointer to the encoder component
200    MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera or splitter to preview
201    MMAL_CONNECTION_T *splitter_connection;/// Pointer to the connection from camera to splitter
202    MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder
203 
204    MMAL_POOL_T *splitter_pool; /// Pointer to the pool of buffers used by splitter output port 0
205    MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
206 
207    PORT_USERDATA callback_data;        /// Used to move data to the encoder callback
208 
209    int bCapturing;                     /// State of capture/pause
210    int bCircularBuffer;                /// Whether we are writing to a circular buffer
211 
212    int inlineMotionVectors;             /// Encoder outputs inline Motion Vectors
213    char *imv_filename;                  /// filename of inline Motion Vectors output
214    int raw_output;                      /// Output raw video from camera as well
215    RAW_OUTPUT_FMT raw_output_fmt;       /// The raw video format
216    char *raw_filename;                  /// Filename for raw video output
217    int intra_refresh_type;              /// What intra refresh type to use. -1 to not set.
218    int frame;
219    char *pts_filename;
220    int save_pts;
221    int64_t starttime;
222    int64_t lasttime;
223 
224    bool netListen;
225    MMAL_BOOL_T addSPSTiming;
226    int slices;
227 };
228 
229 /// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
230 static XREF_T  profile_map[] =
231 {
232    {"baseline",     MMAL_VIDEO_PROFILE_H264_BASELINE},
233    {"main",         MMAL_VIDEO_PROFILE_H264_MAIN},
234    {"high",         MMAL_VIDEO_PROFILE_H264_HIGH},
235 //   {"constrained",  MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this?
236 };
237 
238 static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]);
239 
240 /// Structure to cross reference H264 level strings against the MMAL parameter equivalent
241 static XREF_T  level_map[] =
242 {
243    {"4",           MMAL_VIDEO_LEVEL_H264_4},
244    {"4.1",         MMAL_VIDEO_LEVEL_H264_41},
245    {"4.2",         MMAL_VIDEO_LEVEL_H264_42},
246 };
247 
248 static int level_map_size = sizeof(level_map) / sizeof(level_map[0]);
249 
250 static XREF_T  initial_map[] =
251 {
252    {"record",     0},
253    {"pause",      1},
254 };
255 
256 static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]);
257 
258 static XREF_T  intra_refresh_map[] =
259 {
260    {"cyclic",       MMAL_VIDEO_INTRA_REFRESH_CYCLIC},
261    {"adaptive",     MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE},
262    {"both",         MMAL_VIDEO_INTRA_REFRESH_BOTH},
263    {"cyclicrows",   MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS},
264 //   {"random",       MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why.
265 };
266 
267 static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]);
268 
269 static XREF_T  raw_output_fmt_map[] =
270 {
271    {"yuv",  RAW_OUTPUT_FMT_YUV},
272    {"rgb",  RAW_OUTPUT_FMT_RGB},
273    {"gray", RAW_OUTPUT_FMT_GRAY},
274 };
275 
276 static int raw_output_fmt_map_size = sizeof(raw_output_fmt_map) / sizeof(raw_output_fmt_map[0]);
277 
278 /// Command ID's and Structure defining our command line options
279 enum
280 {
281    CommandBitrate,
282    CommandTimeout,
283    CommandDemoMode,
284    CommandFramerate,
285    CommandPreviewEnc,
286    CommandIntraPeriod,
287    CommandProfile,
288    CommandTimed,
289    CommandSignal,
290    CommandKeypress,
291    CommandInitialState,
292    CommandQP,
293    CommandInlineHeaders,
294    CommandSegmentFile,
295    CommandSegmentWrap,
296    CommandSegmentStart,
297    CommandSplitWait,
298    CommandCircular,
299    CommandIMV,
300    CommandIntraRefreshType,
301    CommandFlush,
302    CommandSavePTS,
303    CommandCodec,
304    CommandLevel,
305    CommandRaw,
306    CommandRawFormat,
307    CommandNetListen,
308    CommandSPSTimings,
309    CommandSlices
310 };
311 
312 static COMMAND_LIST cmdline_commands[] =
313 {
314    { CommandBitrate,       "-bitrate",    "b",  "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 },
315    { CommandTimeout,       "-timeout",    "t",  "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
316    { CommandDemoMode,      "-demo",       "d",  "Run a demo mode (cycle through range of camera options, no capture)", 1},
317    { CommandFramerate,     "-framerate",  "fps","Specify the frames per second to record", 1},
318    { CommandPreviewEnc,    "-penc",       "e",  "Display preview image *after* encoding (shows compression artifacts)", 0},
319    { 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},
320    { CommandProfile,       "-profile",    "pf", "Specify H264 profile to use for encoding", 1},
321    { CommandTimed,         "-timed",      "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0},
322    { CommandSignal,        "-signal",     "s",  "Cycle between capture and pause on Signal", 0},
323    { CommandKeypress,      "-keypress",   "k",  "Cycle between capture and pause on ENTER", 0},
324    { CommandInitialState,  "-initial",    "i",  "Initial state. Use 'record' or 'pause'. Default 'record'", 1},
325    { CommandQP,            "-qp",         "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1},
326    { CommandInlineHeaders, "-inline",     "ih", "Insert inline headers (SPS, PPS) to stream", 0},
327    { CommandSegmentFile,   "-segment",    "sg", "Segment output file in to multiple files at specified interval <ms>", 1},
328    { CommandSegmentWrap,   "-wrap",       "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1},
329    { CommandSegmentStart,  "-start",      "sn", "In segment mode, start with specified segment number", 1},
330    { CommandSplitWait,     "-split",      "sp", "In wait mode, create new output file for each start event", 0},
331    { CommandCircular,      "-circular",   "c",  "Run encoded data through circular buffer until triggered then save", 0},
332    { CommandIMV,           "-vectors",    "x",  "Output filename <filename> for inline motion vectors", 1 },
333    { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1},
334    { CommandFlush,         "-flush",      "fl",  "Flush buffers in order to decrease latency", 0 },
335    { CommandSavePTS,       "-save-pts",   "pts","Save Timestamps to file for mkvmerge", 1 },
336    { CommandCodec,         "-codec",      "cd", "Specify the codec to use - H264 (default) or MJPEG", 1 },
337    { CommandLevel,         "-level",      "lev","Specify H264 level to use for encoding", 1},
338    { CommandRaw,           "-raw",        "r",  "Output filename <filename> for raw video", 1 },
339    { CommandRawFormat,     "-raw-format", "rf", "Specify output format for raw video. Default is yuv", 1},
340    { CommandNetListen,     "-listen",     "l", "Listen on a TCP socket", 0},
341    { CommandSPSTimings,    "-spstimings",    "stm", "Add in h.264 sps timings", 0},
342    { CommandSlices   ,     "-slices",     "sl", "Horizontal slices per frame. Default 1 (off)", 1},
343 };
344 
345 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
346 
347 static struct
348 {
349    char *description;
350    int nextWaitMethod;
351 } wait_method_description[] =
352 {
353    {"Simple capture",         WAIT_METHOD_NONE},
354    {"Capture forever",        WAIT_METHOD_FOREVER},
355    {"Cycle on time",          WAIT_METHOD_TIMED},
356    {"Cycle on keypress",      WAIT_METHOD_KEYPRESS},
357    {"Cycle on signal",        WAIT_METHOD_SIGNAL},
358 };
359 
360 static int wait_method_description_size = sizeof(wait_method_description) / sizeof(wait_method_description[0]);
361 
362 /**
363  * Assign a default set of parameters to the state passed in
364  *
365  * @param state Pointer to state structure to assign defaults to
366  */
default_status(RASPIVID_STATE * state)367 static void default_status(RASPIVID_STATE *state)
368 {
369    if (!state)
370    {
371       vcos_assert(0);
372       return;
373    }
374 
375    // Default everything to zero
376    memset(state, 0, sizeof(RASPIVID_STATE));
377 
378    raspicommonsettings_set_defaults(&state->common_settings);
379 
380    // Now set anything non-zero
381    state->timeout = -1; // replaced with 5000ms later if unset
382    state->common_settings.width = 1920;       // Default to 1080p
383    state->common_settings.height = 1080;
384    state->encoding = MMAL_ENCODING_H264;
385    state->bitrate = 17000000; // This is a decent default bitrate for 1080p
386    state->framerate = VIDEO_FRAME_RATE_NUM;
387    state->intraperiod = -1;    // Not set
388    state->quantisationParameter = 0;
389    state->demoMode = 0;
390    state->demoInterval = 250; // ms
391    state->immutableInput = 1;
392    state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
393    state->level = MMAL_VIDEO_LEVEL_H264_4;
394    state->waitMethod = WAIT_METHOD_NONE;
395    state->onTime = 5000;
396    state->offTime = 5000;
397    state->bCapturing = 0;
398    state->bInlineHeaders = 0;
399    state->segmentSize = 0;  // 0 = not segmenting the file.
400    state->segmentNumber = 1;
401    state->segmentWrap = 0; // Point at which to wrap segment number back to 1. 0 = no wrap
402    state->splitNow = 0;
403    state->splitWait = 0;
404    state->inlineMotionVectors = 0;
405    state->intra_refresh_type = -1;
406    state->frame = 0;
407    state->save_pts = 0;
408    state->netListen = false;
409    state->addSPSTiming = MMAL_FALSE;
410    state->slices = 1;
411 
412    // Setup preview window defaults
413    raspipreview_set_defaults(&state->preview_parameters);
414 
415    // Set up the camera_parameters to default
416    raspicamcontrol_set_defaults(&state->camera_parameters);
417 }
418 
check_camera_model(int cam_num)419 static void check_camera_model(int cam_num)
420 {
421    MMAL_COMPONENT_T *camera_info;
422    MMAL_STATUS_T status;
423 
424    // Try to get the camera name
425    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA_INFO, &camera_info);
426    if (status == MMAL_SUCCESS)
427    {
428       MMAL_PARAMETER_CAMERA_INFO_T param;
429       param.hdr.id = MMAL_PARAMETER_CAMERA_INFO;
430       param.hdr.size = sizeof(param)-4;  // Deliberately undersize to check firmware version
431       status = mmal_port_parameter_get(camera_info->control, &param.hdr);
432 
433       if (status != MMAL_SUCCESS)
434       {
435          // Running on newer firmware
436          param.hdr.size = sizeof(param);
437          status = mmal_port_parameter_get(camera_info->control, &param.hdr);
438          if (status == MMAL_SUCCESS && param.num_cameras > cam_num)
439          {
440             if (!strncmp(param.cameras[cam_num].camera_name, "toshh2c", 7))
441             {
442                fprintf(stderr, "The driver for the TC358743 HDMI to CSI2 chip you are using is NOT supported.\n");
443                fprintf(stderr, "They were written for a demo purposes only, and are in the firmware on an as-is\n");
444                fprintf(stderr, "basis and therefore requests for support or changes will not be acted on.\n\n");
445             }
446          }
447       }
448 
449       mmal_component_destroy(camera_info);
450    }
451 }
452 
453 /**
454  * Dump image state parameters to stderr.
455  *
456  * @param state Pointer to state structure to assign defaults to
457  */
dump_status(RASPIVID_STATE * state)458 static void dump_status(RASPIVID_STATE *state)
459 {
460    int i;
461 
462    if (!state)
463    {
464       vcos_assert(0);
465       return;
466    }
467 
468    raspicommonsettings_dump_parameters(&state->common_settings);
469 
470    fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->bitrate, state->framerate, state->timeout);
471    fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->profile, profile_map, profile_map_size));
472    fprintf(stderr, "H264 Level %s\n", raspicli_unmap_xref(state->level, level_map, level_map_size));
473    fprintf(stderr, "H264 Quantisation level %d, Inline headers %s\n", state->quantisationParameter, state->bInlineHeaders ? "Yes" : "No");
474    fprintf(stderr, "H264 Fill SPS Timings %s\n", state->addSPSTiming ? "Yes" : "No");
475    fprintf(stderr, "H264 Intra refresh type %s, period %d\n", raspicli_unmap_xref(state->intra_refresh_type, intra_refresh_map, intra_refresh_map_size), state->intraperiod);
476    fprintf(stderr, "H264 Slices %d\n", state->slices);
477 
478    // Not going to display segment data unless asked for it.
479    if (state->segmentSize)
480       fprintf(stderr, "Segment size %d, segment wrap value %d, initial segment number %d\n", state->segmentSize, state->segmentWrap, state->segmentNumber);
481 
482    if (state->raw_output)
483       fprintf(stderr, "Raw output enabled, format %s\n", raspicli_unmap_xref(state->raw_output_fmt, raw_output_fmt_map, raw_output_fmt_map_size));
484 
485    fprintf(stderr, "Wait method : ");
486    for (i=0; i<wait_method_description_size; i++)
487    {
488       if (state->waitMethod == wait_method_description[i].nextWaitMethod)
489          fprintf(stderr, "%s", wait_method_description[i].description);
490    }
491    fprintf(stderr, "\nInitial state '%s'\n", raspicli_unmap_xref(state->bCapturing, initial_map, initial_map_size));
492    fprintf(stderr, "\n\n");
493 
494    raspipreview_dump_parameters(&state->preview_parameters);
495    raspicamcontrol_dump_parameters(&state->camera_parameters);
496 }
497 
498 /**
499  * Display usage information for the application to stdout
500  *
501  * @param app_name String to display as the application name
502  */
application_help_message(char * app_name)503 static void application_help_message(char *app_name)
504 {
505    int i;
506 
507    fprintf(stdout, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n");
508    fprintf(stdout, "\nusage: %s [options]\n\n", app_name);
509 
510    fprintf(stdout, "Image parameter commands\n\n");
511 
512    raspicli_display_help(cmdline_commands, cmdline_commands_size);
513 
514    // Profile options
515    fprintf(stdout, "\n\nH264 Profile options :\n%s", profile_map[0].mode );
516 
517    for (i=1; i<profile_map_size; i++)
518    {
519       fprintf(stdout, ",%s", profile_map[i].mode);
520    }
521 
522    // Level options
523    fprintf(stdout, "\n\nH264 Level options :\n%s", level_map[0].mode );
524 
525    for (i=1; i<level_map_size; i++)
526    {
527       fprintf(stdout, ",%s", level_map[i].mode);
528    }
529 
530    // Intra refresh options
531    fprintf(stdout, "\n\nH264 Intra refresh options :\n%s", intra_refresh_map[0].mode );
532 
533    for (i=1; i<intra_refresh_map_size; i++)
534    {
535       fprintf(stdout, ",%s", intra_refresh_map[i].mode);
536    }
537 
538    // Raw output format options
539    fprintf(stdout, "\n\nRaw output format options :\n%s", raw_output_fmt_map[0].mode );
540 
541    for (i=1; i<raw_output_fmt_map_size; i++)
542    {
543       fprintf(stdout, ",%s", raw_output_fmt_map[i].mode);
544    }
545 
546    fprintf(stdout, "\n\n");
547 
548    fprintf(stdout, "Raspivid allows output to a remote IPv4 host e.g. -o tcp://192.168.1.2:1234"
549            "or -o udp://192.168.1.2:1234\n"
550            "To listen on a TCP port (IPv4) and wait for an incoming connection use the -l option\n"
551            "e.g. raspivid -l -o tcp://0.0.0.0:3333 -> bind to all network interfaces,\n"
552            "raspivid -l -o tcp://192.168.1.1:3333 -> bind to a certain local IPv4 port\n");
553 
554    return;
555 }
556 
557 /**
558  * Parse the incoming command line and put resulting parameters in to the state
559  *
560  * @param argc Number of arguments in command line
561  * @param argv Array of pointers to strings from command line
562  * @param state Pointer to state structure to assign any discovered parameters to
563  * @return Non-0 if failed for some reason, 0 otherwise
564  */
parse_cmdline(int argc,const char ** argv,RASPIVID_STATE * state)565 static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state)
566 {
567    // Parse the command line arguments.
568    // We are looking for --<something> or -<abbreviation of something>
569 
570    int valid = 1;
571    int i;
572 
573    for (i = 1; i < argc && valid; i++)
574    {
575       int command_id, num_parameters;
576 
577       if (!argv[i])
578          continue;
579 
580       if (argv[i][0] != '-')
581       {
582          valid = 0;
583          continue;
584       }
585 
586       // Assume parameter is valid until proven otherwise
587       valid = 1;
588 
589       command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
590 
591       // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
592       if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
593          continue;
594 
595       //  We are now dealing with a command line option
596       switch (command_id)
597       {
598       case CommandBitrate: // 1-100
599          if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1)
600          {
601             i++;
602          }
603          else
604             valid = 0;
605 
606          break;
607 
608       case CommandTimeout: // Time to run viewfinder/capture
609       {
610          if (sscanf(argv[i + 1], "%d", &state->timeout) == 1)
611          {
612             // Ensure that if previously selected a waitMethod we don't overwrite it
613             if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE)
614                state->waitMethod = WAIT_METHOD_FOREVER;
615 
616             i++;
617          }
618          else
619             valid = 0;
620          break;
621       }
622 
623       case CommandDemoMode: // Run in demo mode - no capture
624       {
625          // Demo mode might have a timing parameter
626          // so check if a) we have another parameter, b) its not the start of the next option
627          if (i + 1 < argc  && argv[i+1][0] != '-')
628          {
629             if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
630             {
631                // TODO : What limits do we need for timeout?
632                if (state->demoInterval == 0)
633                   state->demoInterval = 250; // ms
634 
635                state->demoMode = 1;
636                i++;
637             }
638             else
639                valid = 0;
640          }
641          else
642          {
643             state->demoMode = 1;
644          }
645 
646          break;
647       }
648 
649       case CommandFramerate: // fps to record
650       {
651          if (sscanf(argv[i + 1], "%u", &state->framerate) == 1)
652          {
653             // TODO : What limits do we need for fps 1 - 30 - 120??
654             i++;
655          }
656          else
657             valid = 0;
658          break;
659       }
660 
661       case CommandPreviewEnc:
662          state->immutableInput = 0;
663          break;
664 
665       case CommandIntraPeriod: // key frame rate
666       {
667          if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1)
668             i++;
669          else
670             valid = 0;
671          break;
672       }
673 
674       case CommandQP: // quantisation parameter
675       {
676          if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1)
677             i++;
678          else
679             valid = 0;
680          break;
681       }
682 
683       case CommandProfile: // H264 profile
684       {
685          state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size);
686 
687          if( state->profile == -1)
688             state->profile = MMAL_VIDEO_PROFILE_H264_HIGH;
689 
690          i++;
691          break;
692       }
693 
694       case CommandInlineHeaders: // H264 inline headers
695       {
696          state->bInlineHeaders = 1;
697          break;
698       }
699 
700       case CommandTimed:
701       {
702          if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2)
703          {
704             i++;
705 
706             if (state->onTime < 1000)
707                state->onTime = 1000;
708 
709             if (state->offTime < 1000)
710                state->offTime = 1000;
711 
712             state->waitMethod = WAIT_METHOD_TIMED;
713 
714             if (state->timeout == -1)
715                state->timeout = 0;
716          }
717          else
718             valid = 0;
719          break;
720       }
721 
722       case CommandKeypress:
723          state->waitMethod = WAIT_METHOD_KEYPRESS;
724 
725          if (state->timeout == -1)
726             state->timeout = 0;
727 
728          break;
729 
730       case CommandSignal:
731          state->waitMethod = WAIT_METHOD_SIGNAL;
732          // Reenable the signal
733          signal(SIGUSR1, default_signal_handler);
734 
735          if (state->timeout == -1)
736             state->timeout = 0;
737 
738          break;
739 
740       case CommandInitialState:
741       {
742          state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size);
743 
744          if( state->bCapturing == -1)
745             state->bCapturing = 0;
746 
747          i++;
748          break;
749       }
750 
751       case CommandSegmentFile: // Segment file in to chunks of specified time
752       {
753          if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1)
754          {
755             // Must enable inline headers for this to work
756             state->bInlineHeaders = 1;
757             i++;
758          }
759          else
760             valid = 0;
761          break;
762       }
763 
764       case CommandSegmentWrap: // segment wrap value
765       {
766          if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1)
767             i++;
768          else
769             valid = 0;
770          break;
771       }
772 
773       case CommandSegmentStart: // initial segment number
774       {
775          if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap)))
776             i++;
777          else
778             valid = 0;
779          break;
780       }
781 
782       case CommandSplitWait: // split files on restart
783       {
784          // Must enable inline headers for this to work
785          state->bInlineHeaders = 1;
786          state->splitWait = 1;
787          break;
788       }
789 
790       case CommandCircular:
791       {
792          state->bCircularBuffer = 1;
793          break;
794       }
795 
796       case CommandIMV:  // output filename
797       {
798          state->inlineMotionVectors = 1;
799          int len = strlen(argv[i + 1]);
800          if (len)
801          {
802             state->imv_filename = malloc(len + 1);
803             vcos_assert(state->imv_filename);
804             if (state->imv_filename)
805                strncpy(state->imv_filename, argv[i + 1], len+1);
806             i++;
807          }
808          else
809             valid = 0;
810          break;
811       }
812 
813       case CommandIntraRefreshType:
814       {
815          state->intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size);
816          i++;
817          break;
818       }
819 
820       case CommandFlush:
821       {
822          state->callback_data.flush_buffers = 1;
823          break;
824       }
825       case CommandSavePTS:  // output filename
826       {
827          state->save_pts = 1;
828          int len = strlen(argv[i + 1]);
829          if (len)
830          {
831             state->pts_filename = malloc(len + 1);
832             vcos_assert(state->pts_filename);
833             if (state->pts_filename)
834                strncpy(state->pts_filename, argv[i + 1], len+1);
835             i++;
836          }
837          else
838             valid = 0;
839          break;
840       }
841       case CommandCodec:  // codec type
842       {
843          int len = strlen(argv[i + 1]);
844          if (len)
845          {
846             if (len==4 && !strncmp("H264", argv[i+1], 4))
847                state->encoding = MMAL_ENCODING_H264;
848             else  if (len==5 && !strncmp("MJPEG", argv[i+1], 5))
849                state->encoding = MMAL_ENCODING_MJPEG;
850             else
851                valid = 0;
852             i++;
853          }
854          else
855             valid = 0;
856          break;
857       }
858 
859       case CommandLevel: // H264 level
860       {
861          state->level = raspicli_map_xref(argv[i + 1], level_map, level_map_size);
862 
863          if( state->level == -1)
864             state->level = MMAL_VIDEO_LEVEL_H264_4;
865 
866          i++;
867          break;
868       }
869 
870       case CommandRaw:  // output filename
871       {
872          state->raw_output = 1;
873          state->raw_output_fmt = RAW_OUTPUT_FMT_YUV;
874          int len = strlen(argv[i + 1]);
875          if (len)
876          {
877             state->raw_filename = malloc(len + 1);
878             vcos_assert(state->raw_filename);
879             if (state->raw_filename)
880                strncpy(state->raw_filename, argv[i + 1], len+1);
881             i++;
882          }
883          else
884             valid = 0;
885          break;
886       }
887 
888       case CommandRawFormat:
889       {
890          state->raw_output_fmt = raspicli_map_xref(argv[i + 1], raw_output_fmt_map, raw_output_fmt_map_size);
891 
892          if (state->raw_output_fmt == -1)
893             valid = 0;
894 
895          i++;
896          break;
897       }
898 
899       case CommandNetListen:
900       {
901          state->netListen = true;
902 
903          break;
904       }
905       case CommandSlices:
906       {
907          if ((sscanf(argv[i + 1], "%d", &state->slices) == 1) && (state->slices > 0))
908             i++;
909          else
910             valid = 0;
911          break;
912       }
913 
914       case CommandSPSTimings:
915       {
916          state->addSPSTiming = MMAL_TRUE;
917 
918          break;
919       }
920 
921       default:
922       {
923          // Try parsing for any image specific parameters
924          // result indicates how many parameters were used up, 0,1,2
925          // but we adjust by -1 as we have used one already
926          const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
927          int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
928 
929          // Still unused, try common settings
930          if (!parms_used)
931             parms_used = raspicommonsettings_parse_cmdline(&state->common_settings, &argv[i][1], second_arg, &application_help_message);
932 
933          // Still unused, try preview options
934          if (!parms_used)
935             parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
936 
937          // If no parms were used, this must be a bad parameter
938          if (!parms_used)
939             valid = 0;
940          else
941             i += parms_used - 1;
942 
943          break;
944       }
945       }
946    }
947 
948    if (!valid)
949    {
950       fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
951       return 1;
952    }
953 
954    return 0;
955 }
956 
957 /**
958  * Open a file based on the settings in state
959  *
960  * @param state Pointer to state
961  */
open_filename(RASPIVID_STATE * pState,char * filename)962 static FILE *open_filename(RASPIVID_STATE *pState, char *filename)
963 {
964    FILE *new_handle = NULL;
965    char *tempname = NULL;
966 
967    if (pState->segmentSize || pState->splitWait)
968    {
969       // Create a new filename string
970 
971       //If %d/%u or any valid combination e.g. %04d is specified, assume segment number.
972       bool bSegmentNumber = false;
973       const char* pPercent = strchr(filename, '%');
974       if (pPercent)
975       {
976          pPercent++;
977          while (isdigit(*pPercent))
978             pPercent++;
979          if (*pPercent == 'u' || *pPercent == 'd')
980             bSegmentNumber = true;
981       }
982 
983       if (bSegmentNumber)
984       {
985          asprintf(&tempname, filename, pState->segmentNumber);
986       }
987       else
988       {
989          char temp_ts_str[100];
990          time_t t = time(NULL);
991          struct tm *tm = localtime(&t);
992          strftime(temp_ts_str, 100, filename, tm);
993          asprintf(&tempname, "%s", temp_ts_str);
994       }
995 
996       filename = tempname;
997    }
998 
999    if (filename)
1000    {
1001       bool bNetwork = false;
1002       int sfd = -1, socktype;
1003 
1004       if(!strncmp("tcp://", filename, 6))
1005       {
1006          bNetwork = true;
1007          socktype = SOCK_STREAM;
1008       }
1009       else if(!strncmp("udp://", filename, 6))
1010       {
1011          if (pState->netListen)
1012          {
1013             fprintf(stderr, "No support for listening in UDP mode\n");
1014             exit(131);
1015          }
1016          bNetwork = true;
1017          socktype = SOCK_DGRAM;
1018       }
1019 
1020       if(bNetwork)
1021       {
1022          unsigned short port;
1023          filename += 6;
1024          char *colon;
1025          if(NULL == (colon = strchr(filename, ':')))
1026          {
1027             fprintf(stderr, "%s is not a valid IPv4:port, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n",
1028                     filename);
1029             exit(132);
1030          }
1031          if(1 != sscanf(colon + 1, "%hu", &port))
1032          {
1033             fprintf(stderr,
1034                     "Port parse failed. %s is not a valid network file name, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n",
1035                     filename);
1036             exit(133);
1037          }
1038          char chTmp = *colon;
1039          *colon = 0;
1040 
1041          struct sockaddr_in saddr= {};
1042          saddr.sin_family = AF_INET;
1043          saddr.sin_port = htons(port);
1044          if(0 == inet_aton(filename, &saddr.sin_addr))
1045          {
1046             fprintf(stderr, "inet_aton failed. %s is not a valid IPv4 address\n",
1047                     filename);
1048             exit(134);
1049          }
1050          *colon = chTmp;
1051 
1052          if (pState->netListen)
1053          {
1054             int sockListen = socket(AF_INET, SOCK_STREAM, 0);
1055             if (sockListen >= 0)
1056             {
1057                int iTmp = 1;
1058                setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &iTmp, sizeof(int));//no error handling, just go on
1059                if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0)
1060                {
1061                   while ((-1 == (iTmp = listen(sockListen, 0))) && (EINTR == errno))
1062                      ;
1063                   if (-1 != iTmp)
1064                   {
1065                      fprintf(stderr, "Waiting for a TCP connection on %s:%"SCNu16"...",
1066                              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
1067                      struct sockaddr_in cli_addr;
1068                      socklen_t clilen = sizeof(cli_addr);
1069                      while ((-1 == (sfd = accept(sockListen, (struct sockaddr *) &cli_addr, &clilen))) && (EINTR == errno))
1070                         ;
1071                      if (sfd >= 0)
1072                         fprintf(stderr, "Client connected from %s:%"SCNu16"\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
1073                      else
1074                         fprintf(stderr, "Error on accept: %s\n", strerror(errno));
1075                   }
1076                   else//if (-1 != iTmp)
1077                   {
1078                      fprintf(stderr, "Error trying to listen on a socket: %s\n", strerror(errno));
1079                   }
1080                }
1081                else//if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0)
1082                {
1083                   fprintf(stderr, "Error on binding socket: %s\n", strerror(errno));
1084                }
1085             }
1086             else//if (sockListen >= 0)
1087             {
1088                fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
1089             }
1090 
1091             if (sockListen >= 0)//regardless success or error
1092                close(sockListen);//do not listen on a given port anymore
1093          }
1094          else//if (pState->netListen)
1095          {
1096             if(0 <= (sfd = socket(AF_INET, socktype, 0)))
1097             {
1098                fprintf(stderr, "Connecting to %s:%hu...", inet_ntoa(saddr.sin_addr), port);
1099 
1100                int iTmp = 1;
1101                while ((-1 == (iTmp = connect(sfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)))) && (EINTR == errno))
1102                   ;
1103                if (iTmp < 0)
1104                   fprintf(stderr, "error: %s\n", strerror(errno));
1105                else
1106                   fprintf(stderr, "connected, sending video...\n");
1107             }
1108             else
1109                fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
1110          }
1111 
1112          if (sfd >= 0)
1113             new_handle = fdopen(sfd, "w");
1114       }
1115       else
1116       {
1117          new_handle = fopen(filename, "wb");
1118       }
1119    }
1120 
1121    if (pState->common_settings.verbose)
1122    {
1123       if (new_handle)
1124          fprintf(stderr, "Opening output file \"%s\"\n", filename);
1125       else
1126          fprintf(stderr, "Failed to open new file \"%s\"\n", filename);
1127    }
1128 
1129    if (tempname)
1130       free(tempname);
1131 
1132    return new_handle;
1133 }
1134 
1135 /**
1136  * Update any annotation data specific to the video.
1137  * This simply passes on the setting from cli, or
1138  * if application defined annotate requested, updates
1139  * with the H264 parameters
1140  *
1141  * @param state Pointer to state control struct
1142  *
1143  */
update_annotation_data(RASPIVID_STATE * state)1144 static void update_annotation_data(RASPIVID_STATE *state)
1145 {
1146    // So, if we have asked for a application supplied string, set it to the H264 or GPS parameters
1147    if (state->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT)
1148    {
1149       char *text;
1150 
1151       if (state->common_settings.gps)
1152       {
1153          text = raspi_gps_location_string();
1154       }
1155       else
1156       {
1157          const char *refresh = raspicli_unmap_xref(state->intra_refresh_type, intra_refresh_map, intra_refresh_map_size);
1158 
1159          asprintf(&text,  "%dk,%df,%s,%d,%s,%s",
1160                   state->bitrate / 1000,  state->framerate,
1161                   refresh ? refresh : "(none)",
1162                   state->intraperiod,
1163                   raspicli_unmap_xref(state->profile, profile_map, profile_map_size),
1164                   raspicli_unmap_xref(state->level, level_map, level_map_size));
1165       }
1166 
1167       raspicamcontrol_set_annotate(state->camera_component, state->camera_parameters.enable_annotate, text,
1168                                    state->camera_parameters.annotate_text_size,
1169                                    state->camera_parameters.annotate_text_colour,
1170                                    state->camera_parameters.annotate_bg_colour,
1171                                    state->camera_parameters.annotate_justify,
1172                                    state->camera_parameters.annotate_x,
1173                                    state->camera_parameters.annotate_y
1174                                   );
1175 
1176       free(text);
1177    }
1178    else
1179    {
1180       raspicamcontrol_set_annotate(state->camera_component, state->camera_parameters.enable_annotate, state->camera_parameters.annotate_string,
1181                                    state->camera_parameters.annotate_text_size,
1182                                    state->camera_parameters.annotate_text_colour,
1183                                    state->camera_parameters.annotate_bg_colour,
1184                                    state->camera_parameters.annotate_justify,
1185                                    state->camera_parameters.annotate_x,
1186                                    state->camera_parameters.annotate_y
1187                                   );
1188    }
1189 }
1190 
1191 /**
1192  *  buffer header callback function for encoder
1193  *
1194  *  Callback will dump buffer data to the specific file
1195  *
1196  * @param port Pointer to port from which callback originated
1197  * @param buffer mmal buffer header pointer
1198  */
encoder_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)1199 static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1200 {
1201    MMAL_BUFFER_HEADER_T *new_buffer;
1202    static int64_t base_time =  -1;
1203    static int64_t last_second = -1;
1204 
1205    // All our segment times based on the receipt of the first encoder callback
1206    if (base_time == -1)
1207       base_time = get_microseconds64()/1000;
1208 
1209    // We pass our file handle and other stuff in via the userdata field.
1210 
1211    PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
1212 
1213    if (pData)
1214    {
1215       int bytes_written = buffer->length;
1216       int64_t current_time = get_microseconds64()/1000;
1217 
1218       vcos_assert(pData->file_handle);
1219       if(pData->pstate->inlineMotionVectors) vcos_assert(pData->imv_file_handle);
1220 
1221       if (pData->cb_buff)
1222       {
1223          int space_in_buff = pData->cb_len - pData->cb_wptr;
1224          int copy_to_end = space_in_buff > buffer->length ? buffer->length : space_in_buff;
1225          int copy_to_start = buffer->length - copy_to_end;
1226 
1227          if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)
1228          {
1229             if(pData->header_wptr + buffer->length > sizeof(pData->header_bytes))
1230             {
1231                vcos_log_error("Error in header bytes\n");
1232             }
1233             else
1234             {
1235                // These are the header bytes, save them for final output
1236                mmal_buffer_header_mem_lock(buffer);
1237                memcpy(pData->header_bytes + pData->header_wptr, buffer->data, buffer->length);
1238                mmal_buffer_header_mem_unlock(buffer);
1239                pData->header_wptr += buffer->length;
1240             }
1241          }
1242          else if((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO))
1243          {
1244             // Do something with the inline motion vectors...
1245          }
1246          else
1247          {
1248             static int frame_start = -1;
1249             int i;
1250 
1251             if(frame_start == -1)
1252                frame_start = pData->cb_wptr;
1253 
1254             if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
1255             {
1256                pData->iframe_buff[pData->iframe_buff_wpos] = frame_start;
1257                pData->iframe_buff_wpos = (pData->iframe_buff_wpos + 1) % IFRAME_BUFSIZE;
1258             }
1259 
1260             if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
1261                frame_start = -1;
1262 
1263             // If we overtake the iframe rptr then move the rptr along
1264             if((pData->iframe_buff_rpos + 1) % IFRAME_BUFSIZE != pData->iframe_buff_wpos)
1265             {
1266                while(
1267                   (
1268                      pData->cb_wptr <= pData->iframe_buff[pData->iframe_buff_rpos] &&
1269                      (pData->cb_wptr + buffer->length) > pData->iframe_buff[pData->iframe_buff_rpos]
1270                   ) ||
1271                   (
1272                      (pData->cb_wptr > pData->iframe_buff[pData->iframe_buff_rpos]) &&
1273                      (pData->cb_wptr + buffer->length) > (pData->iframe_buff[pData->iframe_buff_rpos] + pData->cb_len)
1274                   )
1275                )
1276                   pData->iframe_buff_rpos = (pData->iframe_buff_rpos + 1) % IFRAME_BUFSIZE;
1277             }
1278 
1279             mmal_buffer_header_mem_lock(buffer);
1280             // We are pushing data into a circular buffer
1281             memcpy(pData->cb_buff + pData->cb_wptr, buffer->data, copy_to_end);
1282             memcpy(pData->cb_buff, buffer->data + copy_to_end, copy_to_start);
1283             mmal_buffer_header_mem_unlock(buffer);
1284 
1285             if((pData->cb_wptr + buffer->length) > pData->cb_len)
1286                pData->cb_wrap = 1;
1287 
1288             pData->cb_wptr = (pData->cb_wptr + buffer->length) % pData->cb_len;
1289 
1290             for(i = pData->iframe_buff_rpos; i != pData->iframe_buff_wpos; i = (i + 1) % IFRAME_BUFSIZE)
1291             {
1292                int p = pData->iframe_buff[i];
1293                if(pData->cb_buff[p] != 0 || pData->cb_buff[p+1] != 0 || pData->cb_buff[p+2] != 0 || pData->cb_buff[p+3] != 1)
1294                {
1295                   vcos_log_error("Error in iframe list\n");
1296                }
1297             }
1298          }
1299       }
1300       else
1301       {
1302          // For segmented record mode, we need to see if we have exceeded our time/size,
1303          // but also since we have inline headers turned on we need to break when we get one to
1304          // ensure that the new stream has the header in it. If we break on an I-frame, the
1305          // SPS/PPS header is actually in the previous chunk.
1306          if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) &&
1307                ((pData->pstate->segmentSize && current_time > base_time + pData->pstate->segmentSize) ||
1308                 (pData->pstate->splitWait && pData->pstate->splitNow)))
1309          {
1310             FILE *new_handle;
1311 
1312             base_time = current_time;
1313 
1314             pData->pstate->splitNow = 0;
1315             pData->pstate->segmentNumber++;
1316 
1317             // Only wrap if we have a wrap point set
1318             if (pData->pstate->segmentWrap && pData->pstate->segmentNumber > pData->pstate->segmentWrap)
1319                pData->pstate->segmentNumber = 1;
1320 
1321             if (pData->pstate->common_settings.filename && pData->pstate->common_settings.filename[0] != '-')
1322             {
1323                new_handle = open_filename(pData->pstate, pData->pstate->common_settings.filename);
1324 
1325                if (new_handle)
1326                {
1327                   fclose(pData->file_handle);
1328                   pData->file_handle = new_handle;
1329                }
1330             }
1331 
1332             if (pData->pstate->imv_filename && pData->pstate->imv_filename[0] != '-')
1333             {
1334                new_handle = open_filename(pData->pstate, pData->pstate->imv_filename);
1335 
1336                if (new_handle)
1337                {
1338                   fclose(pData->imv_file_handle);
1339                   pData->imv_file_handle = new_handle;
1340                }
1341             }
1342 
1343             if (pData->pstate->pts_filename && pData->pstate->pts_filename[0] != '-')
1344             {
1345                new_handle = open_filename(pData->pstate, pData->pstate->pts_filename);
1346 
1347                if (new_handle)
1348                {
1349                   fclose(pData->pts_file_handle);
1350                   pData->pts_file_handle = new_handle;
1351                }
1352             }
1353          }
1354          if (buffer->length)
1355          {
1356             mmal_buffer_header_mem_lock(buffer);
1357             if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO)
1358             {
1359                if(pData->pstate->inlineMotionVectors)
1360                {
1361                   bytes_written = fwrite(buffer->data, 1, buffer->length, pData->imv_file_handle);
1362                   if(pData->flush_buffers) fflush(pData->imv_file_handle);
1363                }
1364                else
1365                {
1366                   //We do not want to save inlineMotionVectors...
1367                   bytes_written = buffer->length;
1368                }
1369             }
1370             else
1371             {
1372                bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle);
1373                if(pData->flush_buffers) fflush(pData->file_handle);
1374 
1375                if(pData->pstate->save_pts &&
1376                      (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END ||
1377                       buffer->flags == 0 ||
1378                       buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) &&
1379                      !(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG))
1380                {
1381                   if(buffer->pts != MMAL_TIME_UNKNOWN && buffer->pts != pData->pstate->lasttime)
1382                   {
1383                      int64_t pts;
1384                      if(pData->pstate->frame==0)pData->pstate->starttime=buffer->pts;
1385                      pData->pstate->lasttime=buffer->pts;
1386                      pts = buffer->pts - pData->pstate->starttime;
1387                      fprintf(pData->pts_file_handle,"%lld.%03lld\n", pts/1000, pts%1000);
1388                      pData->pstate->frame++;
1389                   }
1390                }
1391             }
1392 
1393             mmal_buffer_header_mem_unlock(buffer);
1394 
1395             if (bytes_written != buffer->length)
1396             {
1397                vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length);
1398                pData->abort = 1;
1399             }
1400          }
1401       }
1402 
1403       // See if the second count has changed and we need to update any annotation
1404       if (current_time/1000 != last_second)
1405       {
1406          update_annotation_data(pData->pstate);
1407          last_second = current_time/1000;
1408       }
1409    }
1410    else
1411    {
1412       vcos_log_error("Received a encoder buffer callback with no state");
1413    }
1414 
1415    // release buffer back to the pool
1416    mmal_buffer_header_release(buffer);
1417 
1418    // and send one back to the port (if still open)
1419    if (port->is_enabled)
1420    {
1421       MMAL_STATUS_T status;
1422 
1423       new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue);
1424 
1425       if (new_buffer)
1426          status = mmal_port_send_buffer(port, new_buffer);
1427 
1428       if (!new_buffer || status != MMAL_SUCCESS)
1429          vcos_log_error("Unable to return a buffer to the encoder port");
1430    }
1431 }
1432 
1433 /**
1434  *  buffer header callback function for splitter
1435  *
1436  *  Callback will dump buffer data to the specific file
1437  *
1438  * @param port Pointer to port from which callback originated
1439  * @param buffer mmal buffer header pointer
1440  */
splitter_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)1441 static void splitter_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1442 {
1443    MMAL_BUFFER_HEADER_T *new_buffer;
1444    PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
1445 
1446    if (pData)
1447    {
1448       int bytes_written = 0;
1449       int bytes_to_write = buffer->length;
1450 
1451       /* Write only luma component to get grayscale image: */
1452       if (buffer->length && pData->pstate->raw_output_fmt == RAW_OUTPUT_FMT_GRAY)
1453          bytes_to_write = port->format->es->video.width * port->format->es->video.height;
1454 
1455       vcos_assert(pData->raw_file_handle);
1456 
1457       if (bytes_to_write)
1458       {
1459          mmal_buffer_header_mem_lock(buffer);
1460          bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->raw_file_handle);
1461          mmal_buffer_header_mem_unlock(buffer);
1462 
1463          if (bytes_written != bytes_to_write)
1464          {
1465             vcos_log_error("Failed to write raw buffer data (%d from %d)- aborting", bytes_written, bytes_to_write);
1466             pData->abort = 1;
1467          }
1468       }
1469    }
1470    else
1471    {
1472       vcos_log_error("Received a camera buffer callback with no state");
1473    }
1474 
1475    // release buffer back to the pool
1476    mmal_buffer_header_release(buffer);
1477 
1478    // and send one back to the port (if still open)
1479    if (port->is_enabled)
1480    {
1481       MMAL_STATUS_T status;
1482 
1483       new_buffer = mmal_queue_get(pData->pstate->splitter_pool->queue);
1484 
1485       if (new_buffer)
1486          status = mmal_port_send_buffer(port, new_buffer);
1487 
1488       if (!new_buffer || status != MMAL_SUCCESS)
1489          vcos_log_error("Unable to return a buffer to the splitter port");
1490    }
1491 }
1492 
1493 /**
1494  * Create the camera component, set up its ports
1495  *
1496  * @param state Pointer to state control struct
1497  *
1498  * @return MMAL_SUCCESS if all OK, something else otherwise
1499  *
1500  */
create_camera_component(RASPIVID_STATE * state)1501 static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state)
1502 {
1503    MMAL_COMPONENT_T *camera = 0;
1504    MMAL_ES_FORMAT_T *format;
1505    MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
1506    MMAL_STATUS_T status;
1507 
1508    /* Create the component */
1509    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
1510 
1511    if (status != MMAL_SUCCESS)
1512    {
1513       vcos_log_error("Failed to create camera component");
1514       goto error;
1515    }
1516 
1517    status = raspicamcontrol_set_stereo_mode(camera->output[0], &state->camera_parameters.stereo_mode);
1518    status += raspicamcontrol_set_stereo_mode(camera->output[1], &state->camera_parameters.stereo_mode);
1519    status += raspicamcontrol_set_stereo_mode(camera->output[2], &state->camera_parameters.stereo_mode);
1520 
1521    if (status != MMAL_SUCCESS)
1522    {
1523       vcos_log_error("Could not set stereo mode : error %d", status);
1524       goto error;
1525    }
1526 
1527    MMAL_PARAMETER_INT32_T camera_num =
1528    {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->common_settings.cameraNum};
1529 
1530    status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
1531 
1532    if (status != MMAL_SUCCESS)
1533    {
1534       vcos_log_error("Could not select camera : error %d", status);
1535       goto error;
1536    }
1537 
1538    if (!camera->output_num)
1539    {
1540       status = MMAL_ENOSYS;
1541       vcos_log_error("Camera doesn't have output ports");
1542       goto error;
1543    }
1544 
1545    status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->common_settings.sensor_mode);
1546 
1547    if (status != MMAL_SUCCESS)
1548    {
1549       vcos_log_error("Could not set sensor mode : error %d", status);
1550       goto error;
1551    }
1552 
1553    preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
1554    video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
1555    still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
1556 
1557    // Enable the camera, and tell it its control callback function
1558    status = mmal_port_enable(camera->control, default_camera_control_callback);
1559 
1560    if (status != MMAL_SUCCESS)
1561    {
1562       vcos_log_error("Unable to enable control port : error %d", status);
1563       goto error;
1564    }
1565 
1566    //  set up the camera configuration
1567    {
1568       MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
1569       {
1570          { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
1571          .max_stills_w = state->common_settings.width,
1572          .max_stills_h = state->common_settings.height,
1573          .stills_yuv422 = 0,
1574          .one_shot_stills = 0,
1575          .max_preview_video_w = state->common_settings.width,
1576          .max_preview_video_h = state->common_settings.height,
1577          .num_preview_video_frames = 3 + vcos_max(0, (state->framerate-30)/10),
1578          .stills_capture_circular_buffer_height = 0,
1579          .fast_preview_resume = 0,
1580          .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC
1581       };
1582       mmal_port_parameter_set(camera->control, &cam_config.hdr);
1583    }
1584 
1585    // Now set up the port formats
1586 
1587    // Set the encode format on the Preview port
1588    // HW limitations mean we need the preview to be the same size as the required recorded output
1589 
1590    format = preview_port->format;
1591 
1592    format->encoding = MMAL_ENCODING_OPAQUE;
1593    format->encoding_variant = MMAL_ENCODING_I420;
1594 
1595    if(state->camera_parameters.shutter_speed > 6000000)
1596    {
1597       MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1598          { 50, 1000 }, {166, 1000}
1599       };
1600       mmal_port_parameter_set(preview_port, &fps_range.hdr);
1601    }
1602    else if(state->camera_parameters.shutter_speed > 1000000)
1603    {
1604       MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1605          { 166, 1000 }, {999, 1000}
1606       };
1607       mmal_port_parameter_set(preview_port, &fps_range.hdr);
1608    }
1609 
1610    //enable dynamic framerate if necessary
1611    if (state->camera_parameters.shutter_speed)
1612    {
1613       if (state->framerate > 1000000./state->camera_parameters.shutter_speed)
1614       {
1615          state->framerate=0;
1616          if (state->common_settings.verbose)
1617             fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n");
1618       }
1619    }
1620 
1621    format->encoding = MMAL_ENCODING_OPAQUE;
1622    format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
1623    format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
1624    format->es->video.crop.x = 0;
1625    format->es->video.crop.y = 0;
1626    format->es->video.crop.width = state->common_settings.width;
1627    format->es->video.crop.height = state->common_settings.height;
1628    format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM;
1629    format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN;
1630 
1631    status = mmal_port_format_commit(preview_port);
1632 
1633    if (status != MMAL_SUCCESS)
1634    {
1635       vcos_log_error("camera viewfinder format couldn't be set");
1636       goto error;
1637    }
1638 
1639    // Set the encode format on the video  port
1640 
1641    format = video_port->format;
1642    format->encoding_variant = MMAL_ENCODING_I420;
1643 
1644    if(state->camera_parameters.shutter_speed > 6000000)
1645    {
1646       MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1647          { 50, 1000 }, {166, 1000}
1648       };
1649       mmal_port_parameter_set(video_port, &fps_range.hdr);
1650    }
1651    else if(state->camera_parameters.shutter_speed > 1000000)
1652    {
1653       MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
1654          { 167, 1000 }, {999, 1000}
1655       };
1656       mmal_port_parameter_set(video_port, &fps_range.hdr);
1657    }
1658 
1659    format->encoding = MMAL_ENCODING_OPAQUE;
1660    format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
1661    format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
1662    format->es->video.crop.x = 0;
1663    format->es->video.crop.y = 0;
1664    format->es->video.crop.width = state->common_settings.width;
1665    format->es->video.crop.height = state->common_settings.height;
1666    format->es->video.frame_rate.num = state->framerate;
1667    format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN;
1668 
1669    status = mmal_port_format_commit(video_port);
1670 
1671    if (status != MMAL_SUCCESS)
1672    {
1673       vcos_log_error("camera video format couldn't be set");
1674       goto error;
1675    }
1676 
1677    // Ensure there are enough buffers to avoid dropping frames
1678    if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1679       video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1680 
1681    // Set the encode format on the still  port
1682 
1683    format = still_port->format;
1684 
1685    format->encoding = MMAL_ENCODING_OPAQUE;
1686    format->encoding_variant = MMAL_ENCODING_I420;
1687 
1688    format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
1689    format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
1690    format->es->video.crop.x = 0;
1691    format->es->video.crop.y = 0;
1692    format->es->video.crop.width = state->common_settings.width;
1693    format->es->video.crop.height = state->common_settings.height;
1694    format->es->video.frame_rate.num = 0;
1695    format->es->video.frame_rate.den = 1;
1696 
1697    status = mmal_port_format_commit(still_port);
1698 
1699    if (status != MMAL_SUCCESS)
1700    {
1701       vcos_log_error("camera still format couldn't be set");
1702       goto error;
1703    }
1704 
1705    /* Ensure there are enough buffers to avoid dropping frames */
1706    if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1707       still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1708 
1709    /* Enable component */
1710    status = mmal_component_enable(camera);
1711 
1712    if (status != MMAL_SUCCESS)
1713    {
1714       vcos_log_error("camera component couldn't be enabled");
1715       goto error;
1716    }
1717 
1718    // Note: this sets lots of parameters that were not individually addressed before.
1719    raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
1720 
1721    state->camera_component = camera;
1722 
1723    update_annotation_data(state);
1724 
1725    if (state->common_settings.verbose)
1726       fprintf(stderr, "Camera component done\n");
1727 
1728    return status;
1729 
1730 error:
1731 
1732    if (camera)
1733       mmal_component_destroy(camera);
1734 
1735    return status;
1736 }
1737 
1738 /**
1739  * Destroy the camera component
1740  *
1741  * @param state Pointer to state control struct
1742  *
1743  */
destroy_camera_component(RASPIVID_STATE * state)1744 static void destroy_camera_component(RASPIVID_STATE *state)
1745 {
1746    if (state->camera_component)
1747    {
1748       mmal_component_destroy(state->camera_component);
1749       state->camera_component = NULL;
1750    }
1751 }
1752 
1753 /**
1754  * Create the splitter component, set up its ports
1755  *
1756  * @param state Pointer to state control struct
1757  *
1758  * @return MMAL_SUCCESS if all OK, something else otherwise
1759  *
1760  */
create_splitter_component(RASPIVID_STATE * state)1761 static MMAL_STATUS_T create_splitter_component(RASPIVID_STATE *state)
1762 {
1763    MMAL_COMPONENT_T *splitter = 0;
1764    MMAL_PORT_T *splitter_output = NULL;
1765    MMAL_ES_FORMAT_T *format;
1766    MMAL_STATUS_T status;
1767    MMAL_POOL_T *pool;
1768    int i;
1769 
1770    if (state->camera_component == NULL)
1771    {
1772       status = MMAL_ENOSYS;
1773       vcos_log_error("Camera component must be created before splitter");
1774       goto error;
1775    }
1776 
1777    /* Create the component */
1778    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER, &splitter);
1779 
1780    if (status != MMAL_SUCCESS)
1781    {
1782       vcos_log_error("Failed to create splitter component");
1783       goto error;
1784    }
1785 
1786    if (!splitter->input_num)
1787    {
1788       status = MMAL_ENOSYS;
1789       vcos_log_error("Splitter doesn't have any input port");
1790       goto error;
1791    }
1792 
1793    if (splitter->output_num < 2)
1794    {
1795       status = MMAL_ENOSYS;
1796       vcos_log_error("Splitter doesn't have enough output ports");
1797       goto error;
1798    }
1799 
1800    /* Ensure there are enough buffers to avoid dropping frames: */
1801    mmal_format_copy(splitter->input[0]->format, state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]->format);
1802 
1803    if (splitter->input[0]->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1804       splitter->input[0]->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1805 
1806    status = mmal_port_format_commit(splitter->input[0]);
1807 
1808    if (status != MMAL_SUCCESS)
1809    {
1810       vcos_log_error("Unable to set format on splitter input port");
1811       goto error;
1812    }
1813 
1814    /* Splitter can do format conversions, configure format for its output port: */
1815    for (i = 0; i < splitter->output_num; i++)
1816    {
1817       mmal_format_copy(splitter->output[i]->format, splitter->input[0]->format);
1818 
1819       if (i == SPLITTER_OUTPUT_PORT)
1820       {
1821          format = splitter->output[i]->format;
1822 
1823          switch (state->raw_output_fmt)
1824          {
1825          case RAW_OUTPUT_FMT_YUV:
1826          case RAW_OUTPUT_FMT_GRAY: /* Grayscale image contains only luma (Y) component */
1827             format->encoding = MMAL_ENCODING_I420;
1828             format->encoding_variant = MMAL_ENCODING_I420;
1829             break;
1830          case RAW_OUTPUT_FMT_RGB:
1831             if (mmal_util_rgb_order_fixed(state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]))
1832                format->encoding = MMAL_ENCODING_RGB24;
1833             else
1834                format->encoding = MMAL_ENCODING_BGR24;
1835             format->encoding_variant = 0;  /* Irrelevant when not in opaque mode */
1836             break;
1837          default:
1838             status = MMAL_EINVAL;
1839             vcos_log_error("unknown raw output format");
1840             goto error;
1841          }
1842       }
1843 
1844       status = mmal_port_format_commit(splitter->output[i]);
1845 
1846       if (status != MMAL_SUCCESS)
1847       {
1848          vcos_log_error("Unable to set format on splitter output port %d", i);
1849          goto error;
1850       }
1851    }
1852 
1853    /* Enable component */
1854    status = mmal_component_enable(splitter);
1855 
1856    if (status != MMAL_SUCCESS)
1857    {
1858       vcos_log_error("splitter component couldn't be enabled");
1859       goto error;
1860    }
1861 
1862    /* Create pool of buffer headers for the output port to consume */
1863    splitter_output = splitter->output[SPLITTER_OUTPUT_PORT];
1864    pool = mmal_port_pool_create(splitter_output, splitter_output->buffer_num, splitter_output->buffer_size);
1865 
1866    if (!pool)
1867    {
1868       vcos_log_error("Failed to create buffer header pool for splitter output port %s", splitter_output->name);
1869    }
1870 
1871    state->splitter_pool = pool;
1872    state->splitter_component = splitter;
1873 
1874    if (state->common_settings.verbose)
1875       fprintf(stderr, "Splitter component done\n");
1876 
1877    return status;
1878 
1879 error:
1880 
1881    if (splitter)
1882       mmal_component_destroy(splitter);
1883 
1884    return status;
1885 }
1886 
1887 /**
1888  * Destroy the splitter component
1889  *
1890  * @param state Pointer to state control struct
1891  *
1892  */
destroy_splitter_component(RASPIVID_STATE * state)1893 static void destroy_splitter_component(RASPIVID_STATE *state)
1894 {
1895    // Get rid of any port buffers first
1896    if (state->splitter_pool)
1897    {
1898       mmal_port_pool_destroy(state->splitter_component->output[SPLITTER_OUTPUT_PORT], state->splitter_pool);
1899    }
1900 
1901    if (state->splitter_component)
1902    {
1903       mmal_component_destroy(state->splitter_component);
1904       state->splitter_component = NULL;
1905    }
1906 }
1907 
1908 /**
1909  * Create the encoder component, set up its ports
1910  *
1911  * @param state Pointer to state control struct
1912  *
1913  * @return MMAL_SUCCESS if all OK, something else otherwise
1914  *
1915  */
create_encoder_component(RASPIVID_STATE * state)1916 static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
1917 {
1918    MMAL_COMPONENT_T *encoder = 0;
1919    MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
1920    MMAL_STATUS_T status;
1921    MMAL_POOL_T *pool;
1922 
1923    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder);
1924 
1925    if (status != MMAL_SUCCESS)
1926    {
1927       vcos_log_error("Unable to create video encoder component");
1928       goto error;
1929    }
1930 
1931    if (!encoder->input_num || !encoder->output_num)
1932    {
1933       status = MMAL_ENOSYS;
1934       vcos_log_error("Video encoder doesn't have input/output ports");
1935       goto error;
1936    }
1937 
1938    encoder_input = encoder->input[0];
1939    encoder_output = encoder->output[0];
1940 
1941    // We want same format on input and output
1942    mmal_format_copy(encoder_output->format, encoder_input->format);
1943 
1944    // Only supporting H264 at the moment
1945    encoder_output->format->encoding = state->encoding;
1946 
1947    if(state->encoding == MMAL_ENCODING_H264)
1948    {
1949       if(state->level == MMAL_VIDEO_LEVEL_H264_4)
1950       {
1951          if(state->bitrate > MAX_BITRATE_LEVEL4)
1952          {
1953             fprintf(stderr, "Bitrate too high: Reducing to 25MBit/s\n");
1954             state->bitrate = MAX_BITRATE_LEVEL4;
1955          }
1956       }
1957       else
1958       {
1959          if(state->bitrate > MAX_BITRATE_LEVEL42)
1960          {
1961             fprintf(stderr, "Bitrate too high: Reducing to 62.5MBit/s\n");
1962             state->bitrate = MAX_BITRATE_LEVEL42;
1963          }
1964       }
1965    }
1966    else if(state->encoding == MMAL_ENCODING_MJPEG)
1967    {
1968       if(state->bitrate > MAX_BITRATE_MJPEG)
1969       {
1970          fprintf(stderr, "Bitrate too high: Reducing to 25MBit/s\n");
1971          state->bitrate = MAX_BITRATE_MJPEG;
1972       }
1973    }
1974 
1975    encoder_output->format->bitrate = state->bitrate;
1976 
1977    if (state->encoding == MMAL_ENCODING_H264)
1978       encoder_output->buffer_size = encoder_output->buffer_size_recommended;
1979    else
1980       encoder_output->buffer_size = 256<<10;
1981 
1982    if (encoder_output->buffer_size < encoder_output->buffer_size_min)
1983       encoder_output->buffer_size = encoder_output->buffer_size_min;
1984 
1985    encoder_output->buffer_num = encoder_output->buffer_num_recommended;
1986 
1987    if (encoder_output->buffer_num < encoder_output->buffer_num_min)
1988       encoder_output->buffer_num = encoder_output->buffer_num_min;
1989 
1990    // We need to set the frame rate on output to 0, to ensure it gets
1991    // updated correctly from the input framerate when port connected
1992    encoder_output->format->es->video.frame_rate.num = 0;
1993    encoder_output->format->es->video.frame_rate.den = 1;
1994 
1995    // Commit the port changes to the output port
1996    status = mmal_port_format_commit(encoder_output);
1997 
1998    if (status != MMAL_SUCCESS)
1999    {
2000       vcos_log_error("Unable to set format on video encoder output port");
2001       goto error;
2002    }
2003 
2004    // Set the rate control parameter
2005    if (0)
2006    {
2007       MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT};
2008       status = mmal_port_parameter_set(encoder_output, &param.hdr);
2009       if (status != MMAL_SUCCESS)
2010       {
2011          vcos_log_error("Unable to set ratecontrol");
2012          goto error;
2013       }
2014 
2015    }
2016 
2017    if (state->encoding == MMAL_ENCODING_H264 &&
2018          state->intraperiod != -1)
2019    {
2020       MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod};
2021       status = mmal_port_parameter_set(encoder_output, &param.hdr);
2022       if (status != MMAL_SUCCESS)
2023       {
2024          vcos_log_error("Unable to set intraperiod");
2025          goto error;
2026       }
2027    }
2028 
2029    if (state->encoding == MMAL_ENCODING_H264 && state->slices > 1 && state->common_settings.width <= 1280)
2030    {
2031       int frame_mb_rows = VCOS_ALIGN_UP(state->common_settings.height, 16) >> 4;
2032 
2033       if (state->slices > frame_mb_rows) //warn user if too many slices selected
2034       {
2035          fprintf(stderr,"H264 Slice count (%d) exceeds number of macroblock rows (%d). Setting slices to %d.\n", state->slices, frame_mb_rows, frame_mb_rows);
2036          // Continue rather than abort..
2037       }
2038       int slice_row_mb = frame_mb_rows/state->slices;
2039       if (frame_mb_rows - state->slices*slice_row_mb)
2040          slice_row_mb++; //must round up to avoid extra slice if not evenly divided
2041 
2042       status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_MB_ROWS_PER_SLICE, slice_row_mb);
2043       if (status != MMAL_SUCCESS)
2044       {
2045          vcos_log_error("Unable to set number of slices");
2046          goto error;
2047       }
2048    }
2049 
2050    if (state->encoding == MMAL_ENCODING_H264 &&
2051        state->quantisationParameter)
2052    {
2053       MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->quantisationParameter};
2054       status = mmal_port_parameter_set(encoder_output, &param.hdr);
2055       if (status != MMAL_SUCCESS)
2056       {
2057          vcos_log_error("Unable to set initial QP");
2058          goto error;
2059       }
2060 
2061       MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, state->quantisationParameter};
2062       status = mmal_port_parameter_set(encoder_output, &param2.hdr);
2063       if (status != MMAL_SUCCESS)
2064       {
2065          vcos_log_error("Unable to set min QP");
2066          goto error;
2067       }
2068 
2069       MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, state->quantisationParameter};
2070       status = mmal_port_parameter_set(encoder_output, &param3.hdr);
2071       if (status != MMAL_SUCCESS)
2072       {
2073          vcos_log_error("Unable to set max QP");
2074          goto error;
2075       }
2076    }
2077 
2078    if (state->encoding == MMAL_ENCODING_H264)
2079    {
2080       MMAL_PARAMETER_VIDEO_PROFILE_T  param;
2081       param.hdr.id = MMAL_PARAMETER_PROFILE;
2082       param.hdr.size = sizeof(param);
2083 
2084       param.profile[0].profile = state->profile;
2085 
2086       if((VCOS_ALIGN_UP(state->common_settings.width,16) >> 4) * (VCOS_ALIGN_UP(state->common_settings.height,16) >> 4) * state->framerate > 245760)
2087       {
2088          if((VCOS_ALIGN_UP(state->common_settings.width,16) >> 4) * (VCOS_ALIGN_UP(state->common_settings.height,16) >> 4) * state->framerate <= 522240)
2089          {
2090             fprintf(stderr, "Too many macroblocks/s: Increasing H264 Level to 4.2\n");
2091             state->level=MMAL_VIDEO_LEVEL_H264_42;
2092          }
2093          else
2094          {
2095             vcos_log_error("Too many macroblocks/s requested");
2096             status = MMAL_EINVAL;
2097             goto error;
2098          }
2099       }
2100 
2101       param.profile[0].level = state->level;
2102 
2103       status = mmal_port_parameter_set(encoder_output, &param.hdr);
2104       if (status != MMAL_SUCCESS)
2105       {
2106          vcos_log_error("Unable to set H264 profile");
2107          goto error;
2108       }
2109    }
2110 
2111    if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS)
2112    {
2113       vcos_log_error("Unable to set immutable input flag");
2114       // Continue rather than abort..
2115    }
2116 
2117    if (state->encoding == MMAL_ENCODING_H264)
2118    {
2119       //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested
2120       if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, state->bInlineHeaders) != MMAL_SUCCESS)
2121       {
2122          vcos_log_error("failed to set INLINE HEADER FLAG parameters");
2123          // Continue rather than abort..
2124       }
2125 
2126       //set flag for add SPS TIMING
2127       if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_SPS_TIMING, state->addSPSTiming) != MMAL_SUCCESS)
2128       {
2129          vcos_log_error("failed to set SPS TIMINGS FLAG parameters");
2130          // Continue rather than abort..
2131       }
2132 
2133       //set INLINE VECTORS flag to request motion vector estimates
2134       if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, state->inlineMotionVectors) != MMAL_SUCCESS)
2135       {
2136          vcos_log_error("failed to set INLINE VECTORS parameters");
2137          // Continue rather than abort..
2138       }
2139 
2140       // Adaptive intra refresh settings
2141       if ( state->intra_refresh_type != -1)
2142       {
2143          MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T  param;
2144          param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH;
2145          param.hdr.size = sizeof(param);
2146 
2147          // Get first so we don't overwrite anything unexpectedly
2148          status = mmal_port_parameter_get(encoder_output, &param.hdr);
2149          if (status != MMAL_SUCCESS)
2150          {
2151             vcos_log_warn("Unable to get existing H264 intra-refresh values. Please update your firmware");
2152             // Set some defaults, don't just pass random stack data
2153             param.air_mbs = param.air_ref = param.cir_mbs = param.pir_mbs = 0;
2154          }
2155 
2156          param.refresh_mode = state->intra_refresh_type;
2157 
2158          //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
2159          //   param.cir_mbs = 10;
2160 
2161          status = mmal_port_parameter_set(encoder_output, &param.hdr);
2162          if (status != MMAL_SUCCESS)
2163          {
2164             vcos_log_error("Unable to set H264 intra-refresh values");
2165             goto error;
2166          }
2167       }
2168    }
2169 
2170    //  Enable component
2171    status = mmal_component_enable(encoder);
2172 
2173    if (status != MMAL_SUCCESS)
2174    {
2175       vcos_log_error("Unable to enable video encoder component");
2176       goto error;
2177    }
2178 
2179    /* Create pool of buffer headers for the output port to consume */
2180    pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
2181 
2182    if (!pool)
2183    {
2184       vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
2185    }
2186 
2187    state->encoder_pool = pool;
2188    state->encoder_component = encoder;
2189 
2190    if (state->common_settings.verbose)
2191       fprintf(stderr, "Encoder component done\n");
2192 
2193    return status;
2194 
2195 error:
2196    if (encoder)
2197       mmal_component_destroy(encoder);
2198 
2199    state->encoder_component = NULL;
2200 
2201    return status;
2202 }
2203 
2204 /**
2205  * Destroy the encoder component
2206  *
2207  * @param state Pointer to state control struct
2208  *
2209  */
destroy_encoder_component(RASPIVID_STATE * state)2210 static void destroy_encoder_component(RASPIVID_STATE *state)
2211 {
2212    // Get rid of any port buffers first
2213    if (state->encoder_pool)
2214    {
2215       mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
2216    }
2217 
2218    if (state->encoder_component)
2219    {
2220       mmal_component_destroy(state->encoder_component);
2221       state->encoder_component = NULL;
2222    }
2223 }
2224 
2225 /**
2226  * Pause for specified time, but return early if detect an abort request
2227  *
2228  * @param state Pointer to state control struct
2229  * @param pause Time in ms to pause
2230  * @param callback Struct contain an abort flag tested for early termination
2231  *
2232  */
pause_and_test_abort(RASPIVID_STATE * state,int pause)2233 static int pause_and_test_abort(RASPIVID_STATE *state, int pause)
2234 {
2235    int wait;
2236 
2237    if (!pause)
2238       return 0;
2239 
2240    // Going to check every ABORT_INTERVAL milliseconds
2241    for (wait = 0; wait < pause; wait+= ABORT_INTERVAL)
2242    {
2243       vcos_sleep(ABORT_INTERVAL);
2244       if (state->callback_data.abort)
2245          return 1;
2246    }
2247 
2248    return 0;
2249 }
2250 
2251 /**
2252  * Function to wait in various ways (depending on settings)
2253  *
2254  * @param state Pointer to the state data
2255  *
2256  * @return !0 if to continue, 0 if reached end of run
2257  */
wait_for_next_change(RASPIVID_STATE * state)2258 static int wait_for_next_change(RASPIVID_STATE *state)
2259 {
2260    int keep_running = 1;
2261    static int64_t complete_time = -1;
2262 
2263    // Have we actually exceeded our timeout?
2264    int64_t current_time =  get_microseconds64()/1000;
2265 
2266    if (complete_time == -1)
2267       complete_time =  current_time + state->timeout;
2268 
2269    // if we have run out of time, flag we need to exit
2270    if (current_time >= complete_time && state->timeout != 0)
2271       keep_running = 0;
2272 
2273    switch (state->waitMethod)
2274    {
2275    case WAIT_METHOD_NONE:
2276       (void)pause_and_test_abort(state, state->timeout);
2277       return 0;
2278 
2279    case WAIT_METHOD_FOREVER:
2280    {
2281       // We never return from this. Expect a ctrl-c to exit or abort.
2282       while (!state->callback_data.abort)
2283          // Have a sleep so we don't hog the CPU.
2284          vcos_sleep(ABORT_INTERVAL);
2285 
2286       return 0;
2287    }
2288 
2289    case WAIT_METHOD_TIMED:
2290    {
2291       int abort;
2292 
2293       if (state->bCapturing)
2294          abort = pause_and_test_abort(state, state->onTime);
2295       else
2296          abort = pause_and_test_abort(state, state->offTime);
2297 
2298       if (abort)
2299          return 0;
2300       else
2301          return keep_running;
2302    }
2303 
2304    case WAIT_METHOD_KEYPRESS:
2305    {
2306       char ch;
2307 
2308       if (state->common_settings.verbose)
2309          fprintf(stderr, "Press Enter to %s, X then ENTER to exit, [i,o,r] then ENTER to change zoom\n", state->bCapturing ? "pause" : "capture");
2310 
2311       ch = getchar();
2312       if (ch == 'x' || ch == 'X')
2313          return 0;
2314       else if (ch == 'i' || ch == 'I')
2315       {
2316          if (state->common_settings.verbose)
2317             fprintf(stderr, "Starting zoom in\n");
2318 
2319          raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_IN, &(state->camera_parameters).roi);
2320 
2321          if (state->common_settings.verbose)
2322             dump_status(state);
2323       }
2324       else if (ch == 'o' || ch == 'O')
2325       {
2326          if (state->common_settings.verbose)
2327             fprintf(stderr, "Starting zoom out\n");
2328 
2329          raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_OUT, &(state->camera_parameters).roi);
2330 
2331          if (state->common_settings.verbose)
2332             dump_status(state);
2333       }
2334       else if (ch == 'r' || ch == 'R')
2335       {
2336          if (state->common_settings.verbose)
2337             fprintf(stderr, "starting reset zoom\n");
2338 
2339          raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_RESET, &(state->camera_parameters).roi);
2340 
2341          if (state->common_settings.verbose)
2342             dump_status(state);
2343       }
2344 
2345       return keep_running;
2346    }
2347 
2348    case WAIT_METHOD_SIGNAL:
2349    {
2350       // Need to wait for a SIGUSR1 signal
2351       sigset_t waitset;
2352       int sig;
2353       int result = 0;
2354 
2355       sigemptyset( &waitset );
2356       sigaddset( &waitset, SIGUSR1 );
2357 
2358       // We are multi threaded because we use mmal, so need to use the pthread
2359       // variant of procmask to block SIGUSR1 so we can wait on it.
2360       pthread_sigmask( SIG_BLOCK, &waitset, NULL );
2361 
2362       if (state->common_settings.verbose)
2363       {
2364          fprintf(stderr, "Waiting for SIGUSR1 to %s\n", state->bCapturing ? "pause" : "capture");
2365       }
2366 
2367       result = sigwait( &waitset, &sig );
2368 
2369       if (state->common_settings.verbose && result != 0)
2370          fprintf(stderr, "Bad signal received - error %d\n", errno);
2371 
2372       return keep_running;
2373    }
2374 
2375    } // switch
2376 
2377    return keep_running;
2378 }
2379 
2380 /**
2381  * main
2382  */
main(int argc,const char ** argv)2383 int main(int argc, const char **argv)
2384 {
2385    // Our main data storage vessel..
2386    RASPIVID_STATE state;
2387    int exit_code = EX_OK;
2388 
2389    MMAL_STATUS_T status = MMAL_SUCCESS;
2390    MMAL_PORT_T *camera_preview_port = NULL;
2391    MMAL_PORT_T *camera_video_port = NULL;
2392    MMAL_PORT_T *camera_still_port = NULL;
2393    MMAL_PORT_T *preview_input_port = NULL;
2394    MMAL_PORT_T *encoder_input_port = NULL;
2395    MMAL_PORT_T *encoder_output_port = NULL;
2396    MMAL_PORT_T *splitter_input_port = NULL;
2397    MMAL_PORT_T *splitter_output_port = NULL;
2398    MMAL_PORT_T *splitter_preview_port = NULL;
2399 
2400    bcm_host_init();
2401 
2402    // Register our application with the logging system
2403    vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
2404 
2405    signal(SIGINT, default_signal_handler);
2406 
2407    // Disable USR1 for the moment - may be reenabled if go in to signal capture mode
2408    signal(SIGUSR1, SIG_IGN);
2409 
2410    set_app_name(argv[0]);
2411 
2412    // Do we have any parameters
2413    if (argc == 1)
2414    {
2415       display_valid_parameters(basename(get_app_name()), &application_help_message);
2416       exit(EX_USAGE);
2417    }
2418 
2419    default_status(&state);
2420 
2421    // Parse the command line and put options in to our status structure
2422    if (parse_cmdline(argc, argv, &state))
2423    {
2424       status = -1;
2425       exit(EX_USAGE);
2426    }
2427 
2428    if (state.timeout == -1)
2429       state.timeout = 5000;
2430 
2431    // Setup for sensor specific parameters, only set W/H settings if zero on entry
2432    get_sensor_defaults(state.common_settings.cameraNum, state.common_settings.camera_name,
2433                        &state.common_settings.width, &state.common_settings.height);
2434 
2435    if (state.common_settings.verbose)
2436    {
2437       print_app_details(stderr);
2438       dump_status(&state);
2439    }
2440 
2441    check_camera_model(state.common_settings.cameraNum);
2442 
2443    if (state.common_settings.gps)
2444       if (raspi_gps_setup(state.common_settings.verbose))
2445          state.common_settings.gps = 0;
2446 
2447    // OK, we have a nice set of parameters. Now set up our components
2448    // We have three components. Camera, Preview and encoder.
2449 
2450    if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
2451    {
2452       vcos_log_error("%s: Failed to create camera component", __func__);
2453       exit_code = EX_SOFTWARE;
2454    }
2455    else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
2456    {
2457       vcos_log_error("%s: Failed to create preview component", __func__);
2458       destroy_camera_component(&state);
2459       exit_code = EX_SOFTWARE;
2460    }
2461    else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS)
2462    {
2463       vcos_log_error("%s: Failed to create encode component", __func__);
2464       raspipreview_destroy(&state.preview_parameters);
2465       destroy_camera_component(&state);
2466       exit_code = EX_SOFTWARE;
2467    }
2468    else if (state.raw_output && (status = create_splitter_component(&state)) != MMAL_SUCCESS)
2469    {
2470       vcos_log_error("%s: Failed to create splitter component", __func__);
2471       raspipreview_destroy(&state.preview_parameters);
2472       destroy_camera_component(&state);
2473       destroy_encoder_component(&state);
2474       exit_code = EX_SOFTWARE;
2475    }
2476    else
2477    {
2478       if (state.common_settings.verbose)
2479          fprintf(stderr, "Starting component connection stage\n");
2480 
2481       camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
2482       camera_video_port   = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
2483       camera_still_port   = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
2484       preview_input_port  = state.preview_parameters.preview_component->input[0];
2485       encoder_input_port  = state.encoder_component->input[0];
2486       encoder_output_port = state.encoder_component->output[0];
2487 
2488       if (state.raw_output)
2489       {
2490          splitter_input_port = state.splitter_component->input[0];
2491          splitter_output_port = state.splitter_component->output[SPLITTER_OUTPUT_PORT];
2492          splitter_preview_port = state.splitter_component->output[SPLITTER_PREVIEW_PORT];
2493       }
2494 
2495       if (state.preview_parameters.wantPreview )
2496       {
2497          if (state.raw_output)
2498          {
2499             if (state.common_settings.verbose)
2500                fprintf(stderr, "Connecting camera preview port to splitter input port\n");
2501 
2502             // Connect camera to splitter
2503             status = connect_ports(camera_preview_port, splitter_input_port, &state.splitter_connection);
2504 
2505             if (status != MMAL_SUCCESS)
2506             {
2507                state.splitter_connection = NULL;
2508                vcos_log_error("%s: Failed to connect camera preview port to splitter input", __func__);
2509                goto error;
2510             }
2511 
2512             if (state.common_settings.verbose)
2513             {
2514                fprintf(stderr, "Connecting splitter preview port to preview input port\n");
2515                fprintf(stderr, "Starting video preview\n");
2516             }
2517 
2518             // Connect splitter to preview
2519             status = connect_ports(splitter_preview_port, preview_input_port, &state.preview_connection);
2520          }
2521          else
2522          {
2523             if (state.common_settings.verbose)
2524             {
2525                fprintf(stderr, "Connecting camera preview port to preview input port\n");
2526                fprintf(stderr, "Starting video preview\n");
2527             }
2528 
2529             // Connect camera to preview
2530             status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
2531          }
2532 
2533          if (status != MMAL_SUCCESS)
2534             state.preview_connection = NULL;
2535       }
2536       else
2537       {
2538          if (state.raw_output)
2539          {
2540             if (state.common_settings.verbose)
2541                fprintf(stderr, "Connecting camera preview port to splitter input port\n");
2542 
2543             // Connect camera to splitter
2544             status = connect_ports(camera_preview_port, splitter_input_port, &state.splitter_connection);
2545 
2546             if (status != MMAL_SUCCESS)
2547             {
2548                state.splitter_connection = NULL;
2549                vcos_log_error("%s: Failed to connect camera preview port to splitter input", __func__);
2550                goto error;
2551             }
2552          }
2553          else
2554          {
2555             status = MMAL_SUCCESS;
2556          }
2557       }
2558 
2559       if (status == MMAL_SUCCESS)
2560       {
2561          if (state.common_settings.verbose)
2562             fprintf(stderr, "Connecting camera video port to encoder input port\n");
2563 
2564          // Now connect the camera to the encoder
2565          status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection);
2566 
2567          if (status != MMAL_SUCCESS)
2568          {
2569             state.encoder_connection = NULL;
2570             vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
2571             goto error;
2572          }
2573       }
2574 
2575       if (status == MMAL_SUCCESS)
2576       {
2577          // Set up our userdata - this is passed though to the callback where we need the information.
2578          state.callback_data.pstate = &state;
2579          state.callback_data.abort = 0;
2580 
2581          if (state.raw_output)
2582          {
2583             splitter_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data;
2584 
2585             if (state.common_settings.verbose)
2586                fprintf(stderr, "Enabling splitter output port\n");
2587 
2588             // Enable the splitter output port and tell it its callback function
2589             status = mmal_port_enable(splitter_output_port, splitter_buffer_callback);
2590 
2591             if (status != MMAL_SUCCESS)
2592             {
2593                vcos_log_error("%s: Failed to setup splitter output port", __func__);
2594                goto error;
2595             }
2596          }
2597 
2598          state.callback_data.file_handle = NULL;
2599 
2600          if (state.common_settings.filename)
2601          {
2602             if (state.common_settings.filename[0] == '-')
2603             {
2604                state.callback_data.file_handle = stdout;
2605             }
2606             else
2607             {
2608                state.callback_data.file_handle = open_filename(&state, state.common_settings.filename);
2609             }
2610 
2611             if (!state.callback_data.file_handle)
2612             {
2613                // Notify user, carry on but discarding encoded output buffers
2614                vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.common_settings.filename);
2615             }
2616          }
2617 
2618          state.callback_data.imv_file_handle = NULL;
2619 
2620          if (state.imv_filename)
2621          {
2622             if (state.imv_filename[0] == '-')
2623             {
2624                state.callback_data.imv_file_handle = stdout;
2625             }
2626             else
2627             {
2628                state.callback_data.imv_file_handle = open_filename(&state, state.imv_filename);
2629             }
2630 
2631             if (!state.callback_data.imv_file_handle)
2632             {
2633                // Notify user, carry on but discarding encoded output buffers
2634                fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.imv_filename);
2635                state.inlineMotionVectors=0;
2636             }
2637          }
2638 
2639          state.callback_data.pts_file_handle = NULL;
2640 
2641          if (state.pts_filename)
2642          {
2643             if (state.pts_filename[0] == '-')
2644             {
2645                state.callback_data.pts_file_handle = stdout;
2646             }
2647             else
2648             {
2649                state.callback_data.pts_file_handle = open_filename(&state, state.pts_filename);
2650                if (state.callback_data.pts_file_handle) /* save header for mkvmerge */
2651                   fprintf(state.callback_data.pts_file_handle, "# timecode format v2\n");
2652             }
2653 
2654             if (!state.callback_data.pts_file_handle)
2655             {
2656                // Notify user, carry on but discarding encoded output buffers
2657                fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.pts_filename);
2658                state.save_pts=0;
2659             }
2660          }
2661 
2662          state.callback_data.raw_file_handle = NULL;
2663 
2664          if (state.raw_filename)
2665          {
2666             if (state.raw_filename[0] == '-')
2667             {
2668                state.callback_data.raw_file_handle = stdout;
2669             }
2670             else
2671             {
2672                state.callback_data.raw_file_handle = open_filename(&state, state.raw_filename);
2673             }
2674 
2675             if (!state.callback_data.raw_file_handle)
2676             {
2677                // Notify user, carry on but discarding encoded output buffers
2678                fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n", state.raw_filename);
2679                state.raw_output = 0;
2680             }
2681          }
2682 
2683          if(state.bCircularBuffer)
2684          {
2685             if(state.bitrate == 0)
2686             {
2687                vcos_log_error("%s: Error circular buffer requires constant bitrate and small intra period\n", __func__);
2688                goto error;
2689             }
2690             else if(state.timeout == 0)
2691             {
2692                vcos_log_error("%s: Error, circular buffer size is based on timeout must be greater than zero\n", __func__);
2693                goto error;
2694             }
2695             else if(state.waitMethod != WAIT_METHOD_KEYPRESS && state.waitMethod != WAIT_METHOD_SIGNAL)
2696             {
2697                vcos_log_error("%s: Error, Circular buffer mode requires either keypress (-k) or signal (-s) triggering\n", __func__);
2698                goto error;
2699             }
2700             else if(!state.callback_data.file_handle)
2701             {
2702                vcos_log_error("%s: Error require output file (or stdout) for Circular buffer mode\n", __func__);
2703                goto error;
2704             }
2705             else
2706             {
2707                int count = state.bitrate * (state.timeout / 1000) / 8;
2708 
2709                state.callback_data.cb_buff = (char *) malloc(count);
2710                if(state.callback_data.cb_buff == NULL)
2711                {
2712                   vcos_log_error("%s: Unable to allocate circular buffer for %d seconds at %.1f Mbits\n", __func__, state.timeout / 1000, (double)state.bitrate/1000000.0);
2713                   goto error;
2714                }
2715                else
2716                {
2717                   state.callback_data.cb_len = count;
2718                   state.callback_data.cb_wptr = 0;
2719                   state.callback_data.cb_wrap = 0;
2720                   state.callback_data.cb_data = 0;
2721                   state.callback_data.iframe_buff_wpos = 0;
2722                   state.callback_data.iframe_buff_rpos = 0;
2723                   state.callback_data.header_wptr = 0;
2724                }
2725             }
2726          }
2727 
2728          // Set up our userdata - this is passed though to the callback where we need the information.
2729          encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data;
2730 
2731          if (state.common_settings.verbose)
2732             fprintf(stderr, "Enabling encoder output port\n");
2733 
2734          // Enable the encoder output port and tell it its callback function
2735          status = mmal_port_enable(encoder_output_port, encoder_buffer_callback);
2736 
2737          if (status != MMAL_SUCCESS)
2738          {
2739             vcos_log_error("Failed to setup encoder output");
2740             goto error;
2741          }
2742 
2743          if (state.demoMode)
2744          {
2745             // Run for the user specific time..
2746             int num_iterations = state.timeout / state.demoInterval;
2747             int i;
2748 
2749             if (state.common_settings.verbose)
2750                fprintf(stderr, "Running in demo mode\n");
2751 
2752             for (i=0; state.timeout == 0 || i<num_iterations; i++)
2753             {
2754                raspicamcontrol_cycle_test(state.camera_component);
2755                vcos_sleep(state.demoInterval);
2756             }
2757          }
2758          else
2759          {
2760             // Only encode stuff if we have a filename and it opened
2761             // Note we use the copy in the callback, as the call back MIGHT change the file handle
2762             if (state.callback_data.file_handle || state.callback_data.raw_file_handle)
2763             {
2764                int running = 1;
2765 
2766                // Send all the buffers to the encoder output port
2767                if (state.callback_data.file_handle)
2768                {
2769                   int num = mmal_queue_length(state.encoder_pool->queue);
2770                   int q;
2771                   for (q=0; q<num; q++)
2772                   {
2773                      MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.encoder_pool->queue);
2774 
2775                      if (!buffer)
2776                         vcos_log_error("Unable to get a required buffer %d from pool queue", q);
2777 
2778                      if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS)
2779                         vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
2780                   }
2781                }
2782 
2783                // Send all the buffers to the splitter output port
2784                if (state.callback_data.raw_file_handle)
2785                {
2786                   int num = mmal_queue_length(state.splitter_pool->queue);
2787                   int q;
2788                   for (q = 0; q < num; q++)
2789                   {
2790                      MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.splitter_pool->queue);
2791 
2792                      if (!buffer)
2793                         vcos_log_error("Unable to get a required buffer %d from pool queue", q);
2794 
2795                      if (mmal_port_send_buffer(splitter_output_port, buffer)!= MMAL_SUCCESS)
2796                         vcos_log_error("Unable to send a buffer to splitter output port (%d)", q);
2797                   }
2798                }
2799 
2800                int initialCapturing=state.bCapturing;
2801                while (running)
2802                {
2803                   // Change state
2804 
2805                   state.bCapturing = !state.bCapturing;
2806 
2807                   if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, state.bCapturing) != MMAL_SUCCESS)
2808                   {
2809                      // How to handle?
2810                   }
2811 
2812                   // In circular buffer mode, exit and save the buffer (make sure we do this after having paused the capture
2813                   if(state.bCircularBuffer && !state.bCapturing)
2814                   {
2815                      break;
2816                   }
2817 
2818                   if (state.common_settings.verbose)
2819                   {
2820                      if (state.bCapturing)
2821                         fprintf(stderr, "Starting video capture\n");
2822                      else
2823                         fprintf(stderr, "Pausing video capture\n");
2824                   }
2825 
2826                   if(state.splitWait)
2827                   {
2828                      if(state.bCapturing)
2829                      {
2830                         if (mmal_port_parameter_set_boolean(encoder_output_port, MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, 1) != MMAL_SUCCESS)
2831                         {
2832                            vcos_log_error("failed to request I-FRAME");
2833                         }
2834                      }
2835                      else
2836                      {
2837                         if(!initialCapturing)
2838                            state.splitNow=1;
2839                      }
2840                      initialCapturing=0;
2841                   }
2842                   running = wait_for_next_change(&state);
2843                }
2844 
2845                if (state.common_settings.verbose)
2846                   fprintf(stderr, "Finished capture\n");
2847             }
2848             else
2849             {
2850                if (state.timeout)
2851                   vcos_sleep(state.timeout);
2852                else
2853                {
2854                   // timeout = 0 so run forever
2855                   while(1)
2856                      vcos_sleep(ABORT_INTERVAL);
2857                }
2858             }
2859          }
2860       }
2861       else
2862       {
2863          mmal_status_to_int(status);
2864          vcos_log_error("%s: Failed to connect camera to preview", __func__);
2865       }
2866 
2867       if(state.bCircularBuffer)
2868       {
2869          int copy_from_end, copy_from_start;
2870 
2871          copy_from_end = state.callback_data.cb_len - state.callback_data.iframe_buff[state.callback_data.iframe_buff_rpos];
2872          copy_from_start = state.callback_data.cb_len - copy_from_end;
2873          copy_from_start = state.callback_data.cb_wptr < copy_from_start ? state.callback_data.cb_wptr : copy_from_start;
2874          if(!state.callback_data.cb_wrap)
2875          {
2876             copy_from_start = state.callback_data.cb_wptr;
2877             copy_from_end = 0;
2878          }
2879 
2880          fwrite(state.callback_data.header_bytes, 1, state.callback_data.header_wptr, state.callback_data.file_handle);
2881          // Save circular buffer
2882          fwrite(state.callback_data.cb_buff + state.callback_data.iframe_buff[state.callback_data.iframe_buff_rpos], 1, copy_from_end, state.callback_data.file_handle);
2883          fwrite(state.callback_data.cb_buff, 1, copy_from_start, state.callback_data.file_handle);
2884          if(state.callback_data.flush_buffers) fflush(state.callback_data.file_handle);
2885       }
2886 
2887 error:
2888 
2889       mmal_status_to_int(status);
2890 
2891       if (state.common_settings.verbose)
2892          fprintf(stderr, "Closing down\n");
2893 
2894       // Disable all our ports that are not handled by connections
2895       check_disable_port(camera_still_port);
2896       check_disable_port(encoder_output_port);
2897       check_disable_port(splitter_output_port);
2898 
2899       if (state.preview_parameters.wantPreview && state.preview_connection)
2900          mmal_connection_destroy(state.preview_connection);
2901 
2902       if (state.encoder_connection)
2903          mmal_connection_destroy(state.encoder_connection);
2904 
2905       if (state.splitter_connection)
2906          mmal_connection_destroy(state.splitter_connection);
2907 
2908       // Can now close our file. Note disabling ports may flush buffers which causes
2909       // problems if we have already closed the file!
2910       if (state.callback_data.file_handle && state.callback_data.file_handle != stdout)
2911          fclose(state.callback_data.file_handle);
2912       if (state.callback_data.imv_file_handle && state.callback_data.imv_file_handle != stdout)
2913          fclose(state.callback_data.imv_file_handle);
2914       if (state.callback_data.pts_file_handle && state.callback_data.pts_file_handle != stdout)
2915          fclose(state.callback_data.pts_file_handle);
2916       if (state.callback_data.raw_file_handle && state.callback_data.raw_file_handle != stdout)
2917          fclose(state.callback_data.raw_file_handle);
2918 
2919       /* Disable components */
2920       if (state.encoder_component)
2921          mmal_component_disable(state.encoder_component);
2922 
2923       if (state.preview_parameters.preview_component)
2924          mmal_component_disable(state.preview_parameters.preview_component);
2925 
2926       if (state.splitter_component)
2927          mmal_component_disable(state.splitter_component);
2928 
2929       if (state.camera_component)
2930          mmal_component_disable(state.camera_component);
2931 
2932       destroy_encoder_component(&state);
2933       raspipreview_destroy(&state.preview_parameters);
2934       destroy_splitter_component(&state);
2935       destroy_camera_component(&state);
2936 
2937       if (state.common_settings.verbose)
2938          fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
2939    }
2940 
2941    if (status != MMAL_SUCCESS)
2942       raspicamcontrol_check_configuration(128);
2943 
2944    if (state.common_settings.gps)
2945       raspi_gps_shutdown(state.common_settings.verbose);
2946 
2947    return exit_code;
2948 }
2949