1 /*
2 Copyright (c) 2018, Raspberry Pi (Trading) Ltd.
3 Copyright (c) 2014, DSP Group Ltd.
4 Copyright (c) 2014, James Hughes
5 Copyright (c) 2013, Broadcom Europe Ltd.
6
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 * Neither the name of the copyright holder nor the
17 names of its contributors may be used to endorse or promote products
18 derived from this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
24 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /**
33 * \file RaspiVidYUV.c
34 * Command line program to capture a camera video stream and save file
35 * as uncompressed YUV420 data
36 * Also optionally display a preview/viewfinder of current camera input.
37 *
38 * Description
39 *
40 * 2 components are created; camera and preview.
41 * Camera component has three ports, preview, video and stills.
42 * Preview is connected using standard mmal connections, the video output
43 * is written straight to the file in YUV 420 format via the requisite buffer
44 * callback. Still port is not used
45 *
46 * We use the RaspiCamControl code to handle the specific camera settings.
47 * We use the RaspiPreview code to handle the generic preview
48 */
49
50 // We use some GNU extensions (basename)
51 #ifndef _GNU_SOURCE
52 #define _GNU_SOURCE
53 #endif
54
55 #include <stdbool.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <memory.h>
60 #include <sysexits.h>
61
62 #include <sys/types.h>
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
66
67 #include "bcm_host.h"
68 #include "interface/vcos/vcos.h"
69
70 #include "interface/mmal/mmal.h"
71 #include "interface/mmal/mmal_logging.h"
72 #include "interface/mmal/mmal_buffer.h"
73 #include "interface/mmal/util/mmal_util.h"
74 #include "interface/mmal/util/mmal_util_params.h"
75 #include "interface/mmal/util/mmal_default_components.h"
76 #include "interface/mmal/util/mmal_connection.h"
77
78 #include "RaspiCommonSettings.h"
79 #include "RaspiCamControl.h"
80 #include "RaspiPreview.h"
81 #include "RaspiCLI.h"
82 #include "RaspiHelpers.h"
83 #include "RaspiGPS.h"
84
85 #include <semaphore.h>
86
87 // Standard port setting for the camera component
88 #define MMAL_CAMERA_PREVIEW_PORT 0
89 #define MMAL_CAMERA_VIDEO_PORT 1
90 #define MMAL_CAMERA_CAPTURE_PORT 2
91
92 // Video format information
93 // 0 implies variable
94 #define VIDEO_FRAME_RATE_NUM 30
95 #define VIDEO_FRAME_RATE_DEN 1
96
97 /// Video render needs at least 2 buffers.
98 #define VIDEO_OUTPUT_BUFFERS_NUM 3
99
100 /// Interval at which we check for an failure abort during capture
101 const int ABORT_INTERVAL = 100; // ms
102
103 /// Capture/Pause switch method
104 enum
105 {
106 WAIT_METHOD_NONE, /// Simply capture for time specified
107 WAIT_METHOD_TIMED, /// Cycle between capture and pause for times specified
108 WAIT_METHOD_KEYPRESS, /// Switch between capture and pause on keypress
109 WAIT_METHOD_SIGNAL, /// Switch between capture and pause on signal
110 WAIT_METHOD_FOREVER /// Run/record forever
111 };
112
113 // Forward
114 typedef struct RASPIVIDYUV_STATE_S RASPIVIDYUV_STATE;
115
116 /** Struct used to pass information in camera video port userdata to callback
117 */
118 typedef struct
119 {
120 FILE *file_handle; /// File handle to write buffer data to.
121 RASPIVIDYUV_STATE *pstate; /// pointer to our state in case required in callback
122 int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture
123 FILE *pts_file_handle; /// File timestamps
124 int frame;
125 int64_t starttime;
126 int64_t lasttime;
127 } PORT_USERDATA;
128
129 /** Structure containing all state information for the current run
130 */
131 struct RASPIVIDYUV_STATE_S
132 {
133 RASPICOMMONSETTINGS_PARAMETERS common_settings;
134 int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
135 int framerate; /// Requested frame rate (fps)
136 int demoMode; /// Run app in demo mode
137 int demoInterval; /// Interval between camera settings changes
138 int waitMethod; /// Method for switching between pause and capture
139
140 int onTime; /// In timed cycle mode, the amount of time the capture is on per cycle
141 int offTime; /// In timed cycle mode, the amount of time the capture is off per cycle
142
143 int onlyLuma; /// Only output the luma / Y plane of the YUV data
144 int useRGB; /// Output RGB data rather than YUV
145
146 RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
147 RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
148
149 MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
150 MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
151
152 MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera video port
153
154 PORT_USERDATA callback_data; /// Used to move data to the camera callback
155
156 int bCapturing; /// State of capture/pause
157 int frame;
158 char *pts_filename;
159 int save_pts;
160 int64_t starttime;
161 int64_t lasttime;
162
163 bool netListen;
164 };
165
166 static XREF_T initial_map[] =
167 {
168 {"record", 0},
169 {"pause", 1},
170 };
171
172 static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]);
173
174 /// Command ID's and Structure defining our command line options
175 enum
176 {
177 CommandTimeout,
178 CommandDemoMode,
179 CommandFramerate,
180 CommandTimed,
181 CommandSignal,
182 CommandKeypress,
183 CommandInitialState,
184 CommandOnlyLuma,
185 CommandUseRGB,
186 CommandSavePTS,
187 CommandNetListen
188 };
189
190 static COMMAND_LIST cmdline_commands[] =
191 {
192 { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 },
193 { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1},
194 { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1},
195 { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0},
196 { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0},
197 { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0},
198 { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1},
199 { CommandOnlyLuma, "-luma", "y", "Only output the luma / Y of the YUV data'", 0},
200 { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0},
201 { CommandSavePTS, "-save-pts", "pts","Save Timestamps to file", 1 },
202 { CommandNetListen, "-listen", "l", "Listen on a TCP socket", 0},
203 };
204
205 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
206
207 static struct
208 {
209 char *description;
210 int nextWaitMethod;
211 } wait_method_description[] =
212 {
213 {"Simple capture", WAIT_METHOD_NONE},
214 {"Capture forever", WAIT_METHOD_FOREVER},
215 {"Cycle on time", WAIT_METHOD_TIMED},
216 {"Cycle on keypress", WAIT_METHOD_KEYPRESS},
217 {"Cycle on signal", WAIT_METHOD_SIGNAL},
218 };
219
220 static int wait_method_description_size = sizeof(wait_method_description) / sizeof(wait_method_description[0]);
221
222 /**
223 * Assign a default set of parameters to the state passed in
224 *
225 * @param state Pointer to state structure to assign defaults to
226 */
default_status(RASPIVIDYUV_STATE * state)227 static void default_status(RASPIVIDYUV_STATE *state)
228 {
229 if (!state)
230 {
231 vcos_assert(0);
232 return;
233 }
234
235 // Default everything to zero
236 memset(state, 0, sizeof(RASPIVIDYUV_STATE));
237
238 raspicommonsettings_set_defaults(&state->common_settings);
239
240 // Now set anything non-zero
241 state->timeout = -1; // replaced with 5000ms later if unset
242 state->common_settings.width = 1920; // Default to 1080p
243 state->common_settings.height = 1080;
244 state->framerate = VIDEO_FRAME_RATE_NUM;
245 state->demoMode = 0;
246 state->demoInterval = 250; // ms
247 state->waitMethod = WAIT_METHOD_NONE;
248 state->onTime = 5000;
249 state->offTime = 5000;
250 state->bCapturing = 0;
251 state->onlyLuma = 0;
252
253 // Setup preview window defaults
254 raspipreview_set_defaults(&state->preview_parameters);
255
256 // Set up the camera_parameters to default
257 raspicamcontrol_set_defaults(&state->camera_parameters);
258 }
259
260 /**
261 * Dump image state parameters to stderr.
262 *
263 * @param state Pointer to state structure to assign defaults to
264 */
dump_status(RASPIVIDYUV_STATE * state)265 static void dump_status(RASPIVIDYUV_STATE *state)
266 {
267 int i, size, ystride, yheight;
268
269 if (!state)
270 {
271 vcos_assert(0);
272 return;
273 }
274
275 raspicommonsettings_dump_parameters(&state->common_settings);
276
277 fprintf(stderr, "framerate %d, time delay %d\n", state->framerate, state->timeout);
278
279 // Calculate the individual image size
280 // Y stride rounded to multiple of 32. U&V stride is Y stride/2 (ie multiple of 16).
281 // Y height is padded to a 16. U/V height is Y height/2 (ie multiple of 8).
282
283 // Y plane
284 ystride = ((state->common_settings.width + 31) & ~31);
285 yheight = ((state->common_settings.height + 15) & ~15);
286
287 size = ystride * yheight;
288
289 // U and V plane
290 size += 2 * ystride/2 * yheight/2;
291
292 fprintf(stderr, "Sub-image size %d bytes in total.\n Y pitch %d, Y height %d, UV pitch %d, UV Height %d\n", size, ystride, yheight, ystride/2,yheight/2);
293
294 fprintf(stderr, "Wait method : ");
295 for (i=0; i<wait_method_description_size; i++)
296 {
297 if (state->waitMethod == wait_method_description[i].nextWaitMethod)
298 fprintf(stderr, "%s", wait_method_description[i].description);
299 }
300 fprintf(stderr, "\nInitial state '%s'\n", raspicli_unmap_xref(state->bCapturing, initial_map, initial_map_size));
301 fprintf(stderr, "\n");
302
303 raspipreview_dump_parameters(&state->preview_parameters);
304 raspicamcontrol_dump_parameters(&state->camera_parameters);
305 }
306
307 /**
308 * Display usage information for the application to stdout
309 *
310 * @param app_name String to display as the application name
311 */
application_help_message(char * app_name)312 static void application_help_message(char *app_name)
313 {
314 fprintf(stdout, "Display camera output to display, and optionally saves an uncompressed YUV420 or RGB file \n\n");
315 fprintf(stdout, "NOTE: High resolutions and/or frame rates may exceed the bandwidth of the system due\n");
316 fprintf(stdout, "to the large amounts of data being moved to the SD card. This will result in undefined\n");
317 fprintf(stdout, "results in the subsequent file.\n");
318 fprintf(stdout, "The single raw file produced contains all the images. Each image in the files will be of size\n");
319 fprintf(stdout, "width*height*1.5 for YUV or width*height*3 for RGB, unless width and/or height are not divisible by 16.");
320 fprintf(stdout, "Use the image size displayed during the run (in verbose mode) for an accurate value\n");
321
322 fprintf(stdout, "The Linux split command can be used to split up the file to individual frames\n");
323
324 fprintf(stdout, "\nusage: %s [options]\n\n", app_name);
325
326 fprintf(stdout, "Image parameter commands\n\n");
327
328 raspicli_display_help(cmdline_commands, cmdline_commands_size);
329
330 fprintf(stdout, "\n");
331
332 return;
333 }
334
335 /**
336 * Parse the incoming command line and put resulting parameters in to the state
337 *
338 * @param argc Number of arguments in command line
339 * @param argv Array of pointers to strings from command line
340 * @param state Pointer to state structure to assign any discovered parameters to
341 * @return Non-0 if failed for some reason, 0 otherwise
342 */
parse_cmdline(int argc,const char ** argv,RASPIVIDYUV_STATE * state)343 static int parse_cmdline(int argc, const char **argv, RASPIVIDYUV_STATE *state)
344 {
345 // Parse the command line arguments.
346 // We are looking for --<something> or -<abbreviation of something>
347
348 int valid = 1;
349 int i;
350
351 for (i = 1; i < argc && valid; i++)
352 {
353 int command_id, num_parameters;
354
355 if (!argv[i])
356 continue;
357
358 if (argv[i][0] != '-')
359 {
360 valid = 0;
361 continue;
362 }
363
364 // Assume parameter is valid until proven otherwise
365 valid = 1;
366
367 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
368
369 // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
370 if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
371 continue;
372
373 // We are now dealing with a command line option
374 switch (command_id)
375 {
376 case CommandTimeout: // Time to run viewfinder/capture
377 {
378 if (sscanf(argv[i + 1], "%d", &state->timeout) == 1)
379 {
380 // Ensure that if previously selected a waitMethod we don't overwrite it
381 if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE)
382 state->waitMethod = WAIT_METHOD_FOREVER;
383
384 i++;
385 }
386 else
387 valid = 0;
388 break;
389 }
390
391 case CommandDemoMode: // Run in demo mode - no capture
392 {
393 // Demo mode might have a timing parameter
394 // so check if a) we have another parameter, b) its not the start of the next option
395 if (i + 1 < argc && argv[i+1][0] != '-')
396 {
397 if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
398 {
399 // TODO : What limits do we need for timeout?
400 if (state->demoInterval == 0)
401 state->demoInterval = 250; // ms
402
403 state->demoMode = 1;
404 i++;
405 }
406 else
407 valid = 0;
408 }
409 else
410 {
411 state->demoMode = 1;
412 }
413
414 break;
415 }
416
417 case CommandFramerate: // fps to record
418 {
419 if (sscanf(argv[i + 1], "%u", &state->framerate) == 1)
420 {
421 // TODO : What limits do we need for fps 1 - 30 - 120??
422 i++;
423 }
424 else
425 valid = 0;
426 break;
427 }
428
429 case CommandTimed:
430 {
431 if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2)
432 {
433 i++;
434
435 if (state->onTime < 1000)
436 state->onTime = 1000;
437
438 if (state->offTime < 1000)
439 state->offTime = 1000;
440
441 state->waitMethod = WAIT_METHOD_TIMED;
442
443 if (state->timeout == -1)
444 state->timeout = 0;
445 }
446 else
447 valid = 0;
448 break;
449 }
450
451 case CommandKeypress:
452 state->waitMethod = WAIT_METHOD_KEYPRESS;
453
454 if (state->timeout == -1)
455 state->timeout = 0;
456
457 break;
458
459 case CommandSignal:
460 state->waitMethod = WAIT_METHOD_SIGNAL;
461 // Reenable the signal
462 signal(SIGUSR1, default_signal_handler);
463
464 if (state->timeout == -1)
465 state->timeout = 0;
466
467 break;
468
469 case CommandInitialState:
470 {
471 state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size);
472
473 if( state->bCapturing == -1)
474 state->bCapturing = 0;
475
476 i++;
477 break;
478 }
479
480 case CommandOnlyLuma:
481 if (state->useRGB)
482 {
483 fprintf(stderr, "--luma and --rgb are mutually exclusive\n");
484 valid = 0;
485 }
486 state->onlyLuma = 1;
487 break;
488
489 case CommandUseRGB: // display lots of data during run
490 if (state->onlyLuma)
491 {
492 fprintf(stderr, "--luma and --rgb are mutually exclusive\n");
493 valid = 0;
494 }
495 state->useRGB = 1;
496 break;
497
498 case CommandSavePTS: // output filename
499 {
500 state->save_pts = 1;
501 int len = strlen(argv[i + 1]);
502 if (len)
503 {
504 state->pts_filename = malloc(len + 1);
505 vcos_assert(state->pts_filename);
506 if (state->pts_filename)
507 strncpy(state->pts_filename, argv[i + 1], len+1);
508 i++;
509 }
510 else
511 valid = 0;
512 break;
513 }
514
515 case CommandNetListen:
516 {
517 state->netListen = true;
518
519 break;
520 }
521
522 default:
523 {
524 // Try parsing for any image specific parameters
525 // result indicates how many parameters were used up, 0,1,2
526 // but we adjust by -1 as we have used one already
527 const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
528 int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
529
530 // Still unused, try common settings
531 if (!parms_used)
532 parms_used = raspicommonsettings_parse_cmdline(&state->common_settings, &argv[i][1], second_arg, &application_help_message);
533
534 // Still unused, try preview options
535 if (!parms_used)
536 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
537
538 // If no parms were used, this must be a bad parameters
539 if (!parms_used)
540 valid = 0;
541 else
542 i += parms_used - 1;
543
544 break;
545 }
546 }
547 }
548
549 if (!valid)
550 {
551 fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
552 return 1;
553 }
554
555 return 0;
556 }
557
558 /**
559 * Open a file based on the settings in state
560 *
561 * @param state Pointer to state
562 */
open_filename(RASPIVIDYUV_STATE * pState,char * filename)563 static FILE *open_filename(RASPIVIDYUV_STATE *pState, char *filename)
564 {
565 FILE *new_handle = NULL;
566
567 if (filename)
568 {
569 bool bNetwork = false;
570 int sfd = -1, socktype;
571
572 if(!strncmp("tcp://", filename, 6))
573 {
574 bNetwork = true;
575 socktype = SOCK_STREAM;
576 }
577 else if(!strncmp("udp://", filename, 6))
578 {
579 if (pState->netListen)
580 {
581 fprintf(stderr, "No support for listening in UDP mode\n");
582 exit(131);
583 }
584 bNetwork = true;
585 socktype = SOCK_DGRAM;
586 }
587
588 if(bNetwork)
589 {
590 unsigned short port;
591 filename += 6;
592 char *colon;
593 if(NULL == (colon = strchr(filename, ':')))
594 {
595 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",
596 filename);
597 exit(132);
598 }
599 if(1 != sscanf(colon + 1, "%hu", &port))
600 {
601 fprintf(stderr,
602 "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",
603 filename);
604 exit(133);
605 }
606 char chTmp = *colon;
607 *colon = 0;
608
609 struct sockaddr_in saddr= {};
610 saddr.sin_family = AF_INET;
611 saddr.sin_port = htons(port);
612 if(0 == inet_aton(filename, &saddr.sin_addr))
613 {
614 fprintf(stderr, "inet_aton failed. %s is not a valid IPv4 address\n",
615 filename);
616 exit(134);
617 }
618 *colon = chTmp;
619
620 if (pState->netListen)
621 {
622 int sockListen = socket(AF_INET, SOCK_STREAM, 0);
623 if (sockListen >= 0)
624 {
625 int iTmp = 1;
626 setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &iTmp, sizeof(int));//no error handling, just go on
627 if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0)
628 {
629 while ((-1 == (iTmp = listen(sockListen, 0))) && (EINTR == errno))
630 ;
631 if (-1 != iTmp)
632 {
633 fprintf(stderr, "Waiting for a TCP connection on %s:%"SCNu16"...",
634 inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
635 struct sockaddr_in cli_addr;
636 socklen_t clilen = sizeof(cli_addr);
637 while ((-1 == (sfd = accept(sockListen, (struct sockaddr *) &cli_addr, &clilen))) && (EINTR == errno))
638 ;
639 if (sfd >= 0)
640 fprintf(stderr, "Client connected from %s:%"SCNu16"\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
641 else
642 fprintf(stderr, "Error on accept: %s\n", strerror(errno));
643 }
644 else//if (-1 != iTmp)
645 {
646 fprintf(stderr, "Error trying to listen on a socket: %s\n", strerror(errno));
647 }
648 }
649 else//if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0)
650 {
651 fprintf(stderr, "Error on binding socket: %s\n", strerror(errno));
652 }
653 }
654 else//if (sockListen >= 0)
655 {
656 fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
657 }
658
659 if (sockListen >= 0)//regardless success or error
660 close(sockListen);//do not listen on a given port anymore
661 }
662 else//if (pState->netListen)
663 {
664 if(0 <= (sfd = socket(AF_INET, socktype, 0)))
665 {
666 fprintf(stderr, "Connecting to %s:%hu...", inet_ntoa(saddr.sin_addr), port);
667
668 int iTmp = 1;
669 while ((-1 == (iTmp = connect(sfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)))) && (EINTR == errno))
670 ;
671 if (iTmp < 0)
672 fprintf(stderr, "error: %s\n", strerror(errno));
673 else
674 fprintf(stderr, "connected, sending video...\n");
675 }
676 else
677 fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
678 }
679
680 if (sfd >= 0)
681 new_handle = fdopen(sfd, "w");
682 }
683 else
684 {
685 new_handle = fopen(filename, "wb");
686 }
687 }
688
689 if (pState->common_settings.verbose)
690 {
691 if (new_handle)
692 fprintf(stderr, "Opening output file \"%s\"\n", filename);
693 else
694 fprintf(stderr, "Failed to open new file \"%s\"\n", filename);
695 }
696
697 return new_handle;
698 }
699
700 /**
701 * buffer header callback function for camera
702 *
703 * Callback will dump buffer data to internal buffer
704 *
705 * @param port Pointer to port from which callback originated
706 * @param buffer mmal buffer header pointer
707 */
camera_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)708 static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
709 {
710 MMAL_BUFFER_HEADER_T *new_buffer;
711 static int64_t last_second = -1;
712
713 // We pass our file handle and other stuff in via the userdata field.
714
715 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
716 RASPIVIDYUV_STATE *pstate = pData->pstate;
717
718 if (pData)
719 {
720 int bytes_written = 0;
721 int bytes_to_write = buffer->length;
722 int64_t current_time = get_microseconds64()/1000000;
723
724 if (pstate->onlyLuma)
725 bytes_to_write = vcos_min(buffer->length, port->format->es->video.width * port->format->es->video.height);
726
727 vcos_assert(pData->file_handle);
728
729 if (bytes_to_write)
730 {
731 mmal_buffer_header_mem_lock(buffer);
732 bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->file_handle);
733 mmal_buffer_header_mem_unlock(buffer);
734
735 if (bytes_written != bytes_to_write)
736 {
737 vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, bytes_to_write);
738 pData->abort = 1;
739 }
740 if (pData->pts_file_handle)
741 {
742 // Every buffer should be a complete frame, so no need to worry about
743 // fragments or duplicated timestamps. We're also in RESET_STC mode, so
744 // the time on frame 0 should always be 0 anyway, but simply copy the
745 // code from raspivid.
746 // MMAL_TIME_UNKNOWN should never happen, but it'll corrupt the timestamps
747 // file if saved.
748 if(buffer->pts != MMAL_TIME_UNKNOWN)
749 {
750 int64_t pts;
751 if(pstate->frame==0)
752 pstate->starttime=buffer->pts;
753 pData->lasttime=buffer->pts;
754 pts = buffer->pts - pData->starttime;
755 fprintf(pData->pts_file_handle,"%lld.%03lld\n", pts/1000, pts%1000);
756 pData->frame++;
757 }
758 }
759 }
760
761 // See if the second count has changed and we need to update any annotation
762 if (current_time != last_second)
763 {
764 if ((pstate->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) && pstate->common_settings.gps)
765 {
766 char *text = raspi_gps_location_string();
767 raspicamcontrol_set_annotate(pstate->camera_component, pstate->camera_parameters.enable_annotate,
768 text,
769 pstate->camera_parameters.annotate_text_size,
770 pstate->camera_parameters.annotate_text_colour,
771 pstate->camera_parameters.annotate_bg_colour,
772 pstate->camera_parameters.annotate_justify,
773 pstate->camera_parameters.annotate_x,
774 pstate->camera_parameters.annotate_y
775 );
776 free(text);
777 }
778 else
779 raspicamcontrol_set_annotate(pstate->camera_component, pstate->camera_parameters.enable_annotate,
780 pstate->camera_parameters.annotate_string,
781 pstate->camera_parameters.annotate_text_size,
782 pstate->camera_parameters.annotate_text_colour,
783 pstate->camera_parameters.annotate_bg_colour,
784 pstate->camera_parameters.annotate_justify,
785 pstate->camera_parameters.annotate_x,
786 pstate->camera_parameters.annotate_y
787 );
788 last_second = current_time;
789 }
790
791 }
792 else
793 {
794 vcos_log_error("Received a camera buffer callback with no state");
795 }
796
797 // release buffer back to the pool
798 mmal_buffer_header_release(buffer);
799
800 // and send one back to the port (if still open)
801 if (port->is_enabled)
802 {
803 MMAL_STATUS_T status;
804
805 new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue);
806
807 if (new_buffer)
808 status = mmal_port_send_buffer(port, new_buffer);
809
810 if (!new_buffer || status != MMAL_SUCCESS)
811 vcos_log_error("Unable to return a buffer to the camera port");
812 }
813 }
814
815 /**
816 * Create the camera component, set up its ports
817 *
818 * @param state Pointer to state control struct
819 *
820 * @return MMAL_SUCCESS if all OK, something else otherwise
821 *
822 */
create_camera_component(RASPIVIDYUV_STATE * state)823 static MMAL_STATUS_T create_camera_component(RASPIVIDYUV_STATE *state)
824 {
825 MMAL_COMPONENT_T *camera = 0;
826 MMAL_ES_FORMAT_T *format;
827 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
828 MMAL_STATUS_T status;
829 MMAL_POOL_T *pool;
830
831 /* Create the component */
832 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
833
834 if (status != MMAL_SUCCESS)
835 {
836 vcos_log_error("Failed to create camera component");
837 goto error;
838 }
839
840 MMAL_PARAMETER_INT32_T camera_num =
841 {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->common_settings.cameraNum};
842
843 status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
844
845 if (status != MMAL_SUCCESS)
846 {
847 vcos_log_error("Could not select camera : error %d", status);
848 goto error;
849 }
850
851 if (!camera->output_num)
852 {
853 status = MMAL_ENOSYS;
854 vcos_log_error("Camera doesn't have output ports");
855 goto error;
856 }
857
858 status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->common_settings.sensor_mode);
859
860 if (status != MMAL_SUCCESS)
861 {
862 vcos_log_error("Could not set sensor mode : error %d", status);
863 goto error;
864 }
865
866 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
867 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
868 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
869
870 // Enable the camera, and tell it its control callback function
871 status = mmal_port_enable(camera->control, default_camera_control_callback);
872
873 if (status != MMAL_SUCCESS)
874 {
875 vcos_log_error("Unable to enable control port : error %d", status);
876 goto error;
877 }
878
879 // set up the camera configuration
880 {
881 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
882 {
883 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
884 .max_stills_w = state->common_settings.width,
885 .max_stills_h = state->common_settings.height,
886 .stills_yuv422 = 0,
887 .one_shot_stills = 0,
888 .max_preview_video_w = state->common_settings.width,
889 .max_preview_video_h = state->common_settings.height,
890 .num_preview_video_frames = 3,
891 .stills_capture_circular_buffer_height = 0,
892 .fast_preview_resume = 0,
893 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
894 };
895 mmal_port_parameter_set(camera->control, &cam_config.hdr);
896 }
897
898 // Now set up the port formats
899
900 // Set the encode format on the Preview port
901 // HW limitations mean we need the preview to be the same size as the required recorded output
902
903 format = preview_port->format;
904
905 if(state->camera_parameters.shutter_speed > 6000000)
906 {
907 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
908 { 50, 1000 }, {166, 1000}
909 };
910 mmal_port_parameter_set(preview_port, &fps_range.hdr);
911 }
912 else if(state->camera_parameters.shutter_speed > 1000000)
913 {
914 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
915 { 166, 1000 }, {999, 1000}
916 };
917 mmal_port_parameter_set(preview_port, &fps_range.hdr);
918 }
919
920 //enable dynamic framerate if necessary
921 if (state->camera_parameters.shutter_speed)
922 {
923 if (state->framerate > 1000000./state->camera_parameters.shutter_speed)
924 {
925 state->framerate=0;
926 if (state->common_settings.verbose)
927 fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n");
928 }
929 }
930
931 format->encoding = MMAL_ENCODING_OPAQUE;
932 format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
933 format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
934 format->es->video.crop.x = 0;
935 format->es->video.crop.y = 0;
936 format->es->video.crop.width = state->common_settings.width;
937 format->es->video.crop.height = state->common_settings.height;
938 format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM;
939 format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN;
940
941 status = mmal_port_format_commit(preview_port);
942
943 if (status != MMAL_SUCCESS)
944 {
945 vcos_log_error("camera viewfinder format couldn't be set");
946 goto error;
947 }
948
949 // Set the encode format on the video port
950
951 format = video_port->format;
952
953 if(state->camera_parameters.shutter_speed > 6000000)
954 {
955 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
956 { 50, 1000 }, {166, 1000}
957 };
958 mmal_port_parameter_set(video_port, &fps_range.hdr);
959 }
960 else if(state->camera_parameters.shutter_speed > 1000000)
961 {
962 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
963 { 167, 1000 }, {999, 1000}
964 };
965 mmal_port_parameter_set(video_port, &fps_range.hdr);
966 }
967
968 if (state->useRGB)
969 {
970 format->encoding = mmal_util_rgb_order_fixed(still_port) ? MMAL_ENCODING_RGB24 : MMAL_ENCODING_BGR24;
971 format->encoding_variant = 0; //Irrelevant when not in opaque mode
972 }
973 else
974 {
975 format->encoding = MMAL_ENCODING_I420;
976 format->encoding_variant = MMAL_ENCODING_I420;
977 }
978
979 format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
980 format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
981 format->es->video.crop.x = 0;
982 format->es->video.crop.y = 0;
983 format->es->video.crop.width = state->common_settings.width;
984 format->es->video.crop.height = state->common_settings.height;
985 format->es->video.frame_rate.num = state->framerate;
986 format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN;
987
988 status = mmal_port_format_commit(video_port);
989
990 if (status != MMAL_SUCCESS)
991 {
992 vcos_log_error("camera video format couldn't be set");
993 goto error;
994 }
995
996 // Ensure there are enough buffers to avoid dropping frames
997 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
998 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
999
1000 status = mmal_port_parameter_set_boolean(video_port, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
1001 if (status != MMAL_SUCCESS)
1002 {
1003 vcos_log_error("Failed to select zero copy");
1004 goto error;
1005 }
1006
1007 // Set the encode format on the still port
1008
1009 format = still_port->format;
1010
1011 format->encoding = MMAL_ENCODING_OPAQUE;
1012 format->encoding_variant = MMAL_ENCODING_I420;
1013
1014 format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
1015 format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
1016 format->es->video.crop.x = 0;
1017 format->es->video.crop.y = 0;
1018 format->es->video.crop.width = state->common_settings.width;
1019 format->es->video.crop.height = state->common_settings.height;
1020 format->es->video.frame_rate.num = 0;
1021 format->es->video.frame_rate.den = 1;
1022
1023 status = mmal_port_format_commit(still_port);
1024
1025 if (status != MMAL_SUCCESS)
1026 {
1027 vcos_log_error("camera still format couldn't be set");
1028 goto error;
1029 }
1030
1031 /* Ensure there are enough buffers to avoid dropping frames */
1032 if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
1033 still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
1034
1035 /* Enable component */
1036 status = mmal_component_enable(camera);
1037
1038 if (status != MMAL_SUCCESS)
1039 {
1040 vcos_log_error("camera component couldn't be enabled");
1041 goto error;
1042 }
1043
1044 raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
1045
1046 /* Create pool of buffer headers for the output port to consume */
1047 pool = mmal_port_pool_create(video_port, video_port->buffer_num, video_port->buffer_size);
1048
1049 if (!pool)
1050 {
1051 vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name);
1052 }
1053
1054 state->camera_pool = pool;
1055 state->camera_component = camera;
1056
1057 if (state->common_settings.verbose)
1058 fprintf(stderr, "Camera component done\n");
1059
1060 return status;
1061
1062 error:
1063
1064 if (camera)
1065 mmal_component_destroy(camera);
1066
1067 return status;
1068 }
1069
1070 /**
1071 * Destroy the camera component
1072 *
1073 * @param state Pointer to state control struct
1074 *
1075 */
destroy_camera_component(RASPIVIDYUV_STATE * state)1076 static void destroy_camera_component(RASPIVIDYUV_STATE *state)
1077 {
1078 if (state->camera_component)
1079 {
1080 mmal_component_destroy(state->camera_component);
1081 state->camera_component = NULL;
1082 }
1083 }
1084
1085 /**
1086 * Pause for specified time, but return early if detect an abort request
1087 *
1088 * @param state Pointer to state control struct
1089 * @param pause Time in ms to pause
1090 * @param callback Struct contain an abort flag tested for early termination
1091 *
1092 */
pause_and_test_abort(RASPIVIDYUV_STATE * state,int pause)1093 static int pause_and_test_abort(RASPIVIDYUV_STATE *state, int pause)
1094 {
1095 int wait;
1096
1097 if (!pause)
1098 return 0;
1099
1100 // Going to check every ABORT_INTERVAL milliseconds
1101 for (wait = 0; wait < pause; wait+= ABORT_INTERVAL)
1102 {
1103 vcos_sleep(ABORT_INTERVAL);
1104 if (state->callback_data.abort)
1105 return 1;
1106 }
1107
1108 return 0;
1109 }
1110
1111 /**
1112 * Function to wait in various ways (depending on settings)
1113 *
1114 * @param state Pointer to the state data
1115 *
1116 * @return !0 if to continue, 0 if reached end of run
1117 */
wait_for_next_change(RASPIVIDYUV_STATE * state)1118 static int wait_for_next_change(RASPIVIDYUV_STATE *state)
1119 {
1120 int keep_running = 1;
1121 static int64_t complete_time = -1;
1122
1123 // Have we actually exceeded our timeout?
1124 int64_t current_time = get_microseconds64()/1000;
1125
1126 if (complete_time == -1)
1127 complete_time = current_time + state->timeout;
1128
1129 // if we have run out of time, flag we need to exit
1130 if (current_time >= complete_time && state->timeout != 0)
1131 keep_running = 0;
1132
1133 switch (state->waitMethod)
1134 {
1135 case WAIT_METHOD_NONE:
1136 (void)pause_and_test_abort(state, state->timeout);
1137 return 0;
1138
1139 case WAIT_METHOD_FOREVER:
1140 {
1141 // We never return from this. Expect a ctrl-c to exit or abort.
1142 while (!state->callback_data.abort)
1143 // Have a sleep so we don't hog the CPU.
1144 vcos_sleep(ABORT_INTERVAL);
1145
1146 return 0;
1147 }
1148
1149 case WAIT_METHOD_TIMED:
1150 {
1151 int abort;
1152
1153 if (state->bCapturing)
1154 abort = pause_and_test_abort(state, state->onTime);
1155 else
1156 abort = pause_and_test_abort(state, state->offTime);
1157
1158 if (abort)
1159 return 0;
1160 else
1161 return keep_running;
1162 }
1163
1164 case WAIT_METHOD_KEYPRESS:
1165 {
1166 char ch;
1167
1168 if (state->common_settings.verbose)
1169 fprintf(stderr, "Press Enter to %s, X then ENTER to exit\n", state->bCapturing ? "pause" : "capture");
1170
1171 ch = getchar();
1172 if (ch == 'x' || ch == 'X')
1173 return 0;
1174 else
1175 return keep_running;
1176 }
1177
1178 case WAIT_METHOD_SIGNAL:
1179 {
1180 // Need to wait for a SIGUSR1 signal
1181 sigset_t waitset;
1182 int sig;
1183 int result = 0;
1184
1185 sigemptyset( &waitset );
1186 sigaddset( &waitset, SIGUSR1 );
1187
1188 // We are multi threaded because we use mmal, so need to use the pthread
1189 // variant of procmask to block SIGUSR1 so we can wait on it.
1190 pthread_sigmask( SIG_BLOCK, &waitset, NULL );
1191
1192 if (state->common_settings.verbose)
1193 {
1194 fprintf(stderr, "Waiting for SIGUSR1 to %s\n", state->bCapturing ? "pause" : "capture");
1195 }
1196
1197 result = sigwait( &waitset, &sig );
1198
1199 if (state->common_settings.verbose && result != 0)
1200 fprintf(stderr, "Bad signal received - error %d\n", errno);
1201
1202 return keep_running;
1203 }
1204
1205 } // switch
1206
1207 return keep_running;
1208 }
1209
1210 /**
1211 * main
1212 */
main(int argc,const char ** argv)1213 int main(int argc, const char **argv)
1214 {
1215 // Our main data storage vessel..
1216 RASPIVIDYUV_STATE state;
1217 int exit_code = EX_OK;
1218
1219 MMAL_STATUS_T status = MMAL_SUCCESS;
1220 MMAL_PORT_T *camera_preview_port = NULL;
1221 MMAL_PORT_T *camera_video_port = NULL;
1222 MMAL_PORT_T *camera_still_port = NULL;
1223 MMAL_PORT_T *preview_input_port = NULL;
1224
1225 bcm_host_init();
1226
1227 // Register our application with the logging system
1228 vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY);
1229
1230 signal(SIGINT, default_signal_handler);
1231
1232 // Disable USR1 for the moment - may be reenabled if go in to signal capture mode
1233 signal(SIGUSR1, SIG_IGN);
1234
1235 set_app_name(argv[0]);
1236
1237 // Do we have any parameters
1238 if (argc == 1)
1239 {
1240 display_valid_parameters(basename(get_app_name()), &application_help_message);
1241 exit(EX_USAGE);
1242 }
1243
1244 default_status(&state);
1245
1246 // Parse the command line and put options in to our status structure
1247 if (parse_cmdline(argc, argv, &state))
1248 {
1249 status = -1;
1250 exit(EX_USAGE);
1251 }
1252
1253 if (state.timeout == -1)
1254 state.timeout = 5000;
1255
1256 // Setup for sensor specific parameters, only set W/H settings if zero on entry
1257 get_sensor_defaults(state.common_settings.cameraNum, state.common_settings.camera_name,
1258 &state.common_settings.width, &state.common_settings.height);
1259
1260 if (state.common_settings.verbose)
1261 {
1262 print_app_details(stderr);
1263 dump_status(&state);
1264 }
1265
1266 if (state.common_settings.gps)
1267 if (raspi_gps_setup(state.common_settings.verbose))
1268 state.common_settings.gps = false;
1269
1270 // OK, we have a nice set of parameters. Now set up our components
1271 // We have two components. Camera, Preview
1272
1273 if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
1274 {
1275 vcos_log_error("%s: Failed to create camera component", __func__);
1276 exit_code = EX_SOFTWARE;
1277 }
1278 else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
1279 {
1280 vcos_log_error("%s: Failed to create preview component", __func__);
1281 destroy_camera_component(&state);
1282 exit_code = EX_SOFTWARE;
1283 }
1284 else
1285 {
1286 if (state.common_settings.verbose)
1287 fprintf(stderr, "Starting component connection stage\n");
1288
1289 camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1290 camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1291 camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1292 preview_input_port = state.preview_parameters.preview_component->input[0];
1293
1294 if (state.preview_parameters.wantPreview )
1295 {
1296 if (state.common_settings.verbose)
1297 {
1298 fprintf(stderr, "Connecting camera preview port to preview input port\n");
1299 fprintf(stderr, "Starting video preview\n");
1300 }
1301
1302 // Connect camera to preview
1303 status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
1304
1305 if (status != MMAL_SUCCESS)
1306 state.preview_connection = NULL;
1307 }
1308 else
1309 {
1310 status = MMAL_SUCCESS;
1311 }
1312
1313 if (status == MMAL_SUCCESS)
1314 {
1315 state.callback_data.file_handle = NULL;
1316
1317 if (state.common_settings.filename)
1318 {
1319 if (state.common_settings.filename[0] == '-')
1320 {
1321 state.callback_data.file_handle = stdout;
1322 }
1323 else
1324 {
1325 state.callback_data.file_handle = open_filename(&state, state.common_settings.filename);
1326 }
1327
1328 if (!state.callback_data.file_handle)
1329 {
1330 // Notify user, carry on but discarding output buffers
1331 vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.common_settings.filename);
1332 }
1333 }
1334
1335 state.callback_data.pts_file_handle = NULL;
1336
1337 if (state.pts_filename)
1338 {
1339 if (state.pts_filename[0] == '-')
1340 {
1341 state.callback_data.pts_file_handle = stdout;
1342 }
1343 else
1344 {
1345 state.callback_data.pts_file_handle = open_filename(&state, state.pts_filename);
1346 if (state.callback_data.pts_file_handle) /* save header for mkvmerge */
1347 fprintf(state.callback_data.pts_file_handle, "# timecode format v2\n");
1348 }
1349
1350 if (!state.callback_data.pts_file_handle)
1351 {
1352 // Notify user, carry on but discarding encoded output buffers
1353 fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.pts_filename);
1354 state.save_pts=0;
1355 }
1356 }
1357
1358 // Set up our userdata - this is passed though to the callback where we need the information.
1359 state.callback_data.pstate = &state;
1360 state.callback_data.abort = 0;
1361
1362 camera_video_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data;
1363
1364 if (state.demoMode)
1365 {
1366 // Run for the user specific time..
1367 int num_iterations = state.timeout / state.demoInterval;
1368 int i;
1369
1370 if (state.common_settings.verbose)
1371 fprintf(stderr, "Running in demo mode\n");
1372
1373 for (i=0; state.timeout == 0 || i<num_iterations; i++)
1374 {
1375 raspicamcontrol_cycle_test(state.camera_component);
1376 vcos_sleep(state.demoInterval);
1377 }
1378 }
1379 else
1380 {
1381 // Only save stuff if we have a filename and it opened
1382 // Note we use the file handle copy in the callback, as the call back MIGHT change the file handle
1383 if (state.callback_data.file_handle)
1384 {
1385 int running = 1;
1386
1387 if (state.common_settings.verbose)
1388 fprintf(stderr, "Enabling camera video port\n");
1389
1390 // Enable the camera video port and tell it its callback function
1391 status = mmal_port_enable(camera_video_port, camera_buffer_callback);
1392
1393 if (status != MMAL_SUCCESS)
1394 {
1395 vcos_log_error("Failed to setup camera output");
1396 goto error;
1397 }
1398
1399 // Send all the buffers to the camera video port
1400 {
1401 int num = mmal_queue_length(state.camera_pool->queue);
1402 int q;
1403 for (q=0; q<num; q++)
1404 {
1405 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.camera_pool->queue);
1406
1407 if (!buffer)
1408 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1409
1410 if (mmal_port_send_buffer(camera_video_port, buffer)!= MMAL_SUCCESS)
1411 vcos_log_error("Unable to send a buffer to camera video port (%d)", q);
1412 }
1413 }
1414
1415 while (running)
1416 {
1417 // Change state
1418
1419 state.bCapturing = !state.bCapturing;
1420
1421 if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, state.bCapturing) != MMAL_SUCCESS)
1422 {
1423 // How to handle?
1424 }
1425
1426 if (state.common_settings.verbose)
1427 {
1428 if (state.bCapturing)
1429 fprintf(stderr, "Starting video capture\n");
1430 else
1431 fprintf(stderr, "Pausing video capture\n");
1432 }
1433
1434 running = wait_for_next_change(&state);
1435 }
1436
1437 if (state.common_settings.verbose)
1438 fprintf(stderr, "Finished capture\n");
1439 }
1440 else
1441 {
1442 if (state.timeout)
1443 vcos_sleep(state.timeout);
1444 else
1445 {
1446 // timeout = 0 so run forever
1447 while(1)
1448 vcos_sleep(ABORT_INTERVAL);
1449 }
1450 }
1451 }
1452 }
1453 else
1454 {
1455 mmal_status_to_int(status);
1456 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1457 }
1458
1459 error:
1460
1461 mmal_status_to_int(status);
1462
1463 if (state.common_settings.verbose)
1464 fprintf(stderr, "Closing down\n");
1465
1466 // Disable all our ports that are not handled by connections
1467 check_disable_port(camera_video_port);
1468
1469 if (state.preview_parameters.wantPreview && state.preview_connection)
1470 mmal_connection_destroy(state.preview_connection);
1471
1472 if (state.preview_parameters.preview_component)
1473 mmal_component_disable(state.preview_parameters.preview_component);
1474
1475 if (state.camera_component)
1476 mmal_component_disable(state.camera_component);
1477
1478 // Can now close our file. Note disabling ports may flush buffers which causes
1479 // problems if we have already closed the file!
1480 if (state.callback_data.file_handle && state.callback_data.file_handle != stdout)
1481 fclose(state.callback_data.file_handle);
1482 if (state.callback_data.pts_file_handle && state.callback_data.pts_file_handle != stdout)
1483 fclose(state.callback_data.pts_file_handle);
1484
1485 raspipreview_destroy(&state.preview_parameters);
1486 destroy_camera_component(&state);
1487
1488 if (state.common_settings.gps)
1489 raspi_gps_shutdown(state.common_settings.verbose);
1490
1491 if (state.common_settings.verbose)
1492 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1493 }
1494
1495 if (status != MMAL_SUCCESS)
1496 raspicamcontrol_check_configuration(128);
1497
1498 return exit_code;
1499 }
1500
1501