2013-10-11 08:17:05 +00:00
/*
2015-03-05 06:01:33 +00:00
* Copyright ( c ) 2013 - 2015 Jan Schmidt < jan @ centricular . com >
2013-10-11 08:17:05 +00:00
Portions :
Copyright ( c ) 2013 , Broadcom Europe Ltd
Copyright ( c ) 2013 , James Hughes
All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are met :
* Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND
ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
/**
* \ file RaspiCapture . c
*
* Modification of the RaspiVid command line capture program for GStreamer
* use .
*
2015-03-05 06:01:33 +00:00
* \ date 28 th Feb 2013 , 11 Oct 2013 , 5 Mar 2015
2013-10-11 08:17:05 +00:00
* \ Author : James Hughes , Jan Schmidt
*
* Description
*
* 3 components are created ; camera , preview and video encoder .
* Camera component has three ports , preview , video and stills .
* This program connects preview and stills to the preview and video
* encoder . Using mmal we don ' t need to worry about buffers between these
* components , but we do need to handle buffers from the encoder , which
* are simply written straight to the file in the requisite buffer callback .
*
* We use the RaspiCamControl code to handle the specific camera settings .
* We use the RaspiPreview code to handle the ( generic ) preview window
*/
2015-03-05 06:01:33 +00:00
// We use some GNU extensions (basename, asprintf)
# define _GNU_SOURCE
2013-10-11 08:17:05 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <memory.h>
# include <sysexits.h>
2013-10-12 08:23:03 +00:00
# include <gst/gst.h>
2013-10-12 01:42:07 +00:00
2013-10-11 08:17:05 +00:00
# include "bcm_host.h"
# include "interface/vcos/vcos.h"
# include "interface/mmal/mmal.h"
# include "interface/mmal/mmal_logging.h"
# include "interface/mmal/mmal_buffer.h"
# include "interface/mmal/util/mmal_util.h"
# include "interface/mmal/util/mmal_util_params.h"
# include "interface/mmal/util/mmal_default_components.h"
# include "interface/mmal/util/mmal_connection.h"
2013-10-12 01:42:07 +00:00
# include "RaspiCapture.h"
2013-10-11 08:17:05 +00:00
# include "RaspiCamControl.h"
# include "RaspiPreview.h"
2015-03-05 06:01:33 +00:00
# include "RaspiCLI.h"
2013-10-11 08:17:05 +00:00
# include <semaphore.h>
// Standard port setting for the camera component
# define MMAL_CAMERA_PREVIEW_PORT 0
# define MMAL_CAMERA_VIDEO_PORT 1
# define MMAL_CAMERA_CAPTURE_PORT 2
// Video format information
2015-03-05 06:01:33 +00:00
// 0 implies variable
2013-10-11 08:17:05 +00:00
# define VIDEO_FRAME_RATE_NUM 30
# define VIDEO_FRAME_RATE_DEN 1
/// Video render needs at least 2 buffers.
# define VIDEO_OUTPUT_BUFFERS_NUM 3
// Max bitrate we allow for recording
2015-03-05 06:01:33 +00:00
const int MAX_BITRATE = 25000000 ; // 25Mbits/s
2013-10-11 08:17:05 +00:00
/// Interval at which we check for an failure abort during capture
const int ABORT_INTERVAL = 100 ; // ms
int mmal_status_to_int ( MMAL_STATUS_T status ) ;
2013-10-12 01:42:07 +00:00
/** Struct used to pass information in encoder port userdata to callback
2013-10-11 08:17:05 +00:00
*/
typedef struct
{
2013-10-12 01:42:07 +00:00
RASPIVID_STATE * state ; /// pointer to our state in case required in callback
int abort ; /// Set to 1 in callback if an error occurs to attempt to abort the capture
} PORT_USERDATA ;
struct RASPIVID_STATE_T
{
2013-10-12 14:20:51 +00:00
RASPIVID_CONFIG * config ;
2013-10-12 01:42:07 +00:00
FILE * output_file ;
2013-10-11 08:17:05 +00:00
MMAL_COMPONENT_T * camera_component ; /// Pointer to the camera component
MMAL_COMPONENT_T * encoder_component ; /// Pointer to the encoder component
MMAL_CONNECTION_T * preview_connection ; /// Pointer to the connection from camera to preview
MMAL_CONNECTION_T * encoder_connection ; /// Pointer to the connection from camera to encoder
2013-10-12 14:20:51 +00:00
MMAL_PORT_T * camera_video_port ;
2013-10-12 01:42:07 +00:00
MMAL_PORT_T * camera_still_port ;
MMAL_PORT_T * encoder_output_port ;
2013-10-11 08:17:05 +00:00
MMAL_POOL_T * encoder_pool ; /// Pointer to the pool of buffers used by encoder output port
2013-10-12 01:42:07 +00:00
PORT_USERDATA callback_data ;
2013-10-12 08:23:03 +00:00
MMAL_QUEUE_T * encoded_buffer_q ;
2015-03-05 06:01:33 +00:00
int64_t base_time ;
int64_t last_second ;
2013-10-12 01:42:07 +00:00
} ;
2013-10-11 08:17:05 +00:00
2015-03-05 06:01:33 +00:00
2013-10-11 08:17:05 +00:00
/// Structure to cross reference H264 profile strings against the MMAL parameter equivalent
static XREF_T profile_map [ ] =
{
{ " baseline " , MMAL_VIDEO_PROFILE_H264_BASELINE } ,
{ " main " , MMAL_VIDEO_PROFILE_H264_MAIN } ,
{ " high " , MMAL_VIDEO_PROFILE_H264_HIGH } ,
// {"constrained", MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this?
} ;
static int profile_map_size = sizeof ( profile_map ) / sizeof ( profile_map [ 0 ] ) ;
2015-03-05 06:01:33 +00:00
#if 0
static XREF_T initial_map [ ] =
{
{ " record " , 0 } ,
{ " pause " , 1 } ,
} ;
static int initial_map_size = sizeof ( initial_map ) / sizeof ( initial_map [ 0 ] ) ;
# endif
static XREF_T intra_refresh_map [ ] =
{
{ " cyclic " , MMAL_VIDEO_INTRA_REFRESH_CYCLIC } ,
{ " adaptive " , MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE } ,
{ " both " , MMAL_VIDEO_INTRA_REFRESH_BOTH } ,
{ " cyclicrows " , MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS } ,
// {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why.
} ;
static int intra_refresh_map_size = sizeof ( intra_refresh_map ) / sizeof ( intra_refresh_map [ 0 ] ) ;
#if 0
2013-10-11 08:17:05 +00:00
static void display_valid_parameters ( char * app_name ) ;
/// Command ID's and Structure defining our command line options
# define CommandHelp 0
# define CommandWidth 1
# define CommandHeight 2
# define CommandBitrate 3
# define CommandOutput 4
# define CommandVerbose 5
# define CommandTimeout 6
# define CommandDemoMode 7
# define CommandFramerate 8
# define CommandPreviewEnc 9
# define CommandIntraPeriod 10
# define CommandProfile 11
2015-03-05 06:01:33 +00:00
# define CommandTimed 12
# define CommandSignal 13
# define CommandKeypress 14
# define CommandInitialState 15
# define CommandQP 16
# define CommandInlineHeaders 17
# define CommandSegmentFile 18
# define CommandSegmentWrap 19
# define CommandSegmentStart 20
# define CommandSplitWait 21
# define CommandCircular 22
# define CommandIMV 23
# define CommandCamSelect 24
# define CommandSettings 25
# define CommandSensorMode 26
# define CommandIntraRefreshType 27
2013-10-11 08:17:05 +00:00
static COMMAND_LIST cmdline_commands [ ] =
{
2015-03-05 06:01:33 +00:00
{ CommandHelp , " -help " , " ? " , " This help information " , 0 } ,
{ CommandWidth , " -width " , " w " , " Set image width <size>. Default 1920 " , 1 } ,
{ CommandHeight , " -height " , " h " , " Set image height <size>. Default 1080 " , 1 } ,
{ CommandBitrate , " -bitrate " , " b " , " Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000) " , 1 } ,
{ CommandOutput , " -output " , " o " , " Output filename <filename> (to write to stdout, use '-o -') " , 1 } ,
{ CommandVerbose , " -verbose " , " v " , " Output verbose information during run " , 0 } ,
{ CommandTimeout , " -timeout " , " t " , " Time (in ms) to capture for. If not specified, set to 5s. Zero to disable " , 1 } ,
{ CommandDemoMode , " -demo " , " d " , " Run a demo mode (cycle through range of camera options, no capture) " , 1 } ,
{ CommandFramerate , " -framerate " , " fps " , " Specify the frames per second to record " , 1 } ,
{ CommandPreviewEnc , " -penc " , " e " , " Display preview image *after* encoding (shows compression artifacts) " , 0 } ,
{ CommandIntraPeriod , " -intra " , " g " , " Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames. " , 1 } ,
{ CommandProfile , " -profile " , " pf " , " Specify H264 profile to use for encoding " , 1 } ,
{ CommandTimed , " -timed " , " td " , " Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms " , 0 } ,
{ CommandSignal , " -signal " , " s " , " Cycle between capture and pause on Signal " , 0 } ,
{ CommandKeypress , " -keypress " , " k " , " Cycle between capture and pause on ENTER " , 0 } ,
{ CommandInitialState , " -initial " , " i " , " Initial state. Use 'record' or 'pause'. Default 'record' " , 1 } ,
{ CommandQP , " -qp " , " qp " , " Quantisation parameter. Use approximately 10-40. Default 0 (off) " , 1 } ,
{ CommandInlineHeaders , " -inline " , " ih " , " Insert inline headers (SPS, PPS) to stream " , 0 } ,
{ CommandSegmentFile , " -segment " , " sg " , " Segment output file in to multiple files at specified interval <ms> " , 1 } ,
{ CommandSegmentWrap , " -wrap " , " wr " , " In segment mode, wrap any numbered filename back to 1 when reach number " , 1 } ,
{ CommandSegmentStart , " -start " , " sn " , " In segment mode, start with specified segment number " , 1 } ,
{ CommandSplitWait , " -split " , " sp " , " In wait mode, create new output file for each start event " , 0 } ,
{ CommandCircular , " -circular " , " c " , " Run encoded data through circular buffer until triggered then save " , 0 } ,
{ CommandIMV , " -vectors " , " x " , " Output filename <filename> for inline motion vectors " , 1 } ,
{ CommandCamSelect , " -camselect " , " cs " , " Select camera <number>. Default 0 " , 1 } ,
{ CommandSettings , " -settings " , " set " , " Retrieve camera settings and write to stdout " , 0 } ,
{ CommandSensorMode , " -mode " , " md " , " Force sensor mode. 0=auto. See docs for other modes available " , 1 } ,
{ CommandIntraRefreshType , " -irefresh " , " if " , " Set intra refresh type " , 1 } ,
2013-10-11 08:17:05 +00:00
} ;
static int cmdline_commands_size = sizeof ( cmdline_commands ) / sizeof ( cmdline_commands [ 0 ] ) ;
# endif
static void dump_state ( RASPIVID_STATE * state ) ;
/**
* Assign a default set of parameters to the state passed in
*
* @ param state Pointer to state structure to assign defaults to
*/
2013-10-12 01:42:07 +00:00
void raspicapture_default_config ( RASPIVID_CONFIG * config )
2013-10-11 08:17:05 +00:00
{
2015-03-05 06:01:33 +00:00
if ( ! config )
{
vcos_assert ( 0 ) ;
return ;
}
// Default everything to zero
memset ( config , 0 , sizeof ( RASPIVID_CONFIG ) ) ;
2013-10-11 08:17:05 +00:00
// Now set anything non-zero
2013-10-12 01:42:07 +00:00
config - > timeout = 5000 ; // 5s delay before take image
config - > width = 1920 ; // Default to 1080p
config - > height = 1080 ;
config - > bitrate = 17000000 ; // This is a decent default bitrate for 1080p
2013-10-12 14:20:51 +00:00
config - > fps_n = VIDEO_FRAME_RATE_NUM ;
config - > fps_d = VIDEO_FRAME_RATE_DEN ;
2015-03-05 06:01:33 +00:00
config - > intraperiod = - 1 ; // Not set
config - > quantisationParameter = 0 ;
2013-10-12 01:42:07 +00:00
config - > demoMode = 0 ;
config - > demoInterval = 250 ; // ms
config - > immutableInput = 1 ;
config - > profile = MMAL_VIDEO_PROFILE_H264_HIGH ;
2013-10-11 08:17:05 +00:00
2015-03-05 06:01:33 +00:00
config - > bInlineHeaders = 0 ;
config - > inlineMotionVectors = 0 ;
config - > cameraNum = 0 ;
config - > settings = 0 ;
config - > sensor_mode = 0 ;
config - > intra_refresh_type = - 1 ;
2013-10-11 08:17:05 +00:00
// Setup preview window defaults
2013-10-12 01:42:07 +00:00
raspipreview_set_defaults ( & config - > preview_parameters ) ;
2013-10-11 08:17:05 +00:00
// Set up the camera_parameters to default
2013-10-12 01:42:07 +00:00
raspicamcontrol_set_defaults ( & config - > camera_parameters ) ;
2013-10-11 08:17:05 +00:00
}
2015-03-05 06:01:33 +00:00
2013-10-11 08:17:05 +00:00
/**
* Dump image state parameters to printf . Used for debugging
*
* @ param state Pointer to state structure to assign defaults to
*/
static void dump_state ( RASPIVID_STATE * state )
{
if ( ! state )
{
vcos_assert ( 0 ) ;
return ;
}
2013-10-12 14:20:51 +00:00
fprintf ( stderr , " Width %d, Height %d \n " , state - > config - > width , state - > config - > height ) ;
fprintf ( stderr , " bitrate %d, framerate %d/%d, time delay %d \n " , state - > config - > bitrate , state - > config - > fps_n , state - > config - > fps_d , state - > config - > timeout ) ;
//fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->config->profile, profile_map, profile_map_size));
2013-10-11 08:17:05 +00:00
2013-10-12 14:20:51 +00:00
raspipreview_dump_parameters ( & state - > config - > preview_parameters ) ;
raspicamcontrol_dump_parameters ( & state - > config - > camera_parameters ) ;
2013-10-11 08:17:05 +00:00
}
#if 0
/**
* Parse the incoming command line and put resulting parameters in to the state
*
* @ param argc Number of arguments in command line
* @ param argv Array of pointers to strings from command line
* @ param state Pointer to state structure to assign any discovered parameters to
* @ return Non - 0 if failed for some reason , 0 otherwise
*/
static int parse_cmdline ( int argc , const char * * argv , RASPIVID_STATE * state )
{
// Parse the command line arguments.
// We are looking for --<something> or -<abreviation of something>
int valid = 1 ;
int i ;
for ( i = 1 ; i < argc & & valid ; i + + )
{
int command_id , num_parameters ;
if ( ! argv [ i ] )
continue ;
if ( argv [ i ] [ 0 ] ! = ' - ' )
{
valid = 0 ;
continue ;
}
// Assume parameter is valid until proven otherwise
valid = 1 ;
command_id = raspicli_get_command_id ( cmdline_commands , cmdline_commands_size , & argv [ i ] [ 1 ] , & num_parameters ) ;
// If we found a command but are missing a parameter, continue (and we will drop out of the loop)
if ( command_id ! = - 1 & & num_parameters > 0 & & ( i + 1 > = argc ) )
continue ;
// We are now dealing with a command line option
switch ( command_id )
{
case CommandHelp :
display_valid_parameters ( basename ( argv [ 0 ] ) ) ;
return - 1 ;
case CommandWidth : // Width > 0
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > width ) ! = 1 )
valid = 0 ;
else
i + + ;
break ;
case CommandHeight : // Height > 0
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > height ) ! = 1 )
valid = 0 ;
else
i + + ;
break ;
case CommandBitrate : // 1-100
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > bitrate ) = = 1 )
{
if ( state - > bitrate > MAX_BITRATE )
{
state - > bitrate = MAX_BITRATE ;
}
i + + ;
}
else
valid = 0 ;
break ;
case CommandOutput : // output filename
{
int len = strlen ( argv [ i + 1 ] ) ;
if ( len )
{
state - > filename = malloc ( len + 1 ) ;
vcos_assert ( state - > filename ) ;
if ( state - > filename )
2015-03-05 06:01:33 +00:00
strncpy ( state - > filename , argv [ i + 1 ] , len + 1 ) ;
2013-10-11 08:17:05 +00:00
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandVerbose : // display lots of data during run
state - > verbose = 1 ;
break ;
case CommandTimeout : // Time to run viewfinder/capture
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > timeout ) = = 1 )
{
2015-03-05 06:01:33 +00:00
// Ensure that if previously selected a waitMethod we dont overwrite it
if ( state - > timeout = = 0 & & state - > waitMethod = = WAIT_METHOD_NONE )
state - > waitMethod = WAIT_METHOD_FOREVER ;
2013-10-11 08:17:05 +00:00
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandDemoMode : // Run in demo mode - no capture
{
// Demo mode might have a timing parameter
// so check if a) we have another parameter, b) its not the start of the next option
if ( i + 1 < argc & & argv [ i + 1 ] [ 0 ] ! = ' - ' )
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > demoInterval ) = = 1 )
{
// TODO : What limits do we need for timeout?
if ( state - > demoInterval = = 0 )
state - > demoInterval = 250 ; // ms
state - > demoMode = 1 ;
i + + ;
}
else
valid = 0 ;
}
else
{
state - > demoMode = 1 ;
}
break ;
}
case CommandFramerate : // fps to record
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > framerate ) = = 1 )
{
// TODO : What limits do we need for fps 1 - 30 - 120??
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandPreviewEnc :
state - > immutableInput = 0 ;
break ;
case CommandIntraPeriod : // key frame rate
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > intraperiod ) = = 1 )
i + + ;
else
valid = 0 ;
break ;
}
2015-03-05 06:01:33 +00:00
case CommandQP : // quantisation parameter
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > quantisationParameter ) = = 1 )
i + + ;
else
valid = 0 ;
break ;
}
2013-10-11 08:17:05 +00:00
case CommandProfile : // H264 profile
{
state - > profile = raspicli_map_xref ( argv [ i + 1 ] , profile_map , profile_map_size ) ;
if ( state - > profile = = - 1 )
state - > profile = MMAL_VIDEO_PROFILE_H264_HIGH ;
i + + ;
break ;
}
2015-03-05 06:01:33 +00:00
case CommandInlineHeaders : // H264 inline headers
{
state - > bInlineHeaders = 1 ;
break ;
}
case CommandTimed :
{
if ( sscanf ( argv [ i + 1 ] , " %u,%u " , & state - > onTime , & state - > offTime ) = = 2 )
{
i + + ;
if ( state - > onTime < 1000 )
state - > onTime = 1000 ;
if ( state - > offTime < 1000 )
state - > offTime = 1000 ;
state - > waitMethod = WAIT_METHOD_TIMED ;
}
else
valid = 0 ;
break ;
}
case CommandKeypress :
state - > waitMethod = WAIT_METHOD_KEYPRESS ;
break ;
case CommandSignal :
state - > waitMethod = WAIT_METHOD_SIGNAL ;
// Reenable the signal
signal ( SIGUSR1 , signal_handler ) ;
break ;
case CommandInitialState :
{
state - > bCapturing = raspicli_map_xref ( argv [ i + 1 ] , initial_map , initial_map_size ) ;
if ( state - > bCapturing = = - 1 )
state - > bCapturing = 0 ;
i + + ;
break ;
}
case CommandSegmentFile : // Segment file in to chunks of specified time
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > segmentSize ) = = 1 )
{
// Must enable inline headers for this to work
state - > bInlineHeaders = 1 ;
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandSegmentWrap : // segment wrap value
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > segmentWrap ) = = 1 )
i + + ;
else
valid = 0 ;
break ;
}
case CommandSegmentStart : // initial segment number
{
if ( ( sscanf ( argv [ i + 1 ] , " %u " , & state - > segmentNumber ) = = 1 ) & & ( ! state - > segmentWrap | | ( state - > segmentNumber < = state - > segmentWrap ) ) )
i + + ;
else
valid = 0 ;
break ;
}
case CommandSplitWait : // split files on restart
{
// Must enable inline headers for this to work
state - > bInlineHeaders = 1 ;
state - > splitWait = 1 ;
break ;
}
case CommandCircular :
{
state - > bCircularBuffer = 1 ;
break ;
}
case CommandIMV : // output filename
{
state - > inlineMotionVectors = 1 ;
int len = strlen ( argv [ i + 1 ] ) ;
if ( len )
{
state - > imv_filename = malloc ( len + 1 ) ;
vcos_assert ( state - > imv_filename ) ;
if ( state - > imv_filename )
strncpy ( state - > imv_filename , argv [ i + 1 ] , len + 1 ) ;
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandCamSelect : //Select camera input port
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > cameraNum ) = = 1 )
{
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandSettings :
state - > settings = 1 ;
break ;
case CommandSensorMode :
{
if ( sscanf ( argv [ i + 1 ] , " %u " , & state - > sensor_mode ) = = 1 )
{
i + + ;
}
else
valid = 0 ;
break ;
}
case CommandIntraRefreshType :
{
state - > config - > intra_refresh_type = raspicli_map_xref ( argv [ i + 1 ] , intra_refresh_map , intra_refresh_map_size ) ;
i + + ;
break ;
}
2013-10-11 08:17:05 +00:00
default :
{
// Try parsing for any image specific parameters
// result indicates how many parameters were used up, 0,1,2
// but we adjust by -1 as we have used one already
const char * second_arg = ( i + 1 < argc ) ? argv [ i + 1 ] : NULL ;
int parms_used = ( raspicamcontrol_parse_cmdline ( & state - > camera_parameters , & argv [ i ] [ 1 ] , second_arg ) ) ;
// Still unused, try preview options
if ( ! parms_used )
parms_used = raspipreview_parse_cmdline ( & state - > preview_parameters , & argv [ i ] [ 1 ] , second_arg ) ;
// If no parms were used, this must be a bad parameters
if ( ! parms_used )
valid = 0 ;
else
i + = parms_used - 1 ;
break ;
}
}
}
if ( ! valid )
{
2015-03-05 06:01:33 +00:00
fprintf ( stderr , " Invalid command line option (%s) \n " , argv [ i - 1 ] ) ;
2013-10-11 08:17:05 +00:00
return 1 ;
}
2015-03-05 06:01:33 +00:00
// Always disable verbose if output going to stdout
if ( state - > filename & & state - > filename [ 0 ] = = ' - ' )
{
state - > verbose = 0 ;
}
2013-10-11 08:17:05 +00:00
return 0 ;
}
/**
* Display usage information for the application to stdout
*
* @ param app_name String to display as the application name
*/
static void display_valid_parameters ( char * app_name )
{
int i ;
fprintf ( stderr , " Display camera output to display, and optionally saves an H264 capture at requested bitrate \n \n " ) ;
fprintf ( stderr , " \n usage: %s [options] \n \n " , app_name ) ;
fprintf ( stderr , " Image parameter commands \n \n " ) ;
raspicli_display_help ( cmdline_commands , cmdline_commands_size ) ;
// Profile options
fprintf ( stderr , " \n \n H264 Profile options : \n %s " , profile_map [ 0 ] . mode ) ;
for ( i = 1 ; i < profile_map_size ; i + + )
{
fprintf ( stderr , " ,%s " , profile_map [ i ] . mode ) ;
}
2015-03-05 06:01:33 +00:00
fprintf ( stderr , " \n " ) ;
// Intra refresh options
fprintf ( stderr , " \n \n H264 Intra refresh options : \n %s " , intra_refresh_map [ 0 ] . mode ) ;
for ( i = 1 ; i < intra_refresh_map_size ; i + + )
{
fprintf ( stderr , " ,%s " , intra_refresh_map [ i ] . mode ) ;
}
fprintf ( stderr , " \n " ) ;
2013-10-11 08:17:05 +00:00
// Help for preview options
raspipreview_display_help ( ) ;
// Now display any help information from the camcontrol code
raspicamcontrol_display_help ( ) ;
fprintf ( stderr , " \n " ) ;
return ;
}
# endif
/**
* buffer header callback function for camera control
*
* Callback will dump buffer data to the specific file
*
* @ param port Pointer to port from which callback originated
* @ param buffer mmal buffer header pointer
*/
static void camera_control_callback ( MMAL_PORT_T * port , MMAL_BUFFER_HEADER_T * buffer )
{
if ( buffer - > cmd = = MMAL_EVENT_PARAMETER_CHANGED )
{
2015-03-05 06:01:33 +00:00
MMAL_EVENT_PARAMETER_CHANGED_T * param = ( MMAL_EVENT_PARAMETER_CHANGED_T * ) buffer - > data ;
switch ( param - > hdr . id ) {
case MMAL_PARAMETER_CAMERA_SETTINGS :
{
MMAL_PARAMETER_CAMERA_SETTINGS_T * settings = ( MMAL_PARAMETER_CAMERA_SETTINGS_T * ) param ;
vcos_log_error ( " Exposure now %u, analog gain %u/%u, digital gain %u/%u " ,
settings - > exposure ,
settings - > analog_gain . num , settings - > analog_gain . den ,
settings - > digital_gain . num , settings - > digital_gain . den ) ;
vcos_log_error ( " AWB R=%u/%u, B=%u/%u " ,
settings - > awb_red_gain . num , settings - > awb_red_gain . den ,
settings - > awb_blue_gain . num , settings - > awb_blue_gain . den
) ;
}
break ;
}
2013-10-11 08:17:05 +00:00
}
else
{
vcos_log_error ( " Received unexpected camera control callback event, 0x%08x " , buffer - > cmd ) ;
}
mmal_buffer_header_release ( buffer ) ;
}
2015-03-05 06:01:33 +00:00
#if 0
/**
* Open a file based on the settings in state
*
* @ param state Pointer to state
*/
static FILE * open_filename ( RASPIVID_STATE * pState )
{
FILE * new_handle = NULL ;
char * tempname = NULL , * filename = NULL ;
if ( pState - > segmentSize | | pState - > splitWait )
{
// Create a new filename string
asprintf ( & tempname , pState - > filename , pState - > segmentNumber ) ;
filename = tempname ;
}
else
{
filename = pState - > filename ;
}
if ( filename )
new_handle = fopen ( filename , " wb " ) ;
if ( pState - > verbose )
{
if ( new_handle )
fprintf ( stderr , " Opening output file \" %s \" \n " , filename ) ;
else
fprintf ( stderr , " Failed to open new file \" %s \" \n " , filename ) ;
}
if ( tempname )
free ( tempname ) ;
return new_handle ;
}
/**
* Open a file based on the settings in state
*
* This time for the imv output file
*
* @ param state Pointer to state
*/
static FILE * open_imv_filename ( RASPIVID_STATE * pState )
{
FILE * new_handle = NULL ;
char * tempname = NULL , * filename = NULL ;
if ( pState - > segmentSize | | pState - > splitWait )
{
// Create a new filename string
asprintf ( & tempname , pState - > imv_filename , pState - > segmentNumber ) ;
filename = tempname ;
}
else
{
filename = pState - > imv_filename ;
}
if ( filename )
new_handle = fopen ( filename , " wb " ) ;
if ( pState - > verbose )
{
if ( new_handle )
fprintf ( stderr , " Opening imv output file \" %s \" \n " , filename ) ;
else
fprintf ( stderr , " Failed to open new imv file \" %s \" \n " , filename ) ;
}
if ( tempname )
free ( tempname ) ;
return new_handle ;
}
# endif
/**
* Update any annotation data specific to the video .
* This simply passes on the setting from cli , or
* if application defined annotate requested , updates
* with the H264 parameters
*
* @ param state Pointer to state control struct
*
*/
static void update_annotation_data ( RASPIVID_STATE * state )
{
RASPIVID_CONFIG * config = state - > config ;
// So, if we have asked for a application supplied string, set it to the H264 parameters
if ( config - > camera_parameters . enable_annotate & ANNOTATE_APP_TEXT )
{
char * text ;
const char * refresh = raspicli_unmap_xref ( config - > intra_refresh_type , intra_refresh_map , intra_refresh_map_size ) ;
asprintf ( & text , " %dk,%ff,%s,%d,%s " ,
config - > bitrate / 1000 , ( ( float ) ( config - > fps_n ) / config - > fps_d ) ,
refresh ? refresh : " (none) " ,
config - > intraperiod ,
raspicli_unmap_xref ( config - > profile , profile_map , profile_map_size ) ) ;
raspicamcontrol_set_annotate ( state - > camera_component , config - > camera_parameters . enable_annotate , text ) ;
free ( text ) ;
}
else
{
raspicamcontrol_set_annotate ( state - > camera_component , config - > camera_parameters . enable_annotate , config - > camera_parameters . annotate_string ) ;
}
}
2013-10-11 08:17:05 +00:00
/**
* buffer header callback function for encoder
*
* Callback will dump buffer data to the specific file
*
* @ param port Pointer to port from which callback originated
* @ param buffer mmal buffer header pointer
*/
static void encoder_buffer_callback ( MMAL_PORT_T * port , MMAL_BUFFER_HEADER_T * buffer )
{
PORT_USERDATA * pData = ( PORT_USERDATA * ) port - > userdata ;
2013-10-12 01:42:07 +00:00
RASPIVID_STATE * state = pData - > state ;
2015-03-05 06:01:33 +00:00
int64_t current_time ;
// All our segment times based on the receipt of the first encoder callback
if ( state - > base_time = = - 1 )
state - > base_time = vcos_getmicrosecs64 ( ) / 1000 ;
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
if ( pData = = NULL )
2013-10-11 08:17:05 +00:00
{
2013-10-12 08:23:03 +00:00
vcos_log_error ( " Received a encoder buffer callback with no state " ) ;
// release buffer back to the pool
mmal_buffer_header_release ( buffer ) ;
return ;
}
2013-10-11 08:17:05 +00:00
2015-03-05 06:01:33 +00:00
current_time = vcos_getmicrosecs64 ( ) / 1000 ;
if ( state - > base_time = = - 1 )
state - > base_time = current_time ;
// See if the second count has changed and we need to update any annotation
if ( current_time / 1000 ! = state - > last_second )
{
update_annotation_data ( state ) ;
state - > last_second = current_time / 1000 ;
}
2013-10-12 08:23:03 +00:00
/* Send buffer to GStreamer element for pushing to the pipeline */
mmal_queue_put ( state - > encoded_buffer_q , buffer ) ;
}
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
GstFlowReturn
raspi_capture_fill_buffer ( RASPIVID_STATE * state , GstBuffer * * bufp )
{
GstBuffer * buf ;
MMAL_BUFFER_HEADER_T * buffer ;
GstFlowReturn ret = GST_FLOW_ERROR ;
/* FIXME: Use our own interruptible cond wait: */
buffer = mmal_queue_wait ( state - > encoded_buffer_q ) ;
mmal_buffer_header_mem_lock ( buffer ) ;
buf = gst_buffer_new_allocate ( NULL , buffer - > length , NULL ) ;
if ( buf ) {
gst_buffer_fill ( buf , 0 , buffer - > data , buffer - > length ) ;
ret = GST_FLOW_OK ;
}
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
mmal_buffer_header_mem_unlock ( buffer ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
* bufp = buf ;
// release buffer back to the pool
mmal_buffer_header_release ( buffer ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
// and send one back to the port (if still open)
if ( state - > encoder_output_port - > is_enabled )
{
MMAL_STATUS_T status = MMAL_SUCCESS ;
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
buffer = mmal_queue_get ( state - > encoder_pool - > queue ) ;
if ( buffer )
status = mmal_port_send_buffer ( state - > encoder_output_port , buffer ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
if ( ! buffer | | status ! = MMAL_SUCCESS ) {
vcos_log_error ( " Unable to return a buffer to the encoder port " ) ;
ret = GST_FLOW_ERROR ;
}
}
2013-10-11 08:17:05 +00:00
2013-10-12 08:23:03 +00:00
return ret ;
2013-10-11 08:17:05 +00:00
}
/**
* Create the camera component , set up its ports
*
* @ param state Pointer to state control struct
*
* @ return MMAL_SUCCESS if all OK , something else otherwise
*
*/
static MMAL_STATUS_T create_camera_component ( RASPIVID_STATE * state )
{
2013-10-12 14:20:51 +00:00
MMAL_COMPONENT_T * camera = NULL ;
2013-10-11 08:17:05 +00:00
MMAL_STATUS_T status ;
2015-03-05 06:01:33 +00:00
RASPIVID_CONFIG * config = state - > config ;
2013-10-11 08:17:05 +00:00
/* Create the component */
status = mmal_component_create ( MMAL_COMPONENT_DEFAULT_CAMERA , & camera ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Failed to create camera component " ) ;
goto error ;
}
2015-03-05 06:01:33 +00:00
MMAL_PARAMETER_INT32_T camera_num =
{ { MMAL_PARAMETER_CAMERA_NUM , sizeof ( camera_num ) } , config - > cameraNum } ;
2013-10-11 08:17:05 +00:00
2015-03-05 06:01:33 +00:00
status = mmal_port_parameter_set ( camera - > control , & camera_num . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Could not select camera : error %d " , status ) ;
goto error ;
}
2013-10-11 08:17:05 +00:00
if ( ! camera - > output_num )
{
status = MMAL_ENOSYS ;
vcos_log_error ( " Camera doesn't have output ports " ) ;
goto error ;
}
2015-03-05 06:01:33 +00:00
status = mmal_port_parameter_set_uint32 ( camera - > control , MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG , config - > sensor_mode ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Could not set sensor mode : error %d " , status ) ;
goto error ;
}
if ( config - > settings )
{
MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request =
{ { MMAL_PARAMETER_CHANGE_EVENT_REQUEST , sizeof ( MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T ) } ,
MMAL_PARAMETER_CAMERA_SETTINGS , 1 } ;
status = mmal_port_parameter_set ( camera - > control , & change_event_request . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " No camera settings events " ) ;
}
}
2013-10-11 08:17:05 +00:00
// Enable the camera, and tell it its control callback function
status = mmal_port_enable ( camera - > control , camera_control_callback ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to enable control port : error %d " , status ) ;
goto error ;
}
2013-10-12 14:20:51 +00:00
state - > camera_component = camera ;
return status ;
error :
if ( camera )
mmal_component_destroy ( camera ) ;
return status ;
}
MMAL_STATUS_T
raspi_capture_set_format_and_start ( RASPIVID_STATE * state )
{
MMAL_COMPONENT_T * camera = NULL ;
MMAL_STATUS_T status ;
MMAL_ES_FORMAT_T * format ;
MMAL_PORT_T * preview_port = NULL , * video_port = NULL , * still_port = NULL ;
2015-03-05 06:01:33 +00:00
RASPIVID_CONFIG * config = state - > config ;
2013-10-12 14:20:51 +00:00
2013-10-11 08:17:05 +00:00
// set up the camera configuration
2013-10-12 14:20:51 +00:00
MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
2013-10-11 08:17:05 +00:00
{
2013-10-12 14:20:51 +00:00
{ MMAL_PARAMETER_CAMERA_CONFIG , sizeof ( cam_config ) } ,
2015-03-05 06:01:33 +00:00
. max_stills_w = config - > width ,
. max_stills_h = config - > height ,
2013-10-12 14:20:51 +00:00
. stills_yuv422 = 0 ,
. one_shot_stills = 0 ,
2015-03-05 06:01:33 +00:00
. max_preview_video_w = config - > width ,
. max_preview_video_h = config - > height ,
2013-10-12 14:20:51 +00:00
. num_preview_video_frames = 3 ,
. stills_capture_circular_buffer_height = 0 ,
. fast_preview_resume = 0 ,
. use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
} ;
camera = state - > camera_component ;
preview_port = camera - > output [ MMAL_CAMERA_PREVIEW_PORT ] ;
video_port = camera - > output [ MMAL_CAMERA_VIDEO_PORT ] ;
still_port = camera - > output [ MMAL_CAMERA_CAPTURE_PORT ] ;
mmal_port_parameter_set ( camera - > control , & cam_config . hdr ) ;
2013-10-11 08:17:05 +00:00
// Now set up the port formats
// Set the encode format on the Preview port
// HW limitations mean we need the preview to be the same size as the required recorded output
format = preview_port - > format ;
format - > encoding = MMAL_ENCODING_OPAQUE ;
format - > encoding_variant = MMAL_ENCODING_I420 ;
2015-03-05 06:01:33 +00:00
if ( config - > camera_parameters . shutter_speed > 6000000 )
{
MMAL_PARAMETER_FPS_RANGE_T fps_range = { { MMAL_PARAMETER_FPS_RANGE , sizeof ( fps_range ) } ,
{ 50 , 1000 } , { 166 , 1000 } } ;
mmal_port_parameter_set ( preview_port , & fps_range . hdr ) ;
}
else if ( config - > camera_parameters . shutter_speed > 1000000 )
{
MMAL_PARAMETER_FPS_RANGE_T fps_range = { { MMAL_PARAMETER_FPS_RANGE , sizeof ( fps_range ) } ,
{ 166 , 1000 } , { 999 , 1000 } } ;
mmal_port_parameter_set ( preview_port , & fps_range . hdr ) ;
}
//enable dynamic framerate if necessary
if ( config - > camera_parameters . shutter_speed )
{
if ( ( ( float ) ( config - > fps_n ) / config - > fps_d ) > 1000000.0 / config - > camera_parameters . shutter_speed )
{
config - > fps_n = 0 ;
config - > fps_d = 1 ;
if ( config - > verbose )
fprintf ( stderr , " Enable dynamic frame rate to fulfil shutter speed requirement \n " ) ;
}
}
2013-10-11 08:17:05 +00:00
format - > encoding = MMAL_ENCODING_OPAQUE ;
2015-03-05 06:01:33 +00:00
format - > es - > video . width = VCOS_ALIGN_UP ( config - > width , 32 ) ;
format - > es - > video . height = VCOS_ALIGN_UP ( config - > height , 16 ) ;
2013-10-11 08:17:05 +00:00
format - > es - > video . crop . x = 0 ;
format - > es - > video . crop . y = 0 ;
2015-03-05 06:01:33 +00:00
format - > es - > video . crop . width = config - > width ;
format - > es - > video . crop . height = config - > height ;
format - > es - > video . frame_rate . num = config - > fps_n ;
format - > es - > video . frame_rate . den = config - > fps_d ;
2013-10-11 08:17:05 +00:00
status = mmal_port_format_commit ( preview_port ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " camera viewfinder format couldn't be set " ) ;
goto error ;
}
// Set the encode format on the video port
format = video_port - > format ;
format - > encoding_variant = MMAL_ENCODING_I420 ;
2015-03-05 06:01:33 +00:00
if ( config - > camera_parameters . shutter_speed > 6000000 )
{
MMAL_PARAMETER_FPS_RANGE_T fps_range = { { MMAL_PARAMETER_FPS_RANGE , sizeof ( fps_range ) } ,
{ 50 , 1000 } , { 166 , 1000 } } ;
mmal_port_parameter_set ( video_port , & fps_range . hdr ) ;
}
else if ( config - > camera_parameters . shutter_speed > 1000000 )
{
MMAL_PARAMETER_FPS_RANGE_T fps_range = { { MMAL_PARAMETER_FPS_RANGE , sizeof ( fps_range ) } ,
{ 167 , 1000 } , { 999 , 1000 } } ;
mmal_port_parameter_set ( video_port , & fps_range . hdr ) ;
}
2013-10-11 08:17:05 +00:00
format - > encoding = MMAL_ENCODING_OPAQUE ;
2015-03-05 06:01:33 +00:00
format - > es - > video . width = VCOS_ALIGN_UP ( config - > width , 32 ) ;
format - > es - > video . height = VCOS_ALIGN_UP ( config - > height , 16 ) ;
2013-10-11 08:17:05 +00:00
format - > es - > video . crop . x = 0 ;
format - > es - > video . crop . y = 0 ;
2015-03-05 06:01:33 +00:00
format - > es - > video . crop . width = config - > width ;
format - > es - > video . crop . height = config - > height ;
format - > es - > video . frame_rate . num = config - > fps_n ;
format - > es - > video . frame_rate . den = config - > fps_d ;
2013-10-11 08:17:05 +00:00
status = mmal_port_format_commit ( video_port ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " camera video format couldn't be set " ) ;
goto error ;
}
// Ensure there are enough buffers to avoid dropping frames
if ( video_port - > buffer_num < VIDEO_OUTPUT_BUFFERS_NUM )
video_port - > buffer_num = VIDEO_OUTPUT_BUFFERS_NUM ;
// Set the encode format on the still port
format = still_port - > format ;
format - > encoding = MMAL_ENCODING_OPAQUE ;
format - > encoding_variant = MMAL_ENCODING_I420 ;
2015-03-05 06:01:33 +00:00
format - > es - > video . width = VCOS_ALIGN_UP ( config - > width , 32 ) ;
format - > es - > video . height = VCOS_ALIGN_UP ( config - > height , 16 ) ;
2013-10-11 08:17:05 +00:00
format - > es - > video . crop . x = 0 ;
format - > es - > video . crop . y = 0 ;
2015-03-05 06:01:33 +00:00
format - > es - > video . crop . width = config - > width ;
format - > es - > video . crop . height = config - > height ;
format - > es - > video . frame_rate . num = 0 ;
2013-10-11 08:17:05 +00:00
format - > es - > video . frame_rate . den = 1 ;
status = mmal_port_format_commit ( still_port ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " camera still format couldn't be set " ) ;
goto error ;
}
/* Ensure there are enough buffers to avoid dropping frames */
if ( still_port - > buffer_num < VIDEO_OUTPUT_BUFFERS_NUM )
still_port - > buffer_num = VIDEO_OUTPUT_BUFFERS_NUM ;
/* Enable component */
status = mmal_component_enable ( camera ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " camera component couldn't be enabled " ) ;
goto error ;
}
2015-03-05 06:01:33 +00:00
raspicamcontrol_set_all_parameters ( camera , & config - > camera_parameters ) ;
update_annotation_data ( state ) ;
2013-10-11 08:17:05 +00:00
2015-03-05 06:01:33 +00:00
if ( config - > verbose )
2013-10-11 08:17:05 +00:00
fprintf ( stderr , " Camera component done \n " ) ;
2015-03-05 06:01:33 +00:00
return status ;
2013-10-11 08:17:05 +00:00
error :
2015-03-05 06:01:33 +00:00
if ( camera )
mmal_component_destroy ( camera ) ;
2013-10-11 08:17:05 +00:00
return status ;
}
/**
* Destroy the camera component
*
* @ param state Pointer to state control struct
*
*/
static void destroy_camera_component ( RASPIVID_STATE * state )
{
if ( state - > camera_component )
{
mmal_component_destroy ( state - > camera_component ) ;
state - > camera_component = NULL ;
}
}
2014-10-09 20:38:41 +00:00
gboolean raspi_capture_request_i_frame ( RASPIVID_STATE * state )
{
MMAL_PORT_T * encoder_output = NULL ;
MMAL_STATUS_T status ;
MMAL_PARAMETER_BOOLEAN_T param = { { MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME , sizeof ( param ) } , 1 } ;
encoder_output = state - > encoder_component - > output [ 0 ] ;
status = mmal_port_parameter_set ( encoder_output , & param . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to request I-frame " ) ;
return FALSE ;
}
return TRUE ;
}
2013-10-11 08:17:05 +00:00
/**
* Create the encoder component , set up its ports
*
* @ param state Pointer to state control struct
*
* @ return MMAL_SUCCESS if all OK , something else otherwise
*
*/
static MMAL_STATUS_T create_encoder_component ( RASPIVID_STATE * state )
{
MMAL_COMPONENT_T * encoder = 0 ;
MMAL_PORT_T * encoder_input = NULL , * encoder_output = NULL ;
MMAL_STATUS_T status ;
MMAL_POOL_T * pool ;
2015-03-05 06:01:33 +00:00
RASPIVID_CONFIG * config = state - > config ;
2013-10-11 08:17:05 +00:00
status = mmal_component_create ( MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER , & encoder ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to create video encoder component " ) ;
goto error ;
}
if ( ! encoder - > input_num | | ! encoder - > output_num )
{
status = MMAL_ENOSYS ;
vcos_log_error ( " Video encoder doesn't have input/output ports " ) ;
goto error ;
}
encoder_input = encoder - > input [ 0 ] ;
encoder_output = encoder - > output [ 0 ] ;
// We want same format on input and output
mmal_format_copy ( encoder_output - > format , encoder_input - > format ) ;
// Only supporting H264 at the moment
encoder_output - > format - > encoding = MMAL_ENCODING_H264 ;
2015-03-05 06:01:33 +00:00
encoder_output - > format - > bitrate = config - > bitrate ;
2013-10-11 08:17:05 +00:00
encoder_output - > buffer_size = encoder_output - > buffer_size_recommended ;
if ( encoder_output - > buffer_size < encoder_output - > buffer_size_min )
encoder_output - > buffer_size = encoder_output - > buffer_size_min ;
2014-03-12 13:16:18 +00:00
GST_DEBUG ( " encoder buffer size is %u " , ( guint ) encoder_output - > buffer_size ) ;
2013-10-12 08:23:03 +00:00
2013-10-11 08:17:05 +00:00
encoder_output - > buffer_num = encoder_output - > buffer_num_recommended ;
if ( encoder_output - > buffer_num < encoder_output - > buffer_num_min )
encoder_output - > buffer_num = encoder_output - > buffer_num_min ;
2015-03-05 06:01:33 +00:00
// We need to set the frame rate on output to 0, to ensure it gets
// updated correctly from the input framerate when port connected
encoder_output - > format - > es - > video . frame_rate . num = 0 ;
encoder_output - > format - > es - > video . frame_rate . den = 1 ;
2013-10-11 08:17:05 +00:00
// Commit the port changes to the output port
status = mmal_port_format_commit ( encoder_output ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set format on video encoder output port " ) ;
goto error ;
}
// Set the rate control parameter
if ( 0 )
{
MMAL_PARAMETER_VIDEO_RATECONTROL_T param = { { MMAL_PARAMETER_RATECONTROL , sizeof ( param ) } , MMAL_VIDEO_RATECONTROL_DEFAULT } ;
status = mmal_port_parameter_set ( encoder_output , & param . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set ratecontrol " ) ;
goto error ;
}
}
2015-03-05 06:01:33 +00:00
if ( config - > intraperiod ! = - 1 )
2013-10-11 08:17:05 +00:00
{
2013-10-12 14:20:51 +00:00
MMAL_PARAMETER_UINT32_T param = { { MMAL_PARAMETER_INTRAPERIOD , sizeof ( param ) } , state - > config - > intraperiod } ;
2013-10-11 08:17:05 +00:00
status = mmal_port_parameter_set ( encoder_output , & param . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set intraperiod " ) ;
goto error ;
}
2015-03-05 06:01:33 +00:00
}
if ( config - > quantisationParameter )
{
MMAL_PARAMETER_UINT32_T param = { { MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT , sizeof ( param ) } , state - > config - > quantisationParameter } ;
status = mmal_port_parameter_set ( encoder_output , & param . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set initial QP " ) ;
goto error ;
}
MMAL_PARAMETER_UINT32_T param2 = { { MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT , sizeof ( param ) } , config - > quantisationParameter } ;
status = mmal_port_parameter_set ( encoder_output , & param2 . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set min QP " ) ;
goto error ;
}
MMAL_PARAMETER_UINT32_T param3 = { { MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT , sizeof ( param ) } , config - > quantisationParameter } ;
status = mmal_port_parameter_set ( encoder_output , & param3 . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set max QP " ) ;
goto error ;
}
2013-10-11 08:17:05 +00:00
}
{
MMAL_PARAMETER_VIDEO_PROFILE_T param ;
param . hdr . id = MMAL_PARAMETER_PROFILE ;
param . hdr . size = sizeof ( param ) ;
2015-03-05 06:01:33 +00:00
param . profile [ 0 ] . profile = config - > profile ;
2013-10-11 08:17:05 +00:00
param . profile [ 0 ] . level = MMAL_VIDEO_LEVEL_H264_4 ; // This is the only value supported
status = mmal_port_parameter_set ( encoder_output , & param . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set H264 profile " ) ;
goto error ;
}
}
2015-03-05 06:01:33 +00:00
if ( mmal_port_parameter_set_boolean ( encoder_input , MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT , config - > immutableInput ) ! = MMAL_SUCCESS )
2013-10-11 08:17:05 +00:00
{
vcos_log_error ( " Unable to set immutable input flag " ) ;
// Continue rather than abort..
}
2015-03-05 06:01:33 +00:00
//set INLINE HEADER flag to generate SPS and PPS for every IDR if requested
if ( mmal_port_parameter_set_boolean ( encoder_output , MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER , config - > bInlineHeaders ) ! = MMAL_SUCCESS )
{
vcos_log_error ( " failed to set INLINE HEADER FLAG parameters " ) ;
// Continue rather than abort..
}
//set INLINE VECTORS flag to request motion vector estimates
if ( mmal_port_parameter_set_boolean ( encoder_output , MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS , config - > inlineMotionVectors ) ! = MMAL_SUCCESS )
{
vcos_log_error ( " failed to set INLINE VECTORS parameters " ) ;
// Continue rather than abort..
}
// Adaptive intra refresh settings
if ( config - > intra_refresh_type ! = - 1 )
{
MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param ;
param . hdr . id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH ;
param . hdr . size = sizeof ( param ) ;
// Get first so we don't overwrite anything unexpectedly
status = mmal_port_parameter_get ( encoder_output , & param . hdr ) ;
param . refresh_mode = config - > intra_refresh_type ;
//if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS)
// param.cir_mbs = 10;
status = mmal_port_parameter_set ( encoder_output , & param . hdr ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to set H264 intra-refresh values " ) ;
goto error ;
}
}
2013-10-11 08:17:05 +00:00
// Enable component
status = mmal_component_enable ( encoder ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Unable to enable video encoder component " ) ;
goto error ;
}
/* Create pool of buffer headers for the output port to consume */
pool = mmal_port_pool_create ( encoder_output , encoder_output - > buffer_num , encoder_output - > buffer_size ) ;
2015-03-05 06:01:33 +00:00
2013-10-11 08:17:05 +00:00
if ( ! pool )
{
vcos_log_error ( " Failed to create buffer header pool for encoder output port %s " , encoder_output - > name ) ;
}
state - > encoder_pool = pool ;
state - > encoder_component = encoder ;
2015-03-05 06:01:33 +00:00
if ( config - > verbose )
2013-10-11 08:17:05 +00:00
fprintf ( stderr , " Encoder component done \n " ) ;
return status ;
error :
if ( encoder )
mmal_component_destroy ( encoder ) ;
2015-03-05 06:01:33 +00:00
state - > encoder_component = NULL ;
2013-10-11 08:17:05 +00:00
return status ;
}
/**
* Destroy the encoder component
*
* @ param state Pointer to state control struct
*
*/
static void destroy_encoder_component ( RASPIVID_STATE * state )
{
2013-10-12 08:23:03 +00:00
/* Empty the buffer header q */
2013-10-12 14:20:51 +00:00
while ( mmal_queue_length ( state - > encoded_buffer_q ) ) {
MMAL_BUFFER_HEADER_T * buffer = mmal_queue_get ( state - > encoded_buffer_q ) ;
mmal_buffer_header_release ( buffer ) ;
}
2013-10-12 08:23:03 +00:00
mmal_queue_destroy ( state - > encoded_buffer_q ) ;
2013-10-11 08:17:05 +00:00
// Get rid of any port buffers first
if ( state - > encoder_pool )
{
mmal_port_pool_destroy ( state - > encoder_component - > output [ 0 ] , state - > encoder_pool ) ;
}
if ( state - > encoder_component )
{
mmal_component_destroy ( state - > encoder_component ) ;
state - > encoder_component = NULL ;
}
}
/**
* Connect two specific ports together
*
* @ param output_port Pointer the output port
* @ param input_port Pointer the input port
* @ param Pointer to a mmal connection pointer , reassigned if function successful
* @ return Returns a MMAL_STATUS_T giving result of operation
*
*/
static MMAL_STATUS_T connect_ports ( MMAL_PORT_T * output_port , MMAL_PORT_T * input_port , MMAL_CONNECTION_T * * connection )
{
MMAL_STATUS_T status ;
status = mmal_connection_create ( connection , output_port , input_port , MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT ) ;
if ( status = = MMAL_SUCCESS )
{
status = mmal_connection_enable ( * connection ) ;
if ( status ! = MMAL_SUCCESS )
mmal_connection_destroy ( * connection ) ;
}
return status ;
}
/**
* Checks if specified port is valid and enabled , then disables it
*
* @ param port Pointer the port
*
*/
static void check_disable_port ( MMAL_PORT_T * port )
{
if ( port & & port - > is_enabled )
mmal_port_disable ( port ) ;
}
2013-10-12 01:42:07 +00:00
void raspicapture_init ( )
2013-10-11 08:17:05 +00:00
{
bcm_host_init ( ) ;
// Register our application with the logging system
vcos_log_register ( " RaspiVid " , VCOS_LOG_CATEGORY ) ;
2013-10-12 01:42:07 +00:00
}
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
RASPIVID_STATE *
2013-10-12 14:20:51 +00:00
raspi_capture_setup ( RASPIVID_CONFIG * config )
2013-10-12 01:42:07 +00:00
{
// Our main data storage vessel..
RASPIVID_STATE * state ;
MMAL_STATUS_T status = MMAL_SUCCESS ;
/* Default everything to zero */
state = calloc ( 1 , sizeof ( RASPIVID_STATE ) ) ;
/* Apply passed in config */
2013-10-12 14:20:51 +00:00
state - > config = config ;
2013-10-12 01:42:07 +00:00
2015-03-05 06:01:33 +00:00
/* Initialize timestamping */
state - > base_time = state - > last_second = - 1 ;
2013-10-12 14:20:51 +00:00
/* So far, all we can do is create the camera component. Actual
* config and connection of encoders etc happens in _start ( )
*/
2013-10-12 01:42:07 +00:00
// OK, we have a nice set of parameters. Now set up our components
// We have three components. Camera, Preview and encoder.
if ( ( status = create_camera_component ( state ) ) ! = MMAL_SUCCESS )
{
vcos_log_error ( " %s: Failed to create camera component " , __func__ ) ;
return NULL ;
}
2013-10-12 14:20:51 +00:00
if ( ( status = raspipreview_create ( & state - > config - > preview_parameters ) ) ! = MMAL_SUCCESS )
2013-10-12 01:42:07 +00:00
{
vcos_log_error ( " %s: Failed to create preview component " , __func__ ) ;
destroy_camera_component ( state ) ;
return NULL ;
}
if ( ( status = create_encoder_component ( state ) ) ! = MMAL_SUCCESS )
{
vcos_log_error ( " %s: Failed to create encode component " , __func__ ) ;
2013-10-12 14:20:51 +00:00
raspipreview_destroy ( & state - > config - > preview_parameters ) ;
2013-10-12 01:42:07 +00:00
destroy_camera_component ( state ) ;
return NULL ;
}
2013-10-12 14:20:51 +00:00
state - > encoded_buffer_q = mmal_queue_create ( ) ;
return state ;
}
gboolean
raspi_capture_start ( RASPIVID_STATE * state )
{
MMAL_STATUS_T status = MMAL_SUCCESS ;
MMAL_PORT_T * camera_preview_port = NULL ;
MMAL_PORT_T * preview_input_port = NULL ;
MMAL_PORT_T * encoder_input_port = NULL ;
if ( state - > config - > verbose )
{
dump_state ( state ) ;
}
if ( ( status = raspi_capture_set_format_and_start ( state ) ) ! = MMAL_SUCCESS ) {
return FALSE ;
}
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
fprintf ( stderr , " Starting component connection stage \n " ) ;
camera_preview_port = state - > camera_component - > output [ MMAL_CAMERA_PREVIEW_PORT ] ;
2013-10-12 14:20:51 +00:00
preview_input_port = state - > config - > preview_parameters . preview_component - > input [ 0 ] ;
2013-10-12 01:42:07 +00:00
encoder_input_port = state - > encoder_component - > input [ 0 ] ;
2013-10-12 14:20:51 +00:00
state - > camera_video_port = state - > camera_component - > output [ MMAL_CAMERA_VIDEO_PORT ] ;
2013-10-12 01:42:07 +00:00
state - > camera_still_port = state - > camera_component - > output [ MMAL_CAMERA_CAPTURE_PORT ] ;
state - > encoder_output_port = state - > encoder_component - > output [ 0 ] ;
2013-10-12 14:20:51 +00:00
if ( state - > config - > preview_parameters . wantPreview )
2013-10-12 01:42:07 +00:00
{
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
{
fprintf ( stderr , " Connecting camera preview port to preview input port \n " ) ;
fprintf ( stderr , " Starting video preview \n " ) ;
}
// Connect camera to preview
status = connect_ports ( camera_preview_port , preview_input_port , & state - > preview_connection ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " %s: Failed to connect camera to preview " , __func__ ) ;
2013-10-12 14:20:51 +00:00
return FALSE ;
2013-10-12 01:42:07 +00:00
}
}
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
fprintf ( stderr , " Connecting camera stills port to encoder input port \n " ) ;
// Now connect the camera to the encoder
2013-10-12 14:20:51 +00:00
status = connect_ports ( state - > camera_video_port , encoder_input_port , & state - > encoder_connection ) ;
2013-10-12 01:42:07 +00:00
if ( status ! = MMAL_SUCCESS )
{
2013-10-12 14:20:51 +00:00
if ( state - > config - > preview_parameters . wantPreview )
mmal_connection_destroy ( state - > preview_connection ) ;
vcos_log_error ( " %s: Failed to connect camera video port to encoder input " , __func__ ) ;
return FALSE ;
2013-10-12 01:42:07 +00:00
}
// Set up our userdata - this is passed though to the callback where we need the information.
state - > callback_data . state = state ;
state - > callback_data . abort = 0 ;
state - > encoder_output_port - > userdata = ( struct MMAL_PORT_USERDATA_T * ) & state - > callback_data ;
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
fprintf ( stderr , " Enabling encoder output port \n " ) ;
// Enable the encoder output port and tell it its callback function
status = mmal_port_enable ( state - > encoder_output_port , encoder_buffer_callback ) ;
if ( status ! = MMAL_SUCCESS )
{
vcos_log_error ( " Failed to setup encoder output " ) ;
goto error ;
}
2013-10-12 14:20:51 +00:00
if ( state - > config - > demoMode )
2013-10-12 01:42:07 +00:00
{
// Run for the user specific time..
2013-10-12 14:20:51 +00:00
int num_iterations = state - > config - > timeout / state - > config - > demoInterval ;
2013-10-12 01:42:07 +00:00
int i ;
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
fprintf ( stderr , " Running in demo mode \n " ) ;
2013-10-12 14:20:51 +00:00
for ( i = 0 ; state - > config - > timeout = = 0 | | i < num_iterations ; i + + )
2013-10-12 01:42:07 +00:00
{
raspicamcontrol_cycle_test ( state - > camera_component ) ;
2013-10-12 14:20:51 +00:00
vcos_sleep ( state - > config - > demoInterval ) ;
2013-10-12 01:42:07 +00:00
}
}
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
fprintf ( stderr , " Starting video capture \n " ) ;
2013-10-12 01:42:07 +00:00
2013-10-12 14:20:51 +00:00
if ( mmal_port_parameter_set_boolean ( state - > camera_video_port , MMAL_PARAMETER_CAPTURE , 1 ) ! = MMAL_SUCCESS )
{
goto error ;
}
2013-10-12 01:42:07 +00:00
2013-10-12 14:20:51 +00:00
// Send all the buffers to the encoder output port
{
int num = mmal_queue_length ( state - > encoder_pool - > queue ) ;
int q ;
for ( q = 0 ; q < num ; q + + )
{
MMAL_BUFFER_HEADER_T * buffer = mmal_queue_get ( state - > encoder_pool - > queue ) ;
2013-10-12 01:42:07 +00:00
2013-10-12 14:20:51 +00:00
if ( ! buffer )
vcos_log_error ( " Unable to get a required buffer %d from pool queue " , q ) ;
2013-10-12 01:42:07 +00:00
2013-10-12 14:20:51 +00:00
if ( mmal_port_send_buffer ( state - > encoder_output_port , buffer ) ! = MMAL_SUCCESS )
vcos_log_error ( " Unable to send a buffer to encoder output port (%d) " , q ) ;
2013-10-12 01:42:07 +00:00
2013-10-12 14:20:51 +00:00
}
}
2013-10-12 01:42:07 +00:00
2013-10-12 14:20:51 +00:00
// Now wait until we need to stop. Whilst waiting we do need to check to see if we have aborted (for example
// out of storage space)
// Going to check every ABORT_INTERVAL milliseconds
2013-10-12 01:42:07 +00:00
2013-10-12 08:23:03 +00:00
#if 0
2013-10-12 14:20:51 +00:00
for ( wait = 0 ; state - > config - > timeout = = 0 | | wait < state - > config - > timeout ; wait + = ABORT_INTERVAL )
{
vcos_sleep ( ABORT_INTERVAL ) ;
if ( state - > callback_data . abort )
break ;
2013-10-12 01:42:07 +00:00
}
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
fprintf ( stderr , " Finished capture \n " ) ;
# endif
return ( status = = MMAL_SUCCESS ) ;
2013-10-11 08:17:05 +00:00
error :
2013-10-12 01:42:07 +00:00
raspi_capture_stop ( state ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
if ( status ! = MMAL_SUCCESS ) {
mmal_status_to_int ( status ) ;
raspicamcontrol_check_configuration ( 128 ) ;
}
2013-10-11 08:17:05 +00:00
2013-10-12 14:20:51 +00:00
return FALSE ;
2013-10-12 01:42:07 +00:00
}
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
void
raspi_capture_stop ( RASPIVID_STATE * state )
{
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
fprintf ( stderr , " Closing down \n " ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 14:20:51 +00:00
if ( state - > config - > preview_parameters . wantPreview )
mmal_connection_destroy ( state - > preview_connection ) ;
mmal_connection_destroy ( state - > encoder_connection ) ;
2013-10-12 01:42:07 +00:00
// Disable all our ports that are not handled by connections
check_disable_port ( state - > camera_still_port ) ;
check_disable_port ( state - > encoder_output_port ) ;
2013-10-12 14:20:51 +00:00
}
2013-10-11 08:17:05 +00:00
2013-10-12 14:20:51 +00:00
void
raspi_capture_free ( RASPIVID_STATE * state )
{
2013-10-12 01:42:07 +00:00
// Can now close our file. Note disabling ports may flush buffers which causes
// problems if we have already closed the file!
if ( state - > output_file & & state - > output_file ! = stdout )
fclose ( state - > output_file ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
/* Disable components */
if ( state - > encoder_component )
mmal_component_disable ( state - > encoder_component ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 14:20:51 +00:00
if ( state - > config - > preview_parameters . preview_component )
mmal_component_disable ( state - > config - > preview_parameters . preview_component ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
if ( state - > camera_component )
mmal_component_disable ( state - > camera_component ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
destroy_encoder_component ( state ) ;
2013-10-12 14:20:51 +00:00
raspipreview_destroy ( & state - > config - > preview_parameters ) ;
2013-10-12 01:42:07 +00:00
destroy_camera_component ( state ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 14:20:51 +00:00
if ( state - > config - > verbose )
2013-10-12 01:42:07 +00:00
fprintf ( stderr , " Close down completed, all components disconnected, disabled and destroyed \n \n " ) ;
2013-10-11 08:17:05 +00:00
2013-10-12 01:42:07 +00:00
free ( state ) ;
2013-10-11 08:17:05 +00:00
}