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, ¶m.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, ¶m.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, ¶m.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, ¶m.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, ¶m.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, ¶m2.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, ¶m3.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, ¶m.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, ¶m.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, ¶m.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