blob: 1f87b641a00d3b10e5ee05f98d94925d4a3e4aac [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.
James Zern974aaff2012-01-24 12:46:46 -0800167 printf("Features present:");
168 if (flag & ANIMATION_FLAG) printf(" animation");
169 if (flag & TILE_FLAG) printf(" tiling");
170 if (flag & ICCP_FLAG) printf(" icc profile");
171 if (flag & META_FLAG) printf(" metadata");
172 if (flag & ALPHA_FLAG) printf(" transparency");
173 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530174
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");
James Zern974aaff2012-01-24 12:46:46 -0800180 printf("Loop Count : %d\n", loop_count);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530181
182 err = WebPMuxNumNamedElements(mux, "frame", &nFrames);
183 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
184
James Zern974aaff2012-01-24 12:46:46 -0800185 printf("Number of frames: %d\n", nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530186 if (nFrames > 0) {
187 int i;
James Zern974aaff2012-01-24 12:46:46 -0800188 printf("No.: x_offset y_offset duration image_size");
189 if (flag & ALPHA_FLAG) printf(" alpha_size");
190 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;
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);
James Zern974aaff2012-01-24 12:46:46 -0800197 printf("%3d: %8d %8d %8d %10u",
198 i, x_offset, y_offset, duration, image.size_);
199 if (flag & ALPHA_FLAG) printf(" %10u", alpha.size_);
200 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530201 }
202 }
203 }
204
205 if (flag & TILE_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800206 int nTiles;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530207 err = WebPMuxNumNamedElements(mux, "tile", &nTiles);
208 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
209
James Zern974aaff2012-01-24 12:46:46 -0800210 printf("Number of tiles: %d\n", nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530211 if (nTiles > 0) {
212 int i;
213 uint32_t x_offset, y_offset;
James Zern974aaff2012-01-24 12:46:46 -0800214 printf("No.: x_offset y_offset image_size");
215 if (flag & ALPHA_FLAG) printf(" alpha_size");
216 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530217 for (i = 1; i <= nTiles; i++) {
James Zerneec4b872012-01-07 12:44:01 -0800218 WebPData image, alpha;
219 err = WebPMuxGetTile(mux, i, &image, &alpha, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530220 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
James Zern974aaff2012-01-24 12:46:46 -0800221 printf("%3d: %8d %8d %10u",
222 i, x_offset, y_offset, image.size_);
223 if (flag & ALPHA_FLAG) printf(" %10u", alpha.size_);
224 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530225 }
226 }
227 }
228
229 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800230 WebPData icc_profile;
231 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530232 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zern974aaff2012-01-24 12:46:46 -0800233 printf("Size of the color profile data: %u\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530234 }
235
236 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800237 WebPData metadata;
238 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530239 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern974aaff2012-01-24 12:46:46 -0800240 printf("Size of the XMP metadata: %u\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530241 }
242
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530243 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
James Zerneec4b872012-01-07 12:44:01 -0800244 WebPData image, alpha;
245 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530246 RETURN_IF_ERROR("Failed to retrieve the image\n");
James Zern974aaff2012-01-24 12:46:46 -0800247 printf("Size of the alpha data: %u\n", alpha.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530248 }
249
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530250 return WEBP_MUX_OK;
251}
252
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700253static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800254 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
255 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
256 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
257 printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
258 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
259 printf(" -loop LOOP_COUNT -o OUTPUT\n");
260 printf(" webpmux -info INPUT\n");
261 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530262
James Zern974aaff2012-01-24 12:46:46 -0800263 printf("\n");
264 printf("GET_OPTIONS:\n");
265 printf(" Extract relevant data.\n");
266 printf(" icc Get ICCP Color profile.\n");
267 printf(" xmp Get XMP metadata.\n");
268 printf(" tile n Get nth tile.\n");
269 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530270
James Zern974aaff2012-01-24 12:46:46 -0800271 printf("\n");
272 printf("SET_OPTIONS:\n");
273 printf(" Set color profile/metadata.\n");
274 printf(" icc Set ICC Color profile.\n");
275 printf(" xmp Set XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530276
James Zern974aaff2012-01-24 12:46:46 -0800277 printf("\n");
278 printf("STRIP_OPTIONS:\n");
279 printf(" Strip color profile/metadata.\n");
280 printf(" icc Strip ICCP color profile.\n");
281 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530282
James Zern974aaff2012-01-24 12:46:46 -0800283 printf("\n");
284 printf("TILE_OPTIONS(i):\n");
285 printf(" Create tiled image.\n");
286 printf(" file_i +xi+yi\n");
287 printf(" where: 'file_i' is the i'th tile (webp format),\n");
288 printf(" 'xi','yi' specify the image offset for this tile.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530289
James Zern974aaff2012-01-24 12:46:46 -0800290 printf("\n");
291 printf("FRAME_OPTIONS(i):\n");
292 printf(" Create animation.\n");
293 printf(" file_i +xi+yi+di\n");
294 printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
295 printf(" 'xi','yi' specify the image offset for this frame.\n");
296 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530297
James Zern974aaff2012-01-24 12:46:46 -0800298 printf("\nINPUT & OUTPUT are in webp format.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530299}
300
301static int ReadData(const char* filename, void** data_ptr, uint32_t* size_ptr) {
302 void* data = NULL;
303 long size = 0;
304 int ok = 0;
305 FILE* in;
306
307 *size_ptr = 0;
308 in = fopen(filename, "rb");
309 if (!in) {
310 fprintf(stderr, "Failed to open file %s\n", filename);
311 return 0;
312 }
313 fseek(in, 0, SEEK_END);
314 size = ftell(in);
315 fseek(in, 0, SEEK_SET);
316 if (size > 0xffffffffu) {
317 fprintf(stderr, "Size (%ld bytes) is out of range for file %s\n",
318 size, filename);
319 size = 0;
320 goto Err;
321 }
322 if (size < 0) {
323 size = 0;
324 goto Err;
325 }
326 data = malloc(size);
327 if (data) {
328 if (fread(data, size, 1, in) != 1) {
329 free(data);
330 data = NULL;
331 size = 0;
332 fprintf(stderr, "Failed to read %ld bytes from file %s\n",
333 size, filename);
334 goto Err;
335 }
336 ok = 1;
337 } else {
338 fprintf(stderr, "Failed to allocate %ld bytes for reading file %s\n",
339 size, filename);
340 size = 0;
341 }
342
343 Err:
344 if (in != stdin) fclose(in);
345 *size_ptr = (uint32_t)size;
346 *data_ptr = data;
347 return ok;
348}
349
350static int ReadFile(const char* const filename, WebPMux** mux) {
351 uint32_t size = 0;
352 void* data = NULL;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530353 WebPMuxState mux_state;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530354
355 assert(mux != NULL);
356
357 if (!ReadData(filename, &data, &size)) return 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530358 *mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530359 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530360 if (*mux != NULL && mux_state == WEBP_MUX_STATE_COMPLETE) return 1;
361 fprintf(stderr, "Failed to create mux object from file %s. mux_state = %d.\n",
362 filename, mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530363 return 0;
364}
365
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530366static int ReadImage(const char* filename,
367 const uint8_t** data_ptr, uint32_t* size_ptr,
368 const uint8_t** alpha_data_ptr, uint32_t* alpha_size_ptr) {
369 void* data = NULL;
370 uint32_t size = 0;
James Zerneec4b872012-01-07 12:44:01 -0800371 WebPData image, alpha;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530372 WebPMux* mux;
373 WebPMuxError err;
374 int ok = 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530375 WebPMuxState mux_state;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530376
377 if (!ReadData(filename, &data, &size)) return 0;
378
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530379 mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530380 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530381 if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) {
382 fprintf(stderr,
383 "Failed to create mux object from file %s. mux_state = %d.\n",
384 filename, mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530385 return 0;
386 }
James Zerneec4b872012-01-07 12:44:01 -0800387 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530388 if (err == WEBP_MUX_OK) {
James Zerneec4b872012-01-07 12:44:01 -0800389 uint8_t* const data_mem = (uint8_t*)malloc(image.size_);
390 uint8_t* const alpha_mem = (uint8_t*)malloc(alpha.size_);
James Zern2f741d12011-12-02 16:00:51 -0800391 if ((data_mem != NULL) && (alpha_mem != NULL)) {
James Zerneec4b872012-01-07 12:44:01 -0800392 memcpy(data_mem, image.bytes_, image.size_);
393 memcpy(alpha_mem, alpha.bytes_, alpha.size_);
James Zern2f741d12011-12-02 16:00:51 -0800394 *data_ptr = data_mem;
James Zerneec4b872012-01-07 12:44:01 -0800395 *size_ptr = image.size_;
James Zern2f741d12011-12-02 16:00:51 -0800396 *alpha_data_ptr = alpha_mem;
James Zerneec4b872012-01-07 12:44:01 -0800397 *alpha_size_ptr = alpha.size_;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530398 ok = 1;
399 } else {
James Zern2f741d12011-12-02 16:00:51 -0800400 free(data_mem);
401 free(alpha_mem);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530402 err = WEBP_MUX_MEMORY_ERROR;
James Zerneec4b872012-01-07 12:44:01 -0800403 fprintf(stderr, "Failed to allocate %u bytes to extract image data from"
404 " file %s. Error: %d\n",
405 image.size_ + alpha.size_, filename, err);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530406 }
407 } else {
408 fprintf(stderr, "Failed to extract image data from file %s. Error: %d\n",
409 filename, err);
410 }
411 WebPMuxDelete(mux);
412 return ok;
413}
414
James Zerneec4b872012-01-07 12:44:01 -0800415static int WriteData(const char* filename, const void* data, uint32_t size) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530416 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700417 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530418 if (!fout) {
419 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
420 return 0;
421 }
422 if (fwrite(data, size, 1, fout) != 1) {
423 fprintf(stderr, "Error writing file %s!\n", filename);
424 } else {
425 fprintf(stderr, "Saved file %s (%d bytes)\n", filename, size);
426 ok = 1;
427 }
428 if (fout != stdout) fclose(fout);
429 return ok;
430}
431
432static int WriteWebP(WebPMux* const mux, const char* filename) {
433 uint8_t* data = NULL;
434 uint32_t size = 0;
435 int ok;
436
437 WebPMuxError err = WebPMuxAssemble(mux, &data, &size);
438 if (err != WEBP_MUX_OK) {
439 fprintf(stderr, "Error (%d) assembling the WebP file.\n", err);
440 return 0;
441 }
442 ok = WriteData(filename, data, size);
443 free(data);
444 return ok;
445}
446
447static int ParseFrameArgs(const char* args, uint32_t* x_offset,
448 uint32_t* y_offset, uint32_t* duration) {
449 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
450}
451
452static int ParseTileArgs(const char* args, uint32_t* x_offset,
453 uint32_t* y_offset) {
454 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
455}
456
457//------------------------------------------------------------------------------
458// Clean-up.
459
460static void DeleteConfig(WebPMuxConfig* config) {
461 if (config != NULL) {
462 free(config->feature_.args_);
463 free(config);
464 }
465}
466
467//------------------------------------------------------------------------------
468// Parsing.
469
470// Basic syntactic checks on the command-line arguments.
471// Returns 1 on valid, 0 otherwise.
472// Also fills up num_feature_args to be number of feature arguments given.
473// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
474static int ValidateCommandLine(int argc, const char* argv[],
475 int* num_feature_args) {
476 int num_frame_args;
477 int num_tile_args;
478 int num_loop_args;
479 int ok = 1;
480
481 assert(num_feature_args != NULL);
482 *num_feature_args = 0;
483
484 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700485 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530486 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
487 }
James Zern04e84cf2011-11-04 15:20:08 -0700488 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530489 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
490 }
James Zern04e84cf2011-11-04 15:20:08 -0700491 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530492 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
493 }
James Zern04e84cf2011-11-04 15:20:08 -0700494 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530495 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
496 }
James Zern04e84cf2011-11-04 15:20:08 -0700497 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530498 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
499 }
500
501 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700502 num_frame_args = CountOccurrences(argv, argc, "-frame");
503 num_tile_args = CountOccurrences(argv, argc, "-tile");
504 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530505
506 if (num_loop_args > 1) {
507 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
508 }
509
510 if (IsNotCompatible(num_frame_args, num_loop_args)) {
511 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
512 ErrValidate);
513 }
514 if (num_frame_args > 0 && num_tile_args > 0) {
515 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
516 "\n", ErrValidate);
517 }
518
519 assert(ok == 1);
520 if (num_frame_args == 0 && num_tile_args == 0) {
521 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
522 *num_feature_args = 1;
523 } else {
524 // Multiple arguments ('set' action for animation or tiling).
525 if (num_frame_args > 0) {
526 *num_feature_args = num_frame_args + num_loop_args;
527 } else {
528 *num_feature_args = num_tile_args;
529 }
530 }
531
532 ErrValidate:
533 return ok;
534}
535
536#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
537
538#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
539
540#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
541 if (argc < i + (NUM)) { \
542 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
543 goto LABEL; \
544 }
545
546#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
547 if (argc != i + (NUM)) { \
548 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
549 goto LABEL; \
550 }
551
552// Parses command-line arguments to fill up config object. Also performs some
553// semantic checks.
554static int ParseCommandLine(int argc, const char* argv[],
555 WebPMuxConfig* config) {
556 int i = 0;
557 int feature_arg_index = 0;
558 int ok = 1;
559
560 while (i < argc) {
561 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700562 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530563 if (argv[i][0] == '-') { // One of the action types or output.
564 if (!strcmp(argv[i], "-set")) {
565 if (ACTION_IS_NIL) {
566 config->action_type_ = ACTION_SET;
567 } else {
568 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
569 }
570 ++i;
571 } else if (!strcmp(argv[i], "-get")) {
572 if (ACTION_IS_NIL) {
573 config->action_type_ = ACTION_GET;
574 } else {
575 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
576 }
577 ++i;
578 } else if (!strcmp(argv[i], "-strip")) {
579 if (ACTION_IS_NIL) {
580 config->action_type_ = ACTION_STRIP;
581 feature->arg_count_ = 0;
582 } else {
583 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
584 }
585 ++i;
586 } else if (!strcmp(argv[i], "-frame")) {
587 CHECK_NUM_ARGS_LESS(3, ErrParse);
588 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
589 config->action_type_ = ACTION_SET;
590 } else {
591 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
592 }
593 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
594 feature->type_ = FEATURE_FRM;
595 } else {
596 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
597 }
598 arg->subtype_ = SUBTYPE_FRM;
599 arg->filename_ = argv[i + 1];
600 arg->params_ = argv[i + 2];
601 ++feature_arg_index;
602 i += 3;
603 } else if (!strcmp(argv[i], "-loop")) {
604 CHECK_NUM_ARGS_LESS(2, ErrParse);
605 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
606 config->action_type_ = ACTION_SET;
607 } else {
608 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
609 }
610 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
611 feature->type_ = FEATURE_FRM;
612 } else {
613 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
614 }
615 arg->subtype_ = SUBTYPE_LOOP;
616 arg->params_ = argv[i + 1];
617 ++feature_arg_index;
618 i += 2;
619 } else if (!strcmp(argv[i], "-tile")) {
620 CHECK_NUM_ARGS_LESS(3, ErrParse);
621 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
622 config->action_type_ = ACTION_SET;
623 } else {
624 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
625 }
626 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
627 feature->type_ = FEATURE_TILE;
628 } else {
629 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
630 }
631 arg->filename_ = argv[i + 1];
632 arg->params_ = argv[i + 2];
633 ++feature_arg_index;
634 i += 3;
635 } else if (!strcmp(argv[i], "-o")) {
636 CHECK_NUM_ARGS_LESS(2, ErrParse);
637 config->output_ = argv[i + 1];
638 i += 2;
639 } else if (!strcmp(argv[i], "-info")) {
640 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
641 if (config->action_type_ != NIL_ACTION) {
642 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
643 } else {
644 config->action_type_ = ACTION_INFO;
645 feature->arg_count_ = 0;
646 config->input_ = argv[i + 1];
647 }
648 i += 2;
649 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
650 PrintHelp();
651 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800652 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530653 } else {
654 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
655 }
656 } else { // One of the feature types or input.
657 if (ACTION_IS_NIL) {
658 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
659 ErrParse);
660 }
661 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
662 if (FEATURETYPE_IS_NIL) {
663 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
664 FEATURE_XMP;
665 } else {
666 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
667 }
668 if (config->action_type_ == ACTION_SET) {
669 CHECK_NUM_ARGS_LESS(2, ErrParse);
670 arg->filename_ = argv[i + 1];
671 ++feature_arg_index;
672 i += 2;
673 } else {
674 ++i;
675 }
676 } else if ((!strcmp(argv[i], "frame") ||
677 !strcmp(argv[i], "tile")) &&
678 (config->action_type_ == ACTION_GET)) {
679 CHECK_NUM_ARGS_LESS(2, ErrParse);
680 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
681 FEATURE_TILE;
682 arg->params_ = argv[i + 1];
683 ++feature_arg_index;
684 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700685 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530686 if (config->input_ == NULL) {
687 config->input_ = argv[i];
688 } else {
689 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
690 argv[i], ErrParse);
691 }
692 ++i;
693 }
694 }
695 }
696 ErrParse:
697 return ok;
698}
699
James Zern04e84cf2011-11-04 15:20:08 -0700700// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530701static int ValidateConfig(WebPMuxConfig* config) {
702 int ok = 1;
703 Feature* const feature = &config->feature_;
704
705 // Action.
706 if (ACTION_IS_NIL) {
707 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
708 }
709
710 // Feature type.
711 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
712 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
713 }
714
715 // Input file.
716 if (config->input_ == NULL) {
717 if (config->action_type_ != ACTION_SET) {
718 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
719 } else if (feature->type_ != FEATURE_FRM &&
720 feature->type_ != FEATURE_TILE) {
721 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
722 }
723 }
724
725 // Output file.
726 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
727 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
728 }
729
730 ErrValidate2:
731 return ok;
732}
733
734// Create config object from command-line arguments.
735static int InitializeConfig(int argc, const char* argv[],
736 WebPMuxConfig** config) {
737 int num_feature_args = 0;
738 int ok = 1;
739
740 assert(config != NULL);
741 *config = NULL;
742
743 // Validate command-line arguments.
744 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
745 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
746 }
747
748 // Allocate memory.
749 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
750 if (*config == NULL) {
751 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
752 }
753 (*config)->feature_.arg_count_ = num_feature_args;
754 (*config)->feature_.args_ =
755 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
756 if ((*config)->feature_.args_ == NULL) {
757 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
758 }
759
760 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700761 if (!ParseCommandLine(argc, argv, *config) ||
762 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530763 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
764 }
765
766 Err1:
767 return ok;
768}
769
770#undef ACTION_IS_NIL
771#undef FEATURETYPE_IS_NIL
772#undef CHECK_NUM_ARGS_LESS
773#undef CHECK_NUM_ARGS_MORE
774
775//------------------------------------------------------------------------------
776// Processing.
777
James Zern04e84cf2011-11-04 15:20:08 -0700778static int GetFrameTile(const WebPMux* mux,
779 const WebPMuxConfig* config, int isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800780 WebPData image, alpha;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530781 uint32_t x_offset = 0;
782 uint32_t y_offset = 0;
783 uint32_t duration = 0;
784 WebPMuxError err = WEBP_MUX_OK;
785 WebPMux* mux_single = NULL;
786 long num = 0;
787 int ok = 1;
788
789 num = strtol(config->feature_.args_[0].params_, NULL, 10);
790 if (num < 0) {
791 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
792 }
793
794 if (isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800795 err = WebPMuxGetFrame(mux, num, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530796 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530797 if (err != WEBP_MUX_OK) {
798 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
799 }
800 } else {
James Zerneec4b872012-01-07 12:44:01 -0800801 err = WebPMuxGetTile(mux, num, &image, &alpha, &x_offset, &y_offset);
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 }
806
807 mux_single = WebPMuxNew();
808 if (mux_single == NULL) {
809 err = WEBP_MUX_MEMORY_ERROR;
810 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet);
811 }
James Zerneec4b872012-01-07 12:44:01 -0800812 err = WebPMuxSetImage(mux_single, image.bytes_, image.size_,
813 alpha.bytes_, alpha.size_, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530814 if (err != WEBP_MUX_OK) {
815 ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err,
816 ErrGet);
817 }
818 ok = WriteWebP(mux_single, config->output_);
819
820 ErrGet:
821 WebPMuxDelete(mux_single);
822 return ok;
823}
824
825// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700826static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530827 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800828 WebPData webpdata;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530829 const uint8_t* data = NULL;
830 uint32_t size = 0;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530831 const uint8_t* alpha_data = NULL;
832 uint32_t alpha_size = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530833 uint32_t x_offset = 0;
834 uint32_t y_offset = 0;
835 uint32_t duration = 0;
836 uint32_t loop_count = 0;
837 WebPMuxError err = WEBP_MUX_OK;
838 long num;
839 int index = 0;
840 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700841 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530842
James Zern04e84cf2011-11-04 15:20:08 -0700843 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530844 case ACTION_GET:
845 ok = ReadFile(config->input_, &mux);
846 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700847 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530848 case FEATURE_FRM:
849 ok = GetFrameTile(mux, config, 1);
850 break;
851
852 case FEATURE_TILE:
853 ok = GetFrameTile(mux, config, 0);
854 break;
855
856 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800857 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530858 if (err != WEBP_MUX_OK) {
859 ERROR_GOTO2("ERROR#%d: Could not get color profile.\n", err, Err2);
860 }
James Zerneec4b872012-01-07 12:44:01 -0800861 ok = WriteData(config->output_, webpdata.bytes_, webpdata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530862 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530863 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800864 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530865 if (err != WEBP_MUX_OK) {
866 ERROR_GOTO2("ERROR#%d: Could not get XMP metadata.\n", err, Err2);
867 }
James Zerneec4b872012-01-07 12:44:01 -0800868 ok = WriteData(config->output_, webpdata.bytes_, webpdata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 break;
870
871 default:
872 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
873 break;
874 }
875 break;
876
877 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700878 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530879 case FEATURE_FRM:
880 mux = WebPMuxNew();
881 if (mux == NULL) {
882 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
883 WEBP_MUX_MEMORY_ERROR, Err2);
884 }
885 for (index = 0; index < feature->arg_count_; ++index) {
886 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
887 num = strtol(feature->args_[index].params_, NULL, 10);
888 if (num < 0) {
889 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
890 } else {
891 loop_count = num;
892 }
893 err = WebPMuxSetLoopCount(mux, loop_count);
894 if (err != WEBP_MUX_OK) {
895 ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2);
896 }
897 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530898 ok = ReadImage(feature->args_[index].filename_,
899 &data, &size, &alpha_data, &alpha_size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530900 if (!ok) goto Err2;
901 ok = ParseFrameArgs(feature->args_[index].params_,
902 &x_offset, &y_offset, &duration);
903 if (!ok) {
904 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530905 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530906 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
907 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530908 err = WebPMuxAddFrame(mux, 0, data, size, alpha_data, alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530909 x_offset, y_offset, duration, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530910 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530911 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530912 if (err != WEBP_MUX_OK) {
913 ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n",
914 err, index, Err2);
915 }
916 } else {
917 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
918 }
919 }
920 break;
921
922 case FEATURE_TILE:
923 mux = WebPMuxNew();
924 if (mux == NULL) {
925 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
926 WEBP_MUX_MEMORY_ERROR, Err2);
927 }
928 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530929 ok = ReadImage(feature->args_[index].filename_,
930 &data, &size, &alpha_data, &alpha_size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530931 if (!ok) goto Err2;
932 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
933 &y_offset);
934 if (!ok) {
935 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530936 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530937 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
938 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530939 err = WebPMuxAddTile(mux, 0, data, size, alpha_data, alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530940 x_offset, y_offset, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530941 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530942 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530943 if (err != WEBP_MUX_OK) {
944 ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n",
945 err, index, Err2);
946 }
947 }
948 break;
949
950 case FEATURE_ICCP:
951 ok = ReadFile(config->input_, &mux);
952 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700953 ok = ReadData(feature->args_[0].filename_, (void**)&data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530954 if (!ok) goto Err2;
955 err = WebPMuxSetColorProfile(mux, data, size, 1);
956 free((void*)data);
957 if (err != WEBP_MUX_OK) {
958 ERROR_GOTO2("ERROR#%d: Could not set color profile.\n", err, Err2);
959 }
960 break;
961
962 case FEATURE_XMP:
963 ok = ReadFile(config->input_, &mux);
964 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700965 ok = ReadData(feature->args_[0].filename_, (void**)&data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530966 if (!ok) goto Err2;
967 err = WebPMuxSetMetadata(mux, data, size, 1);
968 free((void*)data);
969 if (err != WEBP_MUX_OK) {
970 ERROR_GOTO2("ERROR#%d: Could not set XMP metadata.\n", err, Err2);
971 }
972 break;
973
974 default:
975 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
976 break;
977 }
978 ok = WriteWebP(mux, config->output_);
979 break;
980
981 case ACTION_STRIP:
982 ok = ReadFile(config->input_, &mux);
983 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700984 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530985 case FEATURE_ICCP:
986 err = WebPMuxDeleteColorProfile(mux);
987 if (err != WEBP_MUX_OK) {
988 ERROR_GOTO2("ERROR#%d: Could not delete color profile.\n", err,
989 Err2);
990 }
991 break;
992 case FEATURE_XMP:
993 err = WebPMuxDeleteMetadata(mux);
994 if (err != WEBP_MUX_OK) {
995 ERROR_GOTO2("ERROR#%d: Could not delete XMP metadata.\n", err,
996 Err2);
997 }
998 break;
999 default:
1000 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1001 break;
1002 }
1003 ok = WriteWebP(mux, config->output_);
1004 break;
1005
1006 case ACTION_INFO:
1007 ok = ReadFile(config->input_, &mux);
1008 if (!ok) goto Err2;
1009 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1010 break;
1011
1012 default:
1013 assert(0); // Invalid action.
1014 break;
1015 }
1016
1017 Err2:
1018 WebPMuxDelete(mux);
1019 return ok;
1020}
1021
1022//------------------------------------------------------------------------------
1023// Main.
1024
1025int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301026 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001027 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301028 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001029 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301030 } else {
1031 PrintHelp();
1032 }
1033 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001034 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301035}
1036
1037//------------------------------------------------------------------------------