blob: 463cfc99b89a2f598be4865379e3c74834aeca50 [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 Joshia00a3da2012-10-31 17:49:15 -070020 webpmux -frgm fragment_1.webp +0+0 \
21 -frgm fragment_2.webp +960+0 \
22 -frgm fragment_3.webp +0+576 \
23 -frgm fragment_4.webp +960+576 \
24 -o out_fragment_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053025
skal48600082012-11-14 06:19:31 +010026 webpmux -frame anim_1.webp +100+10+10 \
27 -frame anim_2.webp +100+25+25+1 \
28 -frame anim_3.webp +100+50+50+1 \
29 -frame anim_4.webp +100 \
30 -loop 10 -bgcolor 128,255,255,255 \
Urvang Joshia4f32ca2011-09-30 11:07:01 +053031 -o out_animation_container.webp
32
33 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070034 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
35 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 Extract relevant data from WebP container file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070038 webpmux -get frgm n in.webp -o out_fragment.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053039 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053040 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshif903cba2012-10-31 16:30:41 -070041 webpmux -get exif in.webp -o image_metadata.exif
42 webpmux -get xmp in.webp -o image_metadata.xmp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053043
Urvang Joshia4f32ca2011-09-30 11:07:01 +053044 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070045 webpmux -strip icc in.webp -o out.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070046 webpmux -strip exif in.webp -o out.webp
47 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053048
49 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053050 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070051 webpmux [ -h | -help ]
Urvang Joshia4f32ca2011-09-30 11:07:01 +053052*/
53
54#include <assert.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070059#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053060
Urvang Joshia4f32ca2011-09-30 11:07:01 +053061//------------------------------------------------------------------------------
62// Config object to parse command-line arguments.
63
64typedef enum {
65 NIL_ACTION = 0,
66 ACTION_GET,
67 ACTION_SET,
68 ACTION_STRIP,
69 ACTION_INFO,
70 ACTION_HELP
71} ActionType;
72
73typedef enum {
74 NIL_SUBTYPE = 0,
Urvang Joshifa30c862012-11-01 15:34:46 -070075 SUBTYPE_ANMF,
76 SUBTYPE_LOOP,
77 SUBTYPE_BGCOLOR
Urvang Joshia4f32ca2011-09-30 11:07:01 +053078} FeatureSubType;
79
80typedef struct {
81 FeatureSubType subtype_;
82 const char* filename_;
83 const char* params_;
84} FeatureArg;
85
86typedef enum {
87 NIL_FEATURE = 0,
Urvang Joshif903cba2012-10-31 16:30:41 -070088 FEATURE_EXIF,
89 FEATURE_XMP,
Urvang Joshia4f32ca2011-09-30 11:07:01 +053090 FEATURE_ICCP,
Urvang Joshia00a3da2012-10-31 17:49:15 -070091 FEATURE_ANMF,
92 FEATURE_FRGM,
Urvang Joshif903cba2012-10-31 16:30:41 -070093 LAST_FEATURE
Urvang Joshia4f32ca2011-09-30 11:07:01 +053094} FeatureType;
95
Urvang Joshif903cba2012-10-31 16:30:41 -070096static const char* const kFourccList[LAST_FEATURE] = {
97 NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
98};
99
100static const char* const kDescriptions[LAST_FEATURE] = {
101 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Urvang Joshia00a3da2012-10-31 17:49:15 -0700102 "Animation frame", "Image fragment"
Urvang Joshif903cba2012-10-31 16:30:41 -0700103};
104
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530105typedef struct {
106 FeatureType type_;
107 FeatureArg* args_;
108 int arg_count_;
109} Feature;
110
111typedef struct {
112 ActionType action_type_;
113 const char* input_;
114 const char* output_;
115 Feature feature_;
116} WebPMuxConfig;
117
118//------------------------------------------------------------------------------
119// Helper functions.
120
James Zern04e84cf2011-11-04 15:20:08 -0700121static int CountOccurrences(const char* arglist[], int list_length,
122 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530123 int i;
124 int num_occurences = 0;
125
126 for (i = 0; i < list_length; ++i) {
127 if (!strcmp(arglist[i], arg)) {
128 ++num_occurences;
129 }
130 }
131 return num_occurences;
132}
133
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530134static const char* const kErrorMessages[] = {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530135 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
136 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530137};
138
139static const char* ErrorString(WebPMuxError err) {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530140 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530141 return kErrorMessages[-err];
142}
143
James Zerna0b27362012-01-27 17:39:47 -0800144#define RETURN_IF_ERROR(ERR_MSG) \
145 if (err != WEBP_MUX_OK) { \
146 fprintf(stderr, ERR_MSG); \
147 return err; \
148 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530149
James Zerna0b27362012-01-27 17:39:47 -0800150#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
151 if (err != WEBP_MUX_OK) { \
152 fprintf(stderr, ERR_MSG, FORMAT_STR); \
153 return err; \
154 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530155
Urvang Joshid0c79f02012-08-23 16:28:36 +0530156#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
157 if (err != WEBP_MUX_OK) { \
158 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
159 return err; \
160 }
161
James Zerna0b27362012-01-27 17:39:47 -0800162#define ERROR_GOTO1(ERR_MSG, LABEL) \
163 do { \
164 fprintf(stderr, ERR_MSG); \
165 ok = 0; \
166 goto LABEL; \
167 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530168
James Zerna0b27362012-01-27 17:39:47 -0800169#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
170 do { \
171 fprintf(stderr, ERR_MSG, FORMAT_STR); \
172 ok = 0; \
173 goto LABEL; \
174 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530175
James Zerna0b27362012-01-27 17:39:47 -0800176#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
177 do { \
178 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
179 ok = 0; \
180 goto LABEL; \
181 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530182
183static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530184 uint32_t flag;
185
186 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
187 RETURN_IF_ERROR("Failed to retrieve features\n");
188
189 if (flag == 0) {
190 fprintf(stderr, "No features present.\n");
191 return err;
192 }
193
194 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800195 printf("Features present:");
196 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700197 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zern974aaff2012-01-24 12:46:46 -0800198 if (flag & ICCP_FLAG) printf(" icc profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700199 if (flag & EXIF_FLAG) printf(" EXIF metadata");
200 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800201 if (flag & ALPHA_FLAG) printf(" transparency");
202 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530203
Urvang Joshia00a3da2012-10-31 17:49:15 -0700204 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530205 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700206 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700207 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800208 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530209
Urvang Joshid0c79f02012-08-23 16:28:36 +0530210 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700211 WebPMuxAnimParams params;
212 err = WebPMuxGetAnimationParams(mux, &params);
213 RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
214 printf("Background color : 0x%.8X Loop Count : %d\n",
215 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530216 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530217
Urvang Joshid0c79f02012-08-23 16:28:36 +0530218 err = WebPMuxNumChunks(mux, id, &nFrames);
219 RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
220
221 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530222 if (nFrames > 0) {
223 int i;
Urvang Joshid0c79f02012-08-23 16:28:36 +0530224 printf("No.: x_offset y_offset ");
Urvang Joshifa30c862012-11-01 15:34:46 -0700225 if (is_anim) printf("duration dispose ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530226 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530227 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530228 WebPMuxFrameInfo frame;
229 err = WebPMuxGetFrame(mux, i, &frame);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530230 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia0770722012-10-30 14:54:46 -0700231 printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
Urvang Joshifa30c862012-11-01 15:34:46 -0700232 if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
Urvang Joshia0770722012-10-30 14:54:46 -0700233 printf("%10zu\n", frame.bitstream.size);
234 WebPDataClear(&frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530235 }
236 }
237 }
238
239 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800240 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530241 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshif903cba2012-10-31 16:30:41 -0700242 RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
243 printf("Size of the ICC profile data: %zu\n", icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530244 }
245
Urvang Joshif903cba2012-10-31 16:30:41 -0700246 if (flag & EXIF_FLAG) {
247 WebPData exif;
248 err = WebPMuxGetChunk(mux, "EXIF", &exif);
249 RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
250 printf("Size of the EXIF metadata: %zu\n", exif.size);
251 }
252
253 if (flag & XMP_FLAG) {
254 WebPData xmp;
255 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
256 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
257 printf("Size of the XMP metadata: %zu\n", xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530258 }
259
Urvang Joshia00a3da2012-10-31 17:49:15 -0700260 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530261 WebPMuxFrameInfo image;
262 err = WebPMuxGetFrame(mux, 1, &image);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530263 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshia0770722012-10-30 14:54:46 -0700264 printf("Size of the image (with alpha): %zu\n", image.bitstream.size);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530265 }
266
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530267 return WEBP_MUX_OK;
268}
269
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700270static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800271 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
272 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
273 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700274 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700275 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
276 "\n");
277 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800278 printf(" webpmux -info INPUT\n");
279 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530280
James Zern974aaff2012-01-24 12:46:46 -0800281 printf("\n");
282 printf("GET_OPTIONS:\n");
283 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700284 printf(" icc Get ICC profile.\n");
285 printf(" exif Get EXIF metadata.\n");
286 printf(" xmp Get XMP metadata.\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700287 printf(" frgm n Get nth fragment.\n");
James Zern974aaff2012-01-24 12:46:46 -0800288 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530289
James Zern974aaff2012-01-24 12:46:46 -0800290 printf("\n");
291 printf("SET_OPTIONS:\n");
292 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700293 printf(" icc file.icc Set ICC profile.\n");
294 printf(" exif file.exif Set EXIF metadata.\n");
295 printf(" xmp file.xmp Set XMP metadata.\n");
296 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
297 printf(" 'file.exif' contains the EXIF metadata to be set\n");
298 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530299
James Zern974aaff2012-01-24 12:46:46 -0800300 printf("\n");
301 printf("STRIP_OPTIONS:\n");
302 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700303 printf(" icc Strip ICC profile.\n");
304 printf(" exif Strip EXIF metadata.\n");
305 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530306
James Zern974aaff2012-01-24 12:46:46 -0800307 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700308 printf("FRAGMENT_OPTIONS(i):\n");
309 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800310 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700311 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
312 printf(" 'xi','yi' specify the image offset for this fragment."
313 "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530314
James Zern974aaff2012-01-24 12:46:46 -0800315 printf("\n");
316 printf("FRAME_OPTIONS(i):\n");
317 printf(" Create animation.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700318 printf(" file_i +xi+yi+di+mi\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700319 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800320 printf(" 'xi','yi' specify the image offset for this frame.\n");
321 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700322 printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
323
324 printf("\n");
325 printf("LOOP_COUNT:\n");
326 printf(" Number of times to repeat the animation.\n");
327 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
328
329 printf("\n");
330 printf("BACKGROUND_COLOR:\n");
331 printf(" Background color of the canvas.\n");
332 printf(" A,R,G,B\n");
333 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
334 "specifying\n");
335 printf(" the Alpha, Red, Green and Blue component values "
336 "respectively\n");
337 printf(" [Default: 255,255,255,255].\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530338
Urvang Joshif903cba2012-10-31 16:30:41 -0700339 printf("\nINPUT & OUTPUT are in WebP format.\n");
340
Urvang Joshifa30c862012-11-01 15:34:46 -0700341 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
342 printf(" and is assumed to be\nvalid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530343}
344
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530345static int ReadFileToWebPData(const char* const filename,
346 WebPData* const webp_data) {
347 const uint8_t* data;
348 size_t size;
349 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700350 webp_data->bytes = data;
351 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530352 return 1;
353}
354
James Zern061263a2012-05-11 16:00:57 -0700355static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530356 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530357 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530358 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530359 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700360 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530361 if (*mux != NULL) return 1;
362 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530363 return 0;
364}
365
James Zern0f7820e2012-01-24 14:08:27 -0800366static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530367 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700368 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530369 if (!fout) {
370 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
371 return 0;
372 }
Urvang Joshia0770722012-10-30 14:54:46 -0700373 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530374 fprintf(stderr, "Error writing file %s!\n", filename);
375 } else {
Urvang Joshia0770722012-10-30 14:54:46 -0700376 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530377 ok = 1;
378 }
379 if (fout != stdout) fclose(fout);
380 return ok;
381}
382
383static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530384 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530385 WebPData webp_data;
386 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530387 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530388 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530389 return 0;
390 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530391 ok = WriteData(filename, &webp_data);
392 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530393 return ok;
394}
395
Urvang Joshiab3234a2012-08-23 15:18:51 +0530396static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100397 int dispose_method, dummy;
398 const int num_args = sscanf(args, "+%d+%d+%d+%d+%d",
399 &info->duration, &info->x_offset, &info->y_offset,
400 &dispose_method, &dummy);
401 switch (num_args) {
402 case 1:
403 info->x_offset = info->y_offset = 0; // fall through
404 case 3:
405 dispose_method = 0; // fall through
406 case 4:
407 break;
408 default:
409 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700410 }
411 // Note: The sanity of the following conversion is checked by
412 // WebPMuxSetAnimationParams().
413 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
414 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530415}
416
Urvang Joshia00a3da2012-10-31 17:49:15 -0700417static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700418 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530419}
420
Urvang Joshifa30c862012-11-01 15:34:46 -0700421static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
422 uint32_t a, r, g, b;
423 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
424 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
425 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
426 return 1;
427}
428
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530429//------------------------------------------------------------------------------
430// Clean-up.
431
432static void DeleteConfig(WebPMuxConfig* config) {
433 if (config != NULL) {
434 free(config->feature_.args_);
435 free(config);
436 }
437}
438
439//------------------------------------------------------------------------------
440// Parsing.
441
442// Basic syntactic checks on the command-line arguments.
443// Returns 1 on valid, 0 otherwise.
444// Also fills up num_feature_args to be number of feature arguments given.
445// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
446static int ValidateCommandLine(int argc, const char* argv[],
447 int* num_feature_args) {
448 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700449 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530450 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700451 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530452 int ok = 1;
453
454 assert(num_feature_args != NULL);
455 *num_feature_args = 0;
456
457 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700458 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530459 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
460 }
James Zern04e84cf2011-11-04 15:20:08 -0700461 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530462 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
463 }
James Zern04e84cf2011-11-04 15:20:08 -0700464 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530465 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
466 }
James Zern04e84cf2011-11-04 15:20:08 -0700467 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530468 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
469 }
James Zern04e84cf2011-11-04 15:20:08 -0700470 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530471 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
472 }
473
474 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700475 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700476 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700477 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700478 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530479
480 if (num_loop_args > 1) {
481 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
482 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700483 if (num_bgcolor_args > 1) {
484 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
485 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530486
Urvang Joshifa30c862012-11-01 15:34:46 -0700487 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
488 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
489 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530490 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700491 if (num_frame_args > 0 && num_frgm_args > 0) {
492 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
493 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530494 }
495
496 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700497 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700498 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530499 *num_feature_args = 1;
500 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700501 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530502 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700503 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530504 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700505 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530506 }
507 }
508
509 ErrValidate:
510 return ok;
511}
512
513#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
514
515#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
516
517#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
518 if (argc < i + (NUM)) { \
519 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
520 goto LABEL; \
521 }
522
523#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
524 if (argc != i + (NUM)) { \
525 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
526 goto LABEL; \
527 }
528
529// Parses command-line arguments to fill up config object. Also performs some
530// semantic checks.
531static int ParseCommandLine(int argc, const char* argv[],
532 WebPMuxConfig* config) {
533 int i = 0;
534 int feature_arg_index = 0;
535 int ok = 1;
536
537 while (i < argc) {
538 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700539 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530540 if (argv[i][0] == '-') { // One of the action types or output.
541 if (!strcmp(argv[i], "-set")) {
542 if (ACTION_IS_NIL) {
543 config->action_type_ = ACTION_SET;
544 } else {
545 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
546 }
547 ++i;
548 } else if (!strcmp(argv[i], "-get")) {
549 if (ACTION_IS_NIL) {
550 config->action_type_ = ACTION_GET;
551 } else {
552 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
553 }
554 ++i;
555 } else if (!strcmp(argv[i], "-strip")) {
556 if (ACTION_IS_NIL) {
557 config->action_type_ = ACTION_STRIP;
558 feature->arg_count_ = 0;
559 } else {
560 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
561 }
562 ++i;
563 } else if (!strcmp(argv[i], "-frame")) {
564 CHECK_NUM_ARGS_LESS(3, ErrParse);
565 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
566 config->action_type_ = ACTION_SET;
567 } else {
568 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
569 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700570 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
571 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530572 } else {
573 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
574 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700575 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530576 arg->filename_ = argv[i + 1];
577 arg->params_ = argv[i + 2];
578 ++feature_arg_index;
579 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700580 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530581 CHECK_NUM_ARGS_LESS(2, ErrParse);
582 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
583 config->action_type_ = ACTION_SET;
584 } else {
585 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
586 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700587 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
588 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530589 } else {
590 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
591 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700592 arg->subtype_ =
593 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530594 arg->params_ = argv[i + 1];
595 ++feature_arg_index;
596 i += 2;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700597 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530598 CHECK_NUM_ARGS_LESS(3, ErrParse);
599 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
600 config->action_type_ = ACTION_SET;
601 } else {
602 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
603 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700604 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
605 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530606 } else {
607 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
608 }
609 arg->filename_ = argv[i + 1];
610 arg->params_ = argv[i + 2];
611 ++feature_arg_index;
612 i += 3;
613 } else if (!strcmp(argv[i], "-o")) {
614 CHECK_NUM_ARGS_LESS(2, ErrParse);
615 config->output_ = argv[i + 1];
616 i += 2;
617 } else if (!strcmp(argv[i], "-info")) {
618 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
619 if (config->action_type_ != NIL_ACTION) {
620 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
621 } else {
622 config->action_type_ = ACTION_INFO;
623 feature->arg_count_ = 0;
624 config->input_ = argv[i + 1];
625 }
626 i += 2;
627 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
628 PrintHelp();
629 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800630 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530631 } else {
632 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
633 }
634 } else { // One of the feature types or input.
635 if (ACTION_IS_NIL) {
636 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
637 ErrParse);
638 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700639 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
640 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530641 if (FEATURETYPE_IS_NIL) {
642 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700643 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530644 } else {
645 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
646 }
647 if (config->action_type_ == ACTION_SET) {
648 CHECK_NUM_ARGS_LESS(2, ErrParse);
649 arg->filename_ = argv[i + 1];
650 ++feature_arg_index;
651 i += 2;
652 } else {
653 ++i;
654 }
655 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700656 !strcmp(argv[i], "frgm")) &&
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530657 (config->action_type_ == ACTION_GET)) {
658 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700659 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
660 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530661 arg->params_ = argv[i + 1];
662 ++feature_arg_index;
663 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700664 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530665 if (config->input_ == NULL) {
666 config->input_ = argv[i];
667 } else {
668 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
669 argv[i], ErrParse);
670 }
671 ++i;
672 }
673 }
674 }
675 ErrParse:
676 return ok;
677}
678
James Zern04e84cf2011-11-04 15:20:08 -0700679// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530680static int ValidateConfig(WebPMuxConfig* config) {
681 int ok = 1;
682 Feature* const feature = &config->feature_;
683
684 // Action.
685 if (ACTION_IS_NIL) {
686 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
687 }
688
689 // Feature type.
690 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
691 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
692 }
693
694 // Input file.
695 if (config->input_ == NULL) {
696 if (config->action_type_ != ACTION_SET) {
697 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700698 } else if (feature->type_ != FEATURE_ANMF &&
699 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530700 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
701 }
702 }
703
704 // Output file.
705 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
706 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
707 }
708
709 ErrValidate2:
710 return ok;
711}
712
713// Create config object from command-line arguments.
714static int InitializeConfig(int argc, const char* argv[],
715 WebPMuxConfig** config) {
716 int num_feature_args = 0;
717 int ok = 1;
718
719 assert(config != NULL);
720 *config = NULL;
721
722 // Validate command-line arguments.
723 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
724 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
725 }
726
727 // Allocate memory.
728 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
729 if (*config == NULL) {
730 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
731 }
732 (*config)->feature_.arg_count_ = num_feature_args;
733 (*config)->feature_.args_ =
734 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
735 if ((*config)->feature_.args_ == NULL) {
736 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
737 }
738
739 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700740 if (!ParseCommandLine(argc, argv, *config) ||
741 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530742 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
743 }
744
745 Err1:
746 return ok;
747}
748
749#undef ACTION_IS_NIL
750#undef FEATURETYPE_IS_NIL
751#undef CHECK_NUM_ARGS_LESS
752#undef CHECK_NUM_ARGS_MORE
753
754//------------------------------------------------------------------------------
755// Processing.
756
Urvang Joshia00a3da2012-10-31 17:49:15 -0700757static int GetFrameFragment(const WebPMux* mux,
758 const WebPMuxConfig* config, int isFrame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530759 WebPMuxError err = WEBP_MUX_OK;
760 WebPMux* mux_single = NULL;
761 long num = 0;
762 int ok = 1;
Urvang Joshi92f80592012-10-30 12:14:10 -0700763 const WebPChunkId id = isFrame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530764 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700765 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530766
767 num = strtol(config->feature_.args_[0].params_, NULL, 10);
768 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700769 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530770 }
771
Urvang Joshid0c79f02012-08-23 16:28:36 +0530772 err = WebPMuxGetFrame(mux, num, &info);
773 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
774 if (err != WEBP_MUX_OK) {
775 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
776 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530777 }
778
779 mux_single = WebPMuxNew();
780 if (mux_single == NULL) {
781 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530782 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
783 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530784 }
Urvang Joshia0770722012-10-30 14:54:46 -0700785 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530786 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530787 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
788 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530789 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530790
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530791 ok = WriteWebP(mux_single, config->output_);
792
793 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700794 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530795 WebPMuxDelete(mux_single);
796 return ok;
797}
798
799// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700800static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530801 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700802 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530803 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530804 int index = 0;
805 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700806 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530807
James Zern04e84cf2011-11-04 15:20:08 -0700808 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530809 case ACTION_GET:
James Zern061263a2012-05-11 16:00:57 -0700810 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530811 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700812 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700813 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800814 case FEATURE_FRGM:
815 ok = GetFrameFragment(mux, config,
816 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530817 break;
818
819 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700820 case FEATURE_EXIF:
821 case FEATURE_XMP:
822 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530823 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700824 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
825 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530826 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700827 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530828 break;
829
830 default:
831 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
832 break;
833 }
834 break;
835
836 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700837 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700838 case FEATURE_ANMF: {
839 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530840 mux = WebPMuxNew();
841 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530842 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
843 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530844 }
845 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700846 switch (feature->args_[index].subtype_) {
847 case SUBTYPE_BGCOLOR: {
848 uint32_t bgcolor;
849 ok = ParseBgcolorArgs(feature->args_[index].params_, &bgcolor);
850 if (!ok) {
851 ERROR_GOTO1("ERROR: Could not parse the background color \n",
852 Err2);
853 }
854 params.bgcolor = bgcolor;
855 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530856 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700857 case SUBTYPE_LOOP: {
858 const long loop_count =
859 strtol(feature->args_[index].params_, NULL, 10);
860 if (loop_count != (int)loop_count) {
861 // Note: This is only a 'necessary' condition for loop_count
862 // to be valid. The 'sufficient' conditioned in checked in
863 // WebPMuxSetAnimationParams() method called later.
864 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
865 "65535.\n", Err2);
866 }
867 params.loop_count = (int)loop_count;
868 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700870 case SUBTYPE_ANMF: {
871 WebPMuxFrameInfo frame;
872 frame.id = WEBP_CHUNK_ANMF;
873 ok = ReadFileToWebPData(feature->args_[index].filename_,
874 &frame.bitstream);
875 if (!ok) goto Err2;
876 ok = ParseFrameArgs(feature->args_[index].params_, &frame);
877 if (!ok) {
878 WebPDataClear(&frame.bitstream);
879 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
880 Err2);
881 }
882 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700883 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700884 if (err != WEBP_MUX_OK) {
885 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
886 "\n", ErrorString(err), index, Err2);
887 }
888 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530889 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700890 default: {
891 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
892 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530893 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530894 }
895 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700896 err = WebPMuxSetAnimationParams(mux, &params);
897 if (err != WEBP_MUX_OK) {
898 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
899 ErrorString(err), Err2);
900 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530901 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700902 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530903
Urvang Joshia00a3da2012-10-31 17:49:15 -0700904 case FEATURE_FRGM:
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530905 mux = WebPMuxNew();
906 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530907 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
908 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530909 }
910 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700911 WebPMuxFrameInfo frgm;
912 frgm.id = WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530913 ok = ReadFileToWebPData(feature->args_[index].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700914 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530915 if (!ok) goto Err2;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700916 ok = ParseFragmentArgs(feature->args_[index].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530917 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700918 WebPDataClear(&frgm.bitstream);
919 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
920 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530921 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700922 err = WebPMuxPushFrame(mux, &frgm, 1);
923 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530924 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700925 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530926 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530927 }
928 }
929 break;
930
931 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700932 case FEATURE_EXIF:
933 case FEATURE_XMP:
James Zern061263a2012-05-11 16:00:57 -0700934 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530935 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700936 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530937 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700938 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
939 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700941 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
942 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530943 }
944 break;
945
946 default:
947 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
948 break;
949 }
950 ok = WriteWebP(mux, config->output_);
951 break;
952
953 case ACTION_STRIP:
James Zern061263a2012-05-11 16:00:57 -0700954 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530955 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700956 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
957 feature->type_ == FEATURE_XMP) {
958 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
959 if (err != WEBP_MUX_OK) {
960 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
961 ErrorString(err), kDescriptions[feature->type_], Err2);
962 }
963 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530964 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
965 break;
966 }
967 ok = WriteWebP(mux, config->output_);
968 break;
969
970 case ACTION_INFO:
James Zern061263a2012-05-11 16:00:57 -0700971 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530972 if (!ok) goto Err2;
973 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
974 break;
975
976 default:
977 assert(0); // Invalid action.
978 break;
979 }
980
981 Err2:
982 WebPMuxDelete(mux);
983 return ok;
984}
985
986//------------------------------------------------------------------------------
987// Main.
988
989int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530990 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -0800991 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530992 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -0800993 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530994 } else {
995 PrintHelp();
996 }
997 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800998 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530999}
1000
1001//------------------------------------------------------------------------------