blob: a0f08ec1e8c8af5885621e7152b183b2cc331533 [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 Joshia5042a32013-02-26 14:22:06 -080052 webpmux -version
Urvang Joshia4f32ca2011-09-30 11:07:01 +053053*/
54
55#include <assert.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070060#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053061
Urvang Joshia4f32ca2011-09-30 11:07:01 +053062//------------------------------------------------------------------------------
63// Config object to parse command-line arguments.
64
65typedef enum {
66 NIL_ACTION = 0,
67 ACTION_GET,
68 ACTION_SET,
69 ACTION_STRIP,
70 ACTION_INFO,
71 ACTION_HELP
72} ActionType;
73
74typedef enum {
75 NIL_SUBTYPE = 0,
Urvang Joshifa30c862012-11-01 15:34:46 -070076 SUBTYPE_ANMF,
77 SUBTYPE_LOOP,
78 SUBTYPE_BGCOLOR
Urvang Joshia4f32ca2011-09-30 11:07:01 +053079} FeatureSubType;
80
81typedef struct {
82 FeatureSubType subtype_;
83 const char* filename_;
84 const char* params_;
85} FeatureArg;
86
87typedef enum {
88 NIL_FEATURE = 0,
Urvang Joshif903cba2012-10-31 16:30:41 -070089 FEATURE_EXIF,
90 FEATURE_XMP,
Urvang Joshia4f32ca2011-09-30 11:07:01 +053091 FEATURE_ICCP,
Urvang Joshia00a3da2012-10-31 17:49:15 -070092 FEATURE_ANMF,
93 FEATURE_FRGM,
Urvang Joshif903cba2012-10-31 16:30:41 -070094 LAST_FEATURE
Urvang Joshia4f32ca2011-09-30 11:07:01 +053095} FeatureType;
96
Urvang Joshif903cba2012-10-31 16:30:41 -070097static const char* const kFourccList[LAST_FEATURE] = {
98 NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
99};
100
101static const char* const kDescriptions[LAST_FEATURE] = {
102 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Urvang Joshia00a3da2012-10-31 17:49:15 -0700103 "Animation frame", "Image fragment"
Urvang Joshif903cba2012-10-31 16:30:41 -0700104};
105
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530106typedef struct {
107 FeatureType type_;
108 FeatureArg* args_;
109 int arg_count_;
110} Feature;
111
112typedef struct {
113 ActionType action_type_;
114 const char* input_;
115 const char* output_;
116 Feature feature_;
117} WebPMuxConfig;
118
119//------------------------------------------------------------------------------
120// Helper functions.
121
James Zern04e84cf2011-11-04 15:20:08 -0700122static int CountOccurrences(const char* arglist[], int list_length,
123 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530124 int i;
125 int num_occurences = 0;
126
127 for (i = 0; i < list_length; ++i) {
128 if (!strcmp(arglist[i], arg)) {
129 ++num_occurences;
130 }
131 }
132 return num_occurences;
133}
134
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530135static const char* const kErrorMessages[] = {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530136 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
137 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530138};
139
140static const char* ErrorString(WebPMuxError err) {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530141 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530142 return kErrorMessages[-err];
143}
144
James Zerna0b27362012-01-27 17:39:47 -0800145#define RETURN_IF_ERROR(ERR_MSG) \
146 if (err != WEBP_MUX_OK) { \
147 fprintf(stderr, ERR_MSG); \
148 return err; \
149 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530150
James Zerna0b27362012-01-27 17:39:47 -0800151#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
152 if (err != WEBP_MUX_OK) { \
153 fprintf(stderr, ERR_MSG, FORMAT_STR); \
154 return err; \
155 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530156
Urvang Joshid0c79f02012-08-23 16:28:36 +0530157#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
158 if (err != WEBP_MUX_OK) { \
159 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
160 return err; \
161 }
162
James Zerna0b27362012-01-27 17:39:47 -0800163#define ERROR_GOTO1(ERR_MSG, LABEL) \
164 do { \
165 fprintf(stderr, ERR_MSG); \
166 ok = 0; \
167 goto LABEL; \
168 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530169
James Zerna0b27362012-01-27 17:39:47 -0800170#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
171 do { \
172 fprintf(stderr, ERR_MSG, FORMAT_STR); \
173 ok = 0; \
174 goto LABEL; \
175 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530176
James Zerna0b27362012-01-27 17:39:47 -0800177#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
178 do { \
179 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
180 ok = 0; \
181 goto LABEL; \
182 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530183
184static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530185 uint32_t flag;
186
187 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
188 RETURN_IF_ERROR("Failed to retrieve features\n");
189
190 if (flag == 0) {
191 fprintf(stderr, "No features present.\n");
192 return err;
193 }
194
195 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800196 printf("Features present:");
197 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700198 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zern974aaff2012-01-24 12:46:46 -0800199 if (flag & ICCP_FLAG) printf(" icc profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700200 if (flag & EXIF_FLAG) printf(" EXIF metadata");
201 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800202 if (flag & ALPHA_FLAG) printf(" transparency");
203 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530204
Urvang Joshia00a3da2012-10-31 17:49:15 -0700205 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530206 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700207 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700208 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800209 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530210
Urvang Joshid0c79f02012-08-23 16:28:36 +0530211 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700212 WebPMuxAnimParams params;
213 err = WebPMuxGetAnimationParams(mux, &params);
214 RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
215 printf("Background color : 0x%.8X Loop Count : %d\n",
216 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530217 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530218
Urvang Joshid0c79f02012-08-23 16:28:36 +0530219 err = WebPMuxNumChunks(mux, id, &nFrames);
220 RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
221
222 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530223 if (nFrames > 0) {
224 int i;
Urvang Joshid0c79f02012-08-23 16:28:36 +0530225 printf("No.: x_offset y_offset ");
Urvang Joshifa30c862012-11-01 15:34:46 -0700226 if (is_anim) printf("duration dispose ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530227 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530228 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530229 WebPMuxFrameInfo frame;
230 err = WebPMuxGetFrame(mux, i, &frame);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530231 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia0770722012-10-30 14:54:46 -0700232 printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
Urvang Joshifa30c862012-11-01 15:34:46 -0700233 if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
Urvang Joshia0770722012-10-30 14:54:46 -0700234 printf("%10zu\n", frame.bitstream.size);
235 WebPDataClear(&frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530236 }
237 }
238 }
239
240 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800241 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530242 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshif903cba2012-10-31 16:30:41 -0700243 RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
244 printf("Size of the ICC profile data: %zu\n", icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530245 }
246
Urvang Joshif903cba2012-10-31 16:30:41 -0700247 if (flag & EXIF_FLAG) {
248 WebPData exif;
249 err = WebPMuxGetChunk(mux, "EXIF", &exif);
250 RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
251 printf("Size of the EXIF metadata: %zu\n", exif.size);
252 }
253
254 if (flag & XMP_FLAG) {
255 WebPData xmp;
256 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
257 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
258 printf("Size of the XMP metadata: %zu\n", xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530259 }
260
Urvang Joshia00a3da2012-10-31 17:49:15 -0700261 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530262 WebPMuxFrameInfo image;
263 err = WebPMuxGetFrame(mux, 1, &image);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530264 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshia0770722012-10-30 14:54:46 -0700265 printf("Size of the image (with alpha): %zu\n", image.bitstream.size);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530266 }
267
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530268 return WEBP_MUX_OK;
269}
270
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700271static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800272 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
273 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
274 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700275 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700276 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
277 "\n");
278 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800279 printf(" webpmux -info INPUT\n");
280 printf(" webpmux [-h|-help]\n");
Urvang Joshia5042a32013-02-26 14:22:06 -0800281 printf(" webpmux -version\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530282
James Zern974aaff2012-01-24 12:46:46 -0800283 printf("\n");
284 printf("GET_OPTIONS:\n");
285 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700286 printf(" icc Get ICC profile.\n");
287 printf(" exif Get EXIF metadata.\n");
288 printf(" xmp Get XMP metadata.\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700289 printf(" frgm n Get nth fragment.\n");
James Zern974aaff2012-01-24 12:46:46 -0800290 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530291
James Zern974aaff2012-01-24 12:46:46 -0800292 printf("\n");
293 printf("SET_OPTIONS:\n");
294 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700295 printf(" icc file.icc Set ICC profile.\n");
296 printf(" exif file.exif Set EXIF metadata.\n");
297 printf(" xmp file.xmp Set XMP metadata.\n");
298 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
299 printf(" 'file.exif' contains the EXIF metadata to be set\n");
300 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530301
James Zern974aaff2012-01-24 12:46:46 -0800302 printf("\n");
303 printf("STRIP_OPTIONS:\n");
304 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700305 printf(" icc Strip ICC profile.\n");
306 printf(" exif Strip EXIF metadata.\n");
307 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530308
James Zern974aaff2012-01-24 12:46:46 -0800309 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700310 printf("FRAGMENT_OPTIONS(i):\n");
311 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800312 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700313 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
314 printf(" 'xi','yi' specify the image offset for this fragment."
315 "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530316
James Zern974aaff2012-01-24 12:46:46 -0800317 printf("\n");
318 printf("FRAME_OPTIONS(i):\n");
319 printf(" Create animation.\n");
James Zern8fab1612013-03-07 19:15:37 -0800320 printf(" file_i +di+xi+yi+mi\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700321 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800322 printf(" 'di' is the pause duration before next frame.\n");
James Zern8fab1612013-03-07 19:15:37 -0800323 printf(" 'xi','yi' specify the image offset for this frame.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700324 printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
325
326 printf("\n");
327 printf("LOOP_COUNT:\n");
328 printf(" Number of times to repeat the animation.\n");
329 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
330
331 printf("\n");
332 printf("BACKGROUND_COLOR:\n");
333 printf(" Background color of the canvas.\n");
334 printf(" A,R,G,B\n");
335 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
336 "specifying\n");
337 printf(" the Alpha, Red, Green and Blue component values "
338 "respectively\n");
339 printf(" [Default: 255,255,255,255].\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530340
Urvang Joshif903cba2012-10-31 16:30:41 -0700341 printf("\nINPUT & OUTPUT are in WebP format.\n");
342
Urvang Joshifa30c862012-11-01 15:34:46 -0700343 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
344 printf(" and is assumed to be\nvalid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530345}
346
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530347static int ReadFileToWebPData(const char* const filename,
348 WebPData* const webp_data) {
349 const uint8_t* data;
350 size_t size;
351 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700352 webp_data->bytes = data;
353 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530354 return 1;
355}
356
James Zern061263a2012-05-11 16:00:57 -0700357static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530358 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530359 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530360 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530361 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700362 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530363 if (*mux != NULL) return 1;
364 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530365 return 0;
366}
367
James Zern0f7820e2012-01-24 14:08:27 -0800368static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530369 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700370 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530371 if (!fout) {
372 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
373 return 0;
374 }
Urvang Joshia0770722012-10-30 14:54:46 -0700375 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530376 fprintf(stderr, "Error writing file %s!\n", filename);
377 } else {
Urvang Joshia0770722012-10-30 14:54:46 -0700378 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530379 ok = 1;
380 }
381 if (fout != stdout) fclose(fout);
382 return ok;
383}
384
385static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530386 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530387 WebPData webp_data;
388 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530389 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530390 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530391 return 0;
392 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530393 ok = WriteData(filename, &webp_data);
394 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530395 return ok;
396}
397
Urvang Joshiab3234a2012-08-23 15:18:51 +0530398static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100399 int dispose_method, dummy;
400 const int num_args = sscanf(args, "+%d+%d+%d+%d+%d",
401 &info->duration, &info->x_offset, &info->y_offset,
402 &dispose_method, &dummy);
403 switch (num_args) {
404 case 1:
405 info->x_offset = info->y_offset = 0; // fall through
406 case 3:
407 dispose_method = 0; // fall through
408 case 4:
409 break;
410 default:
411 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700412 }
413 // Note: The sanity of the following conversion is checked by
414 // WebPMuxSetAnimationParams().
415 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
416 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530417}
418
Urvang Joshia00a3da2012-10-31 17:49:15 -0700419static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700420 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530421}
422
Urvang Joshifa30c862012-11-01 15:34:46 -0700423static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
424 uint32_t a, r, g, b;
425 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
426 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
427 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
428 return 1;
429}
430
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530431//------------------------------------------------------------------------------
432// Clean-up.
433
434static void DeleteConfig(WebPMuxConfig* config) {
435 if (config != NULL) {
436 free(config->feature_.args_);
437 free(config);
438 }
439}
440
441//------------------------------------------------------------------------------
442// Parsing.
443
444// Basic syntactic checks on the command-line arguments.
445// Returns 1 on valid, 0 otherwise.
446// Also fills up num_feature_args to be number of feature arguments given.
447// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
448static int ValidateCommandLine(int argc, const char* argv[],
449 int* num_feature_args) {
450 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700451 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530452 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700453 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530454 int ok = 1;
455
456 assert(num_feature_args != NULL);
457 *num_feature_args = 0;
458
459 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700460 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530461 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
462 }
James Zern04e84cf2011-11-04 15:20:08 -0700463 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530464 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
465 }
James Zern04e84cf2011-11-04 15:20:08 -0700466 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530467 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
468 }
James Zern04e84cf2011-11-04 15:20:08 -0700469 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530470 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
471 }
James Zern04e84cf2011-11-04 15:20:08 -0700472 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530473 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
474 }
475
476 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700477 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700478 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700479 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700480 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530481
482 if (num_loop_args > 1) {
483 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
484 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700485 if (num_bgcolor_args > 1) {
486 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
487 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530488
Urvang Joshifa30c862012-11-01 15:34:46 -0700489 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
490 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
491 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530492 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700493 if (num_frame_args > 0 && num_frgm_args > 0) {
494 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
495 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530496 }
497
498 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700499 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700500 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530501 *num_feature_args = 1;
502 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700503 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530504 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700505 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530506 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700507 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530508 }
509 }
510
511 ErrValidate:
512 return ok;
513}
514
515#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
516
517#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
518
519#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
520 if (argc < i + (NUM)) { \
521 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
522 goto LABEL; \
523 }
524
525#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
526 if (argc != i + (NUM)) { \
527 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
528 goto LABEL; \
529 }
530
531// Parses command-line arguments to fill up config object. Also performs some
532// semantic checks.
533static int ParseCommandLine(int argc, const char* argv[],
534 WebPMuxConfig* config) {
535 int i = 0;
536 int feature_arg_index = 0;
537 int ok = 1;
538
539 while (i < argc) {
540 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700541 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530542 if (argv[i][0] == '-') { // One of the action types or output.
543 if (!strcmp(argv[i], "-set")) {
544 if (ACTION_IS_NIL) {
545 config->action_type_ = ACTION_SET;
546 } else {
547 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
548 }
549 ++i;
550 } else if (!strcmp(argv[i], "-get")) {
551 if (ACTION_IS_NIL) {
552 config->action_type_ = ACTION_GET;
553 } else {
554 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
555 }
556 ++i;
557 } else if (!strcmp(argv[i], "-strip")) {
558 if (ACTION_IS_NIL) {
559 config->action_type_ = ACTION_STRIP;
560 feature->arg_count_ = 0;
561 } else {
562 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
563 }
564 ++i;
565 } else if (!strcmp(argv[i], "-frame")) {
566 CHECK_NUM_ARGS_LESS(3, ErrParse);
567 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
568 config->action_type_ = ACTION_SET;
569 } else {
570 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
571 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700572 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
573 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530574 } else {
575 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
576 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700577 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530578 arg->filename_ = argv[i + 1];
579 arg->params_ = argv[i + 2];
580 ++feature_arg_index;
581 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700582 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530583 CHECK_NUM_ARGS_LESS(2, ErrParse);
584 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
585 config->action_type_ = ACTION_SET;
586 } else {
587 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
588 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700589 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
590 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530591 } else {
592 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
593 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700594 arg->subtype_ =
595 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530596 arg->params_ = argv[i + 1];
597 ++feature_arg_index;
598 i += 2;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700599 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530600 CHECK_NUM_ARGS_LESS(3, ErrParse);
601 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
602 config->action_type_ = ACTION_SET;
603 } else {
604 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
605 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700606 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
607 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530608 } else {
609 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
610 }
611 arg->filename_ = argv[i + 1];
612 arg->params_ = argv[i + 2];
613 ++feature_arg_index;
614 i += 3;
615 } else if (!strcmp(argv[i], "-o")) {
616 CHECK_NUM_ARGS_LESS(2, ErrParse);
617 config->output_ = argv[i + 1];
618 i += 2;
619 } else if (!strcmp(argv[i], "-info")) {
620 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
621 if (config->action_type_ != NIL_ACTION) {
622 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
623 } else {
624 config->action_type_ = ACTION_INFO;
625 feature->arg_count_ = 0;
626 config->input_ = argv[i + 1];
627 }
628 i += 2;
629 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
630 PrintHelp();
631 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800632 exit(0);
Urvang Joshia5042a32013-02-26 14:22:06 -0800633 } else if (!strcmp(argv[i], "-version")) {
634 const int version = WebPGetMuxVersion();
635 printf("%d.%d.%d\n",
636 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
637 DeleteConfig(config);
638 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530639 } else {
640 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
641 }
642 } else { // One of the feature types or input.
643 if (ACTION_IS_NIL) {
644 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
645 ErrParse);
646 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700647 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
648 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530649 if (FEATURETYPE_IS_NIL) {
650 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700651 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530652 } else {
653 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
654 }
655 if (config->action_type_ == ACTION_SET) {
656 CHECK_NUM_ARGS_LESS(2, ErrParse);
657 arg->filename_ = argv[i + 1];
658 ++feature_arg_index;
659 i += 2;
660 } else {
661 ++i;
662 }
663 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700664 !strcmp(argv[i], "frgm")) &&
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530665 (config->action_type_ == ACTION_GET)) {
666 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700667 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
668 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530669 arg->params_ = argv[i + 1];
670 ++feature_arg_index;
671 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700672 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530673 if (config->input_ == NULL) {
674 config->input_ = argv[i];
675 } else {
676 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
677 argv[i], ErrParse);
678 }
679 ++i;
680 }
681 }
682 }
683 ErrParse:
684 return ok;
685}
686
James Zern04e84cf2011-11-04 15:20:08 -0700687// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530688static int ValidateConfig(WebPMuxConfig* config) {
689 int ok = 1;
690 Feature* const feature = &config->feature_;
691
692 // Action.
693 if (ACTION_IS_NIL) {
694 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
695 }
696
697 // Feature type.
698 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
699 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
700 }
701
702 // Input file.
703 if (config->input_ == NULL) {
704 if (config->action_type_ != ACTION_SET) {
705 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700706 } else if (feature->type_ != FEATURE_ANMF &&
707 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530708 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
709 }
710 }
711
712 // Output file.
713 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
714 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
715 }
716
717 ErrValidate2:
718 return ok;
719}
720
721// Create config object from command-line arguments.
722static int InitializeConfig(int argc, const char* argv[],
723 WebPMuxConfig** config) {
724 int num_feature_args = 0;
725 int ok = 1;
726
727 assert(config != NULL);
728 *config = NULL;
729
730 // Validate command-line arguments.
731 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
732 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
733 }
734
735 // Allocate memory.
736 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
737 if (*config == NULL) {
738 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
739 }
740 (*config)->feature_.arg_count_ = num_feature_args;
741 (*config)->feature_.args_ =
742 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
743 if ((*config)->feature_.args_ == NULL) {
744 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
745 }
746
747 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700748 if (!ParseCommandLine(argc, argv, *config) ||
749 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530750 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
751 }
752
753 Err1:
754 return ok;
755}
756
757#undef ACTION_IS_NIL
758#undef FEATURETYPE_IS_NIL
759#undef CHECK_NUM_ARGS_LESS
760#undef CHECK_NUM_ARGS_MORE
761
762//------------------------------------------------------------------------------
763// Processing.
764
Urvang Joshia00a3da2012-10-31 17:49:15 -0700765static int GetFrameFragment(const WebPMux* mux,
766 const WebPMuxConfig* config, int isFrame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530767 WebPMuxError err = WEBP_MUX_OK;
768 WebPMux* mux_single = NULL;
769 long num = 0;
770 int ok = 1;
Urvang Joshi92f80592012-10-30 12:14:10 -0700771 const WebPChunkId id = isFrame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530772 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700773 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530774
775 num = strtol(config->feature_.args_[0].params_, NULL, 10);
776 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700777 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530778 }
779
Urvang Joshid0c79f02012-08-23 16:28:36 +0530780 err = WebPMuxGetFrame(mux, num, &info);
781 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
782 if (err != WEBP_MUX_OK) {
783 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
784 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530785 }
786
787 mux_single = WebPMuxNew();
788 if (mux_single == NULL) {
789 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530790 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
791 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530792 }
Urvang Joshia0770722012-10-30 14:54:46 -0700793 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530794 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530795 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
796 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530797 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530798
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530799 ok = WriteWebP(mux_single, config->output_);
800
801 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700802 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530803 WebPMuxDelete(mux_single);
804 return ok;
805}
806
807// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700808static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530809 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700810 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530811 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530812 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700813 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530814
James Zern04e84cf2011-11-04 15:20:08 -0700815 switch (config->action_type_) {
skal0d19fbf2013-01-21 17:20:14 +0100816 case ACTION_GET: {
James Zern061263a2012-05-11 16:00:57 -0700817 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530818 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700819 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700820 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800821 case FEATURE_FRGM:
822 ok = GetFrameFragment(mux, config,
823 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530824 break;
825
826 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700827 case FEATURE_EXIF:
828 case FEATURE_XMP:
829 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530830 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700831 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
832 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530833 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700834 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530835 break;
836
837 default:
838 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
839 break;
840 }
841 break;
skal0d19fbf2013-01-21 17:20:14 +0100842 }
843 case ACTION_SET: {
James Zern04e84cf2011-11-04 15:20:08 -0700844 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700845 case FEATURE_ANMF: {
skal0d19fbf2013-01-21 17:20:14 +0100846 int i;
Urvang Joshifa30c862012-11-01 15:34:46 -0700847 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530848 mux = WebPMuxNew();
849 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530850 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
851 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530852 }
skal0d19fbf2013-01-21 17:20:14 +0100853 for (i = 0; i < feature->arg_count_; ++i) {
854 switch (feature->args_[i].subtype_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700855 case SUBTYPE_BGCOLOR: {
856 uint32_t bgcolor;
skal0d19fbf2013-01-21 17:20:14 +0100857 ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
Urvang Joshifa30c862012-11-01 15:34:46 -0700858 if (!ok) {
859 ERROR_GOTO1("ERROR: Could not parse the background color \n",
860 Err2);
861 }
862 params.bgcolor = bgcolor;
863 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530864 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700865 case SUBTYPE_LOOP: {
866 const long loop_count =
skal0d19fbf2013-01-21 17:20:14 +0100867 strtol(feature->args_[i].params_, NULL, 10);
Urvang Joshifa30c862012-11-01 15:34:46 -0700868 if (loop_count != (int)loop_count) {
869 // Note: This is only a 'necessary' condition for loop_count
870 // to be valid. The 'sufficient' conditioned in checked in
871 // WebPMuxSetAnimationParams() method called later.
872 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
873 "65535.\n", Err2);
874 }
875 params.loop_count = (int)loop_count;
876 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530877 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700878 case SUBTYPE_ANMF: {
879 WebPMuxFrameInfo frame;
880 frame.id = WEBP_CHUNK_ANMF;
skal0d19fbf2013-01-21 17:20:14 +0100881 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshifa30c862012-11-01 15:34:46 -0700882 &frame.bitstream);
883 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100884 ok = ParseFrameArgs(feature->args_[i].params_, &frame);
Urvang Joshifa30c862012-11-01 15:34:46 -0700885 if (!ok) {
886 WebPDataClear(&frame.bitstream);
887 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
888 Err2);
889 }
890 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700891 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700892 if (err != WEBP_MUX_OK) {
893 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
skal0d19fbf2013-01-21 17:20:14 +0100894 "\n", ErrorString(err), i, Err2);
Urvang Joshifa30c862012-11-01 15:34:46 -0700895 }
896 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530897 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700898 default: {
899 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
900 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530901 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530902 }
903 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700904 err = WebPMuxSetAnimationParams(mux, &params);
905 if (err != WEBP_MUX_OK) {
906 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
907 ErrorString(err), Err2);
908 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530909 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700910 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530911
skal0d19fbf2013-01-21 17:20:14 +0100912 case FEATURE_FRGM: {
913 int i;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530914 mux = WebPMuxNew();
915 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530916 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
917 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530918 }
skal0d19fbf2013-01-21 17:20:14 +0100919 for (i = 0; i < feature->arg_count_; ++i) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700920 WebPMuxFrameInfo frgm;
921 frgm.id = WEBP_CHUNK_FRGM;
skal0d19fbf2013-01-21 17:20:14 +0100922 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700923 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530924 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100925 ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530926 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700927 WebPDataClear(&frgm.bitstream);
928 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
929 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530930 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700931 err = WebPMuxPushFrame(mux, &frgm, 1);
932 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530933 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700934 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
skal0d19fbf2013-01-21 17:20:14 +0100935 ErrorString(err), i, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530936 }
937 }
938 break;
skal0d19fbf2013-01-21 17:20:14 +0100939 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940
941 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700942 case FEATURE_EXIF:
skal0d19fbf2013-01-21 17:20:14 +0100943 case FEATURE_XMP: {
James Zern061263a2012-05-11 16:00:57 -0700944 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530945 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700946 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530947 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700948 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
949 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530950 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700951 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
952 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530953 }
954 break;
skal0d19fbf2013-01-21 17:20:14 +0100955 }
956 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530957 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
958 break;
skal0d19fbf2013-01-21 17:20:14 +0100959 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530960 }
961 ok = WriteWebP(mux, config->output_);
962 break;
skal0d19fbf2013-01-21 17:20:14 +0100963 }
964 case ACTION_STRIP: {
James Zern061263a2012-05-11 16:00:57 -0700965 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530966 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700967 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
968 feature->type_ == FEATURE_XMP) {
969 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
970 if (err != WEBP_MUX_OK) {
971 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
972 ErrorString(err), kDescriptions[feature->type_], Err2);
973 }
974 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530975 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
976 break;
977 }
978 ok = WriteWebP(mux, config->output_);
979 break;
skal0d19fbf2013-01-21 17:20:14 +0100980 }
981 case ACTION_INFO: {
James Zern061263a2012-05-11 16:00:57 -0700982 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530983 if (!ok) goto Err2;
984 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
985 break;
skal0d19fbf2013-01-21 17:20:14 +0100986 }
987 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530988 assert(0); // Invalid action.
989 break;
skal0d19fbf2013-01-21 17:20:14 +0100990 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530991 }
992
993 Err2:
994 WebPMuxDelete(mux);
995 return ok;
996}
997
998//------------------------------------------------------------------------------
999// Main.
1000
1001int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301002 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001003 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301004 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001005 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301006 } else {
1007 PrintHelp();
1008 }
1009 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001010 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301011}
1012
1013//------------------------------------------------------------------------------