blob: 51333b06575e33d6933ba791a1f3b9de2f382005 [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[] = {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530120 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
121 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530122};
123
124static const char* ErrorString(WebPMuxError err) {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530125 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530126 return kErrorMessages[-err];
127}
128
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530129static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700130 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530131}
132
James Zerna0b27362012-01-27 17:39:47 -0800133#define RETURN_IF_ERROR(ERR_MSG) \
134 if (err != WEBP_MUX_OK) { \
135 fprintf(stderr, ERR_MSG); \
136 return err; \
137 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530138
James Zerna0b27362012-01-27 17:39:47 -0800139#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
140 if (err != WEBP_MUX_OK) { \
141 fprintf(stderr, ERR_MSG, FORMAT_STR); \
142 return err; \
143 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530144
James Zerna0b27362012-01-27 17:39:47 -0800145#define ERROR_GOTO1(ERR_MSG, LABEL) \
146 do { \
147 fprintf(stderr, ERR_MSG); \
148 ok = 0; \
149 goto LABEL; \
150 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530151
James Zerna0b27362012-01-27 17:39:47 -0800152#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
153 do { \
154 fprintf(stderr, ERR_MSG, FORMAT_STR); \
155 ok = 0; \
156 goto LABEL; \
157 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530158
James Zerna0b27362012-01-27 17:39:47 -0800159#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
160 do { \
161 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
162 ok = 0; \
163 goto LABEL; \
164 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530165
166static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530167 uint32_t flag;
168
169 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
170 RETURN_IF_ERROR("Failed to retrieve features\n");
171
172 if (flag == 0) {
173 fprintf(stderr, "No features present.\n");
174 return err;
175 }
176
177 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800178 printf("Features present:");
179 if (flag & ANIMATION_FLAG) printf(" animation");
180 if (flag & TILE_FLAG) printf(" tiling");
181 if (flag & ICCP_FLAG) printf(" icc profile");
182 if (flag & META_FLAG) printf(" metadata");
183 if (flag & ALPHA_FLAG) printf(" transparency");
184 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530185
186 if (flag & ANIMATION_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800187 int nFrames;
Urvang Joshi6808e692012-07-06 11:35:36 +0530188 int loop_count;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530189 err = WebPMuxGetLoopCount(mux, &loop_count);
190 RETURN_IF_ERROR("Failed to retrieve loop count\n");
James Zern974aaff2012-01-24 12:46:46 -0800191 printf("Loop Count : %d\n", loop_count);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530192
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530193 err = WebPMuxNumChunks(mux, WEBP_CHUNK_FRAME, &nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530194 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
195
James Zern974aaff2012-01-24 12:46:46 -0800196 printf("Number of frames: %d\n", nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530197 if (nFrames > 0) {
198 int i;
James Zern974aaff2012-01-24 12:46:46 -0800199 printf("No.: x_offset y_offset duration image_size");
James Zern974aaff2012-01-24 12:46:46 -0800200 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530201 for (i = 1; i <= nFrames; i++) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530202 int x_offset, y_offset, duration;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700203 WebPData bitstream;
204 err = WebPMuxGetFrame(mux, i, &bitstream,
Urvang Joshic398f592011-11-22 14:40:41 +0530205 &x_offset, &y_offset, &duration);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530206 RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
James Zern95667b82012-04-18 17:13:34 -0700207 printf("%3d: %8d %8d %8d %10zu",
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700208 i, x_offset, y_offset, duration, bitstream.size_);
James Zern974aaff2012-01-24 12:46:46 -0800209 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530210 }
211 }
212 }
213
214 if (flag & TILE_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800215 int nTiles;
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530216 err = WebPMuxNumChunks(mux, WEBP_CHUNK_TILE, &nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530217 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
218
James Zern974aaff2012-01-24 12:46:46 -0800219 printf("Number of tiles: %d\n", nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530220 if (nTiles > 0) {
221 int i;
James Zern974aaff2012-01-24 12:46:46 -0800222 printf("No.: x_offset y_offset image_size");
James Zern974aaff2012-01-24 12:46:46 -0800223 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530224 for (i = 1; i <= nTiles; i++) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530225 int x_offset, y_offset;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700226 WebPData bitstream;
227 err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530228 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700229 printf("%3d: %8d %8d %10zu", i, x_offset, y_offset, bitstream.size_);
James Zern974aaff2012-01-24 12:46:46 -0800230 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530231 }
232 }
233 }
234
235 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800236 WebPData icc_profile;
237 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530238 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zern95667b82012-04-18 17:13:34 -0700239 printf("Size of the color profile data: %zu\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530240 }
241
242 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800243 WebPData metadata;
244 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530245 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern95667b82012-04-18 17:13:34 -0700246 printf("Size of the XMP metadata: %zu\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530247 }
248
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530249 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700250 WebPData bitstream;
251 err = WebPMuxGetImage(mux, &bitstream);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530252 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700253 printf("Size of the image (with alpha): %zu\n", bitstream.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530254 }
255
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530256 return WEBP_MUX_OK;
257}
258
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700259static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800260 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
261 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
262 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
263 printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
264 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
265 printf(" -loop LOOP_COUNT -o OUTPUT\n");
266 printf(" webpmux -info INPUT\n");
267 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530268
James Zern974aaff2012-01-24 12:46:46 -0800269 printf("\n");
270 printf("GET_OPTIONS:\n");
271 printf(" Extract relevant data.\n");
272 printf(" icc Get ICCP Color profile.\n");
273 printf(" xmp Get XMP metadata.\n");
274 printf(" tile n Get nth tile.\n");
275 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530276
James Zern974aaff2012-01-24 12:46:46 -0800277 printf("\n");
278 printf("SET_OPTIONS:\n");
279 printf(" Set color profile/metadata.\n");
280 printf(" icc Set ICC Color profile.\n");
281 printf(" xmp Set XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530282
James Zern974aaff2012-01-24 12:46:46 -0800283 printf("\n");
284 printf("STRIP_OPTIONS:\n");
285 printf(" Strip color profile/metadata.\n");
286 printf(" icc Strip ICCP color profile.\n");
287 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530288
James Zern974aaff2012-01-24 12:46:46 -0800289 printf("\n");
290 printf("TILE_OPTIONS(i):\n");
291 printf(" Create tiled image.\n");
292 printf(" file_i +xi+yi\n");
293 printf(" where: 'file_i' is the i'th tile (webp format),\n");
294 printf(" 'xi','yi' specify the image offset for this tile.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530295
James Zern974aaff2012-01-24 12:46:46 -0800296 printf("\n");
297 printf("FRAME_OPTIONS(i):\n");
298 printf(" Create animation.\n");
299 printf(" file_i +xi+yi+di\n");
300 printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
301 printf(" 'xi','yi' specify the image offset for this frame.\n");
302 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530303
James Zern974aaff2012-01-24 12:46:46 -0800304 printf("\nINPUT & OUTPUT are in webp format.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530305}
306
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530307static int ReadFileToWebPData(const char* const filename,
308 WebPData* const webp_data) {
309 const uint8_t* data;
310 size_t size;
311 if (!ExUtilReadFile(filename, &data, &size)) return 0;
312 webp_data->bytes_ = data;
313 webp_data->size_ = size;
314 return 1;
315}
316
James Zern061263a2012-05-11 16:00:57 -0700317static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530318 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530319 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530320 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530321 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530322 free((void*)bitstream.bytes_);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530323 if (*mux != NULL) return 1;
324 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530325 return 0;
326}
327
James Zern0f7820e2012-01-24 14:08:27 -0800328static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530329 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700330 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530331 if (!fout) {
332 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
333 return 0;
334 }
James Zern0f7820e2012-01-24 14:08:27 -0800335 if (fwrite(webpdata->bytes_, webpdata->size_, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530336 fprintf(stderr, "Error writing file %s!\n", filename);
337 } else {
James Zern95667b82012-04-18 17:13:34 -0700338 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530339 ok = 1;
340 }
341 if (fout != stdout) fclose(fout);
342 return ok;
343}
344
345static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530346 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530347 WebPData webp_data;
348 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530349 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530350 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530351 return 0;
352 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530353 ok = WriteData(filename, &webp_data);
354 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530355 return ok;
356}
357
Urvang Joshi6808e692012-07-06 11:35:36 +0530358static int ParseFrameArgs(const char* args, int* const x_offset,
359 int* const y_offset, int* const duration) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530360 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
361}
362
Urvang Joshi6808e692012-07-06 11:35:36 +0530363static int ParseTileArgs(const char* args,
364 int* const x_offset, int* const y_offset) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530365 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
366}
367
368//------------------------------------------------------------------------------
369// Clean-up.
370
371static void DeleteConfig(WebPMuxConfig* config) {
372 if (config != NULL) {
373 free(config->feature_.args_);
374 free(config);
375 }
376}
377
378//------------------------------------------------------------------------------
379// Parsing.
380
381// Basic syntactic checks on the command-line arguments.
382// Returns 1 on valid, 0 otherwise.
383// Also fills up num_feature_args to be number of feature arguments given.
384// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
385static int ValidateCommandLine(int argc, const char* argv[],
386 int* num_feature_args) {
387 int num_frame_args;
388 int num_tile_args;
389 int num_loop_args;
390 int ok = 1;
391
392 assert(num_feature_args != NULL);
393 *num_feature_args = 0;
394
395 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700396 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530397 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
398 }
James Zern04e84cf2011-11-04 15:20:08 -0700399 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530400 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
401 }
James Zern04e84cf2011-11-04 15:20:08 -0700402 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530403 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
404 }
James Zern04e84cf2011-11-04 15:20:08 -0700405 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530406 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
407 }
James Zern04e84cf2011-11-04 15:20:08 -0700408 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530409 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
410 }
411
412 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700413 num_frame_args = CountOccurrences(argv, argc, "-frame");
414 num_tile_args = CountOccurrences(argv, argc, "-tile");
415 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530416
417 if (num_loop_args > 1) {
418 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
419 }
420
421 if (IsNotCompatible(num_frame_args, num_loop_args)) {
422 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
423 ErrValidate);
424 }
425 if (num_frame_args > 0 && num_tile_args > 0) {
426 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
427 "\n", ErrValidate);
428 }
429
430 assert(ok == 1);
431 if (num_frame_args == 0 && num_tile_args == 0) {
432 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
433 *num_feature_args = 1;
434 } else {
435 // Multiple arguments ('set' action for animation or tiling).
436 if (num_frame_args > 0) {
437 *num_feature_args = num_frame_args + num_loop_args;
438 } else {
439 *num_feature_args = num_tile_args;
440 }
441 }
442
443 ErrValidate:
444 return ok;
445}
446
447#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
448
449#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
450
451#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
452 if (argc < i + (NUM)) { \
453 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
454 goto LABEL; \
455 }
456
457#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
458 if (argc != i + (NUM)) { \
459 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
460 goto LABEL; \
461 }
462
463// Parses command-line arguments to fill up config object. Also performs some
464// semantic checks.
465static int ParseCommandLine(int argc, const char* argv[],
466 WebPMuxConfig* config) {
467 int i = 0;
468 int feature_arg_index = 0;
469 int ok = 1;
470
471 while (i < argc) {
472 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700473 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530474 if (argv[i][0] == '-') { // One of the action types or output.
475 if (!strcmp(argv[i], "-set")) {
476 if (ACTION_IS_NIL) {
477 config->action_type_ = ACTION_SET;
478 } else {
479 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
480 }
481 ++i;
482 } else if (!strcmp(argv[i], "-get")) {
483 if (ACTION_IS_NIL) {
484 config->action_type_ = ACTION_GET;
485 } else {
486 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
487 }
488 ++i;
489 } else if (!strcmp(argv[i], "-strip")) {
490 if (ACTION_IS_NIL) {
491 config->action_type_ = ACTION_STRIP;
492 feature->arg_count_ = 0;
493 } else {
494 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
495 }
496 ++i;
497 } else if (!strcmp(argv[i], "-frame")) {
498 CHECK_NUM_ARGS_LESS(3, ErrParse);
499 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
500 config->action_type_ = ACTION_SET;
501 } else {
502 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
503 }
504 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
505 feature->type_ = FEATURE_FRM;
506 } else {
507 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
508 }
509 arg->subtype_ = SUBTYPE_FRM;
510 arg->filename_ = argv[i + 1];
511 arg->params_ = argv[i + 2];
512 ++feature_arg_index;
513 i += 3;
514 } else if (!strcmp(argv[i], "-loop")) {
515 CHECK_NUM_ARGS_LESS(2, ErrParse);
516 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
517 config->action_type_ = ACTION_SET;
518 } else {
519 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
520 }
521 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
522 feature->type_ = FEATURE_FRM;
523 } else {
524 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
525 }
526 arg->subtype_ = SUBTYPE_LOOP;
527 arg->params_ = argv[i + 1];
528 ++feature_arg_index;
529 i += 2;
530 } else if (!strcmp(argv[i], "-tile")) {
531 CHECK_NUM_ARGS_LESS(3, ErrParse);
532 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
533 config->action_type_ = ACTION_SET;
534 } else {
535 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
536 }
537 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
538 feature->type_ = FEATURE_TILE;
539 } else {
540 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
541 }
542 arg->filename_ = argv[i + 1];
543 arg->params_ = argv[i + 2];
544 ++feature_arg_index;
545 i += 3;
546 } else if (!strcmp(argv[i], "-o")) {
547 CHECK_NUM_ARGS_LESS(2, ErrParse);
548 config->output_ = argv[i + 1];
549 i += 2;
550 } else if (!strcmp(argv[i], "-info")) {
551 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
552 if (config->action_type_ != NIL_ACTION) {
553 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
554 } else {
555 config->action_type_ = ACTION_INFO;
556 feature->arg_count_ = 0;
557 config->input_ = argv[i + 1];
558 }
559 i += 2;
560 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
561 PrintHelp();
562 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800563 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530564 } else {
565 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
566 }
567 } else { // One of the feature types or input.
568 if (ACTION_IS_NIL) {
569 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
570 ErrParse);
571 }
572 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
573 if (FEATURETYPE_IS_NIL) {
574 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
575 FEATURE_XMP;
576 } else {
577 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
578 }
579 if (config->action_type_ == ACTION_SET) {
580 CHECK_NUM_ARGS_LESS(2, ErrParse);
581 arg->filename_ = argv[i + 1];
582 ++feature_arg_index;
583 i += 2;
584 } else {
585 ++i;
586 }
587 } else if ((!strcmp(argv[i], "frame") ||
588 !strcmp(argv[i], "tile")) &&
589 (config->action_type_ == ACTION_GET)) {
590 CHECK_NUM_ARGS_LESS(2, ErrParse);
591 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
592 FEATURE_TILE;
593 arg->params_ = argv[i + 1];
594 ++feature_arg_index;
595 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700596 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530597 if (config->input_ == NULL) {
598 config->input_ = argv[i];
599 } else {
600 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
601 argv[i], ErrParse);
602 }
603 ++i;
604 }
605 }
606 }
607 ErrParse:
608 return ok;
609}
610
James Zern04e84cf2011-11-04 15:20:08 -0700611// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530612static int ValidateConfig(WebPMuxConfig* config) {
613 int ok = 1;
614 Feature* const feature = &config->feature_;
615
616 // Action.
617 if (ACTION_IS_NIL) {
618 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
619 }
620
621 // Feature type.
622 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
623 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
624 }
625
626 // Input file.
627 if (config->input_ == NULL) {
628 if (config->action_type_ != ACTION_SET) {
629 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
630 } else if (feature->type_ != FEATURE_FRM &&
631 feature->type_ != FEATURE_TILE) {
632 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
633 }
634 }
635
636 // Output file.
637 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
638 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
639 }
640
641 ErrValidate2:
642 return ok;
643}
644
645// Create config object from command-line arguments.
646static int InitializeConfig(int argc, const char* argv[],
647 WebPMuxConfig** config) {
648 int num_feature_args = 0;
649 int ok = 1;
650
651 assert(config != NULL);
652 *config = NULL;
653
654 // Validate command-line arguments.
655 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
656 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
657 }
658
659 // Allocate memory.
660 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
661 if (*config == NULL) {
662 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
663 }
664 (*config)->feature_.arg_count_ = num_feature_args;
665 (*config)->feature_.args_ =
666 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
667 if ((*config)->feature_.args_ == NULL) {
668 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
669 }
670
671 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700672 if (!ParseCommandLine(argc, argv, *config) ||
673 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530674 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
675 }
676
677 Err1:
678 return ok;
679}
680
681#undef ACTION_IS_NIL
682#undef FEATURETYPE_IS_NIL
683#undef CHECK_NUM_ARGS_LESS
684#undef CHECK_NUM_ARGS_MORE
685
686//------------------------------------------------------------------------------
687// Processing.
688
James Zern04e84cf2011-11-04 15:20:08 -0700689static int GetFrameTile(const WebPMux* mux,
690 const WebPMuxConfig* config, int isFrame) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700691 WebPData bitstream;
Urvang Joshi6808e692012-07-06 11:35:36 +0530692 int x_offset = 0;
693 int y_offset = 0;
694 int duration = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530695 WebPMuxError err = WEBP_MUX_OK;
696 WebPMux* mux_single = NULL;
697 long num = 0;
698 int ok = 1;
699
700 num = strtol(config->feature_.args_[0].params_, NULL, 10);
701 if (num < 0) {
702 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
703 }
704
705 if (isFrame) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700706 err = WebPMuxGetFrame(mux, num, &bitstream,
Urvang Joshic398f592011-11-22 14:40:41 +0530707 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530708 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530709 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
710 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530711 }
712 } else {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700713 err = WebPMuxGetTile(mux, num, &bitstream, &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530714 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530715 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
716 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530717 }
718 }
719
720 mux_single = WebPMuxNew();
721 if (mux_single == NULL) {
722 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530723 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
724 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530725 }
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700726 err = WebPMuxSetImage(mux_single, &bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530727 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530728 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
729 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530730 }
731 ok = WriteWebP(mux_single, config->output_);
732
733 ErrGet:
734 WebPMuxDelete(mux_single);
735 return ok;
736}
737
738// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700739static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530740 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800741 WebPData webpdata;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530742 WebPData metadata, color_profile;
Urvang Joshi6808e692012-07-06 11:35:36 +0530743 int x_offset = 0;
744 int y_offset = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530745 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530746 int index = 0;
747 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700748 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530749
James Zern04e84cf2011-11-04 15:20:08 -0700750 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530751 case ACTION_GET:
James Zern061263a2012-05-11 16:00:57 -0700752 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530753 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700754 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530755 case FEATURE_FRM:
756 ok = GetFrameTile(mux, config, 1);
757 break;
758
759 case FEATURE_TILE:
760 ok = GetFrameTile(mux, config, 0);
761 break;
762
763 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800764 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530765 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530766 ERROR_GOTO2("ERROR (%s): Could not get color profile.\n",
767 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530768 }
James Zern0f7820e2012-01-24 14:08:27 -0800769 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530770 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530771 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800772 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530773 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530774 ERROR_GOTO2("ERROR (%s): Could not get XMP metadata.\n",
775 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530776 }
James Zern0f7820e2012-01-24 14:08:27 -0800777 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530778 break;
779
780 default:
781 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
782 break;
783 }
784 break;
785
786 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700787 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530788 case FEATURE_FRM:
789 mux = WebPMuxNew();
790 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530791 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
792 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530793 }
794 for (index = 0; index < feature->arg_count_; ++index) {
795 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
James Zern0f7820e2012-01-24 14:08:27 -0800796 const long num = strtol(feature->args_[index].params_, NULL, 10);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530797 if (num < 0) {
798 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530799 }
James Zern0f7820e2012-01-24 14:08:27 -0800800 err = WebPMuxSetLoopCount(mux, num);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530801 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530802 ERROR_GOTO2("ERROR (%s): Could not set loop count.\n",
803 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530804 }
805 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530806 int duration;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700807 ok = ReadFileToWebPData(feature->args_[index].filename_,
808 &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530809 if (!ok) goto Err2;
810 ok = ParseFrameArgs(feature->args_[index].params_,
811 &x_offset, &y_offset, &duration);
812 if (!ok) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700813 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530814 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
815 }
Urvang Joshib494ad52012-06-22 11:17:02 -0700816 err = WebPMuxPushFrame(mux, &webpdata, x_offset, y_offset,
James Zernb9ae4f02012-06-20 16:02:53 -0700817 duration, 1);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700818 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530819 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530820 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d.\n",
821 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530822 }
823 } else {
824 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
825 }
826 }
827 break;
828
829 case FEATURE_TILE:
830 mux = WebPMuxNew();
831 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530832 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
833 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530834 }
835 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700836 ok = ReadFileToWebPData(feature->args_[index].filename_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530837 if (!ok) goto Err2;
838 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
839 &y_offset);
840 if (!ok) {
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700841 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530842 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
843 }
Urvang Joshib494ad52012-06-22 11:17:02 -0700844 err = WebPMuxPushTile(mux, &webpdata, x_offset, y_offset, 1);
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700845 WebPDataClear(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530846 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530847 ERROR_GOTO3("ERROR (%s): Could not add a tile at index %d.\n",
848 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530849 }
850 }
851 break;
852
853 case FEATURE_ICCP:
James Zern061263a2012-05-11 16:00:57 -0700854 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530855 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530856 ok = ReadFileToWebPData(feature->args_[0].filename_, &color_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530857 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530858 err = WebPMuxSetColorProfile(mux, &color_profile, 1);
859 free((void*)color_profile.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530860 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530861 ERROR_GOTO2("ERROR (%s): Could not set color profile.\n",
862 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530863 }
864 break;
865
866 case FEATURE_XMP:
James Zern061263a2012-05-11 16:00:57 -0700867 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530868 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530869 ok = ReadFileToWebPData(feature->args_[0].filename_, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530870 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530871 err = WebPMuxSetMetadata(mux, &metadata, 1);
872 free((void*)metadata.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530873 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530874 ERROR_GOTO2("ERROR (%s): Could not set XMP metadata.\n",
875 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530876 }
877 break;
878
879 default:
880 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
881 break;
882 }
883 ok = WriteWebP(mux, config->output_);
884 break;
885
886 case ACTION_STRIP:
James Zern061263a2012-05-11 16:00:57 -0700887 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530888 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700889 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530890 case FEATURE_ICCP:
891 err = WebPMuxDeleteColorProfile(mux);
892 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530893 ERROR_GOTO2("ERROR (%s): Could not delete color profile.\n",
894 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530895 }
896 break;
897 case FEATURE_XMP:
898 err = WebPMuxDeleteMetadata(mux);
899 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530900 ERROR_GOTO2("ERROR (%s): Could not delete XMP metadata.\n",
901 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530902 }
903 break;
904 default:
905 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
906 break;
907 }
908 ok = WriteWebP(mux, config->output_);
909 break;
910
911 case ACTION_INFO:
James Zern061263a2012-05-11 16:00:57 -0700912 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530913 if (!ok) goto Err2;
914 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
915 break;
916
917 default:
918 assert(0); // Invalid action.
919 break;
920 }
921
922 Err2:
923 WebPMuxDelete(mux);
924 return ok;
925}
926
927//------------------------------------------------------------------------------
928// Main.
929
930int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530931 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -0800932 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530933 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -0800934 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530935 } else {
936 PrintHelp();
937 }
938 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800939 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940}
941
942//------------------------------------------------------------------------------