webpmux: allow reading argument from a file
if a single text file name is supplied as argument (e.g.: 'webpmux my_long_list_of_frames.txt'), the command line arguments are actually parsed from this file. Tokenizer will remove space, tabs, LF, CR, returns, etc. + changed ImgIoUtilReadFile() to return a null-terminated data, for convenience. + misc clean-up in the code BUG=webp:355 Change-Id: I76796305641d660933de5881763d723006712fa9 ---
This commit is contained in:
parent
b69f18a73a
commit
a7f93fe32d
@ -33,6 +33,7 @@ Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
|
|||||||
webpmux -info INPUT
|
webpmux -info INPUT
|
||||||
webpmux [-h|-help]
|
webpmux [-h|-help]
|
||||||
webpmux -version
|
webpmux -version
|
||||||
|
webpmux argument_file_name
|
||||||
|
|
||||||
GET_OPTIONS:
|
GET_OPTIONS:
|
||||||
Extract relevant data:
|
Extract relevant data:
|
||||||
@ -92,6 +93,9 @@ INPUT & OUTPUT are in WebP format.
|
|||||||
Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
|
Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
|
||||||
valid.
|
valid.
|
||||||
|
|
||||||
|
Note: if a single file name is passed as the argument, the arguments will be
|
||||||
|
tokenized from this file. The file name must not start with the character '-'.
|
||||||
|
|
||||||
Visualization tool:
|
Visualization tool:
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
webpmux -info in.webp
|
webpmux -info in.webp
|
||||||
webpmux [ -h | -help ]
|
webpmux [ -h | -help ]
|
||||||
webpmux -version
|
webpmux -version
|
||||||
|
webpmux argument_file_name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
@ -108,28 +109,29 @@ static const char* const kDescriptions[LAST_FEATURE] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FeatureType type_;
|
// command line arguments
|
||||||
FeatureArg* args_;
|
int argc_;
|
||||||
int arg_count_;
|
const char** argv_;
|
||||||
} Feature;
|
WebPData argv_data_;
|
||||||
|
int own_argv_;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ActionType action_type_;
|
ActionType action_type_;
|
||||||
const char* input_;
|
const char* input_;
|
||||||
const char* output_;
|
const char* output_;
|
||||||
Feature feature_;
|
FeatureType type_;
|
||||||
} WebPMuxConfig;
|
FeatureArg* args_;
|
||||||
|
int arg_count_;
|
||||||
|
} Config;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Helper functions.
|
// Helper functions.
|
||||||
|
|
||||||
static int CountOccurrences(const char* arglist[], int list_length,
|
static int CountOccurrences(const Config* const config, const char* arg) {
|
||||||
const char* arg) {
|
|
||||||
int i;
|
int i;
|
||||||
int num_occurences = 0;
|
int num_occurences = 0;
|
||||||
|
|
||||||
for (i = 0; i < list_length; ++i) {
|
for (i = 0; i < config->argc_; ++i) {
|
||||||
if (!strcmp(arglist[i], arg)) {
|
if (!strcmp(config->argv_[i], arg)) {
|
||||||
++num_occurences;
|
++num_occurences;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,6 +303,7 @@ static void PrintHelp(void) {
|
|||||||
printf(" webpmux -info INPUT\n");
|
printf(" webpmux -info INPUT\n");
|
||||||
printf(" webpmux [-h|-help]\n");
|
printf(" webpmux [-h|-help]\n");
|
||||||
printf(" webpmux -version\n");
|
printf(" webpmux -version\n");
|
||||||
|
printf(" webpmux argument_file_name\n");
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("GET_OPTIONS:\n");
|
printf("GET_OPTIONS:\n");
|
||||||
@ -369,6 +372,10 @@ static void PrintHelp(void) {
|
|||||||
|
|
||||||
printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
|
printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
|
||||||
printf(" and is assumed to be\nvalid.\n");
|
printf(" and is assumed to be\nvalid.\n");
|
||||||
|
printf("\nNote: if a single file name is passed as the argument, the "
|
||||||
|
"arguments will be\n");
|
||||||
|
printf("tokenized from this file. The file name must not start with "
|
||||||
|
"the character '-'.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
|
static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
|
||||||
@ -394,7 +401,7 @@ static int CreateMux(const char* const filename, WebPMux** mux) {
|
|||||||
assert(mux != NULL);
|
assert(mux != NULL);
|
||||||
if (!ReadFileToWebPData(filename, &bitstream)) return 0;
|
if (!ReadFileToWebPData(filename, &bitstream)) return 0;
|
||||||
*mux = WebPMuxCreate(&bitstream, 1);
|
*mux = WebPMuxCreate(&bitstream, 1);
|
||||||
free((void*)bitstream.bytes);
|
WebPDataClear(&bitstream);
|
||||||
if (*mux != NULL) return 1;
|
if (*mux != NULL) return 1;
|
||||||
fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
|
fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
|
||||||
return 0;
|
return 0;
|
||||||
@ -517,10 +524,15 @@ static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Clean-up.
|
// Clean-up.
|
||||||
|
|
||||||
static void DeleteConfig(WebPMuxConfig* config) {
|
static void DeleteConfig(Config* const config) {
|
||||||
if (config != NULL) {
|
if (config != NULL) {
|
||||||
free(config->feature_.args_);
|
free(config->args_);
|
||||||
|
if (config->own_argv_) {
|
||||||
|
free(config->argv_);
|
||||||
|
WebPDataClear(&config->argv_data_);
|
||||||
|
}
|
||||||
memset(config, 0, sizeof(*config));
|
memset(config, 0, sizeof(*config));
|
||||||
|
WebPDataInit(&config->argv_data_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,7 +543,7 @@ static void DeleteConfig(WebPMuxConfig* config) {
|
|||||||
// Returns 1 on valid, 0 otherwise.
|
// Returns 1 on valid, 0 otherwise.
|
||||||
// Also fills up num_feature_args to be number of feature arguments given.
|
// Also fills up num_feature_args to be number of feature arguments given.
|
||||||
// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
|
// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
|
||||||
static int ValidateCommandLine(int argc, const char* argv[],
|
static int ValidateCommandLine(const Config* const config,
|
||||||
int* num_feature_args) {
|
int* num_feature_args) {
|
||||||
int num_frame_args;
|
int num_frame_args;
|
||||||
int num_loop_args;
|
int num_loop_args;
|
||||||
@ -543,27 +555,27 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
*num_feature_args = 0;
|
*num_feature_args = 0;
|
||||||
|
|
||||||
// Simple checks.
|
// Simple checks.
|
||||||
if (CountOccurrences(argv, argc, "-get") > 1) {
|
if (CountOccurrences(config, "-get") > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
if (CountOccurrences(argv, argc, "-set") > 1) {
|
if (CountOccurrences(config, "-set") > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
if (CountOccurrences(argv, argc, "-strip") > 1) {
|
if (CountOccurrences(config, "-strip") > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
if (CountOccurrences(argv, argc, "-info") > 1) {
|
if (CountOccurrences(config, "-info") > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
if (CountOccurrences(argv, argc, "-o") > 1) {
|
if (CountOccurrences(config, "-o") > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compound checks.
|
// Compound checks.
|
||||||
num_frame_args = CountOccurrences(argv, argc, "-frame");
|
num_frame_args = CountOccurrences(config, "-frame");
|
||||||
num_loop_args = CountOccurrences(argv, argc, "-loop");
|
num_loop_args = CountOccurrences(config, "-loop");
|
||||||
num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
|
num_bgcolor_args = CountOccurrences(config, "-bgcolor");
|
||||||
num_durations_args = CountOccurrences(argv, argc, "-duration");
|
num_durations_args = CountOccurrences(config, "-duration");
|
||||||
|
|
||||||
if (num_loop_args > 1) {
|
if (num_loop_args > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
|
||||||
@ -598,7 +610,7 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
|
|
||||||
#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
|
#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
|
||||||
|
|
||||||
#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
|
#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
|
||||||
|
|
||||||
#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
|
#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
|
||||||
if (argc < i + (NUM)) { \
|
if (argc < i + (NUM)) { \
|
||||||
@ -614,15 +626,15 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
|
|
||||||
// Parses command-line arguments to fill up config object. Also performs some
|
// Parses command-line arguments to fill up config object. Also performs some
|
||||||
// semantic checks.
|
// semantic checks.
|
||||||
static int ParseCommandLine(int argc, const char* argv[],
|
static int ParseCommandLine(Config* config) {
|
||||||
WebPMuxConfig* config) {
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int feature_arg_index = 0;
|
int feature_arg_index = 0;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
|
int argc = config->argc_;
|
||||||
|
const char* const* argv = config->argv_;
|
||||||
|
|
||||||
while (i < argc) {
|
while (i < argc) {
|
||||||
Feature* const feature = &config->feature_;
|
FeatureArg* const arg = &config->args_[feature_arg_index];
|
||||||
FeatureArg* const arg = &feature->args_[feature_arg_index];
|
|
||||||
if (argv[i][0] == '-') { // One of the action types or output.
|
if (argv[i][0] == '-') { // One of the action types or output.
|
||||||
if (!strcmp(argv[i], "-set")) {
|
if (!strcmp(argv[i], "-set")) {
|
||||||
if (ACTION_IS_NIL) {
|
if (ACTION_IS_NIL) {
|
||||||
@ -638,8 +650,8 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_DURATION) {
|
if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
|
||||||
feature->type_ = FEATURE_DURATION;
|
config->type_ = FEATURE_DURATION;
|
||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
@ -656,7 +668,7 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else if (!strcmp(argv[i], "-strip")) {
|
} else if (!strcmp(argv[i], "-strip")) {
|
||||||
if (ACTION_IS_NIL) {
|
if (ACTION_IS_NIL) {
|
||||||
config->action_type_ = ACTION_STRIP;
|
config->action_type_ = ACTION_STRIP;
|
||||||
feature->arg_count_ = 0;
|
config->arg_count_ = 0;
|
||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
@ -668,8 +680,8 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
|
if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
|
||||||
feature->type_ = FEATURE_ANMF;
|
config->type_ = FEATURE_ANMF;
|
||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
@ -685,8 +697,8 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
|
if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
|
||||||
feature->type_ = FEATURE_ANMF;
|
config->type_ = FEATURE_ANMF;
|
||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
@ -705,7 +717,7 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
} else {
|
} else {
|
||||||
config->action_type_ = ACTION_INFO;
|
config->action_type_ = ACTION_INFO;
|
||||||
feature->arg_count_ = 0;
|
config->arg_count_ = 0;
|
||||||
config->input_ = argv[i + 1];
|
config->input_ = argv[i + 1];
|
||||||
}
|
}
|
||||||
i += 2;
|
i += 2;
|
||||||
@ -741,7 +753,7 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
|
if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
|
||||||
!strcmp(argv[i], "xmp")) {
|
!strcmp(argv[i], "xmp")) {
|
||||||
if (FEATURETYPE_IS_NIL) {
|
if (FEATURETYPE_IS_NIL) {
|
||||||
feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
|
config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
|
||||||
(!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
|
(!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
|
||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
@ -757,7 +769,7 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else if (!strcmp(argv[i], "frame") &&
|
} else if (!strcmp(argv[i], "frame") &&
|
||||||
(config->action_type_ == ACTION_GET)) {
|
(config->action_type_ == ACTION_GET)) {
|
||||||
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
||||||
feature->type_ = FEATURE_ANMF;
|
config->type_ = FEATURE_ANMF;
|
||||||
arg->params_ = argv[i + 1];
|
arg->params_ = argv[i + 1];
|
||||||
++feature_arg_index;
|
++feature_arg_index;
|
||||||
i += 2;
|
i += 2;
|
||||||
@ -777,9 +789,8 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Additional checks after config is filled.
|
// Additional checks after config is filled.
|
||||||
static int ValidateConfig(WebPMuxConfig* config) {
|
static int ValidateConfig(Config* const config) {
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
Feature* const feature = &config->feature_;
|
|
||||||
|
|
||||||
// Action.
|
// Action.
|
||||||
if (ACTION_IS_NIL) {
|
if (ACTION_IS_NIL) {
|
||||||
@ -795,7 +806,7 @@ static int ValidateConfig(WebPMuxConfig* config) {
|
|||||||
if (config->input_ == NULL) {
|
if (config->input_ == NULL) {
|
||||||
if (config->action_type_ != ACTION_SET) {
|
if (config->action_type_ != ACTION_SET) {
|
||||||
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
||||||
} else if (feature->type_ != FEATURE_ANMF) {
|
} else if (config->type_ != FEATURE_ANMF) {
|
||||||
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -809,29 +820,63 @@ static int ValidateConfig(WebPMuxConfig* config) {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create config object from command-line arguments.
|
#define MAX_ARGC 16384
|
||||||
static int InitializeConfig(int argc, const char* argv[],
|
static int SetArguments(int argc, const char* argv[],
|
||||||
WebPMuxConfig* config) {
|
Config* const config) {
|
||||||
int num_feature_args = 0;
|
|
||||||
int ok = 1;
|
|
||||||
|
|
||||||
assert(config != NULL);
|
assert(config != NULL);
|
||||||
memset(config, 0, sizeof(*config));
|
memset(config, 0, sizeof(*config));
|
||||||
|
WebPDataInit(&config->argv_data_);
|
||||||
|
if (argc == 1 && argv[0][0] != '-') {
|
||||||
|
char* cur;
|
||||||
|
const char sep[] = " \t\r\n\f\v";
|
||||||
|
if (!ReadFileToWebPData(argv[0], &config->argv_data_)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
config->own_argv_ = 1;
|
||||||
|
config->argv_ = (const char**)malloc(MAX_ARGC * sizeof(*config->argv_));
|
||||||
|
if (config->argv_ == NULL) return 0;
|
||||||
|
|
||||||
|
argc = 0;
|
||||||
|
for (cur = strtok((char*)config->argv_data_.bytes, sep);
|
||||||
|
cur != NULL;
|
||||||
|
cur = strtok(NULL, sep)) {
|
||||||
|
if (argc == MAX_ARGC) {
|
||||||
|
fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
assert(strlen(cur) != 0);
|
||||||
|
config->argv_[argc++] = cur;
|
||||||
|
}
|
||||||
|
config->argc_ = argc;
|
||||||
|
} else {
|
||||||
|
config->argc_ = argc;
|
||||||
|
config->argv_ = argv;
|
||||||
|
config->own_argv_ = 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create config object from command-line arguments.
|
||||||
|
static int InitializeConfig(int argc, const char* argv[],
|
||||||
|
Config* const config) {
|
||||||
|
int num_feature_args = 0;
|
||||||
|
int ok = SetArguments(argc, argv, config);
|
||||||
|
|
||||||
|
if (!ok) return 0;
|
||||||
|
|
||||||
// Validate command-line arguments.
|
// Validate command-line arguments.
|
||||||
if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
|
if (!ValidateCommandLine(config, &num_feature_args)) {
|
||||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
config->feature_.arg_count_ = num_feature_args;
|
config->arg_count_ = num_feature_args;
|
||||||
config->feature_.args_ =
|
config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
|
||||||
(FeatureArg*)calloc(num_feature_args, sizeof(*config->feature_.args_));
|
if (config->args_ == NULL) {
|
||||||
if (config->feature_.args_ == NULL) {
|
|
||||||
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
|
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse command-line.
|
// Parse command-line.
|
||||||
if (!ParseCommandLine(argc, argv, config) || !ValidateConfig(config)) {
|
if (!ParseCommandLine(config) || !ValidateConfig(config)) {
|
||||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,7 +892,7 @@ static int InitializeConfig(int argc, const char* argv[],
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Processing.
|
// Processing.
|
||||||
|
|
||||||
static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) {
|
static int GetFrame(const WebPMux* mux, const Config* config) {
|
||||||
WebPMuxError err = WEBP_MUX_OK;
|
WebPMuxError err = WEBP_MUX_OK;
|
||||||
WebPMux* mux_single = NULL;
|
WebPMux* mux_single = NULL;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
@ -857,7 +902,7 @@ static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) {
|
|||||||
WebPMuxFrameInfo info;
|
WebPMuxFrameInfo info;
|
||||||
WebPDataInit(&info.bitstream);
|
WebPDataInit(&info.bitstream);
|
||||||
|
|
||||||
num = ExUtilGetInt(config->feature_.args_[0].params_, 10, &parse_error);
|
num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
|
||||||
if (num < 0) {
|
if (num < 0) {
|
||||||
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
|
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
|
||||||
}
|
}
|
||||||
@ -891,18 +936,17 @@ static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read and process config.
|
// Read and process config.
|
||||||
static int Process(const WebPMuxConfig* config) {
|
static int Process(const Config* config) {
|
||||||
WebPMux* mux = NULL;
|
WebPMux* mux = NULL;
|
||||||
WebPData chunk;
|
WebPData chunk;
|
||||||
WebPMuxError err = WEBP_MUX_OK;
|
WebPMuxError err = WEBP_MUX_OK;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const Feature* const feature = &config->feature_;
|
|
||||||
|
|
||||||
switch (config->action_type_) {
|
switch (config->action_type_) {
|
||||||
case ACTION_GET: {
|
case ACTION_GET: {
|
||||||
ok = CreateMux(config->input_, &mux);
|
ok = CreateMux(config->input_, &mux);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
switch (feature->type_) {
|
switch (config->type_) {
|
||||||
case FEATURE_ANMF:
|
case FEATURE_ANMF:
|
||||||
ok = GetFrame(mux, config);
|
ok = GetFrame(mux, config);
|
||||||
break;
|
break;
|
||||||
@ -910,10 +954,10 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
case FEATURE_ICCP:
|
case FEATURE_ICCP:
|
||||||
case FEATURE_EXIF:
|
case FEATURE_EXIF:
|
||||||
case FEATURE_XMP:
|
case FEATURE_XMP:
|
||||||
err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
|
err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
|
ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
|
||||||
ErrorString(err), kDescriptions[feature->type_], Err2);
|
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||||
}
|
}
|
||||||
ok = WriteData(config->output_, &chunk);
|
ok = WriteData(config->output_, &chunk);
|
||||||
break;
|
break;
|
||||||
@ -925,7 +969,7 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACTION_SET: {
|
case ACTION_SET: {
|
||||||
switch (feature->type_) {
|
switch (config->type_) {
|
||||||
case FEATURE_ANMF: {
|
case FEATURE_ANMF: {
|
||||||
int i;
|
int i;
|
||||||
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
||||||
@ -934,11 +978,11 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
||||||
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
||||||
}
|
}
|
||||||
for (i = 0; i < feature->arg_count_; ++i) {
|
for (i = 0; i < config->arg_count_; ++i) {
|
||||||
switch (feature->args_[i].subtype_) {
|
switch (config->args_[i].subtype_) {
|
||||||
case SUBTYPE_BGCOLOR: {
|
case SUBTYPE_BGCOLOR: {
|
||||||
uint32_t bgcolor;
|
uint32_t bgcolor;
|
||||||
ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
|
ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
ERROR_GOTO1("ERROR: Could not parse the background color \n",
|
ERROR_GOTO1("ERROR: Could not parse the background color \n",
|
||||||
Err2);
|
Err2);
|
||||||
@ -949,7 +993,7 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
case SUBTYPE_LOOP: {
|
case SUBTYPE_LOOP: {
|
||||||
int parse_error = 0;
|
int parse_error = 0;
|
||||||
const int loop_count =
|
const int loop_count =
|
||||||
ExUtilGetInt(feature->args_[i].params_, 10, &parse_error);
|
ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
|
||||||
if (loop_count < 0 || loop_count > 65535) {
|
if (loop_count < 0 || loop_count > 65535) {
|
||||||
// Note: This is only a 'necessary' condition for loop_count
|
// Note: This is only a 'necessary' condition for loop_count
|
||||||
// to be valid. The 'sufficient' conditioned in checked in
|
// to be valid. The 'sufficient' conditioned in checked in
|
||||||
@ -965,10 +1009,10 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
case SUBTYPE_ANMF: {
|
case SUBTYPE_ANMF: {
|
||||||
WebPMuxFrameInfo frame;
|
WebPMuxFrameInfo frame;
|
||||||
frame.id = WEBP_CHUNK_ANMF;
|
frame.id = WEBP_CHUNK_ANMF;
|
||||||
ok = ReadFileToWebPData(feature->args_[i].filename_,
|
ok = ReadFileToWebPData(config->args_[i].filename_,
|
||||||
&frame.bitstream);
|
&frame.bitstream);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
ok = ParseFrameArgs(feature->args_[i].params_, &frame);
|
ok = ParseFrameArgs(config->args_[i].params_, &frame);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
WebPDataClear(&frame.bitstream);
|
WebPDataClear(&frame.bitstream);
|
||||||
ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
|
ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
|
||||||
@ -1001,13 +1045,13 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
case FEATURE_XMP: {
|
case FEATURE_XMP: {
|
||||||
ok = CreateMux(config->input_, &mux);
|
ok = CreateMux(config->input_, &mux);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
|
ok = ReadFileToWebPData(config->args_[0].filename_, &chunk);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
|
err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
|
||||||
free((void*)chunk.bytes);
|
free((void*)chunk.bytes);
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
|
ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
|
||||||
ErrorString(err), kDescriptions[feature->type_], Err2);
|
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1043,11 +1087,11 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
for (i = 0; i < num_frames; ++i) durations[i] = -1;
|
for (i = 0; i < num_frames; ++i) durations[i] = -1;
|
||||||
|
|
||||||
// Parse intervals to process.
|
// Parse intervals to process.
|
||||||
for (i = 0; i < feature->arg_count_; ++i) {
|
for (i = 0; i < config->arg_count_; ++i) {
|
||||||
int k;
|
int k;
|
||||||
int args[3];
|
int args[3];
|
||||||
int duration, start, end;
|
int duration, start, end;
|
||||||
const int nb_args = ExUtilGetInts(feature->args_[i].params_,
|
const int nb_args = ExUtilGetInts(config->args_[i].params_,
|
||||||
10, 3, args);
|
10, 3, args);
|
||||||
ok = (nb_args >= 1);
|
ok = (nb_args >= 1);
|
||||||
if (!ok) goto Err3;
|
if (!ok) goto Err3;
|
||||||
@ -1105,12 +1149,12 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
case ACTION_STRIP: {
|
case ACTION_STRIP: {
|
||||||
ok = CreateMux(config->input_, &mux);
|
ok = CreateMux(config->input_, &mux);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
|
if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
|
||||||
feature->type_ == FEATURE_XMP) {
|
config->type_ == FEATURE_XMP) {
|
||||||
err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
|
err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
|
ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
|
||||||
ErrorString(err), kDescriptions[feature->type_], Err2);
|
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
|
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
|
||||||
@ -1140,7 +1184,7 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
// Main.
|
// Main.
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
WebPMuxConfig config;
|
Config config;
|
||||||
int ok = InitializeConfig(argc - 1, argv + 1, &config);
|
int ok = InitializeConfig(argc - 1, argv + 1, &config);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
ok = Process(&config);
|
ok = Process(&config);
|
||||||
|
@ -47,7 +47,8 @@ int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
|||||||
while (!feof(stdin)) {
|
while (!feof(stdin)) {
|
||||||
// We double the buffer size each time and read as much as possible.
|
// We double the buffer size each time and read as much as possible.
|
||||||
const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
|
const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
|
||||||
void* const new_data = realloc(input, max_size + extra_size);
|
// we allocate one extra byte for the \0 terminator
|
||||||
|
void* const new_data = realloc(input, max_size + extra_size + 1);
|
||||||
if (new_data == NULL) goto Error;
|
if (new_data == NULL) goto Error;
|
||||||
input = (uint8_t*)new_data;
|
input = (uint8_t*)new_data;
|
||||||
max_size += extra_size;
|
max_size += extra_size;
|
||||||
@ -55,6 +56,7 @@ int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
|||||||
if (size < max_size) break;
|
if (size < max_size) break;
|
||||||
}
|
}
|
||||||
if (ferror(stdin)) goto Error;
|
if (ferror(stdin)) goto Error;
|
||||||
|
if (input != NULL) input[size] = '\0'; // convenient 0-terminator
|
||||||
*data = input;
|
*data = input;
|
||||||
*data_size = size;
|
*data_size = size;
|
||||||
return 1;
|
return 1;
|
||||||
@ -68,7 +70,7 @@ int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
|||||||
int ImgIoUtilReadFile(const char* const file_name,
|
int ImgIoUtilReadFile(const char* const file_name,
|
||||||
const uint8_t** data, size_t* data_size) {
|
const uint8_t** data, size_t* data_size) {
|
||||||
int ok;
|
int ok;
|
||||||
void* file_data;
|
uint8_t* file_data;
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
FILE* in;
|
FILE* in;
|
||||||
const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
|
const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
|
||||||
@ -87,7 +89,8 @@ int ImgIoUtilReadFile(const char* const file_name,
|
|||||||
fseek(in, 0, SEEK_END);
|
fseek(in, 0, SEEK_END);
|
||||||
file_size = ftell(in);
|
file_size = ftell(in);
|
||||||
fseek(in, 0, SEEK_SET);
|
fseek(in, 0, SEEK_SET);
|
||||||
file_data = malloc(file_size);
|
// we allocate one extra byte for the \0 terminator
|
||||||
|
file_data = (uint8_t*)malloc(file_size + 1);
|
||||||
if (file_data == NULL) {
|
if (file_data == NULL) {
|
||||||
fclose(in);
|
fclose(in);
|
||||||
fprintf(stderr, "memory allocation failure when reading file %s\n",
|
fprintf(stderr, "memory allocation failure when reading file %s\n",
|
||||||
@ -103,11 +106,14 @@ int ImgIoUtilReadFile(const char* const file_name,
|
|||||||
free(file_data);
|
free(file_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
*data = (uint8_t*)file_data;
|
file_data[file_size] = '\0'; // convenient 0-terminator
|
||||||
|
*data = file_data;
|
||||||
*data_size = file_size;
|
*data_size = file_size;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
int ImgIoUtilWriteFile(const char* const file_name,
|
int ImgIoUtilWriteFile(const char* const file_name,
|
||||||
const uint8_t* data, size_t data_size) {
|
const uint8_t* data, size_t data_size) {
|
||||||
int ok;
|
int ok;
|
||||||
|
@ -30,6 +30,9 @@ FILE* ImgIoUtilSetBinaryMode(FILE* file);
|
|||||||
// Allocates storage for entire file 'file_name' and returns contents and size
|
// Allocates storage for entire file 'file_name' and returns contents and size
|
||||||
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
||||||
// be deleted using free().
|
// be deleted using free().
|
||||||
|
// Note: for convenience, the data will be null-terminated with an extra byte
|
||||||
|
// (not accounted for in *data_size), in case the file is text and intended
|
||||||
|
// to be used as a C-string.
|
||||||
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
||||||
// the function ImgIoUtilReadFromStdin().
|
// the function ImgIoUtilReadFromStdin().
|
||||||
int ImgIoUtilReadFile(const char* const file_name,
|
int ImgIoUtilReadFile(const char* const file_name,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH WEBPMUX 1 "November 10, 2016"
|
.TH WEBPMUX 1 "December 1, 2017"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
webpmux \- create animated WebP files from non\-animated WebP images, extract
|
webpmux \- create animated WebP files from non\-animated WebP images, extract
|
||||||
frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
|
frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
|
||||||
@ -48,6 +48,8 @@ frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
|
|||||||
.B webpmux [\-h|\-help]
|
.B webpmux [\-h|\-help]
|
||||||
.br
|
.br
|
||||||
.B webpmux \-version
|
.B webpmux \-version
|
||||||
|
.br
|
||||||
|
.B webpmux argument_file_name
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
This manual page documents the
|
This manual page documents the
|
||||||
.B webpmux
|
.B webpmux
|
||||||
@ -55,6 +57,9 @@ command.
|
|||||||
.PP
|
.PP
|
||||||
\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as
|
\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as
|
||||||
to add/extract/strip XMP/EXIF metadata and ICC profile.
|
to add/extract/strip XMP/EXIF metadata and ICC profile.
|
||||||
|
If a single file name (not starting with the character '\-') is supplied as
|
||||||
|
the argument, the command line argument are actually tokenized from this file.
|
||||||
|
This allows for easy scripting or using large number of arguments.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.SS GET_OPTIONS (\-get):
|
.SS GET_OPTIONS (\-get):
|
||||||
.TP
|
.TP
|
||||||
|
Loading…
Reference in New Issue
Block a user