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