Merge "add a "-duration duration,start,end" option to webpmux"

This commit is contained in:
Urvang Joshi 2016-11-09 19:05:12 +00:00 committed by Gerrit Code Review
commit f90c60d129
5 changed files with 209 additions and 3 deletions

View File

@ -25,6 +25,8 @@ A list of options is available using the -help command line flag:
> webpmux -help > webpmux -help
Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
webpmux -set SET_OPTIONS INPUT -o OUTPUT webpmux -set SET_OPTIONS INPUT -o OUTPUT
webpmux -duration DURATION_OPTIONS [-duration ...]
INPUT -o OUTPUT
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT] webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
[-bgcolor BACKGROUND_COLOR] -o OUTPUT [-bgcolor BACKGROUND_COLOR] -o OUTPUT
@ -48,6 +50,14 @@ SET_OPTIONS:
'file.exif' contains the EXIF metadata to be set 'file.exif' contains the EXIF metadata to be set
'file.xmp' contains the XMP metadata to be set 'file.xmp' contains the XMP metadata to be set
DURATION_OPTIONS:
Set constant duration of frames:
duration[,start[,end]]
where: 'duration' is the duration in milliseconds,
'start' is the start frame index (optional)(default=1),
'end' is the inclusive end frame index (optional).
The special value '0' means: last frame (default=0).
STRIP_OPTIONS: STRIP_OPTIONS:
Strip color profile/metadata: Strip color profile/metadata:
icc strip ICC profile icc strip ICC profile

View File

@ -14,6 +14,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// String parsing // String parsing
@ -33,6 +34,18 @@ int ExUtilGetInt(const char* const v, int base, int* const error) {
return (int)ExUtilGetUInt(v, base, error); return (int)ExUtilGetUInt(v, base, error);
} }
int ExUtilGetInts(const char* v, int base, int max_output, int output[]) {
int n, error = 0;
for (n = 0; v != NULL && n < max_output; ++n) {
const int value = ExUtilGetInt(v, base, &error);
if (error) return -1;
output[n] = value;
v = strchr(v, ',');
if (v != NULL) ++v; // skip over the trailing ','
}
return n;
}
float ExUtilGetFloat(const char* const v, int* const error) { float ExUtilGetFloat(const char* const v, int* const error) {
char* end = NULL; char* end = NULL;
const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f; const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f;

View File

@ -29,6 +29,12 @@ uint32_t ExUtilGetUInt(const char* const v, int base, int* const error);
int ExUtilGetInt(const char* const v, int base, int* const error); int ExUtilGetInt(const char* const v, int base, int* const error);
float ExUtilGetFloat(const char* const v, int* const error); float ExUtilGetFloat(const char* const v, int* const error);
// This variant of ExUtilGetInt() will parse multiple integers from a
// comma-separated list. Up to 'max_output' integers are parsed.
// The result is placed in the output[] array, and the number of integers
// actually parsed is returned, or -1 if an error occurred.
int ExUtilGetInts(const char* v, int base, int max_output, int output[]);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View File

@ -38,6 +38,11 @@
webpmux -strip exif in.webp -o out.webp webpmux -strip exif in.webp -o out.webp
webpmux -strip xmp in.webp -o out.webp webpmux -strip xmp in.webp -o out.webp
Change duration of frame intervals:
webpmux -duration 150 in.webp -o out.webp
webpmux -duration 33,10,0 in.webp -o out.webp
webpmux -duration 200,2 -duration 150,0,50 in.webp -o out.webp
Misc: Misc:
webpmux -info in.webp webpmux -info in.webp
webpmux [ -h | -help ] webpmux [ -h | -help ]
@ -66,7 +71,8 @@ typedef enum {
ACTION_SET, ACTION_SET,
ACTION_STRIP, ACTION_STRIP,
ACTION_INFO, ACTION_INFO,
ACTION_HELP ACTION_HELP,
ACTION_DURATION
} ActionType; } ActionType;
typedef enum { typedef enum {
@ -88,6 +94,7 @@ typedef enum {
FEATURE_XMP, FEATURE_XMP,
FEATURE_ICCP, FEATURE_ICCP,
FEATURE_ANMF, FEATURE_ANMF,
FEATURE_DURATION,
LAST_FEATURE LAST_FEATURE
} FeatureType; } FeatureType;
@ -285,6 +292,8 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
static void PrintHelp(void) { static void PrintHelp(void) {
printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n"); printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n"); printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
printf(" INPUT -o OUTPUT\n");
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n"); printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]" printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
"\n"); "\n");
@ -311,6 +320,15 @@ static void PrintHelp(void) {
printf(" 'file.exif' contains the EXIF metadata to be set\n"); printf(" 'file.exif' contains the EXIF metadata to be set\n");
printf(" 'file.xmp' contains the XMP metadata to be set\n"); printf(" 'file.xmp' contains the XMP metadata to be set\n");
printf("\n");
printf("DURATION_OPTIONS:\n");
printf(" Set constant duration of frames:\n");
printf(" duration[,start[,end]]\n");
printf(" where: 'duration' is the duration in milliseconds,\n");
printf(" 'start' is the start frame index (optional)(default=1),\n");
printf(" 'end' is the inclusive end frame index (optional).\n");
printf(" The special value '0' means: last frame (default=0).\n");
printf("\n"); printf("\n");
printf("STRIP_OPTIONS:\n"); printf("STRIP_OPTIONS:\n");
printf(" Strip color profile/metadata:\n"); printf(" Strip color profile/metadata:\n");
@ -411,6 +429,45 @@ static int WriteWebP(WebPMux* const mux, const char* filename) {
return ok; return ok;
} }
static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
WebPMux* new_mux = WebPMuxNew();
WebPMuxAnimParams p;
WebPMuxError err;
int i;
int ok = 1;
if (new_mux == NULL) return NULL;
err = WebPMuxGetAnimationParams(mux, &p);
if (err == WEBP_MUX_OK) {
err = WebPMuxSetAnimationParams(new_mux, &p);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("Error (%s) handling animation params.\n",
ErrorString(err), End);
}
} else {
/* it might not be an animation. Just keep moving. */
}
for (i = 1; i <= 3; ++i) {
WebPData metadata;
err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
if (err == WEBP_MUX_OK && metadata.size > 0) {
err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
if (err != WEBP_MUX_OK) {
ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End);
}
}
}
End:
if (!ok) {
WebPMuxDelete(new_mux);
new_mux = NULL;
}
return new_mux;
}
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) { static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
int dispose_method, dummy; int dispose_method, dummy;
char plus_minus, blend_method; char plus_minus, blend_method;
@ -476,6 +533,7 @@ static int ValidateCommandLine(int argc, const char* argv[],
int num_frame_args; int num_frame_args;
int num_loop_args; int num_loop_args;
int num_bgcolor_args; int num_bgcolor_args;
int num_durations_args;
int ok = 1; int ok = 1;
assert(num_feature_args != NULL); assert(num_feature_args != NULL);
@ -502,6 +560,7 @@ static int ValidateCommandLine(int argc, const char* argv[],
num_frame_args = CountOccurrences(argv, argc, "-frame"); num_frame_args = CountOccurrences(argv, argc, "-frame");
num_loop_args = CountOccurrences(argv, argc, "-loop"); num_loop_args = CountOccurrences(argv, argc, "-loop");
num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor"); num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
num_durations_args = CountOccurrences(argv, argc, "-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);
@ -514,9 +573,15 @@ static int ValidateCommandLine(int argc, const char* argv[],
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in " ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
"case of animation.\n", ErrValidate); "case of animation.\n", ErrValidate);
} }
if (num_durations_args > 0 && num_frame_args != 0) {
ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
ErrValidate);
}
assert(ok == 1); assert(ok == 1);
if (num_frame_args == 0) { if (num_durations_args > 0) {
*num_feature_args = num_durations_args;
} else if (num_frame_args == 0) {
// Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action). // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
*num_feature_args = 1; *num_feature_args = 1;
} else { } else {
@ -563,6 +628,21 @@ static int ParseCommandLine(int argc, const char* argv[],
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
} }
++i; ++i;
} else if (!strcmp(argv[i], "-duration")) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
config->action_type_ = ACTION_DURATION;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_DURATION) {
feature->type_ = FEATURE_DURATION;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
arg->params_ = argv[i + 1];
++feature_arg_index;
i += 2;
} else if (!strcmp(argv[i], "-get")) { } else if (!strcmp(argv[i], "-get")) {
if (ACTION_IS_NIL) { if (ACTION_IS_NIL) {
config->action_type_ = ACTION_GET; config->action_type_ = ACTION_GET;
@ -936,6 +1016,82 @@ static int Process(const WebPMuxConfig* config) {
ok = WriteWebP(mux, config->output_); ok = WriteWebP(mux, config->output_);
break; break;
} }
case ACTION_DURATION: {
int num_frames;
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
ok = (err == WEBP_MUX_OK);
if (!ok) {
ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
}
if (num_frames == 0) {
fprintf(stderr, "Doesn't look like the source is animated. "
"Skipping duration setting.\n");
ok = WriteWebP(mux, config->output_);
if (!ok) goto Err2;
} else {
int i;
int* durations = NULL;
WebPMux* new_mux = DuplicateMuxHeader(mux);
if (new_mux == NULL) goto Err2;
durations = (int*)malloc((size_t)num_frames * sizeof(*durations));
if (durations == NULL) goto Err2;
for (i = 0; i < num_frames; ++i) durations[i] = -1;
// Parse intervals to process.
for (i = 0; i < feature->arg_count_; ++i) {
int k;
int args[3];
int duration, start, end;
const int nb_args = ExUtilGetInts(feature->args_[i].params_,
10, 3, args);
ok = (nb_args >= 1);
if (!ok) goto Err3;
duration = args[0];
if (duration < 0) {
ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
}
start = (nb_args >= 2) ? args[1] : 1;
if (start <= 0) start = 1;
end = (nb_args >= 3) ? args[2] : num_frames;
if (end == 0) end = num_frames;
if (end > num_frames) end = num_frames;
for (k = start; k <= end; ++k) {
assert(k >= 1 && k <= num_frames);
durations[k - 1] = duration;
}
}
// Apply non-negative durations to their destination frames.
for (i = 1; i <= num_frames; ++i) {
WebPMuxFrameInfo frame;
err = WebPMuxGetFrame(mux, i, &frame);
if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
}
if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
err = WebPMuxPushFrame(new_mux, &frame, 1);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
}
WebPDataClear(&frame.bitstream);
}
WebPMuxDelete(mux);
ok = WriteWebP(new_mux, config->output_);
mux = new_mux; // transfer for the WebPMuxDelete() call
new_mux = NULL;
Err3:
free(durations);
WebPMuxDelete(new_mux);
if (!ok) goto Err2;
}
break;
}
case ACTION_STRIP: { case ACTION_STRIP: {
ok = CreateMux(config->input_, &mux); ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2; if (!ok) goto Err2;

View File

@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH WEBPMUX 1 "June 23, 2016" .TH WEBPMUX 1 "November 8, 2016"
.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.
@ -35,6 +35,13 @@ frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
.I OUTPUT .I OUTPUT
.RE .RE
.br .br
.B webpmux \-duration
.I DURATION OPTIONS
.B [ \-duration ... ]
.I INPUT
.B \-o
.I OUTPUT
.br
.B webpmux \-info .B webpmux \-info
.I INPUT .I INPUT
.br .br
@ -91,6 +98,20 @@ Strip EXIF metadata.
.B xmp .B xmp
Strip XMP metadata. Strip XMP metadata.
.SS DURATION_OPTIONS (\-duration)
Amend the duration of a specific interval of frames.
.TP
.I duration[,start[,end]]
Where:
'duration' is the duration for the interval (mandatory). Must be non-negative.
'start' is the starting frame index of the interval (optional). If 'start'
is less or equal to '1', its value will be set to '1'.
'end' is the ending frame index (inclusive) of the interval (optional). The
value '0' has the special meaning 'last frame of the animation'.
Note that the frames outside of the [start, end] interval will remain untouched.
.I Reminder: frame indexing starts at '1'.
.br
.SS FRAME_OPTIONS (\-frame) .SS FRAME_OPTIONS (\-frame)
Create an animated WebP file from multiple (non\-animated) WebP images. Create an animated WebP file from multiple (non\-animated) WebP images.
.TP .TP