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