diff --git a/CMakeLists.txt b/CMakeLists.txt index 849eacd..535f465 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,10 +82,6 @@ SET( CMAKE_CXX_FLAGS "" ) # Dependencies ###################################################################################### -FIND_PACKAGE(Boost COMPONENTS program_options ) -SET_PACKAGE_PROPERTIES(Boost PROPERTIES URL "http://www.boost.org" DESCRIPTION "Boost library support" TYPE REQUIRED) -INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} ) - OPTION( USE_OPENCV "Add OpenCV support" ON ) IF( USE_OPENCV ) FIND_PACKAGE( OpenCV ) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 266cc0c..758c9ae 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,8 +11,9 @@ set(plaYUver_Lib_SRCS PlaYUVerFrameStats.cpp # Stream PlaYUVerStream.cpp - # Command Line Parser + # Command Parser PlaYUVerCmdParser.cpp + ProgramOptions.cpp ) IF( USE_FFMPEG ) diff --git a/lib/PlaYUVerCmdParser.cpp b/lib/PlaYUVerCmdParser.cpp index 803f0c3..b6fdc20 100644 --- a/lib/PlaYUVerCmdParser.cpp +++ b/lib/PlaYUVerCmdParser.cpp @@ -28,6 +28,7 @@ #include #include #include "PlaYUVerCmdParser.h" +#include "ProgramOptions.h" #include "PlaYUVerFrame.h" namespace plaYUVer @@ -35,9 +36,14 @@ namespace plaYUVer PlaYUVerCmdParser::PlaYUVerCmdParser() { - m_iArgc = 0; - m_apcArgv = NULL; - m_ParserOptions.add( GetCommandOpts() ); + Opts().addOptions()/**/ + ( "help", "produce help message" )/**/ + ( "version", "show version and exit" )/**/ + ( "pel_fmts", "list pixel formats" ) /**/ + ( "quality_metrics", "list supported quality metrics" )/**/ + ( "module_list", "list supported modules" ) /**/ + ( "module_list_full", "detailed list supported modules" ); + } PlaYUVerCmdParser::~PlaYUVerCmdParser() @@ -45,57 +51,23 @@ PlaYUVerCmdParser::~PlaYUVerCmdParser() } -Void PlaYUVerCmdParser::Config( Int argc, Char *argv[] ) +Options& PlaYUVerCmdParser::Opts() { - m_iArgc = argc; - m_apcArgv = argv; + return m_cParserOptions; } -po::options_description PlaYUVerCmdParser::GetCommandOpts() -{ - po::options_description inputOpts( "InputOutput" ); - inputOpts.add_options()/**/ - ( "input,i", po::value >(), "input file" ) /**/ - ( "output,o", po::value >(), "output file" ) /**/ - ( "size,s", po::value(), "size (WxH)" ) /**/ - ( "pel_fmt", po::value(), "pixel format" ) /**/ - ( "pel_fmts", "list pixel formats" ) /**/ - ( "frames,f", po::value(), "number of frames" ); - - po::options_description commonOpts( "Common" ); - commonOpts.add_options()/**/ - ( "quiet,q", "disable verbose" )/**/ - ( "help", "produce help message" )/**/ - ( "version", "show version and exit" ); - commonOpts.add( inputOpts ); - return commonOpts; -} - -Void PlaYUVerCmdParser::addOptions( po::options_description opts ) -{ - m_ParserOptions.add( opts ); -} - -Bool PlaYUVerCmdParser::parse() +Bool PlaYUVerCmdParser::parse( Int argc, Char *argv[] ) { try { - po::store( po::command_line_parser( m_iArgc, m_apcArgv ).options( m_ParserOptions ).allow_unregistered().run(), m_cOptionsMap ); - po::notify( m_cOptionsMap ); + // m_cParserOptions.setDefaults(); + const std::list& argv_unhandled = scanArgv( m_cParserOptions, argc, ( const Char** )argv ); - if( m_cOptionsMap.count( "help" ) ) + for( std::list::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++ ) { - std::cout << m_ParserOptions - << "\n"; - return false; - } - if( m_cOptionsMap.count( "version" ) ) - { - std::cout << "PlaYUVer version " - << PLAYUVER_VERSION_STRING - << "\n"; - return false; + fprintf( stderr, "Unhandled argument ignored: `%s'\n", *it ); } + if( checkListingOpts() ) { return false; @@ -118,7 +90,21 @@ Bool PlaYUVerCmdParser::parse() Bool PlaYUVerCmdParser::checkListingOpts() { Bool bRet = false; - if( m_cOptionsMap.count( "pel_fmts" ) ) + + if( m_cParserOptions["help"]->count() ) + { + doHelp( std::cout, m_cParserOptions ); + bRet |= true; + } + + if( m_cParserOptions["version"]->count() ) + { + std::cout << "PlaYUVer version " + << PLAYUVER_VERSION_STRING + << "\n"; + bRet |= true; + } + if( m_cParserOptions["pel_fmts"]->count() ) { printf( "PlaYUVer supported pixel formats: \n" ); for( UInt i = 0; i < PlaYUVerFrame::supportedPixelFormatListNames().size(); i++ ) diff --git a/lib/PlaYUVerCmdParser.h b/lib/PlaYUVerCmdParser.h index 6dbee30..e602b6e 100644 --- a/lib/PlaYUVerCmdParser.h +++ b/lib/PlaYUVerCmdParser.h @@ -23,8 +23,8 @@ */ #include "PlaYUVerDefs.h" -#include -namespace po = boost::program_options; +#include "ProgramOptions.h" + namespace plaYUVer { @@ -35,22 +35,10 @@ public: PlaYUVerCmdParser(); ~PlaYUVerCmdParser(); - Void Config( Int argc, Char *argv[] ); - Void addOptions( po::options_description ); - Bool parse(); - - po::variables_map getOptionsMap() - { - return m_cOptionsMap; - } - - static po::options_description GetCommandOpts(); - + Bool parse( Int argc, Char *argv[] ); + Options& Opts(); private: - Int m_iArgc; - Char** m_apcArgv; - po::options_description m_ParserOptions; - po::variables_map m_cOptionsMap; + Options m_cParserOptions; Bool checkListingOpts(); }; diff --git a/lib/ProgramOptions.cpp b/lib/ProgramOptions.cpp new file mode 100644 index 0000000..5421e9a --- /dev/null +++ b/lib/ProgramOptions.cpp @@ -0,0 +1,550 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2014, ITU/ISO/IEC + * 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 ITU/ISO/IEC 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 ProgramOptions.cpp + * \brief Input args handler + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ProgramOptions.h" + +using namespace std; + +namespace plaYUVer +{ + +Options::~Options() +{ + for( Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); it++ ) + { + delete *it; + } +} + +OptionBase* Options::operator[]( const std::string& optName ) +{ + return getOption( optName ); +} + +OptionBase* Options::getOption( const std::string& optName ) +{ + Options::NamesMap::iterator opt_it; + opt_it = opt_short_map.find( optName ); + if( opt_it != opt_short_map.end() ) + { + NamesPtrList opt_list = ( *opt_it ).second; + for( Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it ) + { + return ( *it )->opt; + } + } + opt_it = opt_long_map.find( optName ); + if( opt_it != opt_long_map.end() ) + { + NamesPtrList opt_list = ( *opt_it ).second; + for( Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it ) + { + return ( *it )->opt; + } + } + return NULL; +} + +void Options::addOption( OptionBase *opt ) +{ + Names* names = new Names(); + names->opt = opt; + string& opt_string = opt->opt_string; + + size_t opt_start = 0; + for( size_t opt_end = 0; opt_end != string::npos; ) + { + opt_end = opt_string.find_first_of( ',', opt_start ); + bool force_short = 0; + if( opt_string[opt_start] == '-' ) + { + opt_start++; + force_short = 1; + } + string opt_name = opt_string.substr( opt_start, opt_end - opt_start ); + if( force_short || opt_name.size() == 1 ) + { + names->opt_short.push_back( opt_name ); + opt_short_map[opt_name].push_back( names ); + } + else + { + names->opt_long.push_back( opt_name ); + opt_long_map[opt_name].push_back( names ); + } + opt_start += opt_end + 1; + } + opt_list.push_back( names ); +} + +/* Helper method to initiate adding options to Options */ +OptionSpecific Options::addOptions() +{ + return OptionSpecific( *this ); +} + +/* for all options in opts, set their storage to their specified + * default value */ +Void Options::setDefaults() +{ +// for( Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); it++ ) +// { +// ( *it )->opt->setDefault(); +// } +} + +static void setOptions( Options::NamesPtrList& opt_list, const string& value ) +{ + /* multiple options may be registered for the same name: + * allow each to parse value */ + for( Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it ) + { + ( *it )->opt->parse( value ); + } +} + +static const char spaces[41] = " "; + +/* format help text for a single option: + * using the formatting: "-x, --long", + * if a short/long option isn't specified, it is not printed + */ +static void doHelpOpt( ostream& out, const Options::Names& entry, unsigned pad_short = 0 ) +{ + pad_short = min( pad_short, 8u ); + + if( !entry.opt_short.empty() ) + { + unsigned pad = max( ( int )pad_short - ( int )entry.opt_short.front().size(), 0 ); + out << "-" + << entry.opt_short.front(); + if( !entry.opt_long.empty() ) + { + out << ", "; + } + out << &( spaces[40 - pad] ); + } + else + { + out << " "; + out << &( spaces[40 - pad_short] ); + } + + if( !entry.opt_long.empty() ) + { + out << "--" + << entry.opt_long.front(); + } +} + +/* format the help text */ +void doHelp( ostream& out, Options& opts, unsigned columns ) +{ + const unsigned pad_short = 3; + /* first pass: work out the longest option name */ + unsigned max_width = 0; + for( Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++ ) + { + ostringstream line( ios_base::out ); + doHelpOpt( line, **it, pad_short ); + max_width = max( max_width, ( unsigned )line.tellp() ); + } + + unsigned opt_width = min( max_width + 2, 28u + pad_short ) + 2; + unsigned desc_width = columns - opt_width; + + /* second pass: write out formatted option and help text. + * - align start of help text to start at opt_width + * - if the option text is longer than opt_width, place the help + * text at opt_width on the next line. + */ + for( Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++ ) + { + ostringstream line( ios_base::out ); + line << " "; + doHelpOpt( line, **it, pad_short ); + + const string& opt_desc = ( *it )->opt->opt_desc; + if( opt_desc.empty() ) + { + /* no help text: output option, skip further processing */ + cout << line.str() + << endl; + continue; + } + size_t currlength = size_t( line.tellp() ); + if( currlength > opt_width ) + { + /* if option text is too long (and would collide with the + * help text, split onto next line */ + line << endl; + currlength = 0; + } + /* split up the help text, taking into account new lines, + * (add opt_width of padding to each new line) */ + for( size_t newline_pos = 0, cur_pos = 0; cur_pos != string::npos; currlength = 0 ) + { + /* print any required padding space for vertical alignment */ + line << &( spaces[40 - opt_width + currlength] ); + newline_pos = opt_desc.find_first_of( '\n', newline_pos ); + if( newline_pos != string::npos ) + { + /* newline found, print substring (newline needn't be stripped) */ + newline_pos++; + line << opt_desc.substr( cur_pos, newline_pos - cur_pos ); + cur_pos = newline_pos; + continue; + } + if( cur_pos + desc_width > opt_desc.size() ) + { + /* no need to wrap text, remainder is less than avaliable width */ + line << opt_desc.substr( cur_pos ); + break; + } + /* find a suitable point to split text (avoid spliting in middle of word) */ + size_t split_pos = opt_desc.find_last_of( ' ', cur_pos + desc_width ); + if( split_pos != string::npos ) + { + /* eat up multiple space characters */ + split_pos = opt_desc.find_last_not_of( ' ', split_pos ) + 1; + } + + /* bad split if no suitable space to split at. fall back to width */ + bool bad_split = split_pos == string::npos || split_pos <= cur_pos; + if( bad_split ) + { + split_pos = cur_pos + desc_width; + } + line << opt_desc.substr( cur_pos, split_pos - cur_pos ); + + /* eat up any space for the start of the next line */ + if( !bad_split ) + { + split_pos = opt_desc.find_first_not_of( ' ', split_pos ); + } + cur_pos = newline_pos = split_pos; + + if( cur_pos >= opt_desc.size() ) + { + break; + } + line << endl; + } + + cout << line.str() + << endl; + } +} + +bool storePair( Options& opts, bool allow_long, bool allow_short, const string& name, const string& value ) +{ + bool found = false; + Options::NamesMap::iterator opt_it; + if( allow_long ) + { + opt_it = opts.opt_long_map.find( name ); + if( opt_it != opts.opt_long_map.end() ) + { + found = true; + } + } + + /* check for the short list */ + if( allow_short && !( found && allow_long ) ) + { + opt_it = opts.opt_short_map.find( name ); + if( opt_it != opts.opt_short_map.end() ) + { + found = true; + } + } + + if( !found ) + { + /* not found */ + cerr << "Unknown option: `" + << name + << "' (value:`" + << value + << "')" + << endl; + return false; + } + + setOptions( ( *opt_it ).second, value ); + return true; +} + +bool storePair( Options& opts, const string& name, const string& value ) +{ + return storePair( opts, true, true, name, value ); +} + +/** + * returns number of extra arguments consumed + */ +unsigned parseGNU( Options& opts, unsigned argc, const char* argv[] ) +{ + /* gnu style long options can take the forms: + * --option=arg + * --option arg + */ + string arg( argv[0] ); + size_t arg_opt_start = arg.find_first_not_of( '-' ); + size_t arg_opt_sep = arg.find_first_of( '=' ); + string option = arg.substr( arg_opt_start, arg_opt_sep - arg_opt_start ); + + unsigned extra_argc_consumed = 0; + if( arg_opt_sep == string::npos ) + { + /* no argument found => argument in argv[1] (maybe) */ + /* xxx, need to handle case where option isn't required */ +#if 0 + /* commented out, to return to true GNU style processing + * where longopts have to include an =, otherwise they are + * booleans */ + if (argc == 1) + return 0; /* run out of argv for argument */ + extra_argc_consumed = 1; +#endif + if( !storePair( opts, true, false, option, "1" ) ) + { + return 0; + } + } + else + { + /* argument occurs after option_sep */ + string val = arg.substr( arg_opt_sep + 1 ); + storePair( opts, true, false, option, val ); + } + + return extra_argc_consumed; +} + +unsigned parseSHORT( Options& opts, unsigned argc, const char* argv[] ) +{ + /* short options can take the forms: + * --option arg + * -option arg + */ + string arg( argv[0] ); + size_t arg_opt_start = arg.find_first_not_of( '-' ); + string option = arg.substr( arg_opt_start ); + /* lookup option */ + + /* argument in argv[1] */ + /* xxx, need to handle case where option isn't required */ + if( argc == 1 ) + { + cerr << "Not processing option without argument `" + << option + << "'" + << endl; + return 0; /* run out of argv for argument */ + } + storePair( opts, false, true, option, string( argv[1] ) ); + + return 1; +} + +list scanArgv( Options& opts, unsigned argc, const char* argv[] ) +{ + /* a list for anything that didn't get handled as an option */ + list non_option_arguments; + + for( unsigned i = 1; i < argc; i++ ) + { + if( argv[i][0] != '-' ) + { + non_option_arguments.push_back( argv[i] ); + continue; + } + + if( argv[i][1] == 0 ) + { + /* a lone single dash is an argument (usually signifying stdin) */ + non_option_arguments.push_back( argv[i] ); + continue; + } + + if( argv[i][1] != '-' ) + { + /* handle short (single dash) options */ +#if 0 + i += parsePOSIX(opts, argc - i, &argv[i]); +#else + i += parseSHORT( opts, argc - i, &argv[i] ); +#endif + continue; + } + + if( argv[i][2] == 0 ) + { + /* a lone double dash ends option processing */ + while( ++i < argc ) + non_option_arguments.push_back( argv[i] ); + break; + } + + /* handle long (double dash) options */ + i += parseGNU( opts, argc - i, &argv[i] ); + } + + return non_option_arguments; +} + +void scanLine( Options& opts, string& line ) +{ + /* strip any leading whitespace */ + size_t start = line.find_first_not_of( " \t\n\r" ); + if( start == string::npos ) + { + /* blank line */ + return; + } + if( line[start] == '#' ) + { + /* comment line */ + return; + } + /* look for first whitespace or ':' after the option end */ + size_t option_end = line.find_first_of( ": \t\n\r", start ); + string option = line.substr( start, option_end - start ); + + /* look for ':', eat up any whitespace first */ + start = line.find_first_not_of( " \t\n\r", option_end ); + if( start == string::npos ) + { + /* error: badly formatted line */ + return; + } + if( line[start] != ':' ) + { + /* error: badly formatted line */ + return; + } + + /* look for start of value string -- eat up any leading whitespace */ + start = line.find_first_not_of( " \t\n\r", ++start ); + if( start == string::npos ) + { + /* error: badly formatted line */ + return; + } + + /* extract the value part, which may contain embedded spaces + * by searching for a word at a time, until we hit a comment or end of line */ + size_t value_end = start; + do + { + if( line[value_end] == '#' ) + { + /* rest of line is a comment */ + value_end--; + break; + } + value_end = line.find_first_of( " \t\n\r", value_end ); + /* consume any white space, incase there is another word. + * any trailing whitespace will be removed shortly */ + value_end = line.find_first_not_of( " \t\n\r", value_end ); + } + while( value_end != string::npos ); + /* strip any trailing space from value*/ + value_end = line.find_last_not_of( " \t\n\r", value_end ); + + string value; + if( value_end >= start ) + { + value = line.substr( start, value_end + 1 - start ); + } + else + { + /* error: no value */ + return; + } + + /* store the value in option */ + storePair( opts, true, false, option, value ); +} + +void scanFile( Options& opts, istream& in ) +{ + do + { + string line; + getline( in, line ); + scanLine( opts, line ); + } + while( !!in ); +} + +/* for all options in opts, set their storage to their specified + * default value */ +void setDefaults( Options& opts ) +{ +// for( Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++ ) +// { +// ( *it )->opt->setDefault(); +// } +} + +void parseConfigFile( Options& opts, const string& filename ) +{ + ifstream cfgstream( filename.c_str(), ifstream::in ); + if( !cfgstream ) + { + cerr << "Failed to open config file: `" + << filename + << "'" + << endl; + exit( EXIT_FAILURE ); + } + scanFile( opts, cfgstream ); +} + +} // NAMESPACE + diff --git a/lib/ProgramOptions.h b/lib/ProgramOptions.h new file mode 100644 index 0000000..0007cc1 --- /dev/null +++ b/lib/ProgramOptions.h @@ -0,0 +1,348 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2014, ITU/ISO/IEC + * 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 ITU/ISO/IEC 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 ProgramOptions.h + * \brief Input args handler + */ + +#ifndef __PROGRAMOPTIONS_H__ +#define __PROGRAMOPTIONS_H__ + +#include +#include +#include +#include +#include +#include +#include "PlaYUVerDefs.h" + +namespace plaYUVer +{ + +struct Options; + +struct ParseFailure: public std::exception +{ + ParseFailure( std::string arg0, std::string val0 ) throw() : + arg( arg0 ), + val( val0 ) + { + } + + ~ParseFailure() throw() + { + } + ; + + std::string arg; + std::string val; + + const char* what() const throw() + { + return "Option Parse Failure"; + } +}; + +void doHelp( std::ostream& out, Options& opts, unsigned columns = 80 ); +unsigned parseGNU( Options& opts, unsigned argc, const char* argv[] ); +unsigned parseSHORT( Options& opts, unsigned argc, const char* argv[] ); +std::list scanArgv( Options& opts, unsigned argc, const char* argv[] ); +void scanLine( Options& opts, std::string& line ); +void scanFile( Options& opts, std::istream& in ); +void setDefaults( Options& opts ); +void setDefaults( Options* opts ); +void parseConfigFile( Options& opts, const std::string& filename ); +bool storePair( Options& opts, const std::string& name, const std::string& value ); + +/** OptionBase: Virtual base class for storing information relating to a + * specific option This base class describes common elements. Type specific + * information should be stored in a derived class. */ +struct OptionBase +{ + OptionBase( const std::string& name, const std::string& desc ) : + arg_count( 0 ), + opt_string( name ), + opt_desc( desc ) + { + } + ; + + virtual ~OptionBase() + { + } + + /* parse argument arg, to obtain a value for the option */ + virtual void parse( const std::string& arg ) = 0; + /* set the argument to the default value */ + //virtual void setDefault() = 0; + + int count() + { + return arg_count; + } + + int arg_count; + std::string opt_string; + std::string opt_desc; +}; + +/** Type specific option storage */ +struct FlagOpt: public OptionBase +{ + FlagOpt( const std::string& name, Bool default_val, const std::string& desc ) : + OptionBase( name, desc ), + opt_storage( false ), + opt_default_val( default_val ) + { + } + + void parse( const std::string& arg ) + { + opt_storage = true; + arg_count++; + } + + void setDefault() + { + opt_storage = opt_default_val; + } + Bool opt_storage; + Bool opt_default_val; +}; + +/** Type specific option storage */ +template +struct Option: public OptionBase +{ + Option( const std::string& name, T& storage, const std::string& desc ) : + OptionBase( name, desc ), + opt_storage( storage ) + { + } + + void parse( const std::string& arg ); + +// void setDefault() +// { +// opt_storage = opt_default_val; +// } + + T& opt_storage; +}; + +/* Generic parsing */ +template +inline void Option::parse( const std::string& arg ) +{ + std::istringstream arg_ss( arg, std::istringstream::in ); + arg_ss.exceptions( std::ios::failbit ); + try + { + arg_ss >> opt_storage; + } + catch( ... ) + { + throw ParseFailure( opt_string, arg ); + } + arg_count++; +} + +/* string parsing is specialized -- copy the whole string, not just the + * first word */ +template<> +inline void Option::parse( const std::string& arg ) +{ + opt_storage = arg; + arg_count++; +} + +/* string parsing is specialized -- copy the whole string, not just the + * first word */ +template<> +inline void Option >::parse( const std::string& arg ) +{ + opt_storage.push_back( arg ); + arg_count++; +} + +/** Option class for argument handling using a user provided function */ +struct OptionFunc: public OptionBase +{ + typedef Void (Func)( Options&, const std::string& ); + + OptionFunc( const std::string& name, Options& parent_, Func *func_, const std::string& desc ) : + OptionBase( name, desc ), + parent( parent_ ), + func( func_ ) + { + } + + void parse( const std::string& arg ) + { + func( parent, arg ); + arg_count++; + } + + void setDefault() + { + return; + } + +private: + Options& parent; + void (*func)( Options&, const std::string& ); +}; + +class OptionSpecific; +struct Options +{ + ~Options(); + + OptionSpecific addOptions(); + Void setDefaults(); + + struct Names + { + Names() : + opt( 0 ) + { + } + ; + ~Names() + { + if( opt ) + delete opt; + } + std::list opt_long; + std::list opt_short; + OptionBase* opt; + }; + + OptionBase* operator[]( const std::string& optName ); + OptionBase* getOption( const std::string& optName ); + + void addOption( OptionBase *opt ); + + typedef std::list NamesPtrList; + NamesPtrList opt_list; + + typedef std::map NamesMap; + NamesMap opt_long_map; + NamesMap opt_short_map; + +}; + +/* Class with templated overloaded operator(), for use by Options::addOptions() */ +class OptionSpecific +{ +public: + OptionSpecific( Options& parent_ ) : + parent( parent_ ) + { + } + + /** + * Add option described by name to the parent Options list, + * with storage for the option's value + * with default_val as the default value + * with desc as an optional help description + */ + template + OptionSpecific& + operator()( const std::string& name, T& storage, const std::string& desc ) + { + parent.addOption( new Option( name, storage, desc ) ); + return *this; + } + +// /** +// * Add option described by name to the parent Options list, +// * with storage for the option's value +// * with default_val as the default value +// * with desc as an optional help description +// */ +// template +// OptionSpecific& +// operator()( const std::string& name, T& storage, const std::string& desc ) +// { +// parent.addOption( new Option( name, storage, desc ) ); +// return *this; +// } + + /** + * Add option described by name to the parent Options list, + * with storage for the option's value + * with default_val as the default value + * with desc as an optional help description + */ + OptionSpecific& + operator()( const std::string& name, const std::string& desc ) + { + parent.addOption( new FlagOpt( name, false, desc ) ); + return *this; + } + + /** + * Add option described by name to the parent Options list, + * with storage for the option's value + * with default_val as the default value + * with desc as an optional help description + */ + OptionSpecific& + operator()( const std::string& name, Bool default_val, const std::string& desc ) + { + parent.addOption( new FlagOpt( name, default_val, desc ) ); + return *this; + } + + /** + * Add option described by name to the parent Options list, + * with desc as an optional help description + * instead of storing the value somewhere, a function of type + * OptionFunc::Func is called. It is upto this function to correctly + * handle evaluating the option's value. + */ + OptionSpecific& + operator()( const std::string& name, OptionFunc::Func *func, const std::string& desc ) + { + parent.addOption( new OptionFunc( name, parent, func, desc ) ); + return *this; + } +private: + Options& parent; +}; + +} // NAMESPACE + +#endif // __PROGRAMOPTIONS_H__ diff --git a/tools/CommandLineHandle.cpp b/tools/CommandLineHandle.cpp index 22a79d2..f6b2b70 100644 --- a/tools/CommandLineHandle.cpp +++ b/tools/CommandLineHandle.cpp @@ -26,6 +26,7 @@ #include #include "CommandLineHandle.h" #include "lib/PlaYUVerFrame.h" +#include "lib/PlaYUVerStream.h" #include "modules/PlaYUVerModuleIf.h" #include "modules/PlaYUVerModuleFactory.h" @@ -35,6 +36,9 @@ namespace plaYUVer CommandLineHandle::CommandLineHandle() { m_uiLogLevel = 0; + m_bQuiet = false; + m_strPelFmt = std::string( "YUV420p" ); + m_iFrames = -1; } CommandLineHandle::~CommandLineHandle() @@ -53,30 +57,36 @@ Void CommandLineHandle::log( UInt level, const char *fmt, ... ) } } -Int CommandLineHandle::parseToolsArgs() +Int CommandLineHandle::parseToolsArgs( Int argc, Char *argv[] ) { Int iRet = 0; - po::options_description qualityOpts( "Quality" ); - qualityOpts.add_options() /**/ - ( "quality", po::value(), "select a quality metric" ) /**/ - ( "quality_metrics", "list supported quality metrics" ); + Opts().addOptions()/**/ + ( "quiet,q", m_bQuiet, "disable verbose" ); - po::options_description moduleOpts( "Module" ); - moduleOpts.add_options() /**/ - ( "module", po::value(), "select a module (use internal name)" ) /**/ - ( "module_list", "list supported modules" ) /**/ - ( "module_list_full", "detailed list supported modules" ); + Opts().addOptions()/**/ + ( "input,i", m_apcInputs, "input file" ) /**/ + //( "input,i", m_strInput, "input file" ) /**/ + ( "output,o", m_strOutput, "output file" ) /**/ + ( "size,s", m_strResolution, "size (WxH)" ) /**/ + ( "pel_fmt", m_strPelFmt, "pixel format" ) /**/ + ( "frames,f", m_iFrames, "number of frames to parse" ); - addOptions( qualityOpts ); - addOptions( moduleOpts ); + Opts().addOptions()/**/ + ( "quality", m_strQualityMetric, "select a quality metric" ) /**/ + ( "module", m_strModule, "select a module (use internal name)" ); - if( !parse() ) + if( !parse( argc, argv ) ) { iRet = 1; } - if( getOptionsMap().count( "quality_metrics" ) ) + if( m_bQuiet ) + { + m_uiLogLevel = RESULT; + } + + if( Opts()["quality_metrics"]->count() ) { printf( "PlaYUVer supported quality metrics: \n" ); for( UInt i = 0; i < PlaYUVerFrame::supportedQualityMetricsList().size(); i++ ) @@ -86,7 +96,7 @@ Int CommandLineHandle::parseToolsArgs() iRet = 1; } - if( getOptionsMap().count( "module_list" ) || getOptionsMap().count( "module_list_full" ) ) + if( Opts()["module_list"]->count() || Opts()["module_list_full"]->count() ) { listModules(); iRet = 1; @@ -99,7 +109,7 @@ Void CommandLineHandle::listModules() { Bool bDetailed = false; - if( getOptionsMap().count( "module_list_full" ) ) + if( Opts()["module_list_full"]->count() ) bDetailed = true; PlaYUVerModuleIf* pcCurrModuleIf; diff --git a/tools/CommandLineHandle.h b/tools/CommandLineHandle.h index bbfeba2..e1a0942 100644 --- a/tools/CommandLineHandle.h +++ b/tools/CommandLineHandle.h @@ -28,12 +28,16 @@ #include "config.h" #include "lib/PlaYUVerDefs.h" #include "lib/PlaYUVerCmdParser.h" +#include "lib/ProgramOptions.h" namespace plaYUVer { +class PlaYUVerStream; + class CommandLineHandle: public PlaYUVerCmdParser { + friend class PlaYUVerTools; public: CommandLineHandle(); ~CommandLineHandle(); @@ -64,11 +68,33 @@ public: */ Void log( UInt level, const char *fmt, ... ); - Int parseToolsArgs(); - Void listModules(); + Int parseToolsArgs( Int argc, Char *argv[] ); private: + Options m_cParserOptions; UInt m_uiLogLevel; + + /** + * Command line opts for PlaYUVerTools + */ + Bool m_bShowHelp; + Bool m_bShowVersion; + Bool m_bQuiet; + std::vector m_apcInputs; + std::string m_strInput; + std::string m_strOutput; + std::string m_strResolution; + std::string m_strPelFmt; + Bool m_bListPelFmts; + Int m_iFrames; + + Bool m_bListQuality; + std::string m_strQualityMetric; + Bool m_bListModules; + std::string m_strModule; + + Void listModules(); + }; } // NAMESPACE diff --git a/tools/PlaYUVerTools.cpp b/tools/PlaYUVerTools.cpp index c3a6b82..c99a3b6 100644 --- a/tools/PlaYUVerTools.cpp +++ b/tools/PlaYUVerTools.cpp @@ -22,6 +22,7 @@ * \brief Main definition of the PlaYUVerTools APp */ +#include #include "PlaYUVerTools.h" #include "modules/PlaYUVerModuleIf.h" #include "modules/PlaYUVerModuleFactory.h" @@ -54,28 +55,23 @@ PlaYUVerTools::~PlaYUVerTools() Int PlaYUVerTools::openInputs() { - if( m_cCmdLineHandler.getOptionsMap().count( "quiet" ) ) - { - m_bVerbose = false; - m_cCmdLineHandler.setLogLevel( CommandLineHandle::RESULT ); - } /** * Create input streams */ - if( m_cCmdLineHandler.getOptionsMap().count( "input" ) ) + if( m_cCmdLineHandler.Opts()["input"]->count() ) { std::string resolutionString( "" ); - if( m_cCmdLineHandler.getOptionsMap().count( "size" ) ) + if( m_cCmdLineHandler.Opts()["size"]->count() ) { - resolutionString = m_cCmdLineHandler.getOptionsMap()["size"].as(); + resolutionString = m_cCmdLineHandler.m_strResolution; } std::string fmtString( "yuv420p" ); - if( m_cCmdLineHandler.getOptionsMap().count( "pel_fmt" ) ) + if( m_cCmdLineHandler.Opts()["pel_fmt"]->count() ) { - fmtString = m_cCmdLineHandler.getOptionsMap()["pel_fmt"].as(); + fmtString = m_cCmdLineHandler.m_strPelFmt; } - std::vector inputFileNames = m_cCmdLineHandler.getOptionsMap()["input"].as >(); + std::vector inputFileNames = m_cCmdLineHandler.m_apcInputs; PlaYUVerStream* pcStream; for( UInt i = 0; i < inputFileNames.size(); i++ ) @@ -96,30 +92,23 @@ Int PlaYUVerTools::openInputs() return 2; } } - m_uiNumberOfFrames = MAX_UINT; - if( m_cCmdLineHandler.getOptionsMap().count( "frames" ) ) - { - m_uiNumberOfFrames = m_cCmdLineHandler.getOptionsMap()["frames"].as(); - } - - m_uiNumberOfComponents = MAX_UINT; - for( UInt i = 0; i < m_apcInputStreams.size(); i++ ) - { - if( m_apcInputStreams[i]->getFrameNum() < m_uiNumberOfFrames ) - m_uiNumberOfFrames = m_apcInputStreams[i]->getFrameNum(); - if( m_apcInputStreams[i]->getCurrFrame()->getNumberChannels() < m_uiNumberOfComponents ) - m_uiNumberOfComponents = m_apcInputStreams[i]->getCurrFrame()->getNumberChannels(); - } } - return 0; -} -Int PlaYUVerTools::openOutputs() -{ - if( m_cCmdLineHandler.getOptionsMap().count( "output" ) ) + m_uiNumberOfFrames = MAX_UINT; + if( m_cCmdLineHandler.Opts()["frames"]->count() ) { - + m_uiNumberOfFrames = m_cCmdLineHandler.m_iFrames; } + + m_uiNumberOfComponents = MAX_UINT; + for( UInt i = 0; i < m_apcInputStreams.size(); i++ ) + { + if( m_apcInputStreams[i]->getFrameNum() < m_uiNumberOfFrames ) + m_uiNumberOfFrames = m_apcInputStreams[i]->getFrameNum(); + if( m_apcInputStreams[i]->getCurrFrame()->getNumberChannels() < m_uiNumberOfComponents ) + m_uiNumberOfComponents = m_apcInputStreams[i]->getCurrFrame()->getNumberChannels(); + } + return 0; } @@ -127,8 +116,7 @@ Int PlaYUVerTools::Open( Int argc, Char *argv[] ) { Int iRet = 0; - m_cCmdLineHandler.Config( argc, argv ); - if( ( iRet = m_cCmdLineHandler.parseToolsArgs() ) > 0 ) + if( ( iRet = m_cCmdLineHandler.parseToolsArgs( argc, argv ) ) > 0 ) { return iRet; } @@ -141,9 +129,9 @@ Int PlaYUVerTools::Open( Int argc, Char *argv[] ) /** * Check Quality operation */ - if( m_cCmdLineHandler.getOptionsMap().count( "quality" ) ) + if( m_cCmdLineHandler.Opts()["quality"]->count() ) { - std::string qualityMetric = m_cCmdLineHandler.getOptionsMap()["quality"].as(); + std::string qualityMetric = m_cCmdLineHandler.m_strQualityMetric; if( m_apcInputStreams.size() != 2 ) { m_cCmdLineHandler.log( CommandLineHandle::ERROR, "Invalid number of inputs! " ); @@ -169,9 +157,9 @@ Int PlaYUVerTools::Open( Int argc, Char *argv[] ) /** * Check Module operation */ - if( m_cCmdLineHandler.getOptionsMap().count( "module" ) ) + if( m_cCmdLineHandler.Opts()["module"]->count() ) { - std::string moduleName = m_cCmdLineHandler.getOptionsMap()["module"].as(); + std::string moduleName = m_cCmdLineHandler.m_strModule; PlaYUVerModuleFactoryMap& PlaYUVerModuleFactoryMap = PlaYUVerModuleFactory::Get()->getMap(); PlaYUVerModuleFactoryMap::iterator it = PlaYUVerModuleFactoryMap.begin(); @@ -196,8 +184,9 @@ Int PlaYUVerTools::Open( Int argc, Char *argv[] ) { // Check outputs std::vector outputFileNames; - if( m_cCmdLineHandler.getOptionsMap().count( "output" ) ) - outputFileNames = m_cCmdLineHandler.getOptionsMap()["output"].as >(); + if( m_cCmdLineHandler.Opts()["output"]->count() ) + outputFileNames.push_back( m_cCmdLineHandler.m_strOutput ); + ; if( outputFileNames.size() == 1 ) { diff --git a/tools/PlaYUVerTools.h b/tools/PlaYUVerTools.h index 966e366..d39f9a7 100644 --- a/tools/PlaYUVerTools.h +++ b/tools/PlaYUVerTools.h @@ -64,7 +64,6 @@ private: std::vector m_apcOutputStreams; Int openInputs(); - Int openOutputs(); typedef Int (PlaYUVerTools::*FpProcess) (); FpProcess m_fpProcess;