blob: 7fe677031a64e299bf17c202093b7463d759ebc8 [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple command-line to create a WebP container file and to extract or strip
9// relevant data from the container file.
10//
James Zern04e84cf2011-11-04 15:20:08 -070011// Compile with: gcc -o webpmux webpmux.c -lwebpmux -lwebp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053012//
13//
14// Authors: Vikas (vikaas.arora@gmail.com),
15// Urvang (urvang@google.com)
16
17/* Usage examples:
18
19 Create container WebP file:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053020 webpmux -tile tile_1.webp +0+0 \
21 -tile tile_2.webp +960+0 \
22 -tile tile_3.webp +0+576 \
23 -tile tile_4.webp +960+576 \
24 -o out_tile_container.webp
25
26 webpmux -frame anim_1.webp +0+0+0 \
27 -frame anim_2.webp +25+25+100 \
28 -frame anim_3.webp +50+50+100 \
29 -frame anim_4.webp +0+0+100 \
30 -loop 10 \
31 -o out_animation_container.webp
32
33 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053034 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
35
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036 Extract relevant data from WebP container file:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 webpmux -get tile n in.webp -o out_tile.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053038 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053039 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshia4f32ca2011-09-30 11:07:01 +053040 webpmux -get xmp in.webp -o image_metadata.xmp
41
Urvang Joshia4f32ca2011-09-30 11:07:01 +053042 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070043 webpmux -strip icc in.webp -o out.webp
44 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053045
46 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053047 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070048 webpmux [ -h | -help ]
Urvang Joshia4f32ca2011-09-30 11:07:01 +053049*/
50
51#include <assert.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070056#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057
Urvang Joshia4f32ca2011-09-30 11:07:01 +053058//------------------------------------------------------------------------------
59// Config object to parse command-line arguments.
60
61typedef enum {
62 NIL_ACTION = 0,
63 ACTION_GET,
64 ACTION_SET,
65 ACTION_STRIP,
66 ACTION_INFO,
67 ACTION_HELP
68} ActionType;
69
70typedef enum {
71 NIL_SUBTYPE = 0,
72 SUBTYPE_FRM,
73 SUBTYPE_LOOP
74} FeatureSubType;
75
76typedef struct {
77 FeatureSubType subtype_;
78 const char* filename_;
79 const char* params_;
80} FeatureArg;
81
82typedef enum {
83 NIL_FEATURE = 0,
84 FEATURE_XMP,
85 FEATURE_ICCP,
86 FEATURE_FRM,
87 FEATURE_TILE
88} FeatureType;
89
90typedef struct {
91 FeatureType type_;
92 FeatureArg* args_;
93 int arg_count_;
94} Feature;
95
96typedef struct {
97 ActionType action_type_;
98 const char* input_;
99 const char* output_;
100 Feature feature_;
101} WebPMuxConfig;
102
103//------------------------------------------------------------------------------
104// Helper functions.
105
James Zern04e84cf2011-11-04 15:20:08 -0700106static int CountOccurrences(const char* arglist[], int list_length,
107 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530108 int i;
109 int num_occurences = 0;
110
111 for (i = 0; i < list_length; ++i) {
112 if (!strcmp(arglist[i], arg)) {
113 ++num_occurences;
114 }
115 }
116 return num_occurences;
117}
118
119static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700120 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530121}
122
James Zerna0b27362012-01-27 17:39:47 -0800123#define RETURN_IF_ERROR(ERR_MSG) \
124 if (err != WEBP_MUX_OK) { \
125 fprintf(stderr, ERR_MSG); \
126 return err; \
127 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530128
James Zerna0b27362012-01-27 17:39:47 -0800129#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
130 if (err != WEBP_MUX_OK) { \
131 fprintf(stderr, ERR_MSG, FORMAT_STR); \
132 return err; \
133 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530134
James Zerna0b27362012-01-27 17:39:47 -0800135#define ERROR_GOTO1(ERR_MSG, LABEL) \
136 do { \
137 fprintf(stderr, ERR_MSG); \
138 ok = 0; \
139 goto LABEL; \
140 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530141
James Zerna0b27362012-01-27 17:39:47 -0800142#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
143 do { \
144 fprintf(stderr, ERR_MSG, FORMAT_STR); \
145 ok = 0; \
146 goto LABEL; \
147 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530148
James Zerna0b27362012-01-27 17:39:47 -0800149#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
150 do { \
151 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
152 ok = 0; \
153 goto LABEL; \
154 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530155
156static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530157 uint32_t flag;
158
159 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
160 RETURN_IF_ERROR("Failed to retrieve features\n");
161
162 if (flag == 0) {
163 fprintf(stderr, "No features present.\n");
164 return err;
165 }
166
167 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800168 printf("Features present:");
169 if (flag & ANIMATION_FLAG) printf(" animation");
170 if (flag & TILE_FLAG) printf(" tiling");
171 if (flag & ICCP_FLAG) printf(" icc profile");
172 if (flag & META_FLAG) printf(" metadata");
173 if (flag & ALPHA_FLAG) printf(" transparency");
174 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530175
176 if (flag & ANIMATION_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800177 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530178 uint32_t loop_count;
179 err = WebPMuxGetLoopCount(mux, &loop_count);
180 RETURN_IF_ERROR("Failed to retrieve loop count\n");
James Zern974aaff2012-01-24 12:46:46 -0800181 printf("Loop Count : %d\n", loop_count);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530182
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530183 err = WebPMuxNumChunks(mux, WEBP_CHUNK_FRAME, &nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530184 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
185
James Zern974aaff2012-01-24 12:46:46 -0800186 printf("Number of frames: %d\n", nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530187 if (nFrames > 0) {
188 int i;
James Zern974aaff2012-01-24 12:46:46 -0800189 printf("No.: x_offset y_offset duration image_size");
James Zern974aaff2012-01-24 12:46:46 -0800190 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530191 for (i = 1; i <= nFrames; i++) {
James Zern974aaff2012-01-24 12:46:46 -0800192 uint32_t x_offset, y_offset, duration;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700193 WebPData bitstream;
194 err = WebPMuxGetFrame(mux, i, &bitstream,
Urvang Joshic398f592011-11-22 14:40:41 +0530195 &x_offset, &y_offset, &duration);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530196 RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
James Zern95667b82012-04-18 17:13:34 -0700197 printf("%3d: %8d %8d %8d %10zu",
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700198 i, x_offset, y_offset, duration, bitstream.size_);
James Zern974aaff2012-01-24 12:46:46 -0800199 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530200 }
201 }
202 }
203
204 if (flag & TILE_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800205 int nTiles;
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530206 err = WebPMuxNumChunks(mux, WEBP_CHUNK_TILE, &nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530207 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
208
James Zern974aaff2012-01-24 12:46:46 -0800209 printf("Number of tiles: %d\n", nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530210 if (nTiles > 0) {
211 int i;
James Zern974aaff2012-01-24 12:46:46 -0800212 printf("No.: x_offset y_offset image_size");
James Zern974aaff2012-01-24 12:46:46 -0800213 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530214 for (i = 1; i <= nTiles; i++) {
James Zern0f7820e2012-01-24 14:08:27 -0800215 uint32_t x_offset, y_offset;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700216 WebPData bitstream;
217 err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530218 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700219 printf("%3d: %8d %8d %10zu", i, x_offset, y_offset, bitstream.size_);
James Zern974aaff2012-01-24 12:46:46 -0800220 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530221 }
222 }
223 }
224
225 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800226 WebPData icc_profile;
227 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530228 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zern95667b82012-04-18 17:13:34 -0700229 printf("Size of the color profile data: %zu\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530230 }
231
232 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800233 WebPData metadata;
234 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530235 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern95667b82012-04-18 17:13:34 -0700236 printf("Size of the XMP metadata: %zu\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530237 }
238
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530239 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700240 WebPData bitstream;
241 err = WebPMuxGetImage(mux, &bitstream);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530242 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700243 printf("Size of the image (with alpha): %zu\n", bitstream.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530244 }
245
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530246 return WEBP_MUX_OK;
247}
248
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700249static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800250 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
251 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
252 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
253 printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
254 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
255 printf(" -loop LOOP_COUNT -o OUTPUT\n");
256 printf(" webpmux -info INPUT\n");
257 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530258
James Zern974aaff2012-01-24 12:46:46 -0800259 printf("\n");
260 printf("GET_OPTIONS:\n");
261 printf(" Extract relevant data.\n");
262 printf(" icc Get ICCP Color profile.\n");
263 printf(" xmp Get XMP metadata.\n");
264 printf(" tile n Get nth tile.\n");
265 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530266
James Zern974aaff2012-01-24 12:46:46 -0800267 printf("\n");
268 printf("SET_OPTIONS:\n");
269 printf(" Set color profile/metadata.\n");
270 printf(" icc Set ICC Color profile.\n");
271 printf(" xmp Set XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530272
James Zern974aaff2012-01-24 12:46:46 -0800273 printf("\n");
274 printf("STRIP_OPTIONS:\n");
275 printf(" Strip color profile/metadata.\n");
276 printf(" icc Strip ICCP color profile.\n");
277 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530278
James Zern974aaff2012-01-24 12:46:46 -0800279 printf("\n");
280 printf("TILE_OPTIONS(i):\n");
281 printf(" Create tiled image.\n");
282 printf(" file_i +xi+yi\n");
283 printf(" where: 'file_i' is the i'th tile (webp format),\n");
284 printf(" 'xi','yi' specify the image offset for this tile.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530285
James Zern974aaff2012-01-24 12:46:46 -0800286 printf("\n");
287 printf("FRAME_OPTIONS(i):\n");
288 printf(" Create animation.\n");
289 printf(" file_i +xi+yi+di\n");
290 printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
291 printf(" 'xi','yi' specify the image offset for this frame.\n");
292 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530293
James Zern974aaff2012-01-24 12:46:46 -0800294 printf("\nINPUT & OUTPUT are in webp format.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530295}
296
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530297static int ReadFileToWebPData(const char* const filename,
298 WebPData* const webp_data) {
299 const uint8_t* data;
300 size_t size;
301 if (!ExUtilReadFile(filename, &data, &size)) return 0;
302 webp_data->bytes_ = data;
303 webp_data->size_ = size;
304 return 1;
305}
306
James Zern061263a2012-05-11 16:00:57 -0700307static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530308 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530309 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530310 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530311 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530312 free((void*)bitstream.bytes_);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530313 if (*mux != NULL) return 1;
314 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530315 return 0;
316}
317
James Zern0f7820e2012-01-24 14:08:27 -0800318static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530319 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700320 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530321 if (!fout) {
322 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
323 return 0;
324 }
James Zern0f7820e2012-01-24 14:08:27 -0800325 if (fwrite(webpdata->bytes_, webpdata->size_, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530326 fprintf(stderr, "Error writing file %s!\n", filename);
327 } else {
James Zern95667b82012-04-18 17:13:34 -0700328 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530329 ok = 1;
330 }
331 if (fout != stdout) fclose(fout);
332 return ok;
333}
334
335static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530336 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530337 WebPData webp_data;
338 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530339 if (err != WEBP_MUX_OK) {
340 fprintf(stderr, "Error (%d) assembling the WebP file.\n", err);
341 return 0;
342 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530343 ok = WriteData(filename, &webp_data);
344 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530345 return ok;
346}
347
348static int ParseFrameArgs(const char* args, uint32_t* x_offset,
349 uint32_t* y_offset, uint32_t* duration) {
350 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
351}
352
353static int ParseTileArgs(const char* args, uint32_t* x_offset,
354 uint32_t* y_offset) {
355 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
356}
357
358//------------------------------------------------------------------------------
359// Clean-up.
360
361static void DeleteConfig(WebPMuxConfig* config) {
362 if (config != NULL) {
363 free(config->feature_.args_);
364 free(config);
365 }
366}
367
368//------------------------------------------------------------------------------
369// Parsing.
370
371// Basic syntactic checks on the command-line arguments.
372// Returns 1 on valid, 0 otherwise.
373// Also fills up num_feature_args to be number of feature arguments given.
374// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
375static int ValidateCommandLine(int argc, const char* argv[],
376 int* num_feature_args) {
377 int num_frame_args;
378 int num_tile_args;
379 int num_loop_args;
380 int ok = 1;
381
382 assert(num_feature_args != NULL);
383 *num_feature_args = 0;
384
385 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700386 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530387 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
388 }
James Zern04e84cf2011-11-04 15:20:08 -0700389 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530390 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
391 }
James Zern04e84cf2011-11-04 15:20:08 -0700392 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530393 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
394 }
James Zern04e84cf2011-11-04 15:20:08 -0700395 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530396 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
397 }
James Zern04e84cf2011-11-04 15:20:08 -0700398 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530399 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
400 }
401
402 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700403 num_frame_args = CountOccurrences(argv, argc, "-frame");
404 num_tile_args = CountOccurrences(argv, argc, "-tile");
405 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530406
407 if (num_loop_args > 1) {
408 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
409 }
410
411 if (IsNotCompatible(num_frame_args, num_loop_args)) {
412 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
413 ErrValidate);
414 }
415 if (num_frame_args > 0 && num_tile_args > 0) {
416 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
417 "\n", ErrValidate);
418 }
419
420 assert(ok == 1);
421 if (num_frame_args == 0 && num_tile_args == 0) {
422 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
423 *num_feature_args = 1;
424 } else {
425 // Multiple arguments ('set' action for animation or tiling).
426 if (num_frame_args > 0) {
427 *num_feature_args = num_frame_args + num_loop_args;
428 } else {
429 *num_feature_args = num_tile_args;
430 }
431 }
432
433 ErrValidate:
434 return ok;
435}
436
437#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
438
439#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
440
441#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
442 if (argc < i + (NUM)) { \
443 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
444 goto LABEL; \
445 }
446
447#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
448 if (argc != i + (NUM)) { \
449 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
450 goto LABEL; \
451 }
452
453// Parses command-line arguments to fill up config object. Also performs some
454// semantic checks.
455static int ParseCommandLine(int argc, const char* argv[],
456 WebPMuxConfig* config) {
457 int i = 0;
458 int feature_arg_index = 0;
459 int ok = 1;
460
461 while (i < argc) {
462 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700463 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530464 if (argv[i][0] == '-') { // One of the action types or output.
465 if (!strcmp(argv[i], "-set")) {
466 if (ACTION_IS_NIL) {
467 config->action_type_ = ACTION_SET;
468 } else {
469 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
470 }
471 ++i;
472 } else if (!strcmp(argv[i], "-get")) {
473 if (ACTION_IS_NIL) {
474 config->action_type_ = ACTION_GET;
475 } else {
476 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
477 }
478 ++i;
479 } else if (!strcmp(argv[i], "-strip")) {
480 if (ACTION_IS_NIL) {
481 config->action_type_ = ACTION_STRIP;
482 feature->arg_count_ = 0;
483 } else {
484 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
485 }
486 ++i;
487 } else if (!strcmp(argv[i], "-frame")) {
488 CHECK_NUM_ARGS_LESS(3, ErrParse);
489 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
490 config->action_type_ = ACTION_SET;
491 } else {
492 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
493 }
494 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
495 feature->type_ = FEATURE_FRM;
496 } else {
497 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
498 }
499 arg->subtype_ = SUBTYPE_FRM;
500 arg->filename_ = argv[i + 1];
501 arg->params_ = argv[i + 2];
502 ++feature_arg_index;
503 i += 3;
504 } else if (!strcmp(argv[i], "-loop")) {
505 CHECK_NUM_ARGS_LESS(2, ErrParse);
506 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
507 config->action_type_ = ACTION_SET;
508 } else {
509 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
510 }
511 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
512 feature->type_ = FEATURE_FRM;
513 } else {
514 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
515 }
516 arg->subtype_ = SUBTYPE_LOOP;
517 arg->params_ = argv[i + 1];
518 ++feature_arg_index;
519 i += 2;
520 } else if (!strcmp(argv[i], "-tile")) {
521 CHECK_NUM_ARGS_LESS(3, ErrParse);
522 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
523 config->action_type_ = ACTION_SET;
524 } else {
525 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
526 }
527 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
528 feature->type_ = FEATURE_TILE;
529 } else {
530 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
531 }
532 arg->filename_ = argv[i + 1];
533 arg->params_ = argv[i + 2];
534 ++feature_arg_index;
535 i += 3;
536 } else if (!strcmp(argv[i], "-o")) {
537 CHECK_NUM_ARGS_LESS(2, ErrParse);
538 config->output_ = argv[i + 1];
539 i += 2;
540 } else if (!strcmp(argv[i], "-info")) {
541 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
542 if (config->action_type_ != NIL_ACTION) {
543 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
544 } else {
545 config->action_type_ = ACTION_INFO;
546 feature->arg_count_ = 0;
547 config->input_ = argv[i + 1];
548 }
549 i += 2;
550 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
551 PrintHelp();
552 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800553 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530554 } else {
555 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
556 }
557 } else { // One of the feature types or input.
558 if (ACTION_IS_NIL) {
559 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
560 ErrParse);
561 }
562 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
563 if (FEATURETYPE_IS_NIL) {
564 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
565 FEATURE_XMP;
566 } else {
567 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
568 }
569 if (config->action_type_ == ACTION_SET) {
570 CHECK_NUM_ARGS_LESS(2, ErrParse);
571 arg->filename_ = argv[i + 1];
572 ++feature_arg_index;
573 i += 2;
574 } else {
575 ++i;
576 }
577 } else if ((!strcmp(argv[i], "frame") ||
578 !strcmp(argv[i], "tile")) &&
579 (config->action_type_ == ACTION_GET)) {
580 CHECK_NUM_ARGS_LESS(2, ErrParse);
581 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
582 FEATURE_TILE;
583 arg->params_ = argv[i + 1];
584 ++feature_arg_index;
585 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700586 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530587 if (config->input_ == NULL) {
588 config->input_ = argv[i];
589 } else {
590 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
591 argv[i], ErrParse);
592 }
593 ++i;
594 }
595 }
596 }
597 ErrParse:
598 return ok;
599}
600
James Zern04e84cf2011-11-04 15:20:08 -0700601// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530602static int ValidateConfig(WebPMuxConfig* config) {
603 int ok = 1;
604 Feature* const feature = &config->feature_;
605
606 // Action.
607 if (ACTION_IS_NIL) {
608 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
609 }
610
611 // Feature type.
612 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
613 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
614 }
615
616 // Input file.
617 if (config->input_ == NULL) {
618 if (config->action_type_ != ACTION_SET) {
619 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
620 } else if (feature->type_ != FEATURE_FRM &&
621 feature->type_ != FEATURE_TILE) {
622 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
623 }
624 }
625
626 // Output file.
627 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
628 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
629 }
630
631 ErrValidate2:
632 return ok;
633}
634
635// Create config object from command-line arguments.
636static int InitializeConfig(int argc, const char* argv[],
637 WebPMuxConfig** config) {
638 int num_feature_args = 0;
639 int ok = 1;
640
641 assert(config != NULL);
642 *config = NULL;
643
644 // Validate command-line arguments.
645 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
646 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
647 }
648
649 // Allocate memory.
650 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
651 if (*config == NULL) {
652 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
653 }
654 (*config)->feature_.arg_count_ = num_feature_args;
655 (*config)->feature_.args_ =
656 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
657 if ((*config)->feature_.args_ == NULL) {
658 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
659 }
660
661 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700662 if (!ParseCommandLine(argc, argv, *config) ||
663 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530664 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
665 }
666
667 Err1:
668 return ok;
669}
670
671#undef ACTION_IS_NIL
672#undef FEATURETYPE_IS_NIL
673#undef CHECK_NUM_ARGS_LESS
674#undef CHECK_NUM_ARGS_MORE
675
676//------------------------------------------------------------------------------
677// Processing.
678
James Zern04e84cf2011-11-04 15:20:08 -0700679static int GetFrameTile(const WebPMux* mux,
680 const WebPMuxConfig* config, int isFrame) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700681 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530682 uint32_t x_offset = 0;
683 uint32_t y_offset = 0;
684 uint32_t duration = 0;
685 WebPMuxError err = WEBP_MUX_OK;
686 WebPMux* mux_single = NULL;
687 long num = 0;
688 int ok = 1;
689
690 num = strtol(config->feature_.args_[0].params_, NULL, 10);
691 if (num < 0) {
692 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
693 }
694
695 if (isFrame) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700696 err = WebPMuxGetFrame(mux, num, &bitstream,
Urvang Joshic398f592011-11-22 14:40:41 +0530697 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530698 if (err != WEBP_MUX_OK) {
699 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
700 }
701 } else {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700702 err = WebPMuxGetTile(mux, num, &bitstream, &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530703 if (err != WEBP_MUX_OK) {
704 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
705 }
706 }
707
708 mux_single = WebPMuxNew();
709 if (mux_single == NULL) {
710 err = WEBP_MUX_MEMORY_ERROR;
711 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet);
712 }
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700713 err = WebPMuxSetImage(mux_single, &bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530714 if (err != WEBP_MUX_OK) {
715 ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err,
716 ErrGet);
717 }
718 ok = WriteWebP(mux_single, config->output_);
719
720 ErrGet:
721 WebPMuxDelete(mux_single);
722 return ok;
723}
724
725// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700726static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530727 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800728 WebPData webpdata;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530729 WebPData metadata, color_profile;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530730 uint32_t x_offset = 0;
731 uint32_t y_offset = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530732 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530733 int index = 0;
734 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700735 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530736
James Zern04e84cf2011-11-04 15:20:08 -0700737 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530738 case ACTION_GET:
James Zern061263a2012-05-11 16:00:57 -0700739 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530740 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700741 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530742 case FEATURE_FRM:
743 ok = GetFrameTile(mux, config, 1);
744 break;
745
746 case FEATURE_TILE:
747 ok = GetFrameTile(mux, config, 0);
748 break;
749
750 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800751 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530752 if (err != WEBP_MUX_OK) {
753 ERROR_GOTO2("ERROR#%d: Could not get color profile.\n", err, Err2);
754 }
James Zern0f7820e2012-01-24 14:08:27 -0800755 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530756 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530757 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800758 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530759 if (err != WEBP_MUX_OK) {
760 ERROR_GOTO2("ERROR#%d: Could not get XMP metadata.\n", err, Err2);
761 }
James Zern0f7820e2012-01-24 14:08:27 -0800762 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530763 break;
764
765 default:
766 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
767 break;
768 }
769 break;
770
771 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700772 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530773 case FEATURE_FRM:
774 mux = WebPMuxNew();
775 if (mux == NULL) {
776 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
777 WEBP_MUX_MEMORY_ERROR, Err2);
778 }
779 for (index = 0; index < feature->arg_count_; ++index) {
780 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
James Zern0f7820e2012-01-24 14:08:27 -0800781 const long num = strtol(feature->args_[index].params_, NULL, 10);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530782 if (num < 0) {
783 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530784 }
James Zern0f7820e2012-01-24 14:08:27 -0800785 err = WebPMuxSetLoopCount(mux, num);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530786 if (err != WEBP_MUX_OK) {
787 ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2);
788 }
789 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
James Zern0f7820e2012-01-24 14:08:27 -0800790 uint32_t duration;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700791 ok = ReadFileToWebPData(feature->args_[index].filename_,
792 &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530793 if (!ok) goto Err2;
794 ok = ParseFrameArgs(feature->args_[index].params_,
795 &x_offset, &y_offset, &duration);
796 if (!ok) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700797 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530798 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
799 }
Urvang Joshib494ad52012-06-22 11:17:02 -0700800 err = WebPMuxPushFrame(mux, &webpdata, x_offset, y_offset,
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700801 duration, 1);
802 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530803 if (err != WEBP_MUX_OK) {
804 ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n",
805 err, index, Err2);
806 }
807 } else {
808 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
809 }
810 }
811 break;
812
813 case FEATURE_TILE:
814 mux = WebPMuxNew();
815 if (mux == NULL) {
816 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
817 WEBP_MUX_MEMORY_ERROR, Err2);
818 }
819 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700820 ok = ReadFileToWebPData(feature->args_[index].filename_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530821 if (!ok) goto Err2;
822 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
823 &y_offset);
824 if (!ok) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700825 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530826 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
827 }
Urvang Joshib494ad52012-06-22 11:17:02 -0700828 err = WebPMuxPushTile(mux, &webpdata, x_offset, y_offset, 1);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700829 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530830 if (err != WEBP_MUX_OK) {
831 ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n",
832 err, index, Err2);
833 }
834 }
835 break;
836
837 case FEATURE_ICCP:
James Zern061263a2012-05-11 16:00:57 -0700838 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530839 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530840 ok = ReadFileToWebPData(feature->args_[0].filename_, &color_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530841 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530842 err = WebPMuxSetColorProfile(mux, &color_profile, 1);
843 free((void*)color_profile.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530844 if (err != WEBP_MUX_OK) {
845 ERROR_GOTO2("ERROR#%d: Could not set color profile.\n", err, Err2);
846 }
847 break;
848
849 case FEATURE_XMP:
James Zern061263a2012-05-11 16:00:57 -0700850 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530851 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530852 ok = ReadFileToWebPData(feature->args_[0].filename_, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530853 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530854 err = WebPMuxSetMetadata(mux, &metadata, 1);
855 free((void*)metadata.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530856 if (err != WEBP_MUX_OK) {
857 ERROR_GOTO2("ERROR#%d: Could not set XMP metadata.\n", err, Err2);
858 }
859 break;
860
861 default:
862 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
863 break;
864 }
865 ok = WriteWebP(mux, config->output_);
866 break;
867
868 case ACTION_STRIP:
James Zern061263a2012-05-11 16:00:57 -0700869 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530870 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700871 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530872 case FEATURE_ICCP:
873 err = WebPMuxDeleteColorProfile(mux);
874 if (err != WEBP_MUX_OK) {
875 ERROR_GOTO2("ERROR#%d: Could not delete color profile.\n", err,
876 Err2);
877 }
878 break;
879 case FEATURE_XMP:
880 err = WebPMuxDeleteMetadata(mux);
881 if (err != WEBP_MUX_OK) {
882 ERROR_GOTO2("ERROR#%d: Could not delete XMP metadata.\n", err,
883 Err2);
884 }
885 break;
886 default:
887 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
888 break;
889 }
890 ok = WriteWebP(mux, config->output_);
891 break;
892
893 case ACTION_INFO:
James Zern061263a2012-05-11 16:00:57 -0700894 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530895 if (!ok) goto Err2;
896 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
897 break;
898
899 default:
900 assert(0); // Invalid action.
901 break;
902 }
903
904 Err2:
905 WebPMuxDelete(mux);
906 return ok;
907}
908
909//------------------------------------------------------------------------------
910// Main.
911
912int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530913 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -0800914 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530915 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -0800916 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530917 } else {
918 PrintHelp();
919 }
920 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800921 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530922}
923
924//------------------------------------------------------------------------------