blob: a33a86dbe813b24a48fdf54a7eacf0d72fcb79e8 [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
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530119static const char* const kErrorMessages[] = {
120 "WEBP_MUX_ERROR", "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT",
121 "WEBP_MUX_INVALID_PARAMETER", "WEBP_MUX_BAD_DATA", "WEBP_MUX_MEMORY_ERROR",
122 "WEBP_MUX_NOT_ENOUGH_DATA"
123};
124
125static const char* ErrorString(WebPMuxError err) {
126 assert(err <= WEBP_MUX_ERROR && err >= WEBP_MUX_NOT_ENOUGH_DATA);
127 return kErrorMessages[-err];
128}
129
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530130static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700131 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530132}
133
James Zerna0b27362012-01-27 17:39:47 -0800134#define RETURN_IF_ERROR(ERR_MSG) \
135 if (err != WEBP_MUX_OK) { \
136 fprintf(stderr, ERR_MSG); \
137 return err; \
138 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530139
James Zerna0b27362012-01-27 17:39:47 -0800140#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
141 if (err != WEBP_MUX_OK) { \
142 fprintf(stderr, ERR_MSG, FORMAT_STR); \
143 return err; \
144 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530145
James Zerna0b27362012-01-27 17:39:47 -0800146#define ERROR_GOTO1(ERR_MSG, LABEL) \
147 do { \
148 fprintf(stderr, ERR_MSG); \
149 ok = 0; \
150 goto LABEL; \
151 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530152
James Zerna0b27362012-01-27 17:39:47 -0800153#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
154 do { \
155 fprintf(stderr, ERR_MSG, FORMAT_STR); \
156 ok = 0; \
157 goto LABEL; \
158 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530159
James Zerna0b27362012-01-27 17:39:47 -0800160#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
161 do { \
162 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
163 ok = 0; \
164 goto LABEL; \
165 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530166
167static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530168 uint32_t flag;
169
170 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
171 RETURN_IF_ERROR("Failed to retrieve features\n");
172
173 if (flag == 0) {
174 fprintf(stderr, "No features present.\n");
175 return err;
176 }
177
178 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800179 printf("Features present:");
180 if (flag & ANIMATION_FLAG) printf(" animation");
181 if (flag & TILE_FLAG) printf(" tiling");
182 if (flag & ICCP_FLAG) printf(" icc profile");
183 if (flag & META_FLAG) printf(" metadata");
184 if (flag & ALPHA_FLAG) printf(" transparency");
185 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530186
187 if (flag & ANIMATION_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800188 int nFrames;
Urvang Joshi6808e692012-07-06 11:35:36 +0530189 int loop_count;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530190 err = WebPMuxGetLoopCount(mux, &loop_count);
191 RETURN_IF_ERROR("Failed to retrieve loop count\n");
James Zern974aaff2012-01-24 12:46:46 -0800192 printf("Loop Count : %d\n", loop_count);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530193
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530194 err = WebPMuxNumChunks(mux, WEBP_CHUNK_FRAME, &nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530195 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
196
James Zern974aaff2012-01-24 12:46:46 -0800197 printf("Number of frames: %d\n", nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530198 if (nFrames > 0) {
199 int i;
James Zern974aaff2012-01-24 12:46:46 -0800200 printf("No.: x_offset y_offset duration image_size");
James Zern974aaff2012-01-24 12:46:46 -0800201 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530202 for (i = 1; i <= nFrames; i++) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530203 int x_offset, y_offset, duration;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700204 WebPData bitstream;
205 err = WebPMuxGetFrame(mux, i, &bitstream,
Urvang Joshic398f592011-11-22 14:40:41 +0530206 &x_offset, &y_offset, &duration);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530207 RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
James Zern95667b82012-04-18 17:13:34 -0700208 printf("%3d: %8d %8d %8d %10zu",
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700209 i, x_offset, y_offset, duration, bitstream.size_);
James Zern974aaff2012-01-24 12:46:46 -0800210 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530211 }
212 }
213 }
214
215 if (flag & TILE_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800216 int nTiles;
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530217 err = WebPMuxNumChunks(mux, WEBP_CHUNK_TILE, &nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530218 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
219
James Zern974aaff2012-01-24 12:46:46 -0800220 printf("Number of tiles: %d\n", nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530221 if (nTiles > 0) {
222 int i;
James Zern974aaff2012-01-24 12:46:46 -0800223 printf("No.: x_offset y_offset image_size");
James Zern974aaff2012-01-24 12:46:46 -0800224 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530225 for (i = 1; i <= nTiles; i++) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530226 int x_offset, y_offset;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700227 WebPData bitstream;
228 err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530229 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700230 printf("%3d: %8d %8d %10zu", i, x_offset, y_offset, bitstream.size_);
James Zern974aaff2012-01-24 12:46:46 -0800231 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530232 }
233 }
234 }
235
236 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800237 WebPData icc_profile;
238 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530239 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zern95667b82012-04-18 17:13:34 -0700240 printf("Size of the color profile data: %zu\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530241 }
242
243 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800244 WebPData metadata;
245 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530246 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern95667b82012-04-18 17:13:34 -0700247 printf("Size of the XMP metadata: %zu\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530248 }
249
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530250 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700251 WebPData bitstream;
252 err = WebPMuxGetImage(mux, &bitstream);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530253 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700254 printf("Size of the image (with alpha): %zu\n", bitstream.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530255 }
256
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530257 return WEBP_MUX_OK;
258}
259
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700260static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800261 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
262 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
263 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
264 printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
265 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
266 printf(" -loop LOOP_COUNT -o OUTPUT\n");
267 printf(" webpmux -info INPUT\n");
268 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530269
James Zern974aaff2012-01-24 12:46:46 -0800270 printf("\n");
271 printf("GET_OPTIONS:\n");
272 printf(" Extract relevant data.\n");
273 printf(" icc Get ICCP Color profile.\n");
274 printf(" xmp Get XMP metadata.\n");
275 printf(" tile n Get nth tile.\n");
276 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530277
James Zern974aaff2012-01-24 12:46:46 -0800278 printf("\n");
279 printf("SET_OPTIONS:\n");
280 printf(" Set color profile/metadata.\n");
281 printf(" icc Set ICC Color profile.\n");
282 printf(" xmp Set XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530283
James Zern974aaff2012-01-24 12:46:46 -0800284 printf("\n");
285 printf("STRIP_OPTIONS:\n");
286 printf(" Strip color profile/metadata.\n");
287 printf(" icc Strip ICCP color profile.\n");
288 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530289
James Zern974aaff2012-01-24 12:46:46 -0800290 printf("\n");
291 printf("TILE_OPTIONS(i):\n");
292 printf(" Create tiled image.\n");
293 printf(" file_i +xi+yi\n");
294 printf(" where: 'file_i' is the i'th tile (webp format),\n");
295 printf(" 'xi','yi' specify the image offset for this tile.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530296
James Zern974aaff2012-01-24 12:46:46 -0800297 printf("\n");
298 printf("FRAME_OPTIONS(i):\n");
299 printf(" Create animation.\n");
300 printf(" file_i +xi+yi+di\n");
301 printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
302 printf(" 'xi','yi' specify the image offset for this frame.\n");
303 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530304
James Zern974aaff2012-01-24 12:46:46 -0800305 printf("\nINPUT & OUTPUT are in webp format.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530306}
307
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530308static int ReadFileToWebPData(const char* const filename,
309 WebPData* const webp_data) {
310 const uint8_t* data;
311 size_t size;
312 if (!ExUtilReadFile(filename, &data, &size)) return 0;
313 webp_data->bytes_ = data;
314 webp_data->size_ = size;
315 return 1;
316}
317
James Zern061263a2012-05-11 16:00:57 -0700318static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530319 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530320 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530321 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530322 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530323 free((void*)bitstream.bytes_);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530324 if (*mux != NULL) return 1;
325 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530326 return 0;
327}
328
James Zern0f7820e2012-01-24 14:08:27 -0800329static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530330 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700331 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530332 if (!fout) {
333 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
334 return 0;
335 }
James Zern0f7820e2012-01-24 14:08:27 -0800336 if (fwrite(webpdata->bytes_, webpdata->size_, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530337 fprintf(stderr, "Error writing file %s!\n", filename);
338 } else {
James Zern95667b82012-04-18 17:13:34 -0700339 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530340 ok = 1;
341 }
342 if (fout != stdout) fclose(fout);
343 return ok;
344}
345
346static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530347 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530348 WebPData webp_data;
349 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530350 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530351 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530352 return 0;
353 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530354 ok = WriteData(filename, &webp_data);
355 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530356 return ok;
357}
358
Urvang Joshi6808e692012-07-06 11:35:36 +0530359static int ParseFrameArgs(const char* args, int* const x_offset,
360 int* const y_offset, int* const duration) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530361 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
362}
363
Urvang Joshi6808e692012-07-06 11:35:36 +0530364static int ParseTileArgs(const char* args,
365 int* const x_offset, int* const y_offset) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530366 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
367}
368
369//------------------------------------------------------------------------------
370// Clean-up.
371
372static void DeleteConfig(WebPMuxConfig* config) {
373 if (config != NULL) {
374 free(config->feature_.args_);
375 free(config);
376 }
377}
378
379//------------------------------------------------------------------------------
380// Parsing.
381
382// Basic syntactic checks on the command-line arguments.
383// Returns 1 on valid, 0 otherwise.
384// Also fills up num_feature_args to be number of feature arguments given.
385// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
386static int ValidateCommandLine(int argc, const char* argv[],
387 int* num_feature_args) {
388 int num_frame_args;
389 int num_tile_args;
390 int num_loop_args;
391 int ok = 1;
392
393 assert(num_feature_args != NULL);
394 *num_feature_args = 0;
395
396 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700397 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530398 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
399 }
James Zern04e84cf2011-11-04 15:20:08 -0700400 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530401 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
402 }
James Zern04e84cf2011-11-04 15:20:08 -0700403 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530404 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
405 }
James Zern04e84cf2011-11-04 15:20:08 -0700406 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530407 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
408 }
James Zern04e84cf2011-11-04 15:20:08 -0700409 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530410 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
411 }
412
413 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700414 num_frame_args = CountOccurrences(argv, argc, "-frame");
415 num_tile_args = CountOccurrences(argv, argc, "-tile");
416 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530417
418 if (num_loop_args > 1) {
419 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
420 }
421
422 if (IsNotCompatible(num_frame_args, num_loop_args)) {
423 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
424 ErrValidate);
425 }
426 if (num_frame_args > 0 && num_tile_args > 0) {
427 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
428 "\n", ErrValidate);
429 }
430
431 assert(ok == 1);
432 if (num_frame_args == 0 && num_tile_args == 0) {
433 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
434 *num_feature_args = 1;
435 } else {
436 // Multiple arguments ('set' action for animation or tiling).
437 if (num_frame_args > 0) {
438 *num_feature_args = num_frame_args + num_loop_args;
439 } else {
440 *num_feature_args = num_tile_args;
441 }
442 }
443
444 ErrValidate:
445 return ok;
446}
447
448#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
449
450#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
451
452#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
453 if (argc < i + (NUM)) { \
454 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
455 goto LABEL; \
456 }
457
458#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
459 if (argc != i + (NUM)) { \
460 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
461 goto LABEL; \
462 }
463
464// Parses command-line arguments to fill up config object. Also performs some
465// semantic checks.
466static int ParseCommandLine(int argc, const char* argv[],
467 WebPMuxConfig* config) {
468 int i = 0;
469 int feature_arg_index = 0;
470 int ok = 1;
471
472 while (i < argc) {
473 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700474 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530475 if (argv[i][0] == '-') { // One of the action types or output.
476 if (!strcmp(argv[i], "-set")) {
477 if (ACTION_IS_NIL) {
478 config->action_type_ = ACTION_SET;
479 } else {
480 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
481 }
482 ++i;
483 } else if (!strcmp(argv[i], "-get")) {
484 if (ACTION_IS_NIL) {
485 config->action_type_ = ACTION_GET;
486 } else {
487 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
488 }
489 ++i;
490 } else if (!strcmp(argv[i], "-strip")) {
491 if (ACTION_IS_NIL) {
492 config->action_type_ = ACTION_STRIP;
493 feature->arg_count_ = 0;
494 } else {
495 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
496 }
497 ++i;
498 } else if (!strcmp(argv[i], "-frame")) {
499 CHECK_NUM_ARGS_LESS(3, ErrParse);
500 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
501 config->action_type_ = ACTION_SET;
502 } else {
503 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
504 }
505 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
506 feature->type_ = FEATURE_FRM;
507 } else {
508 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
509 }
510 arg->subtype_ = SUBTYPE_FRM;
511 arg->filename_ = argv[i + 1];
512 arg->params_ = argv[i + 2];
513 ++feature_arg_index;
514 i += 3;
515 } else if (!strcmp(argv[i], "-loop")) {
516 CHECK_NUM_ARGS_LESS(2, ErrParse);
517 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
518 config->action_type_ = ACTION_SET;
519 } else {
520 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
521 }
522 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
523 feature->type_ = FEATURE_FRM;
524 } else {
525 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
526 }
527 arg->subtype_ = SUBTYPE_LOOP;
528 arg->params_ = argv[i + 1];
529 ++feature_arg_index;
530 i += 2;
531 } else if (!strcmp(argv[i], "-tile")) {
532 CHECK_NUM_ARGS_LESS(3, ErrParse);
533 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
534 config->action_type_ = ACTION_SET;
535 } else {
536 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
537 }
538 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
539 feature->type_ = FEATURE_TILE;
540 } else {
541 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
542 }
543 arg->filename_ = argv[i + 1];
544 arg->params_ = argv[i + 2];
545 ++feature_arg_index;
546 i += 3;
547 } else if (!strcmp(argv[i], "-o")) {
548 CHECK_NUM_ARGS_LESS(2, ErrParse);
549 config->output_ = argv[i + 1];
550 i += 2;
551 } else if (!strcmp(argv[i], "-info")) {
552 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
553 if (config->action_type_ != NIL_ACTION) {
554 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
555 } else {
556 config->action_type_ = ACTION_INFO;
557 feature->arg_count_ = 0;
558 config->input_ = argv[i + 1];
559 }
560 i += 2;
561 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
562 PrintHelp();
563 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800564 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530565 } else {
566 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
567 }
568 } else { // One of the feature types or input.
569 if (ACTION_IS_NIL) {
570 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
571 ErrParse);
572 }
573 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
574 if (FEATURETYPE_IS_NIL) {
575 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
576 FEATURE_XMP;
577 } else {
578 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
579 }
580 if (config->action_type_ == ACTION_SET) {
581 CHECK_NUM_ARGS_LESS(2, ErrParse);
582 arg->filename_ = argv[i + 1];
583 ++feature_arg_index;
584 i += 2;
585 } else {
586 ++i;
587 }
588 } else if ((!strcmp(argv[i], "frame") ||
589 !strcmp(argv[i], "tile")) &&
590 (config->action_type_ == ACTION_GET)) {
591 CHECK_NUM_ARGS_LESS(2, ErrParse);
592 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
593 FEATURE_TILE;
594 arg->params_ = argv[i + 1];
595 ++feature_arg_index;
596 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700597 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530598 if (config->input_ == NULL) {
599 config->input_ = argv[i];
600 } else {
601 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
602 argv[i], ErrParse);
603 }
604 ++i;
605 }
606 }
607 }
608 ErrParse:
609 return ok;
610}
611
James Zern04e84cf2011-11-04 15:20:08 -0700612// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530613static int ValidateConfig(WebPMuxConfig* config) {
614 int ok = 1;
615 Feature* const feature = &config->feature_;
616
617 // Action.
618 if (ACTION_IS_NIL) {
619 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
620 }
621
622 // Feature type.
623 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
624 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
625 }
626
627 // Input file.
628 if (config->input_ == NULL) {
629 if (config->action_type_ != ACTION_SET) {
630 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
631 } else if (feature->type_ != FEATURE_FRM &&
632 feature->type_ != FEATURE_TILE) {
633 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
634 }
635 }
636
637 // Output file.
638 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
639 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
640 }
641
642 ErrValidate2:
643 return ok;
644}
645
646// Create config object from command-line arguments.
647static int InitializeConfig(int argc, const char* argv[],
648 WebPMuxConfig** config) {
649 int num_feature_args = 0;
650 int ok = 1;
651
652 assert(config != NULL);
653 *config = NULL;
654
655 // Validate command-line arguments.
656 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
657 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
658 }
659
660 // Allocate memory.
661 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
662 if (*config == NULL) {
663 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
664 }
665 (*config)->feature_.arg_count_ = num_feature_args;
666 (*config)->feature_.args_ =
667 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
668 if ((*config)->feature_.args_ == NULL) {
669 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
670 }
671
672 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700673 if (!ParseCommandLine(argc, argv, *config) ||
674 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530675 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
676 }
677
678 Err1:
679 return ok;
680}
681
682#undef ACTION_IS_NIL
683#undef FEATURETYPE_IS_NIL
684#undef CHECK_NUM_ARGS_LESS
685#undef CHECK_NUM_ARGS_MORE
686
687//------------------------------------------------------------------------------
688// Processing.
689
James Zern04e84cf2011-11-04 15:20:08 -0700690static int GetFrameTile(const WebPMux* mux,
691 const WebPMuxConfig* config, int isFrame) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700692 WebPData bitstream;
Urvang Joshi6808e692012-07-06 11:35:36 +0530693 int x_offset = 0;
694 int y_offset = 0;
695 int duration = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530696 WebPMuxError err = WEBP_MUX_OK;
697 WebPMux* mux_single = NULL;
698 long num = 0;
699 int ok = 1;
700
701 num = strtol(config->feature_.args_[0].params_, NULL, 10);
702 if (num < 0) {
703 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
704 }
705
706 if (isFrame) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700707 err = WebPMuxGetFrame(mux, num, &bitstream,
Urvang Joshic398f592011-11-22 14:40:41 +0530708 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530709 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530710 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
711 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530712 }
713 } else {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700714 err = WebPMuxGetTile(mux, num, &bitstream, &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530715 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530716 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
717 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530718 }
719 }
720
721 mux_single = WebPMuxNew();
722 if (mux_single == NULL) {
723 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530724 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
725 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530726 }
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700727 err = WebPMuxSetImage(mux_single, &bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530728 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530729 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
730 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530731 }
732 ok = WriteWebP(mux_single, config->output_);
733
734 ErrGet:
735 WebPMuxDelete(mux_single);
736 return ok;
737}
738
739// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700740static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530741 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800742 WebPData webpdata;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530743 WebPData metadata, color_profile;
Urvang Joshi6808e692012-07-06 11:35:36 +0530744 int x_offset = 0;
745 int y_offset = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530746 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530747 int index = 0;
748 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700749 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530750
James Zern04e84cf2011-11-04 15:20:08 -0700751 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530752 case ACTION_GET:
James Zern061263a2012-05-11 16:00:57 -0700753 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530754 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700755 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530756 case FEATURE_FRM:
757 ok = GetFrameTile(mux, config, 1);
758 break;
759
760 case FEATURE_TILE:
761 ok = GetFrameTile(mux, config, 0);
762 break;
763
764 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800765 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530766 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530767 ERROR_GOTO2("ERROR (%s): Could not get color profile.\n",
768 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530769 }
James Zern0f7820e2012-01-24 14:08:27 -0800770 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530771 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530772 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800773 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530774 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530775 ERROR_GOTO2("ERROR (%s): Could not get XMP metadata.\n",
776 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530777 }
James Zern0f7820e2012-01-24 14:08:27 -0800778 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530779 break;
780
781 default:
782 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
783 break;
784 }
785 break;
786
787 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700788 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530789 case FEATURE_FRM:
790 mux = WebPMuxNew();
791 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530792 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
793 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530794 }
795 for (index = 0; index < feature->arg_count_; ++index) {
796 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
James Zern0f7820e2012-01-24 14:08:27 -0800797 const long num = strtol(feature->args_[index].params_, NULL, 10);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530798 if (num < 0) {
799 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530800 }
James Zern0f7820e2012-01-24 14:08:27 -0800801 err = WebPMuxSetLoopCount(mux, num);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530802 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530803 ERROR_GOTO2("ERROR (%s): Could not set loop count.\n",
804 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530805 }
806 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530807 int duration;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700808 ok = ReadFileToWebPData(feature->args_[index].filename_,
809 &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530810 if (!ok) goto Err2;
811 ok = ParseFrameArgs(feature->args_[index].params_,
812 &x_offset, &y_offset, &duration);
813 if (!ok) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700814 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530815 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
816 }
Urvang Joshib494ad52012-06-22 11:17:02 -0700817 err = WebPMuxPushFrame(mux, &webpdata, x_offset, y_offset,
James Zernb9ae4f02012-06-20 16:02:53 -0700818 duration, 1);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700819 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530820 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530821 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d.\n",
822 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530823 }
824 } else {
825 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
826 }
827 }
828 break;
829
830 case FEATURE_TILE:
831 mux = WebPMuxNew();
832 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530833 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
834 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530835 }
836 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700837 ok = ReadFileToWebPData(feature->args_[index].filename_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530838 if (!ok) goto Err2;
839 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
840 &y_offset);
841 if (!ok) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700842 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530843 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
844 }
Urvang Joshib494ad52012-06-22 11:17:02 -0700845 err = WebPMuxPushTile(mux, &webpdata, x_offset, y_offset, 1);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700846 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530847 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530848 ERROR_GOTO3("ERROR (%s): Could not add a tile at index %d.\n",
849 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530850 }
851 }
852 break;
853
854 case FEATURE_ICCP:
James Zern061263a2012-05-11 16:00:57 -0700855 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530856 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530857 ok = ReadFileToWebPData(feature->args_[0].filename_, &color_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530858 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530859 err = WebPMuxSetColorProfile(mux, &color_profile, 1);
860 free((void*)color_profile.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530861 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530862 ERROR_GOTO2("ERROR (%s): Could not set color profile.\n",
863 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530864 }
865 break;
866
867 case FEATURE_XMP:
James Zern061263a2012-05-11 16:00:57 -0700868 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530870 ok = ReadFileToWebPData(feature->args_[0].filename_, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530871 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530872 err = WebPMuxSetMetadata(mux, &metadata, 1);
873 free((void*)metadata.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530874 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530875 ERROR_GOTO2("ERROR (%s): Could not set XMP metadata.\n",
876 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530877 }
878 break;
879
880 default:
881 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
882 break;
883 }
884 ok = WriteWebP(mux, config->output_);
885 break;
886
887 case ACTION_STRIP:
James Zern061263a2012-05-11 16:00:57 -0700888 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530889 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700890 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530891 case FEATURE_ICCP:
892 err = WebPMuxDeleteColorProfile(mux);
893 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530894 ERROR_GOTO2("ERROR (%s): Could not delete color profile.\n",
895 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530896 }
897 break;
898 case FEATURE_XMP:
899 err = WebPMuxDeleteMetadata(mux);
900 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530901 ERROR_GOTO2("ERROR (%s): Could not delete XMP metadata.\n",
902 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530903 }
904 break;
905 default:
906 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
907 break;
908 }
909 ok = WriteWebP(mux, config->output_);
910 break;
911
912 case ACTION_INFO:
James Zern061263a2012-05-11 16:00:57 -0700913 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530914 if (!ok) goto Err2;
915 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
916 break;
917
918 default:
919 assert(0); // Invalid action.
920 break;
921 }
922
923 Err2:
924 WebPMuxDelete(mux);
925 return ok;
926}
927
928//------------------------------------------------------------------------------
929// Main.
930
931int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530932 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -0800933 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530934 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -0800935 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530936 } else {
937 PrintHelp();
938 }
939 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800940 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530941}
942
943//------------------------------------------------------------------------------