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