1*b2055c35SXin Li // Copyright 2011 Google Inc. All Rights Reserved.
2*b2055c35SXin Li //
3*b2055c35SXin Li // Use of this source code is governed by a BSD-style license
4*b2055c35SXin Li // that can be found in the COPYING file in the root of the source
5*b2055c35SXin Li // tree. An additional intellectual property rights grant can be found
6*b2055c35SXin Li // in the file PATENTS. All contributing project authors may
7*b2055c35SXin Li // be found in the AUTHORS file in the root of the source tree.
8*b2055c35SXin Li // -----------------------------------------------------------------------------
9*b2055c35SXin Li //
10*b2055c35SXin Li // Simple command-line to create a WebP container file and to extract or strip
11*b2055c35SXin Li // relevant data from the container file.
12*b2055c35SXin Li //
13*b2055c35SXin Li // Authors: Vikas ([email protected]),
14*b2055c35SXin Li // Urvang ([email protected])
15*b2055c35SXin Li
16*b2055c35SXin Li /* Usage examples:
17*b2055c35SXin Li
18*b2055c35SXin Li Create container WebP file:
19*b2055c35SXin Li webpmux -frame anim_1.webp +100+10+10 \
20*b2055c35SXin Li -frame anim_2.webp +100+25+25+1 \
21*b2055c35SXin Li -frame anim_3.webp +100+50+50+1 \
22*b2055c35SXin Li -frame anim_4.webp +100 \
23*b2055c35SXin Li -loop 10 -bgcolor 128,255,255,255 \
24*b2055c35SXin Li -o out_animation_container.webp
25*b2055c35SXin Li
26*b2055c35SXin Li webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
27*b2055c35SXin Li webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
28*b2055c35SXin Li webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
29*b2055c35SXin Li webpmux -set loop 1 in.webp -o out_looped.webp
30*b2055c35SXin Li
31*b2055c35SXin Li Extract relevant data from WebP container file:
32*b2055c35SXin Li webpmux -get frame n in.webp -o out_frame.webp
33*b2055c35SXin Li webpmux -get icc in.webp -o image_profile.icc
34*b2055c35SXin Li webpmux -get exif in.webp -o image_metadata.exif
35*b2055c35SXin Li webpmux -get xmp in.webp -o image_metadata.xmp
36*b2055c35SXin Li
37*b2055c35SXin Li Strip data from WebP Container file:
38*b2055c35SXin Li webpmux -strip icc in.webp -o out.webp
39*b2055c35SXin Li webpmux -strip exif in.webp -o out.webp
40*b2055c35SXin Li webpmux -strip xmp in.webp -o out.webp
41*b2055c35SXin Li
42*b2055c35SXin Li Change duration of frame intervals:
43*b2055c35SXin Li webpmux -duration 150 in.webp -o out.webp
44*b2055c35SXin Li webpmux -duration 33,2 in.webp -o out.webp
45*b2055c35SXin Li webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
46*b2055c35SXin Li
47*b2055c35SXin Li Misc:
48*b2055c35SXin Li webpmux -info in.webp
49*b2055c35SXin Li webpmux [ -h | -help ]
50*b2055c35SXin Li webpmux -version
51*b2055c35SXin Li webpmux argument_file_name
52*b2055c35SXin Li */
53*b2055c35SXin Li
54*b2055c35SXin Li #ifdef HAVE_CONFIG_H
55*b2055c35SXin Li #include "webp/config.h"
56*b2055c35SXin Li #endif
57*b2055c35SXin Li
58*b2055c35SXin Li #include <assert.h>
59*b2055c35SXin Li #include <stdio.h>
60*b2055c35SXin Li #include <stdlib.h>
61*b2055c35SXin Li #include <string.h>
62*b2055c35SXin Li #include "webp/decode.h"
63*b2055c35SXin Li #include "webp/mux.h"
64*b2055c35SXin Li #include "../examples/example_util.h"
65*b2055c35SXin Li #include "../imageio/imageio_util.h"
66*b2055c35SXin Li #include "./unicode.h"
67*b2055c35SXin Li
68*b2055c35SXin Li //------------------------------------------------------------------------------
69*b2055c35SXin Li // Config object to parse command-line arguments.
70*b2055c35SXin Li
71*b2055c35SXin Li typedef enum {
72*b2055c35SXin Li NIL_ACTION = 0,
73*b2055c35SXin Li ACTION_GET,
74*b2055c35SXin Li ACTION_SET,
75*b2055c35SXin Li ACTION_STRIP,
76*b2055c35SXin Li ACTION_INFO,
77*b2055c35SXin Li ACTION_HELP,
78*b2055c35SXin Li ACTION_DURATION
79*b2055c35SXin Li } ActionType;
80*b2055c35SXin Li
81*b2055c35SXin Li typedef enum {
82*b2055c35SXin Li NIL_SUBTYPE = 0,
83*b2055c35SXin Li SUBTYPE_ANMF,
84*b2055c35SXin Li SUBTYPE_LOOP,
85*b2055c35SXin Li SUBTYPE_BGCOLOR
86*b2055c35SXin Li } FeatureSubType;
87*b2055c35SXin Li
88*b2055c35SXin Li typedef struct {
89*b2055c35SXin Li FeatureSubType subtype_;
90*b2055c35SXin Li const char* filename_;
91*b2055c35SXin Li const char* params_;
92*b2055c35SXin Li } FeatureArg;
93*b2055c35SXin Li
94*b2055c35SXin Li typedef enum {
95*b2055c35SXin Li NIL_FEATURE = 0,
96*b2055c35SXin Li FEATURE_EXIF,
97*b2055c35SXin Li FEATURE_XMP,
98*b2055c35SXin Li FEATURE_ICCP,
99*b2055c35SXin Li FEATURE_ANMF,
100*b2055c35SXin Li FEATURE_DURATION,
101*b2055c35SXin Li FEATURE_LOOP,
102*b2055c35SXin Li FEATURE_BGCOLOR,
103*b2055c35SXin Li LAST_FEATURE
104*b2055c35SXin Li } FeatureType;
105*b2055c35SXin Li
106*b2055c35SXin Li static const char* const kFourccList[LAST_FEATURE] = {
107*b2055c35SXin Li NULL, "EXIF", "XMP ", "ICCP", "ANMF"
108*b2055c35SXin Li };
109*b2055c35SXin Li
110*b2055c35SXin Li static const char* const kDescriptions[LAST_FEATURE] = {
111*b2055c35SXin Li NULL, "EXIF metadata", "XMP metadata", "ICC profile",
112*b2055c35SXin Li "Animation frame"
113*b2055c35SXin Li };
114*b2055c35SXin Li
115*b2055c35SXin Li typedef struct {
116*b2055c35SXin Li CommandLineArguments cmd_args_;
117*b2055c35SXin Li
118*b2055c35SXin Li ActionType action_type_;
119*b2055c35SXin Li const char* input_;
120*b2055c35SXin Li const char* output_;
121*b2055c35SXin Li FeatureType type_;
122*b2055c35SXin Li FeatureArg* args_;
123*b2055c35SXin Li int arg_count_;
124*b2055c35SXin Li } Config;
125*b2055c35SXin Li
126*b2055c35SXin Li //------------------------------------------------------------------------------
127*b2055c35SXin Li // Helper functions.
128*b2055c35SXin Li
CountOccurrences(const CommandLineArguments * const args,const char * const arg)129*b2055c35SXin Li static int CountOccurrences(const CommandLineArguments* const args,
130*b2055c35SXin Li const char* const arg) {
131*b2055c35SXin Li int i;
132*b2055c35SXin Li int num_occurences = 0;
133*b2055c35SXin Li
134*b2055c35SXin Li for (i = 0; i < args->argc_; ++i) {
135*b2055c35SXin Li if (!strcmp(args->argv_[i], arg)) {
136*b2055c35SXin Li ++num_occurences;
137*b2055c35SXin Li }
138*b2055c35SXin Li }
139*b2055c35SXin Li return num_occurences;
140*b2055c35SXin Li }
141*b2055c35SXin Li
142*b2055c35SXin Li static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
143*b2055c35SXin Li "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
144*b2055c35SXin Li "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
145*b2055c35SXin Li };
146*b2055c35SXin Li
ErrorString(WebPMuxError err)147*b2055c35SXin Li static const char* ErrorString(WebPMuxError err) {
148*b2055c35SXin Li assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
149*b2055c35SXin Li return kErrorMessages[-err];
150*b2055c35SXin Li }
151*b2055c35SXin Li
152*b2055c35SXin Li #define RETURN_IF_ERROR(ERR_MSG) \
153*b2055c35SXin Li do { \
154*b2055c35SXin Li if (err != WEBP_MUX_OK) { \
155*b2055c35SXin Li fprintf(stderr, ERR_MSG); \
156*b2055c35SXin Li return err; \
157*b2055c35SXin Li } \
158*b2055c35SXin Li } while (0)
159*b2055c35SXin Li
160*b2055c35SXin Li #define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
161*b2055c35SXin Li do { \
162*b2055c35SXin Li if (err != WEBP_MUX_OK) { \
163*b2055c35SXin Li fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
164*b2055c35SXin Li return err; \
165*b2055c35SXin Li } \
166*b2055c35SXin Li } while (0)
167*b2055c35SXin Li
168*b2055c35SXin Li #define ERROR_GOTO1(ERR_MSG, LABEL) \
169*b2055c35SXin Li do { \
170*b2055c35SXin Li fprintf(stderr, ERR_MSG); \
171*b2055c35SXin Li ok = 0; \
172*b2055c35SXin Li goto LABEL; \
173*b2055c35SXin Li } while (0)
174*b2055c35SXin Li
175*b2055c35SXin Li #define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
176*b2055c35SXin Li do { \
177*b2055c35SXin Li fprintf(stderr, ERR_MSG, FORMAT_STR); \
178*b2055c35SXin Li ok = 0; \
179*b2055c35SXin Li goto LABEL; \
180*b2055c35SXin Li } while (0)
181*b2055c35SXin Li
182*b2055c35SXin Li #define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
183*b2055c35SXin Li do { \
184*b2055c35SXin Li fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
185*b2055c35SXin Li ok = 0; \
186*b2055c35SXin Li goto LABEL; \
187*b2055c35SXin Li } while (0)
188*b2055c35SXin Li
DisplayInfo(const WebPMux * mux)189*b2055c35SXin Li static WebPMuxError DisplayInfo(const WebPMux* mux) {
190*b2055c35SXin Li int width, height;
191*b2055c35SXin Li uint32_t flag;
192*b2055c35SXin Li
193*b2055c35SXin Li WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
194*b2055c35SXin Li assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier.
195*b2055c35SXin Li printf("Canvas size: %d x %d\n", width, height);
196*b2055c35SXin Li
197*b2055c35SXin Li err = WebPMuxGetFeatures(mux, &flag);
198*b2055c35SXin Li RETURN_IF_ERROR("Failed to retrieve features\n");
199*b2055c35SXin Li
200*b2055c35SXin Li if (flag == 0) {
201*b2055c35SXin Li printf("No features present.\n");
202*b2055c35SXin Li return err;
203*b2055c35SXin Li }
204*b2055c35SXin Li
205*b2055c35SXin Li // Print the features present.
206*b2055c35SXin Li printf("Features present:");
207*b2055c35SXin Li if (flag & ANIMATION_FLAG) printf(" animation");
208*b2055c35SXin Li if (flag & ICCP_FLAG) printf(" ICC profile");
209*b2055c35SXin Li if (flag & EXIF_FLAG) printf(" EXIF metadata");
210*b2055c35SXin Li if (flag & XMP_FLAG) printf(" XMP metadata");
211*b2055c35SXin Li if (flag & ALPHA_FLAG) printf(" transparency");
212*b2055c35SXin Li printf("\n");
213*b2055c35SXin Li
214*b2055c35SXin Li if (flag & ANIMATION_FLAG) {
215*b2055c35SXin Li const WebPChunkId id = WEBP_CHUNK_ANMF;
216*b2055c35SXin Li const char* const type_str = "frame";
217*b2055c35SXin Li int nFrames;
218*b2055c35SXin Li
219*b2055c35SXin Li WebPMuxAnimParams params;
220*b2055c35SXin Li err = WebPMuxGetAnimationParams(mux, ¶ms);
221*b2055c35SXin Li assert(err == WEBP_MUX_OK);
222*b2055c35SXin Li printf("Background color : 0x%.8X Loop Count : %d\n",
223*b2055c35SXin Li params.bgcolor, params.loop_count);
224*b2055c35SXin Li
225*b2055c35SXin Li err = WebPMuxNumChunks(mux, id, &nFrames);
226*b2055c35SXin Li assert(err == WEBP_MUX_OK);
227*b2055c35SXin Li
228*b2055c35SXin Li printf("Number of %ss: %d\n", type_str, nFrames);
229*b2055c35SXin Li if (nFrames > 0) {
230*b2055c35SXin Li int i;
231*b2055c35SXin Li printf("No.: width height alpha x_offset y_offset ");
232*b2055c35SXin Li printf("duration dispose blend ");
233*b2055c35SXin Li printf("image_size compression\n");
234*b2055c35SXin Li for (i = 1; i <= nFrames; i++) {
235*b2055c35SXin Li WebPMuxFrameInfo frame;
236*b2055c35SXin Li err = WebPMuxGetFrame(mux, i, &frame);
237*b2055c35SXin Li if (err == WEBP_MUX_OK) {
238*b2055c35SXin Li WebPBitstreamFeatures features;
239*b2055c35SXin Li const VP8StatusCode status = WebPGetFeatures(
240*b2055c35SXin Li frame.bitstream.bytes, frame.bitstream.size, &features);
241*b2055c35SXin Li assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate().
242*b2055c35SXin Li (void)status;
243*b2055c35SXin Li printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
244*b2055c35SXin Li features.height, features.has_alpha ? "yes" : "no",
245*b2055c35SXin Li frame.x_offset, frame.y_offset);
246*b2055c35SXin Li {
247*b2055c35SXin Li const char* const dispose =
248*b2055c35SXin Li (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
249*b2055c35SXin Li : "background";
250*b2055c35SXin Li const char* const blend =
251*b2055c35SXin Li (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
252*b2055c35SXin Li printf("%8d %10s %5s ", frame.duration, dispose, blend);
253*b2055c35SXin Li }
254*b2055c35SXin Li printf("%10d %11s\n", (int)frame.bitstream.size,
255*b2055c35SXin Li (features.format == 1) ? "lossy" :
256*b2055c35SXin Li (features.format == 2) ? "lossless" :
257*b2055c35SXin Li "undefined");
258*b2055c35SXin Li }
259*b2055c35SXin Li WebPDataClear(&frame.bitstream);
260*b2055c35SXin Li RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
261*b2055c35SXin Li }
262*b2055c35SXin Li }
263*b2055c35SXin Li }
264*b2055c35SXin Li
265*b2055c35SXin Li if (flag & ICCP_FLAG) {
266*b2055c35SXin Li WebPData icc_profile;
267*b2055c35SXin Li err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
268*b2055c35SXin Li assert(err == WEBP_MUX_OK);
269*b2055c35SXin Li printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
270*b2055c35SXin Li }
271*b2055c35SXin Li
272*b2055c35SXin Li if (flag & EXIF_FLAG) {
273*b2055c35SXin Li WebPData exif;
274*b2055c35SXin Li err = WebPMuxGetChunk(mux, "EXIF", &exif);
275*b2055c35SXin Li assert(err == WEBP_MUX_OK);
276*b2055c35SXin Li printf("Size of the EXIF metadata: %d\n", (int)exif.size);
277*b2055c35SXin Li }
278*b2055c35SXin Li
279*b2055c35SXin Li if (flag & XMP_FLAG) {
280*b2055c35SXin Li WebPData xmp;
281*b2055c35SXin Li err = WebPMuxGetChunk(mux, "XMP ", &xmp);
282*b2055c35SXin Li assert(err == WEBP_MUX_OK);
283*b2055c35SXin Li printf("Size of the XMP metadata: %d\n", (int)xmp.size);
284*b2055c35SXin Li }
285*b2055c35SXin Li
286*b2055c35SXin Li if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) {
287*b2055c35SXin Li WebPMuxFrameInfo image;
288*b2055c35SXin Li err = WebPMuxGetFrame(mux, 1, &image);
289*b2055c35SXin Li if (err == WEBP_MUX_OK) {
290*b2055c35SXin Li printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
291*b2055c35SXin Li }
292*b2055c35SXin Li WebPDataClear(&image.bitstream);
293*b2055c35SXin Li RETURN_IF_ERROR("Failed to retrieve the image\n");
294*b2055c35SXin Li }
295*b2055c35SXin Li
296*b2055c35SXin Li return WEBP_MUX_OK;
297*b2055c35SXin Li }
298*b2055c35SXin Li
PrintHelp(void)299*b2055c35SXin Li static void PrintHelp(void) {
300*b2055c35SXin Li printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
301*b2055c35SXin Li printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
302*b2055c35SXin Li printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
303*b2055c35SXin Li printf(" INPUT -o OUTPUT\n");
304*b2055c35SXin Li printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
305*b2055c35SXin Li printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
306*b2055c35SXin Li "\n");
307*b2055c35SXin Li printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
308*b2055c35SXin Li printf(" webpmux -info INPUT\n");
309*b2055c35SXin Li printf(" webpmux [-h|-help]\n");
310*b2055c35SXin Li printf(" webpmux -version\n");
311*b2055c35SXin Li printf(" webpmux argument_file_name\n");
312*b2055c35SXin Li
313*b2055c35SXin Li printf("\n");
314*b2055c35SXin Li printf("GET_OPTIONS:\n");
315*b2055c35SXin Li printf(" Extract relevant data:\n");
316*b2055c35SXin Li printf(" icc get ICC profile\n");
317*b2055c35SXin Li printf(" exif get EXIF metadata\n");
318*b2055c35SXin Li printf(" xmp get XMP metadata\n");
319*b2055c35SXin Li printf(" frame n get nth frame\n");
320*b2055c35SXin Li
321*b2055c35SXin Li printf("\n");
322*b2055c35SXin Li printf("SET_OPTIONS:\n");
323*b2055c35SXin Li printf(" Set color profile/metadata/parameters:\n");
324*b2055c35SXin Li printf(" loop LOOP_COUNT set the loop count\n");
325*b2055c35SXin Li printf(" bgcolor BACKGROUND_COLOR set the animation background color\n");
326*b2055c35SXin Li printf(" icc file.icc set ICC profile\n");
327*b2055c35SXin Li printf(" exif file.exif set EXIF metadata\n");
328*b2055c35SXin Li printf(" xmp file.xmp set XMP metadata\n");
329*b2055c35SXin Li printf(" where: 'file.icc' contains the ICC profile to be set,\n");
330*b2055c35SXin Li printf(" 'file.exif' contains the EXIF metadata to be set\n");
331*b2055c35SXin Li printf(" 'file.xmp' contains the XMP metadata to be set\n");
332*b2055c35SXin Li
333*b2055c35SXin Li printf("\n");
334*b2055c35SXin Li printf("DURATION_OPTIONS:\n");
335*b2055c35SXin Li printf(" Set duration of selected frames:\n");
336*b2055c35SXin Li printf(" duration set duration for all frames\n");
337*b2055c35SXin Li printf(" duration,frame set duration of a particular frame\n");
338*b2055c35SXin Li printf(" duration,start,end set duration of frames in the\n");
339*b2055c35SXin Li printf(" interval [start,end])\n");
340*b2055c35SXin Li printf(" where: 'duration' is the duration in milliseconds\n");
341*b2055c35SXin Li printf(" 'start' is the start frame index\n");
342*b2055c35SXin Li printf(" 'end' is the inclusive end frame index\n");
343*b2055c35SXin Li printf(" The special 'end' value '0' means: last frame.\n");
344*b2055c35SXin Li
345*b2055c35SXin Li printf("\n");
346*b2055c35SXin Li printf("STRIP_OPTIONS:\n");
347*b2055c35SXin Li printf(" Strip color profile/metadata:\n");
348*b2055c35SXin Li printf(" icc strip ICC profile\n");
349*b2055c35SXin Li printf(" exif strip EXIF metadata\n");
350*b2055c35SXin Li printf(" xmp strip XMP metadata\n");
351*b2055c35SXin Li
352*b2055c35SXin Li printf("\n");
353*b2055c35SXin Li printf("FRAME_OPTIONS(i):\n");
354*b2055c35SXin Li printf(" Create animation:\n");
355*b2055c35SXin Li printf(" file_i +di[+xi+yi[+mi[bi]]]\n");
356*b2055c35SXin Li printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
357*b2055c35SXin Li printf(" 'di' is the pause duration before next frame,\n");
358*b2055c35SXin Li printf(" 'xi','yi' specify the image offset for this frame,\n");
359*b2055c35SXin Li printf(" 'mi' is the dispose method for this frame (0 or 1),\n");
360*b2055c35SXin Li printf(" 'bi' is the blending method for this frame (+b or -b)"
361*b2055c35SXin Li "\n");
362*b2055c35SXin Li
363*b2055c35SXin Li printf("\n");
364*b2055c35SXin Li printf("LOOP_COUNT:\n");
365*b2055c35SXin Li printf(" Number of times to repeat the animation.\n");
366*b2055c35SXin Li printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
367*b2055c35SXin Li
368*b2055c35SXin Li printf("\n");
369*b2055c35SXin Li printf("BACKGROUND_COLOR:\n");
370*b2055c35SXin Li printf(" Background color of the canvas.\n");
371*b2055c35SXin Li printf(" A,R,G,B\n");
372*b2055c35SXin Li printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
373*b2055c35SXin Li "specifying\n");
374*b2055c35SXin Li printf(" the Alpha, Red, Green and Blue component values "
375*b2055c35SXin Li "respectively\n");
376*b2055c35SXin Li printf(" [Default: 255,255,255,255]\n");
377*b2055c35SXin Li
378*b2055c35SXin Li printf("\nINPUT & OUTPUT are in WebP format.\n");
379*b2055c35SXin Li
380*b2055c35SXin Li printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
381*b2055c35SXin Li printf(" and is assumed to be\nvalid.\n");
382*b2055c35SXin Li printf("\nNote: if a single file name is passed as the argument, the "
383*b2055c35SXin Li "arguments will be\n");
384*b2055c35SXin Li printf("tokenized from this file. The file name must not start with "
385*b2055c35SXin Li "the character '-'.\n");
386*b2055c35SXin Li }
387*b2055c35SXin Li
WarnAboutOddOffset(const WebPMuxFrameInfo * const info)388*b2055c35SXin Li static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
389*b2055c35SXin Li if ((info->x_offset | info->y_offset) & 1) {
390*b2055c35SXin Li fprintf(stderr, "Warning: odd offsets will be snapped to even values"
391*b2055c35SXin Li " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
392*b2055c35SXin Li info->x_offset & ~1, info->y_offset & ~1);
393*b2055c35SXin Li }
394*b2055c35SXin Li }
395*b2055c35SXin Li
CreateMux(const char * const filename,WebPMux ** mux)396*b2055c35SXin Li static int CreateMux(const char* const filename, WebPMux** mux) {
397*b2055c35SXin Li WebPData bitstream;
398*b2055c35SXin Li assert(mux != NULL);
399*b2055c35SXin Li if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0;
400*b2055c35SXin Li *mux = WebPMuxCreate(&bitstream, 1);
401*b2055c35SXin Li WebPDataClear(&bitstream);
402*b2055c35SXin Li if (*mux != NULL) return 1;
403*b2055c35SXin Li WFPRINTF(stderr, "Failed to create mux object from file %s.\n",
404*b2055c35SXin Li (const W_CHAR*)filename);
405*b2055c35SXin Li return 0;
406*b2055c35SXin Li }
407*b2055c35SXin Li
WriteData(const char * filename,const WebPData * const webpdata)408*b2055c35SXin Li static int WriteData(const char* filename, const WebPData* const webpdata) {
409*b2055c35SXin Li int ok = 0;
410*b2055c35SXin Li FILE* fout = WSTRCMP(filename, "-") ? WFOPEN(filename, "wb")
411*b2055c35SXin Li : ImgIoUtilSetBinaryMode(stdout);
412*b2055c35SXin Li if (fout == NULL) {
413*b2055c35SXin Li WFPRINTF(stderr, "Error opening output WebP file %s!\n",
414*b2055c35SXin Li (const W_CHAR*)filename);
415*b2055c35SXin Li return 0;
416*b2055c35SXin Li }
417*b2055c35SXin Li if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
418*b2055c35SXin Li WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename);
419*b2055c35SXin Li } else {
420*b2055c35SXin Li WFPRINTF(stderr, "Saved file %s (%d bytes)\n",
421*b2055c35SXin Li (const W_CHAR*)filename, (int)webpdata->size);
422*b2055c35SXin Li ok = 1;
423*b2055c35SXin Li }
424*b2055c35SXin Li if (fout != stdout) fclose(fout);
425*b2055c35SXin Li return ok;
426*b2055c35SXin Li }
427*b2055c35SXin Li
WriteWebP(WebPMux * const mux,const char * filename)428*b2055c35SXin Li static int WriteWebP(WebPMux* const mux, const char* filename) {
429*b2055c35SXin Li int ok;
430*b2055c35SXin Li WebPData webp_data;
431*b2055c35SXin Li const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
432*b2055c35SXin Li if (err != WEBP_MUX_OK) {
433*b2055c35SXin Li fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
434*b2055c35SXin Li return 0;
435*b2055c35SXin Li }
436*b2055c35SXin Li ok = WriteData(filename, &webp_data);
437*b2055c35SXin Li WebPDataClear(&webp_data);
438*b2055c35SXin Li return ok;
439*b2055c35SXin Li }
440*b2055c35SXin Li
DuplicateMuxHeader(const WebPMux * const mux)441*b2055c35SXin Li static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
442*b2055c35SXin Li WebPMux* new_mux = WebPMuxNew();
443*b2055c35SXin Li WebPMuxAnimParams p;
444*b2055c35SXin Li WebPMuxError err;
445*b2055c35SXin Li int i;
446*b2055c35SXin Li int ok = 1;
447*b2055c35SXin Li
448*b2055c35SXin Li if (new_mux == NULL) return NULL;
449*b2055c35SXin Li
450*b2055c35SXin Li err = WebPMuxGetAnimationParams(mux, &p);
451*b2055c35SXin Li if (err == WEBP_MUX_OK) {
452*b2055c35SXin Li err = WebPMuxSetAnimationParams(new_mux, &p);
453*b2055c35SXin Li if (err != WEBP_MUX_OK) {
454*b2055c35SXin Li ERROR_GOTO2("Error (%s) handling animation params.\n",
455*b2055c35SXin Li ErrorString(err), End);
456*b2055c35SXin Li }
457*b2055c35SXin Li } else {
458*b2055c35SXin Li /* it might not be an animation. Just keep moving. */
459*b2055c35SXin Li }
460*b2055c35SXin Li
461*b2055c35SXin Li for (i = 1; i <= 3; ++i) {
462*b2055c35SXin Li WebPData metadata;
463*b2055c35SXin Li err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
464*b2055c35SXin Li if (err == WEBP_MUX_OK && metadata.size > 0) {
465*b2055c35SXin Li err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
466*b2055c35SXin Li if (err != WEBP_MUX_OK) {
467*b2055c35SXin Li ERROR_GOTO1("Error transferring metadata in DuplicateMuxHeader().",
468*b2055c35SXin Li End);
469*b2055c35SXin Li }
470*b2055c35SXin Li }
471*b2055c35SXin Li }
472*b2055c35SXin Li
473*b2055c35SXin Li End:
474*b2055c35SXin Li if (!ok) {
475*b2055c35SXin Li WebPMuxDelete(new_mux);
476*b2055c35SXin Li new_mux = NULL;
477*b2055c35SXin Li }
478*b2055c35SXin Li return new_mux;
479*b2055c35SXin Li }
480*b2055c35SXin Li
ParseFrameArgs(const char * args,WebPMuxFrameInfo * const info)481*b2055c35SXin Li static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
482*b2055c35SXin Li int dispose_method, unused;
483*b2055c35SXin Li char plus_minus, blend_method;
484*b2055c35SXin Li const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
485*b2055c35SXin Li &info->x_offset, &info->y_offset, &dispose_method,
486*b2055c35SXin Li &plus_minus, &blend_method, &unused);
487*b2055c35SXin Li switch (num_args) {
488*b2055c35SXin Li case 1:
489*b2055c35SXin Li info->x_offset = info->y_offset = 0; // fall through
490*b2055c35SXin Li case 3:
491*b2055c35SXin Li dispose_method = 0; // fall through
492*b2055c35SXin Li case 4:
493*b2055c35SXin Li plus_minus = '+';
494*b2055c35SXin Li blend_method = 'b'; // fall through
495*b2055c35SXin Li case 6:
496*b2055c35SXin Li break;
497*b2055c35SXin Li case 2:
498*b2055c35SXin Li case 5:
499*b2055c35SXin Li default:
500*b2055c35SXin Li return 0;
501*b2055c35SXin Li }
502*b2055c35SXin Li
503*b2055c35SXin Li WarnAboutOddOffset(info);
504*b2055c35SXin Li
505*b2055c35SXin Li // Note: The validity of the following conversion is checked by
506*b2055c35SXin Li // WebPMuxPushFrame().
507*b2055c35SXin Li info->dispose_method = (WebPMuxAnimDispose)dispose_method;
508*b2055c35SXin Li
509*b2055c35SXin Li if (blend_method != 'b') return 0;
510*b2055c35SXin Li if (plus_minus != '-' && plus_minus != '+') return 0;
511*b2055c35SXin Li info->blend_method =
512*b2055c35SXin Li (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
513*b2055c35SXin Li return 1;
514*b2055c35SXin Li }
515*b2055c35SXin Li
ParseBgcolorArgs(const char * args,uint32_t * const bgcolor)516*b2055c35SXin Li static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
517*b2055c35SXin Li uint32_t a, r, g, b;
518*b2055c35SXin Li if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
519*b2055c35SXin Li if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
520*b2055c35SXin Li *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
521*b2055c35SXin Li return 1;
522*b2055c35SXin Li }
523*b2055c35SXin Li
524*b2055c35SXin Li //------------------------------------------------------------------------------
525*b2055c35SXin Li // Clean-up.
526*b2055c35SXin Li
DeleteConfig(Config * const config)527*b2055c35SXin Li static void DeleteConfig(Config* const config) {
528*b2055c35SXin Li if (config != NULL) {
529*b2055c35SXin Li free(config->args_);
530*b2055c35SXin Li ExUtilDeleteCommandLineArguments(&config->cmd_args_);
531*b2055c35SXin Li memset(config, 0, sizeof(*config));
532*b2055c35SXin Li }
533*b2055c35SXin Li }
534*b2055c35SXin Li
535*b2055c35SXin Li //------------------------------------------------------------------------------
536*b2055c35SXin Li // Parsing.
537*b2055c35SXin Li
538*b2055c35SXin Li // Basic syntactic checks on the command-line arguments.
539*b2055c35SXin Li // Returns 1 on valid, 0 otherwise.
540*b2055c35SXin Li // Also fills up num_feature_args to be number of feature arguments given.
541*b2055c35SXin Li // (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
ValidateCommandLine(const CommandLineArguments * const cmd_args,int * num_feature_args)542*b2055c35SXin Li static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
543*b2055c35SXin Li int* num_feature_args) {
544*b2055c35SXin Li int num_frame_args;
545*b2055c35SXin Li int num_loop_args;
546*b2055c35SXin Li int num_bgcolor_args;
547*b2055c35SXin Li int num_durations_args;
548*b2055c35SXin Li int ok = 1;
549*b2055c35SXin Li
550*b2055c35SXin Li assert(num_feature_args != NULL);
551*b2055c35SXin Li *num_feature_args = 0;
552*b2055c35SXin Li
553*b2055c35SXin Li // Simple checks.
554*b2055c35SXin Li if (CountOccurrences(cmd_args, "-get") > 1) {
555*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
556*b2055c35SXin Li }
557*b2055c35SXin Li if (CountOccurrences(cmd_args, "-set") > 1) {
558*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
559*b2055c35SXin Li }
560*b2055c35SXin Li if (CountOccurrences(cmd_args, "-strip") > 1) {
561*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
562*b2055c35SXin Li }
563*b2055c35SXin Li if (CountOccurrences(cmd_args, "-info") > 1) {
564*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
565*b2055c35SXin Li }
566*b2055c35SXin Li if (CountOccurrences(cmd_args, "-o") > 1) {
567*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
568*b2055c35SXin Li }
569*b2055c35SXin Li
570*b2055c35SXin Li // Compound checks.
571*b2055c35SXin Li num_frame_args = CountOccurrences(cmd_args, "-frame");
572*b2055c35SXin Li num_loop_args = CountOccurrences(cmd_args, "-loop");
573*b2055c35SXin Li num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor");
574*b2055c35SXin Li num_durations_args = CountOccurrences(cmd_args, "-duration");
575*b2055c35SXin Li
576*b2055c35SXin Li if (num_loop_args > 1) {
577*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
578*b2055c35SXin Li }
579*b2055c35SXin Li if (num_bgcolor_args > 1) {
580*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
581*b2055c35SXin Li }
582*b2055c35SXin Li
583*b2055c35SXin Li if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
584*b2055c35SXin Li ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
585*b2055c35SXin Li "case of animation.\n", ErrValidate);
586*b2055c35SXin Li }
587*b2055c35SXin Li if (num_durations_args > 0 && num_frame_args != 0) {
588*b2055c35SXin Li ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
589*b2055c35SXin Li ErrValidate);
590*b2055c35SXin Li }
591*b2055c35SXin Li
592*b2055c35SXin Li assert(ok == 1);
593*b2055c35SXin Li if (num_durations_args > 0) {
594*b2055c35SXin Li *num_feature_args = num_durations_args;
595*b2055c35SXin Li } else if (num_frame_args == 0) {
596*b2055c35SXin Li // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
597*b2055c35SXin Li *num_feature_args = 1;
598*b2055c35SXin Li } else {
599*b2055c35SXin Li // Multiple arguments ('set' action for animation)
600*b2055c35SXin Li *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
601*b2055c35SXin Li }
602*b2055c35SXin Li
603*b2055c35SXin Li ErrValidate:
604*b2055c35SXin Li return ok;
605*b2055c35SXin Li }
606*b2055c35SXin Li
607*b2055c35SXin Li #define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
608*b2055c35SXin Li
609*b2055c35SXin Li #define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
610*b2055c35SXin Li
611*b2055c35SXin Li #define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \
612*b2055c35SXin Li do { \
613*b2055c35SXin Li if (argc < i + (NUM)) { \
614*b2055c35SXin Li fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
615*b2055c35SXin Li goto LABEL; \
616*b2055c35SXin Li } \
617*b2055c35SXin Li } while (0)
618*b2055c35SXin Li
619*b2055c35SXin Li #define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \
620*b2055c35SXin Li do { \
621*b2055c35SXin Li if (argc > i + (NUM)) { \
622*b2055c35SXin Li fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
623*b2055c35SXin Li goto LABEL; \
624*b2055c35SXin Li } \
625*b2055c35SXin Li } while (0)
626*b2055c35SXin Li
627*b2055c35SXin Li #define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
628*b2055c35SXin Li do { \
629*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
630*b2055c35SXin Li CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); \
631*b2055c35SXin Li } while (0)
632*b2055c35SXin Li
633*b2055c35SXin Li // Parses command-line arguments to fill up config object. Also performs some
634*b2055c35SXin Li // semantic checks. unicode_argv contains wchar_t arguments or is null.
ParseCommandLine(Config * config,const W_CHAR ** const unicode_argv)635*b2055c35SXin Li static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
636*b2055c35SXin Li int i = 0;
637*b2055c35SXin Li int feature_arg_index = 0;
638*b2055c35SXin Li int ok = 1;
639*b2055c35SXin Li int argc = config->cmd_args_.argc_;
640*b2055c35SXin Li const char* const* argv = config->cmd_args_.argv_;
641*b2055c35SXin Li // Unicode file paths will be used if available.
642*b2055c35SXin Li const char* const* wargv =
643*b2055c35SXin Li (unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv;
644*b2055c35SXin Li
645*b2055c35SXin Li while (i < argc) {
646*b2055c35SXin Li FeatureArg* const arg = &config->args_[feature_arg_index];
647*b2055c35SXin Li if (argv[i][0] == '-') { // One of the action types or output.
648*b2055c35SXin Li if (!strcmp(argv[i], "-set")) {
649*b2055c35SXin Li if (ACTION_IS_NIL) {
650*b2055c35SXin Li config->action_type_ = ACTION_SET;
651*b2055c35SXin Li } else {
652*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
653*b2055c35SXin Li }
654*b2055c35SXin Li ++i;
655*b2055c35SXin Li } else if (!strcmp(argv[i], "-duration")) {
656*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
657*b2055c35SXin Li if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
658*b2055c35SXin Li config->action_type_ = ACTION_DURATION;
659*b2055c35SXin Li } else {
660*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
661*b2055c35SXin Li }
662*b2055c35SXin Li if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
663*b2055c35SXin Li config->type_ = FEATURE_DURATION;
664*b2055c35SXin Li } else {
665*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
666*b2055c35SXin Li }
667*b2055c35SXin Li arg->params_ = argv[i + 1];
668*b2055c35SXin Li ++feature_arg_index;
669*b2055c35SXin Li i += 2;
670*b2055c35SXin Li } else if (!strcmp(argv[i], "-get")) {
671*b2055c35SXin Li if (ACTION_IS_NIL) {
672*b2055c35SXin Li config->action_type_ = ACTION_GET;
673*b2055c35SXin Li } else {
674*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
675*b2055c35SXin Li }
676*b2055c35SXin Li ++i;
677*b2055c35SXin Li } else if (!strcmp(argv[i], "-strip")) {
678*b2055c35SXin Li if (ACTION_IS_NIL) {
679*b2055c35SXin Li config->action_type_ = ACTION_STRIP;
680*b2055c35SXin Li config->arg_count_ = 0;
681*b2055c35SXin Li } else {
682*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
683*b2055c35SXin Li }
684*b2055c35SXin Li ++i;
685*b2055c35SXin Li } else if (!strcmp(argv[i], "-frame")) {
686*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(3, ErrParse);
687*b2055c35SXin Li if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
688*b2055c35SXin Li config->action_type_ = ACTION_SET;
689*b2055c35SXin Li } else {
690*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
691*b2055c35SXin Li }
692*b2055c35SXin Li if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
693*b2055c35SXin Li config->type_ = FEATURE_ANMF;
694*b2055c35SXin Li } else {
695*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
696*b2055c35SXin Li }
697*b2055c35SXin Li arg->subtype_ = SUBTYPE_ANMF;
698*b2055c35SXin Li arg->filename_ = wargv[i + 1];
699*b2055c35SXin Li arg->params_ = argv[i + 2];
700*b2055c35SXin Li ++feature_arg_index;
701*b2055c35SXin Li i += 3;
702*b2055c35SXin Li } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
703*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
704*b2055c35SXin Li if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
705*b2055c35SXin Li config->action_type_ = ACTION_SET;
706*b2055c35SXin Li } else {
707*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
708*b2055c35SXin Li }
709*b2055c35SXin Li if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
710*b2055c35SXin Li config->type_ = FEATURE_ANMF;
711*b2055c35SXin Li } else {
712*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
713*b2055c35SXin Li }
714*b2055c35SXin Li arg->subtype_ =
715*b2055c35SXin Li !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
716*b2055c35SXin Li arg->params_ = argv[i + 1];
717*b2055c35SXin Li ++feature_arg_index;
718*b2055c35SXin Li i += 2;
719*b2055c35SXin Li } else if (!strcmp(argv[i], "-o")) {
720*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
721*b2055c35SXin Li config->output_ = wargv[i + 1];
722*b2055c35SXin Li i += 2;
723*b2055c35SXin Li } else if (!strcmp(argv[i], "-info")) {
724*b2055c35SXin Li CHECK_NUM_ARGS_EXACTLY(2, ErrParse);
725*b2055c35SXin Li if (config->action_type_ != NIL_ACTION) {
726*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
727*b2055c35SXin Li } else {
728*b2055c35SXin Li config->action_type_ = ACTION_INFO;
729*b2055c35SXin Li config->arg_count_ = 0;
730*b2055c35SXin Li config->input_ = wargv[i + 1];
731*b2055c35SXin Li }
732*b2055c35SXin Li i += 2;
733*b2055c35SXin Li } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
734*b2055c35SXin Li PrintHelp();
735*b2055c35SXin Li DeleteConfig(config);
736*b2055c35SXin Li LOCAL_FREE((W_CHAR** const)unicode_argv);
737*b2055c35SXin Li exit(0);
738*b2055c35SXin Li } else if (!strcmp(argv[i], "-version")) {
739*b2055c35SXin Li const int version = WebPGetMuxVersion();
740*b2055c35SXin Li printf("%d.%d.%d\n",
741*b2055c35SXin Li (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
742*b2055c35SXin Li DeleteConfig(config);
743*b2055c35SXin Li LOCAL_FREE((W_CHAR** const)unicode_argv);
744*b2055c35SXin Li exit(0);
745*b2055c35SXin Li } else if (!strcmp(argv[i], "--")) {
746*b2055c35SXin Li if (i < argc - 1) {
747*b2055c35SXin Li ++i;
748*b2055c35SXin Li if (config->input_ == NULL) {
749*b2055c35SXin Li config->input_ = wargv[i];
750*b2055c35SXin Li } else {
751*b2055c35SXin Li ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
752*b2055c35SXin Li argv[i], ErrParse);
753*b2055c35SXin Li }
754*b2055c35SXin Li }
755*b2055c35SXin Li break;
756*b2055c35SXin Li } else {
757*b2055c35SXin Li ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
758*b2055c35SXin Li }
759*b2055c35SXin Li } else { // One of the feature types or input.
760*b2055c35SXin Li if (ACTION_IS_NIL) {
761*b2055c35SXin Li ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
762*b2055c35SXin Li ErrParse);
763*b2055c35SXin Li }
764*b2055c35SXin Li if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
765*b2055c35SXin Li !strcmp(argv[i], "xmp")) {
766*b2055c35SXin Li if (FEATURETYPE_IS_NIL) {
767*b2055c35SXin Li config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
768*b2055c35SXin Li (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
769*b2055c35SXin Li } else {
770*b2055c35SXin Li ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
771*b2055c35SXin Li }
772*b2055c35SXin Li if (config->action_type_ == ACTION_SET) {
773*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
774*b2055c35SXin Li arg->filename_ = wargv[i + 1];
775*b2055c35SXin Li ++feature_arg_index;
776*b2055c35SXin Li i += 2;
777*b2055c35SXin Li } else {
778*b2055c35SXin Li ++i;
779*b2055c35SXin Li }
780*b2055c35SXin Li } else if (!strcmp(argv[i], "frame") &&
781*b2055c35SXin Li (config->action_type_ == ACTION_GET)) {
782*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
783*b2055c35SXin Li config->type_ = FEATURE_ANMF;
784*b2055c35SXin Li arg->params_ = argv[i + 1];
785*b2055c35SXin Li ++feature_arg_index;
786*b2055c35SXin Li i += 2;
787*b2055c35SXin Li } else if (!strcmp(argv[i], "loop") &&
788*b2055c35SXin Li (config->action_type_ == ACTION_SET)) {
789*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
790*b2055c35SXin Li config->type_ = FEATURE_LOOP;
791*b2055c35SXin Li arg->params_ = argv[i + 1];
792*b2055c35SXin Li ++feature_arg_index;
793*b2055c35SXin Li i += 2;
794*b2055c35SXin Li } else if (!strcmp(argv[i], "bgcolor") &&
795*b2055c35SXin Li (config->action_type_ == ACTION_SET)) {
796*b2055c35SXin Li CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
797*b2055c35SXin Li config->type_ = FEATURE_BGCOLOR;
798*b2055c35SXin Li arg->params_ = argv[i + 1];
799*b2055c35SXin Li ++feature_arg_index;
800*b2055c35SXin Li i += 2;
801*b2055c35SXin Li } else { // Assume input file.
802*b2055c35SXin Li if (config->input_ == NULL) {
803*b2055c35SXin Li config->input_ = wargv[i];
804*b2055c35SXin Li } else {
805*b2055c35SXin Li ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
806*b2055c35SXin Li argv[i], ErrParse);
807*b2055c35SXin Li }
808*b2055c35SXin Li ++i;
809*b2055c35SXin Li }
810*b2055c35SXin Li }
811*b2055c35SXin Li }
812*b2055c35SXin Li ErrParse:
813*b2055c35SXin Li return ok;
814*b2055c35SXin Li }
815*b2055c35SXin Li
816*b2055c35SXin Li // Additional checks after config is filled.
ValidateConfig(Config * const config)817*b2055c35SXin Li static int ValidateConfig(Config* const config) {
818*b2055c35SXin Li int ok = 1;
819*b2055c35SXin Li
820*b2055c35SXin Li // Action.
821*b2055c35SXin Li if (ACTION_IS_NIL) {
822*b2055c35SXin Li ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
823*b2055c35SXin Li }
824*b2055c35SXin Li
825*b2055c35SXin Li // Feature type.
826*b2055c35SXin Li if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
827*b2055c35SXin Li ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
828*b2055c35SXin Li }
829*b2055c35SXin Li
830*b2055c35SXin Li // Input file.
831*b2055c35SXin Li if (config->input_ == NULL) {
832*b2055c35SXin Li if (config->action_type_ != ACTION_SET) {
833*b2055c35SXin Li ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
834*b2055c35SXin Li } else if (config->type_ != FEATURE_ANMF) {
835*b2055c35SXin Li ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
836*b2055c35SXin Li }
837*b2055c35SXin Li }
838*b2055c35SXin Li
839*b2055c35SXin Li // Output file.
840*b2055c35SXin Li if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
841*b2055c35SXin Li ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
842*b2055c35SXin Li }
843*b2055c35SXin Li
844*b2055c35SXin Li ErrValidate2:
845*b2055c35SXin Li return ok;
846*b2055c35SXin Li }
847*b2055c35SXin Li
848*b2055c35SXin Li // Create config object from command-line arguments.
InitializeConfig(int argc,const char * argv[],Config * const config,const W_CHAR ** const unicode_argv)849*b2055c35SXin Li static int InitializeConfig(int argc, const char* argv[], Config* const config,
850*b2055c35SXin Li const W_CHAR** const unicode_argv) {
851*b2055c35SXin Li int num_feature_args = 0;
852*b2055c35SXin Li int ok;
853*b2055c35SXin Li
854*b2055c35SXin Li memset(config, 0, sizeof(*config));
855*b2055c35SXin Li
856*b2055c35SXin Li ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
857*b2055c35SXin Li if (!ok) return 0;
858*b2055c35SXin Li
859*b2055c35SXin Li // Validate command-line arguments.
860*b2055c35SXin Li if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
861*b2055c35SXin Li ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
862*b2055c35SXin Li }
863*b2055c35SXin Li
864*b2055c35SXin Li config->arg_count_ = num_feature_args;
865*b2055c35SXin Li config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
866*b2055c35SXin Li if (config->args_ == NULL) {
867*b2055c35SXin Li ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
868*b2055c35SXin Li }
869*b2055c35SXin Li
870*b2055c35SXin Li // Parse command-line.
871*b2055c35SXin Li if (!ParseCommandLine(config, unicode_argv) || !ValidateConfig(config)) {
872*b2055c35SXin Li ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
873*b2055c35SXin Li }
874*b2055c35SXin Li
875*b2055c35SXin Li Err1:
876*b2055c35SXin Li return ok;
877*b2055c35SXin Li }
878*b2055c35SXin Li
879*b2055c35SXin Li #undef ACTION_IS_NIL
880*b2055c35SXin Li #undef FEATURETYPE_IS_NIL
881*b2055c35SXin Li #undef CHECK_NUM_ARGS_AT_LEAST
882*b2055c35SXin Li #undef CHECK_NUM_ARGS_AT_MOST
883*b2055c35SXin Li #undef CHECK_NUM_ARGS_EXACTLY
884*b2055c35SXin Li
885*b2055c35SXin Li //------------------------------------------------------------------------------
886*b2055c35SXin Li // Processing.
887*b2055c35SXin Li
GetFrame(const WebPMux * mux,const Config * config)888*b2055c35SXin Li static int GetFrame(const WebPMux* mux, const Config* config) {
889*b2055c35SXin Li WebPMuxError err = WEBP_MUX_OK;
890*b2055c35SXin Li WebPMux* mux_single = NULL;
891*b2055c35SXin Li int num = 0;
892*b2055c35SXin Li int ok = 1;
893*b2055c35SXin Li int parse_error = 0;
894*b2055c35SXin Li const WebPChunkId id = WEBP_CHUNK_ANMF;
895*b2055c35SXin Li WebPMuxFrameInfo info;
896*b2055c35SXin Li WebPDataInit(&info.bitstream);
897*b2055c35SXin Li
898*b2055c35SXin Li num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
899*b2055c35SXin Li if (num < 0) {
900*b2055c35SXin Li ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
901*b2055c35SXin Li }
902*b2055c35SXin Li if (parse_error) goto ErrGet;
903*b2055c35SXin Li
904*b2055c35SXin Li err = WebPMuxGetFrame(mux, num, &info);
905*b2055c35SXin Li if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
906*b2055c35SXin Li if (err != WEBP_MUX_OK) {
907*b2055c35SXin Li ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
908*b2055c35SXin Li ErrorString(err), num, ErrGet);
909*b2055c35SXin Li }
910*b2055c35SXin Li
911*b2055c35SXin Li mux_single = WebPMuxNew();
912*b2055c35SXin Li if (mux_single == NULL) {
913*b2055c35SXin Li err = WEBP_MUX_MEMORY_ERROR;
914*b2055c35SXin Li ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
915*b2055c35SXin Li ErrorString(err), ErrGet);
916*b2055c35SXin Li }
917*b2055c35SXin Li err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
918*b2055c35SXin Li if (err != WEBP_MUX_OK) {
919*b2055c35SXin Li ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
920*b2055c35SXin Li ErrorString(err), ErrGet);
921*b2055c35SXin Li }
922*b2055c35SXin Li
923*b2055c35SXin Li ok = WriteWebP(mux_single, config->output_);
924*b2055c35SXin Li
925*b2055c35SXin Li ErrGet:
926*b2055c35SXin Li WebPDataClear(&info.bitstream);
927*b2055c35SXin Li WebPMuxDelete(mux_single);
928*b2055c35SXin Li return ok && !parse_error;
929*b2055c35SXin Li }
930*b2055c35SXin Li
931*b2055c35SXin Li // Read and process config.
Process(const Config * config)932*b2055c35SXin Li static int Process(const Config* config) {
933*b2055c35SXin Li WebPMux* mux = NULL;
934*b2055c35SXin Li WebPData chunk;
935*b2055c35SXin Li WebPMuxError err = WEBP_MUX_OK;
936*b2055c35SXin Li int ok = 1;
937*b2055c35SXin Li
938*b2055c35SXin Li switch (config->action_type_) {
939*b2055c35SXin Li case ACTION_GET: {
940*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
941*b2055c35SXin Li if (!ok) goto Err2;
942*b2055c35SXin Li switch (config->type_) {
943*b2055c35SXin Li case FEATURE_ANMF:
944*b2055c35SXin Li ok = GetFrame(mux, config);
945*b2055c35SXin Li break;
946*b2055c35SXin Li
947*b2055c35SXin Li case FEATURE_ICCP:
948*b2055c35SXin Li case FEATURE_EXIF:
949*b2055c35SXin Li case FEATURE_XMP:
950*b2055c35SXin Li err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
951*b2055c35SXin Li if (err != WEBP_MUX_OK) {
952*b2055c35SXin Li ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
953*b2055c35SXin Li ErrorString(err), kDescriptions[config->type_], Err2);
954*b2055c35SXin Li }
955*b2055c35SXin Li ok = WriteData(config->output_, &chunk);
956*b2055c35SXin Li break;
957*b2055c35SXin Li
958*b2055c35SXin Li default:
959*b2055c35SXin Li ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
960*b2055c35SXin Li break;
961*b2055c35SXin Li }
962*b2055c35SXin Li break;
963*b2055c35SXin Li }
964*b2055c35SXin Li case ACTION_SET: {
965*b2055c35SXin Li switch (config->type_) {
966*b2055c35SXin Li case FEATURE_ANMF: {
967*b2055c35SXin Li int i;
968*b2055c35SXin Li WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
969*b2055c35SXin Li mux = WebPMuxNew();
970*b2055c35SXin Li if (mux == NULL) {
971*b2055c35SXin Li ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
972*b2055c35SXin Li ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
973*b2055c35SXin Li }
974*b2055c35SXin Li for (i = 0; i < config->arg_count_; ++i) {
975*b2055c35SXin Li switch (config->args_[i].subtype_) {
976*b2055c35SXin Li case SUBTYPE_BGCOLOR: {
977*b2055c35SXin Li uint32_t bgcolor;
978*b2055c35SXin Li ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
979*b2055c35SXin Li if (!ok) {
980*b2055c35SXin Li ERROR_GOTO1("ERROR: Could not parse the background color \n",
981*b2055c35SXin Li Err2);
982*b2055c35SXin Li }
983*b2055c35SXin Li params.bgcolor = bgcolor;
984*b2055c35SXin Li break;
985*b2055c35SXin Li }
986*b2055c35SXin Li case SUBTYPE_LOOP: {
987*b2055c35SXin Li int parse_error = 0;
988*b2055c35SXin Li const int loop_count =
989*b2055c35SXin Li ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
990*b2055c35SXin Li if (loop_count < 0 || loop_count > 65535) {
991*b2055c35SXin Li // Note: This is only a 'necessary' condition for loop_count
992*b2055c35SXin Li // to be valid. The 'sufficient' conditioned in checked in
993*b2055c35SXin Li // WebPMuxSetAnimationParams() method called later.
994*b2055c35SXin Li ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
995*b2055c35SXin Li "65535.\n", Err2);
996*b2055c35SXin Li }
997*b2055c35SXin Li ok = !parse_error;
998*b2055c35SXin Li if (!ok) goto Err2;
999*b2055c35SXin Li params.loop_count = loop_count;
1000*b2055c35SXin Li break;
1001*b2055c35SXin Li }
1002*b2055c35SXin Li case SUBTYPE_ANMF: {
1003*b2055c35SXin Li WebPMuxFrameInfo frame;
1004*b2055c35SXin Li frame.id = WEBP_CHUNK_ANMF;
1005*b2055c35SXin Li ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
1006*b2055c35SXin Li &frame.bitstream);
1007*b2055c35SXin Li if (!ok) goto Err2;
1008*b2055c35SXin Li ok = ParseFrameArgs(config->args_[i].params_, &frame);
1009*b2055c35SXin Li if (!ok) {
1010*b2055c35SXin Li WebPDataClear(&frame.bitstream);
1011*b2055c35SXin Li ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
1012*b2055c35SXin Li Err2);
1013*b2055c35SXin Li }
1014*b2055c35SXin Li err = WebPMuxPushFrame(mux, &frame, 1);
1015*b2055c35SXin Li WebPDataClear(&frame.bitstream);
1016*b2055c35SXin Li if (err != WEBP_MUX_OK) {
1017*b2055c35SXin Li ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
1018*b2055c35SXin Li "\n", ErrorString(err), i, Err2);
1019*b2055c35SXin Li }
1020*b2055c35SXin Li break;
1021*b2055c35SXin Li }
1022*b2055c35SXin Li default: {
1023*b2055c35SXin Li ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
1024*b2055c35SXin Li break;
1025*b2055c35SXin Li }
1026*b2055c35SXin Li }
1027*b2055c35SXin Li }
1028*b2055c35SXin Li err = WebPMuxSetAnimationParams(mux, ¶ms);
1029*b2055c35SXin Li if (err != WEBP_MUX_OK) {
1030*b2055c35SXin Li ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
1031*b2055c35SXin Li ErrorString(err), Err2);
1032*b2055c35SXin Li }
1033*b2055c35SXin Li break;
1034*b2055c35SXin Li }
1035*b2055c35SXin Li
1036*b2055c35SXin Li case FEATURE_ICCP:
1037*b2055c35SXin Li case FEATURE_EXIF:
1038*b2055c35SXin Li case FEATURE_XMP: {
1039*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
1040*b2055c35SXin Li if (!ok) goto Err2;
1041*b2055c35SXin Li ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
1042*b2055c35SXin Li if (!ok) goto Err2;
1043*b2055c35SXin Li err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
1044*b2055c35SXin Li WebPDataClear(&chunk);
1045*b2055c35SXin Li if (err != WEBP_MUX_OK) {
1046*b2055c35SXin Li ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
1047*b2055c35SXin Li ErrorString(err), kDescriptions[config->type_], Err2);
1048*b2055c35SXin Li }
1049*b2055c35SXin Li break;
1050*b2055c35SXin Li }
1051*b2055c35SXin Li case FEATURE_LOOP: {
1052*b2055c35SXin Li WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
1053*b2055c35SXin Li int parse_error = 0;
1054*b2055c35SXin Li const int loop_count =
1055*b2055c35SXin Li ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
1056*b2055c35SXin Li if (loop_count < 0 || loop_count > 65535 || parse_error) {
1057*b2055c35SXin Li ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n",
1058*b2055c35SXin Li Err2);
1059*b2055c35SXin Li }
1060*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
1061*b2055c35SXin Li if (!ok) goto Err2;
1062*b2055c35SXin Li ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
1063*b2055c35SXin Li if (!ok) {
1064*b2055c35SXin Li ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n",
1065*b2055c35SXin Li Err2);
1066*b2055c35SXin Li }
1067*b2055c35SXin Li params.loop_count = loop_count;
1068*b2055c35SXin Li err = WebPMuxSetAnimationParams(mux, ¶ms);
1069*b2055c35SXin Li ok = (err == WEBP_MUX_OK);
1070*b2055c35SXin Li if (!ok) {
1071*b2055c35SXin Li ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
1072*b2055c35SXin Li ErrorString(err), Err2);
1073*b2055c35SXin Li }
1074*b2055c35SXin Li break;
1075*b2055c35SXin Li }
1076*b2055c35SXin Li case FEATURE_BGCOLOR: {
1077*b2055c35SXin Li WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
1078*b2055c35SXin Li uint32_t bgcolor;
1079*b2055c35SXin Li ok = ParseBgcolorArgs(config->args_[0].params_, &bgcolor);
1080*b2055c35SXin Li if (!ok) {
1081*b2055c35SXin Li ERROR_GOTO1("ERROR: Could not parse the background color.\n",
1082*b2055c35SXin Li Err2);
1083*b2055c35SXin Li }
1084*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
1085*b2055c35SXin Li if (!ok) goto Err2;
1086*b2055c35SXin Li ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
1087*b2055c35SXin Li if (!ok) {
1088*b2055c35SXin Li ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n",
1089*b2055c35SXin Li Err2);
1090*b2055c35SXin Li }
1091*b2055c35SXin Li params.bgcolor = bgcolor;
1092*b2055c35SXin Li err = WebPMuxSetAnimationParams(mux, ¶ms);
1093*b2055c35SXin Li ok = (err == WEBP_MUX_OK);
1094*b2055c35SXin Li if (!ok) {
1095*b2055c35SXin Li ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
1096*b2055c35SXin Li ErrorString(err), Err2);
1097*b2055c35SXin Li }
1098*b2055c35SXin Li break;
1099*b2055c35SXin Li }
1100*b2055c35SXin Li default: {
1101*b2055c35SXin Li ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
1102*b2055c35SXin Li break;
1103*b2055c35SXin Li }
1104*b2055c35SXin Li }
1105*b2055c35SXin Li ok = WriteWebP(mux, config->output_);
1106*b2055c35SXin Li break;
1107*b2055c35SXin Li }
1108*b2055c35SXin Li case ACTION_DURATION: {
1109*b2055c35SXin Li int num_frames;
1110*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
1111*b2055c35SXin Li if (!ok) goto Err2;
1112*b2055c35SXin Li err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
1113*b2055c35SXin Li ok = (err == WEBP_MUX_OK);
1114*b2055c35SXin Li if (!ok) {
1115*b2055c35SXin Li ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
1116*b2055c35SXin Li }
1117*b2055c35SXin Li if (num_frames == 0) {
1118*b2055c35SXin Li fprintf(stderr, "Doesn't look like the source is animated. "
1119*b2055c35SXin Li "Skipping duration setting.\n");
1120*b2055c35SXin Li ok = WriteWebP(mux, config->output_);
1121*b2055c35SXin Li if (!ok) goto Err2;
1122*b2055c35SXin Li } else {
1123*b2055c35SXin Li int i;
1124*b2055c35SXin Li int* durations = NULL;
1125*b2055c35SXin Li WebPMux* new_mux = DuplicateMuxHeader(mux);
1126*b2055c35SXin Li if (new_mux == NULL) goto Err2;
1127*b2055c35SXin Li durations = (int*)WebPMalloc((size_t)num_frames * sizeof(*durations));
1128*b2055c35SXin Li if (durations == NULL) goto Err2;
1129*b2055c35SXin Li for (i = 0; i < num_frames; ++i) durations[i] = -1;
1130*b2055c35SXin Li
1131*b2055c35SXin Li // Parse intervals to process.
1132*b2055c35SXin Li for (i = 0; i < config->arg_count_; ++i) {
1133*b2055c35SXin Li int k;
1134*b2055c35SXin Li int args[3];
1135*b2055c35SXin Li int duration, start, end;
1136*b2055c35SXin Li const int nb_args = ExUtilGetInts(config->args_[i].params_,
1137*b2055c35SXin Li 10, 3, args);
1138*b2055c35SXin Li ok = (nb_args >= 1);
1139*b2055c35SXin Li if (!ok) goto Err3;
1140*b2055c35SXin Li duration = args[0];
1141*b2055c35SXin Li if (duration < 0) {
1142*b2055c35SXin Li ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
1143*b2055c35SXin Li }
1144*b2055c35SXin Li
1145*b2055c35SXin Li if (nb_args == 1) { // only duration is present -> use full interval
1146*b2055c35SXin Li start = 1;
1147*b2055c35SXin Li end = num_frames;
1148*b2055c35SXin Li } else {
1149*b2055c35SXin Li start = args[1];
1150*b2055c35SXin Li if (start <= 0) {
1151*b2055c35SXin Li start = 1;
1152*b2055c35SXin Li } else if (start > num_frames) {
1153*b2055c35SXin Li start = num_frames;
1154*b2055c35SXin Li }
1155*b2055c35SXin Li end = (nb_args >= 3) ? args[2] : start;
1156*b2055c35SXin Li if (end == 0 || end > num_frames) end = num_frames;
1157*b2055c35SXin Li }
1158*b2055c35SXin Li
1159*b2055c35SXin Li for (k = start; k <= end; ++k) {
1160*b2055c35SXin Li assert(k >= 1 && k <= num_frames);
1161*b2055c35SXin Li durations[k - 1] = duration;
1162*b2055c35SXin Li }
1163*b2055c35SXin Li }
1164*b2055c35SXin Li
1165*b2055c35SXin Li // Apply non-negative durations to their destination frames.
1166*b2055c35SXin Li for (i = 1; i <= num_frames; ++i) {
1167*b2055c35SXin Li WebPMuxFrameInfo frame;
1168*b2055c35SXin Li err = WebPMuxGetFrame(mux, i, &frame);
1169*b2055c35SXin Li if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
1170*b2055c35SXin Li ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
1171*b2055c35SXin Li }
1172*b2055c35SXin Li if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
1173*b2055c35SXin Li err = WebPMuxPushFrame(new_mux, &frame, 1);
1174*b2055c35SXin Li if (err != WEBP_MUX_OK) {
1175*b2055c35SXin Li ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
1176*b2055c35SXin Li }
1177*b2055c35SXin Li WebPDataClear(&frame.bitstream);
1178*b2055c35SXin Li }
1179*b2055c35SXin Li WebPMuxDelete(mux);
1180*b2055c35SXin Li ok = WriteWebP(new_mux, config->output_);
1181*b2055c35SXin Li mux = new_mux; // transfer for the WebPMuxDelete() call
1182*b2055c35SXin Li new_mux = NULL;
1183*b2055c35SXin Li
1184*b2055c35SXin Li Err3:
1185*b2055c35SXin Li WebPFree(durations);
1186*b2055c35SXin Li WebPMuxDelete(new_mux);
1187*b2055c35SXin Li if (!ok) goto Err2;
1188*b2055c35SXin Li }
1189*b2055c35SXin Li break;
1190*b2055c35SXin Li }
1191*b2055c35SXin Li case ACTION_STRIP: {
1192*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
1193*b2055c35SXin Li if (!ok) goto Err2;
1194*b2055c35SXin Li if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
1195*b2055c35SXin Li config->type_ == FEATURE_XMP) {
1196*b2055c35SXin Li err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
1197*b2055c35SXin Li if (err != WEBP_MUX_OK) {
1198*b2055c35SXin Li ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
1199*b2055c35SXin Li ErrorString(err), kDescriptions[config->type_], Err2);
1200*b2055c35SXin Li }
1201*b2055c35SXin Li } else {
1202*b2055c35SXin Li ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1203*b2055c35SXin Li break;
1204*b2055c35SXin Li }
1205*b2055c35SXin Li ok = WriteWebP(mux, config->output_);
1206*b2055c35SXin Li break;
1207*b2055c35SXin Li }
1208*b2055c35SXin Li case ACTION_INFO: {
1209*b2055c35SXin Li ok = CreateMux(config->input_, &mux);
1210*b2055c35SXin Li if (!ok) goto Err2;
1211*b2055c35SXin Li ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1212*b2055c35SXin Li break;
1213*b2055c35SXin Li }
1214*b2055c35SXin Li default: {
1215*b2055c35SXin Li assert(0); // Invalid action.
1216*b2055c35SXin Li break;
1217*b2055c35SXin Li }
1218*b2055c35SXin Li }
1219*b2055c35SXin Li
1220*b2055c35SXin Li Err2:
1221*b2055c35SXin Li WebPMuxDelete(mux);
1222*b2055c35SXin Li return ok;
1223*b2055c35SXin Li }
1224*b2055c35SXin Li
1225*b2055c35SXin Li //------------------------------------------------------------------------------
1226*b2055c35SXin Li // Main.
1227*b2055c35SXin Li
main(int argc,const char * argv[])1228*b2055c35SXin Li int main(int argc, const char* argv[]) {
1229*b2055c35SXin Li Config config;
1230*b2055c35SXin Li int ok;
1231*b2055c35SXin Li
1232*b2055c35SXin Li INIT_WARGV(argc, argv);
1233*b2055c35SXin Li
1234*b2055c35SXin Li ok = InitializeConfig(argc - 1, argv + 1, &config, GET_WARGV_OR_NULL());
1235*b2055c35SXin Li if (ok) {
1236*b2055c35SXin Li ok = Process(&config);
1237*b2055c35SXin Li } else {
1238*b2055c35SXin Li PrintHelp();
1239*b2055c35SXin Li }
1240*b2055c35SXin Li DeleteConfig(&config);
1241*b2055c35SXin Li FREE_WARGV_AND_RETURN(!ok);
1242*b2055c35SXin Li }
1243*b2055c35SXin Li
1244*b2055c35SXin Li //------------------------------------------------------------------------------
1245