blob: 50eef1a5cd15b742ab3752649d73329b9989fdc9 [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"
56
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057//------------------------------------------------------------------------------
58// Config object to parse command-line arguments.
59
60typedef enum {
61 NIL_ACTION = 0,
62 ACTION_GET,
63 ACTION_SET,
64 ACTION_STRIP,
65 ACTION_INFO,
66 ACTION_HELP
67} ActionType;
68
69typedef enum {
70 NIL_SUBTYPE = 0,
71 SUBTYPE_FRM,
72 SUBTYPE_LOOP
73} FeatureSubType;
74
75typedef struct {
76 FeatureSubType subtype_;
77 const char* filename_;
78 const char* params_;
79} FeatureArg;
80
81typedef enum {
82 NIL_FEATURE = 0,
83 FEATURE_XMP,
84 FEATURE_ICCP,
85 FEATURE_FRM,
86 FEATURE_TILE
87} FeatureType;
88
89typedef struct {
90 FeatureType type_;
91 FeatureArg* args_;
92 int arg_count_;
93} Feature;
94
95typedef struct {
96 ActionType action_type_;
97 const char* input_;
98 const char* output_;
99 Feature feature_;
100} WebPMuxConfig;
101
102//------------------------------------------------------------------------------
103// Helper functions.
104
James Zern04e84cf2011-11-04 15:20:08 -0700105static int CountOccurrences(const char* arglist[], int list_length,
106 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530107 int i;
108 int num_occurences = 0;
109
110 for (i = 0; i < list_length; ++i) {
111 if (!strcmp(arglist[i], arg)) {
112 ++num_occurences;
113 }
114 }
115 return num_occurences;
116}
117
118static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700119 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530120}
121
122#define RETURN_IF_ERROR(ERR_MSG) \
123 if (err != WEBP_MUX_OK) { \
124 fprintf(stderr, ERR_MSG); \
125 return err; \
126 }
127
128#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
129 if (err != WEBP_MUX_OK) { \
130 fprintf(stderr, ERR_MSG, FORMAT_STR); \
131 return err; \
132 }
133
134#define ERROR_GOTO1(ERR_MSG, LABEL) \
James Zern04e84cf2011-11-04 15:20:08 -0700135 do { \
136 fprintf(stderr, ERR_MSG); \
137 ok = 0; \
138 goto LABEL; \
139 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530140
141#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
James Zern04e84cf2011-11-04 15:20:08 -0700142 do { \
143 fprintf(stderr, ERR_MSG, FORMAT_STR); \
144 ok = 0; \
145 goto LABEL; \
146 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530147
148#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
James Zern04e84cf2011-11-04 15:20:08 -0700149 do { \
150 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
151 ok = 0; \
152 goto LABEL; \
153 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530154
155static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530156 uint32_t flag;
157
158 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
159 RETURN_IF_ERROR("Failed to retrieve features\n");
160
161 if (flag == 0) {
162 fprintf(stderr, "No features present.\n");
163 return err;
164 }
165
166 // Print the features present.
167 fprintf(stderr, "Features present:");
168 if (flag & ANIMATION_FLAG) fprintf(stderr, " animation");
169 if (flag & TILE_FLAG) fprintf(stderr, " tiling");
170 if (flag & ICCP_FLAG) fprintf(stderr, " icc profile");
171 if (flag & META_FLAG) fprintf(stderr, " metadata");
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530172 if (flag & ALPHA_FLAG) fprintf(stderr, " transparency");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530173 fprintf(stderr, "\n");
174
175 if (flag & ANIMATION_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800176 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530177 uint32_t loop_count;
178 err = WebPMuxGetLoopCount(mux, &loop_count);
179 RETURN_IF_ERROR("Failed to retrieve loop count\n");
180 fprintf(stderr, "Loop Count : %d\n", loop_count);
181
182 err = WebPMuxNumNamedElements(mux, "frame", &nFrames);
183 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
184
185 fprintf(stderr, "Number of frames: %d\n", nFrames);
186 if (nFrames > 0) {
187 int i;
188 uint32_t x_offset, y_offset, duration;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530189 fprintf(stderr, "No.: x_offset y_offset duration");
190 if (flag & ALPHA_FLAG) fprintf(stderr, " alpha_size");
191 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530192 for (i = 1; i <= nFrames; i++) {
James Zerneec4b872012-01-07 12:44:01 -0800193 WebPData image, alpha;
194 err = WebPMuxGetFrame(mux, i, &image, &alpha,
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);
197 fprintf(stderr, "%3d: %8d %8d %8d", i, x_offset, y_offset, duration);
James Zerneec4b872012-01-07 12:44:01 -0800198 if (flag & ALPHA_FLAG) fprintf(stderr, " %10u", alpha.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530199 fprintf(stderr, "\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 Joshia4f32ca2011-09-30 11:07:01 +0530206 err = WebPMuxNumNamedElements(mux, "tile", &nTiles);
207 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
208
209 fprintf(stderr, "Number of tiles: %d\n", nTiles);
210 if (nTiles > 0) {
211 int i;
212 uint32_t x_offset, y_offset;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530213 fprintf(stderr, "No.: x_offset y_offset");
214 if (flag & ALPHA_FLAG) fprintf(stderr, " alpha_size");
215 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530216 for (i = 1; i <= nTiles; i++) {
James Zerneec4b872012-01-07 12:44:01 -0800217 WebPData image, alpha;
218 err = WebPMuxGetTile(mux, i, &image, &alpha, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530219 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
220 fprintf(stderr, "%3d: %8d %8d", i, x_offset, y_offset);
James Zerneec4b872012-01-07 12:44:01 -0800221 if (flag & ALPHA_FLAG) fprintf(stderr, " %10u", alpha.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530222 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530223 }
224 }
225 }
226
227 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800228 WebPData icc_profile;
229 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530230 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zerneec4b872012-01-07 12:44:01 -0800231 fprintf(stderr, "Size of the color profile data: %u\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530232 }
233
234 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800235 WebPData metadata;
236 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530237 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zerneec4b872012-01-07 12:44:01 -0800238 fprintf(stderr, "Size of the XMP metadata: %u\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530239 }
240
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530241 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
James Zerneec4b872012-01-07 12:44:01 -0800242 WebPData image, alpha;
243 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530244 RETURN_IF_ERROR("Failed to retrieve the image\n");
James Zerneec4b872012-01-07 12:44:01 -0800245 fprintf(stderr, "Size of the alpha data: %u\n", alpha.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530246 }
247
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530248 return WEBP_MUX_OK;
249}
250
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700251static void PrintHelp(void) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530252 fprintf(stderr, "Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT "
253 " Extract relevant data.\n");
254 fprintf(stderr, " or: webpmux -set SET_OPTIONS INPUT -o OUTPUT "
255 " Set color profile/metadata.\n");
256 fprintf(stderr, " or: webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT "
257 " Strip color profile/metadata.\n");
258 fprintf(stderr, " or: webpmux [-tile TILE_OPTIONS]... -o OUTPUT "
259 " Create tiled image.\n");
260 fprintf(stderr, " or: webpmux [-frame FRAME_OPTIONS]... -loop LOOP_COUNT"
261 " -o OUTPUT Create animation.\n");
262 fprintf(stderr, " or: webpmux -info INPUT "
263 " Print info about given webp file.\n");
264 fprintf(stderr, " or: webpmux -help OR -h "
265 " Print this help.\n");
266
267 fprintf(stderr, "\n");
268 fprintf(stderr, "GET_OPTIONS:\n");
269 fprintf(stderr, " icc Get ICCP Color profile.\n");
270 fprintf(stderr, " xmp Get XMP metadata.\n");
271 fprintf(stderr, " tile n Get nth tile.\n");
272 fprintf(stderr, " frame n Get nth frame.\n");
273
274 fprintf(stderr, "\n");
275 fprintf(stderr, "SET_OPTIONS:\n");
276 fprintf(stderr, " icc Set ICC Color profile.\n");
277 fprintf(stderr, " xmp Set XMP metadata.\n");
278
279 fprintf(stderr, "\n");
280 fprintf(stderr, "STRIP_OPTIONS:\n");
281 fprintf(stderr, " icc Strip ICCP color profile.\n");
282 fprintf(stderr, " xmp Strip XMP metadata.\n");
283
284 fprintf(stderr, "\n");
285 fprintf(stderr, "TILE_OPTIONS(i):\n");
286 fprintf(stderr, " file_i +xi+yi\n");
287 fprintf(stderr, " where: 'file_i' is the i'th tile (webp format),\n");
288 fprintf(stderr, " 'xi','yi' specify the image offset for this "
289 "tile.\n");
290
291 fprintf(stderr, "\n");
292 fprintf(stderr, "FRAME_OPTIONS(i):\n");
293 fprintf(stderr, " file_i +xi+yi+di\n");
294 fprintf(stderr, " where: 'file_i' is the i'th animation frame (webp "
295 "format),\n");
296 fprintf(stderr, " 'xi','yi' specify the image offset for this "
297 "frame.\n");
298 fprintf(stderr, " 'di' is the pause duration before next frame."
299 "\n");
300
301 fprintf(stderr, "\n");
302 fprintf(stderr, "INPUT & OUTPUT are in webp format.");
303 fprintf(stderr, "\n");
304}
305
306static int ReadData(const char* filename, void** data_ptr, uint32_t* size_ptr) {
307 void* data = NULL;
308 long size = 0;
309 int ok = 0;
310 FILE* in;
311
312 *size_ptr = 0;
313 in = fopen(filename, "rb");
314 if (!in) {
315 fprintf(stderr, "Failed to open file %s\n", filename);
316 return 0;
317 }
318 fseek(in, 0, SEEK_END);
319 size = ftell(in);
320 fseek(in, 0, SEEK_SET);
321 if (size > 0xffffffffu) {
322 fprintf(stderr, "Size (%ld bytes) is out of range for file %s\n",
323 size, filename);
324 size = 0;
325 goto Err;
326 }
327 if (size < 0) {
328 size = 0;
329 goto Err;
330 }
331 data = malloc(size);
332 if (data) {
333 if (fread(data, size, 1, in) != 1) {
334 free(data);
335 data = NULL;
336 size = 0;
337 fprintf(stderr, "Failed to read %ld bytes from file %s\n",
338 size, filename);
339 goto Err;
340 }
341 ok = 1;
342 } else {
343 fprintf(stderr, "Failed to allocate %ld bytes for reading file %s\n",
344 size, filename);
345 size = 0;
346 }
347
348 Err:
349 if (in != stdin) fclose(in);
350 *size_ptr = (uint32_t)size;
351 *data_ptr = data;
352 return ok;
353}
354
355static int ReadFile(const char* const filename, WebPMux** mux) {
356 uint32_t size = 0;
357 void* data = NULL;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530358 WebPMuxState mux_state;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530359
360 assert(mux != NULL);
361
362 if (!ReadData(filename, &data, &size)) return 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530363 *mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530364 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530365 if (*mux != NULL && mux_state == WEBP_MUX_STATE_COMPLETE) return 1;
366 fprintf(stderr, "Failed to create mux object from file %s. mux_state = %d.\n",
367 filename, mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530368 return 0;
369}
370
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530371static int ReadImage(const char* filename,
372 const uint8_t** data_ptr, uint32_t* size_ptr,
373 const uint8_t** alpha_data_ptr, uint32_t* alpha_size_ptr) {
374 void* data = NULL;
375 uint32_t size = 0;
James Zerneec4b872012-01-07 12:44:01 -0800376 WebPData image, alpha;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530377 WebPMux* mux;
378 WebPMuxError err;
379 int ok = 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530380 WebPMuxState mux_state;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530381
382 if (!ReadData(filename, &data, &size)) return 0;
383
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530384 mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530385 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530386 if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) {
387 fprintf(stderr,
388 "Failed to create mux object from file %s. mux_state = %d.\n",
389 filename, mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530390 return 0;
391 }
James Zerneec4b872012-01-07 12:44:01 -0800392 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530393 if (err == WEBP_MUX_OK) {
James Zerneec4b872012-01-07 12:44:01 -0800394 uint8_t* const data_mem = (uint8_t*)malloc(image.size_);
395 uint8_t* const alpha_mem = (uint8_t*)malloc(alpha.size_);
James Zern2f741d12011-12-02 16:00:51 -0800396 if ((data_mem != NULL) && (alpha_mem != NULL)) {
James Zerneec4b872012-01-07 12:44:01 -0800397 memcpy(data_mem, image.bytes_, image.size_);
398 memcpy(alpha_mem, alpha.bytes_, alpha.size_);
James Zern2f741d12011-12-02 16:00:51 -0800399 *data_ptr = data_mem;
James Zerneec4b872012-01-07 12:44:01 -0800400 *size_ptr = image.size_;
James Zern2f741d12011-12-02 16:00:51 -0800401 *alpha_data_ptr = alpha_mem;
James Zerneec4b872012-01-07 12:44:01 -0800402 *alpha_size_ptr = alpha.size_;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530403 ok = 1;
404 } else {
James Zern2f741d12011-12-02 16:00:51 -0800405 free(data_mem);
406 free(alpha_mem);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530407 err = WEBP_MUX_MEMORY_ERROR;
James Zerneec4b872012-01-07 12:44:01 -0800408 fprintf(stderr, "Failed to allocate %u bytes to extract image data from"
409 " file %s. Error: %d\n",
410 image.size_ + alpha.size_, filename, err);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530411 }
412 } else {
413 fprintf(stderr, "Failed to extract image data from file %s. Error: %d\n",
414 filename, err);
415 }
416 WebPMuxDelete(mux);
417 return ok;
418}
419
James Zerneec4b872012-01-07 12:44:01 -0800420static int WriteData(const char* filename, const void* data, uint32_t size) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530421 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700422 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530423 if (!fout) {
424 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
425 return 0;
426 }
427 if (fwrite(data, size, 1, fout) != 1) {
428 fprintf(stderr, "Error writing file %s!\n", filename);
429 } else {
430 fprintf(stderr, "Saved file %s (%d bytes)\n", filename, size);
431 ok = 1;
432 }
433 if (fout != stdout) fclose(fout);
434 return ok;
435}
436
437static int WriteWebP(WebPMux* const mux, const char* filename) {
438 uint8_t* data = NULL;
439 uint32_t size = 0;
440 int ok;
441
442 WebPMuxError err = WebPMuxAssemble(mux, &data, &size);
443 if (err != WEBP_MUX_OK) {
444 fprintf(stderr, "Error (%d) assembling the WebP file.\n", err);
445 return 0;
446 }
447 ok = WriteData(filename, data, size);
448 free(data);
449 return ok;
450}
451
452static int ParseFrameArgs(const char* args, uint32_t* x_offset,
453 uint32_t* y_offset, uint32_t* duration) {
454 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
455}
456
457static int ParseTileArgs(const char* args, uint32_t* x_offset,
458 uint32_t* y_offset) {
459 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
460}
461
462//------------------------------------------------------------------------------
463// Clean-up.
464
465static void DeleteConfig(WebPMuxConfig* config) {
466 if (config != NULL) {
467 free(config->feature_.args_);
468 free(config);
469 }
470}
471
472//------------------------------------------------------------------------------
473// Parsing.
474
475// Basic syntactic checks on the command-line arguments.
476// Returns 1 on valid, 0 otherwise.
477// Also fills up num_feature_args to be number of feature arguments given.
478// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
479static int ValidateCommandLine(int argc, const char* argv[],
480 int* num_feature_args) {
481 int num_frame_args;
482 int num_tile_args;
483 int num_loop_args;
484 int ok = 1;
485
486 assert(num_feature_args != NULL);
487 *num_feature_args = 0;
488
489 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700490 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530491 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
492 }
James Zern04e84cf2011-11-04 15:20:08 -0700493 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530494 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
495 }
James Zern04e84cf2011-11-04 15:20:08 -0700496 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530497 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
498 }
James Zern04e84cf2011-11-04 15:20:08 -0700499 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530500 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
501 }
James Zern04e84cf2011-11-04 15:20:08 -0700502 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530503 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
504 }
505
506 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700507 num_frame_args = CountOccurrences(argv, argc, "-frame");
508 num_tile_args = CountOccurrences(argv, argc, "-tile");
509 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530510
511 if (num_loop_args > 1) {
512 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
513 }
514
515 if (IsNotCompatible(num_frame_args, num_loop_args)) {
516 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
517 ErrValidate);
518 }
519 if (num_frame_args > 0 && num_tile_args > 0) {
520 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
521 "\n", ErrValidate);
522 }
523
524 assert(ok == 1);
525 if (num_frame_args == 0 && num_tile_args == 0) {
526 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
527 *num_feature_args = 1;
528 } else {
529 // Multiple arguments ('set' action for animation or tiling).
530 if (num_frame_args > 0) {
531 *num_feature_args = num_frame_args + num_loop_args;
532 } else {
533 *num_feature_args = num_tile_args;
534 }
535 }
536
537 ErrValidate:
538 return ok;
539}
540
541#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
542
543#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
544
545#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
546 if (argc < i + (NUM)) { \
547 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
548 goto LABEL; \
549 }
550
551#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
552 if (argc != i + (NUM)) { \
553 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
554 goto LABEL; \
555 }
556
557// Parses command-line arguments to fill up config object. Also performs some
558// semantic checks.
559static int ParseCommandLine(int argc, const char* argv[],
560 WebPMuxConfig* config) {
561 int i = 0;
562 int feature_arg_index = 0;
563 int ok = 1;
564
565 while (i < argc) {
566 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700567 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530568 if (argv[i][0] == '-') { // One of the action types or output.
569 if (!strcmp(argv[i], "-set")) {
570 if (ACTION_IS_NIL) {
571 config->action_type_ = ACTION_SET;
572 } else {
573 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
574 }
575 ++i;
576 } else if (!strcmp(argv[i], "-get")) {
577 if (ACTION_IS_NIL) {
578 config->action_type_ = ACTION_GET;
579 } else {
580 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
581 }
582 ++i;
583 } else if (!strcmp(argv[i], "-strip")) {
584 if (ACTION_IS_NIL) {
585 config->action_type_ = ACTION_STRIP;
586 feature->arg_count_ = 0;
587 } else {
588 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
589 }
590 ++i;
591 } else if (!strcmp(argv[i], "-frame")) {
592 CHECK_NUM_ARGS_LESS(3, ErrParse);
593 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
594 config->action_type_ = ACTION_SET;
595 } else {
596 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
597 }
598 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
599 feature->type_ = FEATURE_FRM;
600 } else {
601 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
602 }
603 arg->subtype_ = SUBTYPE_FRM;
604 arg->filename_ = argv[i + 1];
605 arg->params_ = argv[i + 2];
606 ++feature_arg_index;
607 i += 3;
608 } else if (!strcmp(argv[i], "-loop")) {
609 CHECK_NUM_ARGS_LESS(2, ErrParse);
610 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
611 config->action_type_ = ACTION_SET;
612 } else {
613 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
614 }
615 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
616 feature->type_ = FEATURE_FRM;
617 } else {
618 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
619 }
620 arg->subtype_ = SUBTYPE_LOOP;
621 arg->params_ = argv[i + 1];
622 ++feature_arg_index;
623 i += 2;
624 } else if (!strcmp(argv[i], "-tile")) {
625 CHECK_NUM_ARGS_LESS(3, ErrParse);
626 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
627 config->action_type_ = ACTION_SET;
628 } else {
629 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
630 }
631 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
632 feature->type_ = FEATURE_TILE;
633 } else {
634 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
635 }
636 arg->filename_ = argv[i + 1];
637 arg->params_ = argv[i + 2];
638 ++feature_arg_index;
639 i += 3;
640 } else if (!strcmp(argv[i], "-o")) {
641 CHECK_NUM_ARGS_LESS(2, ErrParse);
642 config->output_ = argv[i + 1];
643 i += 2;
644 } else if (!strcmp(argv[i], "-info")) {
645 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
646 if (config->action_type_ != NIL_ACTION) {
647 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
648 } else {
649 config->action_type_ = ACTION_INFO;
650 feature->arg_count_ = 0;
651 config->input_ = argv[i + 1];
652 }
653 i += 2;
654 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
655 PrintHelp();
656 DeleteConfig(config);
657 exit(1);
658 } else {
659 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
660 }
661 } else { // One of the feature types or input.
662 if (ACTION_IS_NIL) {
663 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
664 ErrParse);
665 }
666 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
667 if (FEATURETYPE_IS_NIL) {
668 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
669 FEATURE_XMP;
670 } else {
671 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
672 }
673 if (config->action_type_ == ACTION_SET) {
674 CHECK_NUM_ARGS_LESS(2, ErrParse);
675 arg->filename_ = argv[i + 1];
676 ++feature_arg_index;
677 i += 2;
678 } else {
679 ++i;
680 }
681 } else if ((!strcmp(argv[i], "frame") ||
682 !strcmp(argv[i], "tile")) &&
683 (config->action_type_ == ACTION_GET)) {
684 CHECK_NUM_ARGS_LESS(2, ErrParse);
685 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
686 FEATURE_TILE;
687 arg->params_ = argv[i + 1];
688 ++feature_arg_index;
689 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700690 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530691 if (config->input_ == NULL) {
692 config->input_ = argv[i];
693 } else {
694 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
695 argv[i], ErrParse);
696 }
697 ++i;
698 }
699 }
700 }
701 ErrParse:
702 return ok;
703}
704
James Zern04e84cf2011-11-04 15:20:08 -0700705// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530706static int ValidateConfig(WebPMuxConfig* config) {
707 int ok = 1;
708 Feature* const feature = &config->feature_;
709
710 // Action.
711 if (ACTION_IS_NIL) {
712 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
713 }
714
715 // Feature type.
716 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
717 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
718 }
719
720 // Input file.
721 if (config->input_ == NULL) {
722 if (config->action_type_ != ACTION_SET) {
723 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
724 } else if (feature->type_ != FEATURE_FRM &&
725 feature->type_ != FEATURE_TILE) {
726 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
727 }
728 }
729
730 // Output file.
731 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
732 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
733 }
734
735 ErrValidate2:
736 return ok;
737}
738
739// Create config object from command-line arguments.
740static int InitializeConfig(int argc, const char* argv[],
741 WebPMuxConfig** config) {
742 int num_feature_args = 0;
743 int ok = 1;
744
745 assert(config != NULL);
746 *config = NULL;
747
748 // Validate command-line arguments.
749 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
750 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
751 }
752
753 // Allocate memory.
754 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
755 if (*config == NULL) {
756 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
757 }
758 (*config)->feature_.arg_count_ = num_feature_args;
759 (*config)->feature_.args_ =
760 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
761 if ((*config)->feature_.args_ == NULL) {
762 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
763 }
764
765 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700766 if (!ParseCommandLine(argc, argv, *config) ||
767 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530768 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
769 }
770
771 Err1:
772 return ok;
773}
774
775#undef ACTION_IS_NIL
776#undef FEATURETYPE_IS_NIL
777#undef CHECK_NUM_ARGS_LESS
778#undef CHECK_NUM_ARGS_MORE
779
780//------------------------------------------------------------------------------
781// Processing.
782
James Zern04e84cf2011-11-04 15:20:08 -0700783static int GetFrameTile(const WebPMux* mux,
784 const WebPMuxConfig* config, int isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800785 WebPData image, alpha;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530786 uint32_t x_offset = 0;
787 uint32_t y_offset = 0;
788 uint32_t duration = 0;
789 WebPMuxError err = WEBP_MUX_OK;
790 WebPMux* mux_single = NULL;
791 long num = 0;
792 int ok = 1;
793
794 num = strtol(config->feature_.args_[0].params_, NULL, 10);
795 if (num < 0) {
796 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
797 }
798
799 if (isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800800 err = WebPMuxGetFrame(mux, num, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530801 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530802 if (err != WEBP_MUX_OK) {
803 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
804 }
805 } else {
James Zerneec4b872012-01-07 12:44:01 -0800806 err = WebPMuxGetTile(mux, num, &image, &alpha, &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530807 if (err != WEBP_MUX_OK) {
808 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
809 }
810 }
811
812 mux_single = WebPMuxNew();
813 if (mux_single == NULL) {
814 err = WEBP_MUX_MEMORY_ERROR;
815 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet);
816 }
James Zerneec4b872012-01-07 12:44:01 -0800817 err = WebPMuxSetImage(mux_single, image.bytes_, image.size_,
818 alpha.bytes_, alpha.size_, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530819 if (err != WEBP_MUX_OK) {
820 ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err,
821 ErrGet);
822 }
823 ok = WriteWebP(mux_single, config->output_);
824
825 ErrGet:
826 WebPMuxDelete(mux_single);
827 return ok;
828}
829
830// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700831static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530832 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800833 WebPData webpdata;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530834 const uint8_t* data = NULL;
835 uint32_t size = 0;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530836 const uint8_t* alpha_data = NULL;
837 uint32_t alpha_size = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530838 uint32_t x_offset = 0;
839 uint32_t y_offset = 0;
840 uint32_t duration = 0;
841 uint32_t loop_count = 0;
842 WebPMuxError err = WEBP_MUX_OK;
843 long num;
844 int index = 0;
845 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700846 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530847
James Zern04e84cf2011-11-04 15:20:08 -0700848 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530849 case ACTION_GET:
850 ok = ReadFile(config->input_, &mux);
851 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700852 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530853 case FEATURE_FRM:
854 ok = GetFrameTile(mux, config, 1);
855 break;
856
857 case FEATURE_TILE:
858 ok = GetFrameTile(mux, config, 0);
859 break;
860
861 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800862 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530863 if (err != WEBP_MUX_OK) {
864 ERROR_GOTO2("ERROR#%d: Could not get color profile.\n", err, Err2);
865 }
James Zerneec4b872012-01-07 12:44:01 -0800866 ok = WriteData(config->output_, webpdata.bytes_, webpdata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530867 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530868 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800869 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530870 if (err != WEBP_MUX_OK) {
871 ERROR_GOTO2("ERROR#%d: Could not get XMP metadata.\n", err, Err2);
872 }
James Zerneec4b872012-01-07 12:44:01 -0800873 ok = WriteData(config->output_, webpdata.bytes_, webpdata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530874 break;
875
876 default:
877 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
878 break;
879 }
880 break;
881
882 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700883 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530884 case FEATURE_FRM:
885 mux = WebPMuxNew();
886 if (mux == NULL) {
887 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
888 WEBP_MUX_MEMORY_ERROR, Err2);
889 }
890 for (index = 0; index < feature->arg_count_; ++index) {
891 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
892 num = strtol(feature->args_[index].params_, NULL, 10);
893 if (num < 0) {
894 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
895 } else {
896 loop_count = num;
897 }
898 err = WebPMuxSetLoopCount(mux, loop_count);
899 if (err != WEBP_MUX_OK) {
900 ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2);
901 }
902 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530903 ok = ReadImage(feature->args_[index].filename_,
904 &data, &size, &alpha_data, &alpha_size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530905 if (!ok) goto Err2;
906 ok = ParseFrameArgs(feature->args_[index].params_,
907 &x_offset, &y_offset, &duration);
908 if (!ok) {
909 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530910 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530911 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
912 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530913 err = WebPMuxAddFrame(mux, 0, data, size, alpha_data, alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530914 x_offset, y_offset, duration, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530915 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530916 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530917 if (err != WEBP_MUX_OK) {
918 ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n",
919 err, index, Err2);
920 }
921 } else {
922 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
923 }
924 }
925 break;
926
927 case FEATURE_TILE:
928 mux = WebPMuxNew();
929 if (mux == NULL) {
930 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
931 WEBP_MUX_MEMORY_ERROR, Err2);
932 }
933 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530934 ok = ReadImage(feature->args_[index].filename_,
935 &data, &size, &alpha_data, &alpha_size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530936 if (!ok) goto Err2;
937 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
938 &y_offset);
939 if (!ok) {
940 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530941 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530942 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
943 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530944 err = WebPMuxAddTile(mux, 0, data, size, alpha_data, alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530945 x_offset, y_offset, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530946 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530947 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530948 if (err != WEBP_MUX_OK) {
949 ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n",
950 err, index, Err2);
951 }
952 }
953 break;
954
955 case FEATURE_ICCP:
956 ok = ReadFile(config->input_, &mux);
957 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700958 ok = ReadData(feature->args_[0].filename_, (void**)&data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530959 if (!ok) goto Err2;
960 err = WebPMuxSetColorProfile(mux, data, size, 1);
961 free((void*)data);
962 if (err != WEBP_MUX_OK) {
963 ERROR_GOTO2("ERROR#%d: Could not set color profile.\n", err, Err2);
964 }
965 break;
966
967 case FEATURE_XMP:
968 ok = ReadFile(config->input_, &mux);
969 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700970 ok = ReadData(feature->args_[0].filename_, (void**)&data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530971 if (!ok) goto Err2;
972 err = WebPMuxSetMetadata(mux, data, size, 1);
973 free((void*)data);
974 if (err != WEBP_MUX_OK) {
975 ERROR_GOTO2("ERROR#%d: Could not set XMP metadata.\n", err, Err2);
976 }
977 break;
978
979 default:
980 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
981 break;
982 }
983 ok = WriteWebP(mux, config->output_);
984 break;
985
986 case ACTION_STRIP:
987 ok = ReadFile(config->input_, &mux);
988 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700989 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530990 case FEATURE_ICCP:
991 err = WebPMuxDeleteColorProfile(mux);
992 if (err != WEBP_MUX_OK) {
993 ERROR_GOTO2("ERROR#%d: Could not delete color profile.\n", err,
994 Err2);
995 }
996 break;
997 case FEATURE_XMP:
998 err = WebPMuxDeleteMetadata(mux);
999 if (err != WEBP_MUX_OK) {
1000 ERROR_GOTO2("ERROR#%d: Could not delete XMP metadata.\n", err,
1001 Err2);
1002 }
1003 break;
1004 default:
1005 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1006 break;
1007 }
1008 ok = WriteWebP(mux, config->output_);
1009 break;
1010
1011 case ACTION_INFO:
1012 ok = ReadFile(config->input_, &mux);
1013 if (!ok) goto Err2;
1014 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1015 break;
1016
1017 default:
1018 assert(0); // Invalid action.
1019 break;
1020 }
1021
1022 Err2:
1023 WebPMuxDelete(mux);
1024 return ok;
1025}
1026
1027//------------------------------------------------------------------------------
1028// Main.
1029
1030int main(int argc, const char* argv[]) {
1031 int ok = 1;
1032 WebPMuxConfig* config;
1033 ok = InitializeConfig(argc-1, argv+1, &config);
1034 if (ok) {
1035 Process(config);
1036 } else {
1037 PrintHelp();
1038 }
1039 DeleteConfig(config);
1040 return ok;
1041}
1042
1043//------------------------------------------------------------------------------