blob: a9709677b929b8a4dc16d5c2c021cbb91714d79d [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//
James Zernd6406142013-06-06 23:05:58 -07003// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05308// -----------------------------------------------------------------------------
9//
10// Simple command-line to create a WebP container file and to extract or strip
11// relevant data from the container file.
12//
Urvang Joshia4f32ca2011-09-30 11:07:01 +053013// Authors: Vikas (vikaas.arora@gmail.com),
14// Urvang (urvang@google.com)
15
16/* Usage examples:
17
18 Create container WebP file:
skal48600082012-11-14 06:19:31 +010019 webpmux -frame anim_1.webp +100+10+10 \
20 -frame anim_2.webp +100+25+25+1 \
21 -frame anim_3.webp +100+50+50+1 \
22 -frame anim_4.webp +100 \
23 -loop 10 -bgcolor 128,255,255,255 \
Urvang Joshia4f32ca2011-09-30 11:07:01 +053024 -o out_animation_container.webp
25
26 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070027 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
28 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053029
Urvang Joshia4f32ca2011-09-30 11:07:01 +053030 Extract relevant data from WebP container file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070031 webpmux -get frgm n in.webp -o out_fragment.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053032 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053033 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshif903cba2012-10-31 16:30:41 -070034 webpmux -get exif in.webp -o image_metadata.exif
35 webpmux -get xmp in.webp -o image_metadata.xmp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070038 webpmux -strip icc in.webp -o out.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070039 webpmux -strip exif in.webp -o out.webp
40 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053041
42 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053043 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070044 webpmux [ -h | -help ]
Urvang Joshia5042a32013-02-26 14:22:06 -080045 webpmux -version
Urvang Joshia4f32ca2011-09-30 11:07:01 +053046*/
47
Urvang Joshi5dbd4032013-03-15 14:46:12 -070048#ifdef HAVE_CONFIG_H
49#include "config.h"
50#endif
51
Urvang Joshia4f32ca2011-09-30 11:07:01 +053052#include <assert.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070057#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053058
Urvang Joshia4f32ca2011-09-30 11:07:01 +053059//------------------------------------------------------------------------------
60// Config object to parse command-line arguments.
61
62typedef enum {
63 NIL_ACTION = 0,
64 ACTION_GET,
65 ACTION_SET,
66 ACTION_STRIP,
67 ACTION_INFO,
68 ACTION_HELP
69} ActionType;
70
71typedef enum {
72 NIL_SUBTYPE = 0,
Urvang Joshifa30c862012-11-01 15:34:46 -070073 SUBTYPE_ANMF,
74 SUBTYPE_LOOP,
75 SUBTYPE_BGCOLOR
Urvang Joshia4f32ca2011-09-30 11:07:01 +053076} FeatureSubType;
77
78typedef struct {
79 FeatureSubType subtype_;
80 const char* filename_;
81 const char* params_;
82} FeatureArg;
83
84typedef enum {
85 NIL_FEATURE = 0,
Urvang Joshif903cba2012-10-31 16:30:41 -070086 FEATURE_EXIF,
87 FEATURE_XMP,
Urvang Joshia4f32ca2011-09-30 11:07:01 +053088 FEATURE_ICCP,
Urvang Joshia00a3da2012-10-31 17:49:15 -070089 FEATURE_ANMF,
90 FEATURE_FRGM,
Urvang Joshif903cba2012-10-31 16:30:41 -070091 LAST_FEATURE
Urvang Joshia4f32ca2011-09-30 11:07:01 +053092} FeatureType;
93
Urvang Joshif903cba2012-10-31 16:30:41 -070094static const char* const kFourccList[LAST_FEATURE] = {
95 NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
96};
97
98static const char* const kDescriptions[LAST_FEATURE] = {
99 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Urvang Joshia00a3da2012-10-31 17:49:15 -0700100 "Animation frame", "Image fragment"
Urvang Joshif903cba2012-10-31 16:30:41 -0700101};
102
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530103typedef struct {
104 FeatureType type_;
105 FeatureArg* args_;
106 int arg_count_;
107} Feature;
108
109typedef struct {
110 ActionType action_type_;
111 const char* input_;
112 const char* output_;
113 Feature feature_;
114} WebPMuxConfig;
115
116//------------------------------------------------------------------------------
117// Helper functions.
118
James Zern04e84cf2011-11-04 15:20:08 -0700119static int CountOccurrences(const char* arglist[], int list_length,
120 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530121 int i;
122 int num_occurences = 0;
123
124 for (i = 0; i < list_length; ++i) {
125 if (!strcmp(arglist[i], arg)) {
126 ++num_occurences;
127 }
128 }
129 return num_occurences;
130}
131
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530132static const char* const kErrorMessages[] = {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530133 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
134 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530135};
136
137static const char* ErrorString(WebPMuxError err) {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530138 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530139 return kErrorMessages[-err];
140}
141
James Zerna0b27362012-01-27 17:39:47 -0800142#define RETURN_IF_ERROR(ERR_MSG) \
143 if (err != WEBP_MUX_OK) { \
144 fprintf(stderr, ERR_MSG); \
145 return err; \
146 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530147
James Zerna0b27362012-01-27 17:39:47 -0800148#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
149 if (err != WEBP_MUX_OK) { \
150 fprintf(stderr, ERR_MSG, FORMAT_STR); \
151 return err; \
152 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530153
Urvang Joshid0c79f02012-08-23 16:28:36 +0530154#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
155 if (err != WEBP_MUX_OK) { \
156 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
157 return err; \
158 }
159
James Zerna0b27362012-01-27 17:39:47 -0800160#define ERROR_GOTO1(ERR_MSG, LABEL) \
161 do { \
162 fprintf(stderr, ERR_MSG); \
163 ok = 0; \
164 goto LABEL; \
165 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530166
James Zerna0b27362012-01-27 17:39:47 -0800167#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
168 do { \
169 fprintf(stderr, ERR_MSG, FORMAT_STR); \
170 ok = 0; \
171 goto LABEL; \
172 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530173
James Zerna0b27362012-01-27 17:39:47 -0800174#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
Urvang Joshi6393fe42013-04-26 15:55:42 -0700175 do { \
176 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
177 ok = 0; \
178 goto LABEL; \
179 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530180
181static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshifffefd12013-05-02 13:54:25 -0700182 int width, height;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530183 uint32_t flag;
184
Urvang Joshifffefd12013-05-02 13:54:25 -0700185 WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
186 RETURN_IF_ERROR("Failed to retrieve canvas width/height.\n");
187 printf("Canvas size: %d x %d\n", width, height);
188
189 err = WebPMuxGetFeatures(mux, &flag);
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700190#ifndef WEBP_EXPERIMENTAL_FEATURES
191 if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
192#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530193 RETURN_IF_ERROR("Failed to retrieve features\n");
194
195 if (flag == 0) {
196 fprintf(stderr, "No features present.\n");
197 return err;
198 }
199
200 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800201 printf("Features present:");
202 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700203 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zernd8dc72a2013-03-13 14:04:20 -0700204 if (flag & ICCP_FLAG) printf(" ICC profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700205 if (flag & EXIF_FLAG) printf(" EXIF metadata");
206 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800207 if (flag & ALPHA_FLAG) printf(" transparency");
208 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530209
Urvang Joshia00a3da2012-10-31 17:49:15 -0700210 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530211 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700212 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700213 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800214 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530215
Urvang Joshid0c79f02012-08-23 16:28:36 +0530216 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700217 WebPMuxAnimParams params;
218 err = WebPMuxGetAnimationParams(mux, &params);
219 RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
220 printf("Background color : 0x%.8X Loop Count : %d\n",
221 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530222 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530223
Urvang Joshid0c79f02012-08-23 16:28:36 +0530224 err = WebPMuxNumChunks(mux, id, &nFrames);
225 RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
226
227 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530228 if (nFrames > 0) {
229 int i;
Urvang Joshid0c79f02012-08-23 16:28:36 +0530230 printf("No.: x_offset y_offset ");
Urvang Joshie81fac82013-08-26 18:04:52 -0700231 if (is_anim) printf("duration dispose blend ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530232 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530233 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530234 WebPMuxFrameInfo frame;
235 err = WebPMuxGetFrame(mux, i, &frame);
skal3e59a742013-05-22 00:58:53 +0200236 if (err == WEBP_MUX_OK) {
237 printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
Urvang Joshie81fac82013-08-26 18:04:52 -0700238 if (is_anim) {
239 printf("%8d %7d %5d", frame.duration, frame.dispose_method,
240 frame.blend_method);
241 }
skal3e59a742013-05-22 00:58:53 +0200242 printf("%10d\n", (int)frame.bitstream.size);
243 }
Urvang Joshia0770722012-10-30 14:54:46 -0700244 WebPDataClear(&frame.bitstream);
skal3e59a742013-05-22 00:58:53 +0200245 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530246 }
247 }
248 }
249
250 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800251 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530252 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshif903cba2012-10-31 16:30:41 -0700253 RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
James Zern14d42af2013-03-20 16:59:35 -0700254 printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530255 }
256
Urvang Joshif903cba2012-10-31 16:30:41 -0700257 if (flag & EXIF_FLAG) {
258 WebPData exif;
259 err = WebPMuxGetChunk(mux, "EXIF", &exif);
260 RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700261 printf("Size of the EXIF metadata: %d\n", (int)exif.size);
Urvang Joshif903cba2012-10-31 16:30:41 -0700262 }
263
264 if (flag & XMP_FLAG) {
265 WebPData xmp;
266 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
267 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700268 printf("Size of the XMP metadata: %d\n", (int)xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530269 }
270
Urvang Joshia00a3da2012-10-31 17:49:15 -0700271 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530272 WebPMuxFrameInfo image;
273 err = WebPMuxGetFrame(mux, 1, &image);
skal3e59a742013-05-22 00:58:53 +0200274 if (err == WEBP_MUX_OK) {
275 printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
276 }
277 WebPDataClear(&image.bitstream);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530278 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530279 }
280
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530281 return WEBP_MUX_OK;
282}
283
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700284static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800285 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
286 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
287 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700288#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700289 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700290#endif
Urvang Joshifa30c862012-11-01 15:34:46 -0700291 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
292 "\n");
293 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800294 printf(" webpmux -info INPUT\n");
295 printf(" webpmux [-h|-help]\n");
Urvang Joshia5042a32013-02-26 14:22:06 -0800296 printf(" webpmux -version\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530297
James Zern974aaff2012-01-24 12:46:46 -0800298 printf("\n");
299 printf("GET_OPTIONS:\n");
300 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700301 printf(" icc Get ICC profile.\n");
302 printf(" exif Get EXIF metadata.\n");
303 printf(" xmp Get XMP metadata.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700304#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700305 printf(" frgm n Get nth fragment.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700306#endif
James Zern974aaff2012-01-24 12:46:46 -0800307 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530308
James Zern974aaff2012-01-24 12:46:46 -0800309 printf("\n");
310 printf("SET_OPTIONS:\n");
311 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700312 printf(" icc file.icc Set ICC profile.\n");
313 printf(" exif file.exif Set EXIF metadata.\n");
314 printf(" xmp file.xmp Set XMP metadata.\n");
315 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
316 printf(" 'file.exif' contains the EXIF metadata to be set\n");
317 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530318
James Zern974aaff2012-01-24 12:46:46 -0800319 printf("\n");
320 printf("STRIP_OPTIONS:\n");
321 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700322 printf(" icc Strip ICC profile.\n");
323 printf(" exif Strip EXIF metadata.\n");
324 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530325
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700326#ifdef WEBP_EXPERIMENTAL_FEATURES
James Zern974aaff2012-01-24 12:46:46 -0800327 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700328 printf("FRAGMENT_OPTIONS(i):\n");
329 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800330 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700331 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
332 printf(" 'xi','yi' specify the image offset for this fragment."
333 "\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700334#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530335
James Zern974aaff2012-01-24 12:46:46 -0800336 printf("\n");
337 printf("FRAME_OPTIONS(i):\n");
338 printf(" Create animation.\n");
Urvang Joshie81fac82013-08-26 18:04:52 -0700339 printf(" file_i +di+[xi+yi[+mi[bi]]]\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700340 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800341 printf(" 'di' is the pause duration before next frame.\n");
James Zern8fab1612013-03-07 19:15:37 -0800342 printf(" 'xi','yi' specify the image offset for this frame.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700343 printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
Urvang Joshie81fac82013-08-26 18:04:52 -0700344 printf(" 'bi' is the blending method for this frame (+b or -b)."
345 "\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700346
347 printf("\n");
348 printf("LOOP_COUNT:\n");
349 printf(" Number of times to repeat the animation.\n");
350 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
351
352 printf("\n");
353 printf("BACKGROUND_COLOR:\n");
354 printf(" Background color of the canvas.\n");
355 printf(" A,R,G,B\n");
356 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
357 "specifying\n");
358 printf(" the Alpha, Red, Green and Blue component values "
359 "respectively\n");
360 printf(" [Default: 255,255,255,255].\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530361
Urvang Joshif903cba2012-10-31 16:30:41 -0700362 printf("\nINPUT & OUTPUT are in WebP format.\n");
363
Urvang Joshifa30c862012-11-01 15:34:46 -0700364 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
365 printf(" and is assumed to be\nvalid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530366}
367
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530368static int ReadFileToWebPData(const char* const filename,
369 WebPData* const webp_data) {
370 const uint8_t* data;
371 size_t size;
372 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700373 webp_data->bytes = data;
374 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530375 return 1;
376}
377
James Zern061263a2012-05-11 16:00:57 -0700378static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530379 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530380 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530381 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530382 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700383 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530384 if (*mux != NULL) return 1;
385 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530386 return 0;
387}
388
James Zern0f7820e2012-01-24 14:08:27 -0800389static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530390 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700391 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530392 if (!fout) {
393 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
394 return 0;
395 }
Urvang Joshia0770722012-10-30 14:54:46 -0700396 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530397 fprintf(stderr, "Error writing file %s!\n", filename);
398 } else {
James Zern14d42af2013-03-20 16:59:35 -0700399 fprintf(stderr, "Saved file %s (%d bytes)\n",
400 filename, (int)webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530401 ok = 1;
402 }
403 if (fout != stdout) fclose(fout);
404 return ok;
405}
406
407static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530408 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530409 WebPData webp_data;
410 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530411 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530412 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530413 return 0;
414 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530415 ok = WriteData(filename, &webp_data);
416 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530417 return ok;
418}
419
Urvang Joshiab3234a2012-08-23 15:18:51 +0530420static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100421 int dispose_method, dummy;
Urvang Joshie81fac82013-08-26 18:04:52 -0700422 char plus_minus, blend_method;
423 const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
424 &info->x_offset, &info->y_offset, &dispose_method,
425 &plus_minus, &blend_method, &dummy);
skal48600082012-11-14 06:19:31 +0100426 switch (num_args) {
427 case 1:
428 info->x_offset = info->y_offset = 0; // fall through
429 case 3:
430 dispose_method = 0; // fall through
431 case 4:
Urvang Joshie81fac82013-08-26 18:04:52 -0700432 plus_minus = '+';
433 blend_method = 'b'; // fall through
434 case 6:
skal48600082012-11-14 06:19:31 +0100435 break;
Urvang Joshie81fac82013-08-26 18:04:52 -0700436 case 2:
437 case 5:
skal48600082012-11-14 06:19:31 +0100438 default:
439 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700440 }
441 // Note: The sanity of the following conversion is checked by
Urvang Joshie81fac82013-08-26 18:04:52 -0700442 // WebPMuxPushFrame().
Urvang Joshifa30c862012-11-01 15:34:46 -0700443 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
Urvang Joshie81fac82013-08-26 18:04:52 -0700444
445 if (blend_method != 'b') return 0;
446 if (plus_minus != '-' && plus_minus != '+') return 0;
447 info->blend_method =
448 (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
Urvang Joshifa30c862012-11-01 15:34:46 -0700449 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530450}
451
Urvang Joshia00a3da2012-10-31 17:49:15 -0700452static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700453 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530454}
455
Urvang Joshifa30c862012-11-01 15:34:46 -0700456static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
457 uint32_t a, r, g, b;
458 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
459 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
460 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
461 return 1;
462}
463
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530464//------------------------------------------------------------------------------
465// Clean-up.
466
467static void DeleteConfig(WebPMuxConfig* config) {
468 if (config != NULL) {
469 free(config->feature_.args_);
470 free(config);
471 }
472}
473
474//------------------------------------------------------------------------------
475// Parsing.
476
477// Basic syntactic checks on the command-line arguments.
478// Returns 1 on valid, 0 otherwise.
479// Also fills up num_feature_args to be number of feature arguments given.
480// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
481static int ValidateCommandLine(int argc, const char* argv[],
482 int* num_feature_args) {
483 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700484 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530485 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700486 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530487 int ok = 1;
488
489 assert(num_feature_args != NULL);
490 *num_feature_args = 0;
491
492 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700493 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530494 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
495 }
James Zern04e84cf2011-11-04 15:20:08 -0700496 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530497 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
498 }
James Zern04e84cf2011-11-04 15:20:08 -0700499 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530500 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
501 }
James Zern04e84cf2011-11-04 15:20:08 -0700502 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530503 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
504 }
James Zern04e84cf2011-11-04 15:20:08 -0700505 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530506 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
507 }
508
509 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700510 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700511 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700512 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700513 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530514
515 if (num_loop_args > 1) {
516 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
517 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700518 if (num_bgcolor_args > 1) {
519 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
520 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530521
Urvang Joshifa30c862012-11-01 15:34:46 -0700522 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
523 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
524 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530525 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700526 if (num_frame_args > 0 && num_frgm_args > 0) {
527 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
528 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530529 }
530
531 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700532 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700533 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530534 *num_feature_args = 1;
535 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700536 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530537 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700538 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530539 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700540 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530541 }
542 }
543
544 ErrValidate:
545 return ok;
546}
547
548#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
549
550#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
551
552#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
553 if (argc < i + (NUM)) { \
554 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
555 goto LABEL; \
556 }
557
558#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
559 if (argc != i + (NUM)) { \
560 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
561 goto LABEL; \
562 }
563
564// Parses command-line arguments to fill up config object. Also performs some
565// semantic checks.
566static int ParseCommandLine(int argc, const char* argv[],
567 WebPMuxConfig* config) {
568 int i = 0;
569 int feature_arg_index = 0;
570 int ok = 1;
571
572 while (i < argc) {
573 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700574 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530575 if (argv[i][0] == '-') { // One of the action types or output.
576 if (!strcmp(argv[i], "-set")) {
577 if (ACTION_IS_NIL) {
578 config->action_type_ = ACTION_SET;
579 } else {
580 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
581 }
582 ++i;
583 } else if (!strcmp(argv[i], "-get")) {
584 if (ACTION_IS_NIL) {
585 config->action_type_ = ACTION_GET;
586 } else {
587 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
588 }
589 ++i;
590 } else if (!strcmp(argv[i], "-strip")) {
591 if (ACTION_IS_NIL) {
592 config->action_type_ = ACTION_STRIP;
593 feature->arg_count_ = 0;
594 } else {
595 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
596 }
597 ++i;
598 } else if (!strcmp(argv[i], "-frame")) {
599 CHECK_NUM_ARGS_LESS(3, ErrParse);
600 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
601 config->action_type_ = ACTION_SET;
602 } else {
603 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
604 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700605 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
606 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530607 } else {
608 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
609 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700610 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530611 arg->filename_ = argv[i + 1];
612 arg->params_ = argv[i + 2];
613 ++feature_arg_index;
614 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700615 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530616 CHECK_NUM_ARGS_LESS(2, ErrParse);
617 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
618 config->action_type_ = ACTION_SET;
619 } else {
620 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
621 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700622 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
623 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530624 } else {
625 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
626 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700627 arg->subtype_ =
628 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530629 arg->params_ = argv[i + 1];
630 ++feature_arg_index;
631 i += 2;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700632#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700633 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530634 CHECK_NUM_ARGS_LESS(3, ErrParse);
635 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
636 config->action_type_ = ACTION_SET;
637 } else {
638 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
639 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700640 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
641 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530642 } else {
643 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
644 }
645 arg->filename_ = argv[i + 1];
646 arg->params_ = argv[i + 2];
647 ++feature_arg_index;
648 i += 3;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700649#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530650 } else if (!strcmp(argv[i], "-o")) {
651 CHECK_NUM_ARGS_LESS(2, ErrParse);
652 config->output_ = argv[i + 1];
653 i += 2;
654 } else if (!strcmp(argv[i], "-info")) {
655 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
656 if (config->action_type_ != NIL_ACTION) {
657 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
658 } else {
659 config->action_type_ = ACTION_INFO;
660 feature->arg_count_ = 0;
661 config->input_ = argv[i + 1];
662 }
663 i += 2;
664 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
665 PrintHelp();
666 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800667 exit(0);
Urvang Joshia5042a32013-02-26 14:22:06 -0800668 } else if (!strcmp(argv[i], "-version")) {
669 const int version = WebPGetMuxVersion();
670 printf("%d.%d.%d\n",
671 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
672 DeleteConfig(config);
673 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530674 } else {
675 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
676 }
677 } else { // One of the feature types or input.
678 if (ACTION_IS_NIL) {
679 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
680 ErrParse);
681 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700682 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
683 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530684 if (FEATURETYPE_IS_NIL) {
685 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700686 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530687 } else {
688 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
689 }
690 if (config->action_type_ == ACTION_SET) {
691 CHECK_NUM_ARGS_LESS(2, ErrParse);
692 arg->filename_ = argv[i + 1];
693 ++feature_arg_index;
694 i += 2;
695 } else {
696 ++i;
697 }
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700698#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530699 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700700 !strcmp(argv[i], "frgm")) &&
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700701#else
702 } else if (!strcmp(argv[i], "frame") &&
703#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530704 (config->action_type_ == ACTION_GET)) {
705 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700706 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
707 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530708 arg->params_ = argv[i + 1];
709 ++feature_arg_index;
710 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700711 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530712 if (config->input_ == NULL) {
713 config->input_ = argv[i];
714 } else {
715 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
716 argv[i], ErrParse);
717 }
718 ++i;
719 }
720 }
721 }
722 ErrParse:
723 return ok;
724}
725
James Zern04e84cf2011-11-04 15:20:08 -0700726// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530727static int ValidateConfig(WebPMuxConfig* config) {
728 int ok = 1;
729 Feature* const feature = &config->feature_;
730
731 // Action.
732 if (ACTION_IS_NIL) {
733 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
734 }
735
736 // Feature type.
737 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
738 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
739 }
740
741 // Input file.
742 if (config->input_ == NULL) {
743 if (config->action_type_ != ACTION_SET) {
744 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700745 } else if (feature->type_ != FEATURE_ANMF &&
746 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530747 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
748 }
749 }
750
751 // Output file.
752 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
753 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
754 }
755
756 ErrValidate2:
757 return ok;
758}
759
760// Create config object from command-line arguments.
761static int InitializeConfig(int argc, const char* argv[],
762 WebPMuxConfig** config) {
763 int num_feature_args = 0;
764 int ok = 1;
765
766 assert(config != NULL);
767 *config = NULL;
768
769 // Validate command-line arguments.
770 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
771 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
772 }
773
774 // Allocate memory.
775 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
776 if (*config == NULL) {
777 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
778 }
779 (*config)->feature_.arg_count_ = num_feature_args;
780 (*config)->feature_.args_ =
781 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
782 if ((*config)->feature_.args_ == NULL) {
783 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
784 }
785
786 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700787 if (!ParseCommandLine(argc, argv, *config) ||
788 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530789 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
790 }
791
792 Err1:
793 return ok;
794}
795
796#undef ACTION_IS_NIL
797#undef FEATURETYPE_IS_NIL
798#undef CHECK_NUM_ARGS_LESS
799#undef CHECK_NUM_ARGS_MORE
800
801//------------------------------------------------------------------------------
802// Processing.
803
Urvang Joshia00a3da2012-10-31 17:49:15 -0700804static int GetFrameFragment(const WebPMux* mux,
Urvang Joshi7681bb92013-03-13 18:10:56 -0700805 const WebPMuxConfig* config, int is_frame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530806 WebPMuxError err = WEBP_MUX_OK;
807 WebPMux* mux_single = NULL;
808 long num = 0;
809 int ok = 1;
Urvang Joshi7681bb92013-03-13 18:10:56 -0700810 const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530811 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700812 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530813
814 num = strtol(config->feature_.args_[0].params_, NULL, 10);
815 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700816 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530817 }
818
Urvang Joshid0c79f02012-08-23 16:28:36 +0530819 err = WebPMuxGetFrame(mux, num, &info);
820 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
821 if (err != WEBP_MUX_OK) {
822 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
823 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530824 }
825
826 mux_single = WebPMuxNew();
827 if (mux_single == NULL) {
828 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530829 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
830 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530831 }
Urvang Joshia0770722012-10-30 14:54:46 -0700832 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530833 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530834 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
835 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530836 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530837
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530838 ok = WriteWebP(mux_single, config->output_);
839
840 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700841 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530842 WebPMuxDelete(mux_single);
843 return ok;
844}
845
846// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700847static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530848 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700849 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530850 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530851 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700852 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530853
James Zern04e84cf2011-11-04 15:20:08 -0700854 switch (config->action_type_) {
skal0d19fbf2013-01-21 17:20:14 +0100855 case ACTION_GET: {
James Zern061263a2012-05-11 16:00:57 -0700856 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530857 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700858 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700859 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800860 case FEATURE_FRGM:
861 ok = GetFrameFragment(mux, config,
862 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530863 break;
864
865 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700866 case FEATURE_EXIF:
867 case FEATURE_XMP:
868 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700870 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
871 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530872 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700873 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530874 break;
875
876 default:
877 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
878 break;
879 }
880 break;
skal0d19fbf2013-01-21 17:20:14 +0100881 }
882 case ACTION_SET: {
James Zern04e84cf2011-11-04 15:20:08 -0700883 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700884 case FEATURE_ANMF: {
skal0d19fbf2013-01-21 17:20:14 +0100885 int i;
Urvang Joshifa30c862012-11-01 15:34:46 -0700886 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530887 mux = WebPMuxNew();
888 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530889 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
890 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530891 }
skal0d19fbf2013-01-21 17:20:14 +0100892 for (i = 0; i < feature->arg_count_; ++i) {
893 switch (feature->args_[i].subtype_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700894 case SUBTYPE_BGCOLOR: {
895 uint32_t bgcolor;
skal0d19fbf2013-01-21 17:20:14 +0100896 ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
Urvang Joshifa30c862012-11-01 15:34:46 -0700897 if (!ok) {
898 ERROR_GOTO1("ERROR: Could not parse the background color \n",
899 Err2);
900 }
901 params.bgcolor = bgcolor;
902 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530903 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700904 case SUBTYPE_LOOP: {
905 const long loop_count =
skal0d19fbf2013-01-21 17:20:14 +0100906 strtol(feature->args_[i].params_, NULL, 10);
Urvang Joshifa30c862012-11-01 15:34:46 -0700907 if (loop_count != (int)loop_count) {
908 // Note: This is only a 'necessary' condition for loop_count
909 // to be valid. The 'sufficient' conditioned in checked in
910 // WebPMuxSetAnimationParams() method called later.
911 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
912 "65535.\n", Err2);
913 }
914 params.loop_count = (int)loop_count;
915 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530916 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700917 case SUBTYPE_ANMF: {
918 WebPMuxFrameInfo frame;
919 frame.id = WEBP_CHUNK_ANMF;
skal0d19fbf2013-01-21 17:20:14 +0100920 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshifa30c862012-11-01 15:34:46 -0700921 &frame.bitstream);
922 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100923 ok = ParseFrameArgs(feature->args_[i].params_, &frame);
Urvang Joshifa30c862012-11-01 15:34:46 -0700924 if (!ok) {
925 WebPDataClear(&frame.bitstream);
926 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
927 Err2);
928 }
929 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700930 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700931 if (err != WEBP_MUX_OK) {
932 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
skal0d19fbf2013-01-21 17:20:14 +0100933 "\n", ErrorString(err), i, Err2);
Urvang Joshifa30c862012-11-01 15:34:46 -0700934 }
935 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530936 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700937 default: {
938 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
939 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530941 }
942 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700943 err = WebPMuxSetAnimationParams(mux, &params);
944 if (err != WEBP_MUX_OK) {
945 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
946 ErrorString(err), Err2);
947 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530948 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700949 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530950
skal0d19fbf2013-01-21 17:20:14 +0100951 case FEATURE_FRGM: {
952 int i;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530953 mux = WebPMuxNew();
954 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530955 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
956 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530957 }
skal0d19fbf2013-01-21 17:20:14 +0100958 for (i = 0; i < feature->arg_count_; ++i) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700959 WebPMuxFrameInfo frgm;
960 frgm.id = WEBP_CHUNK_FRGM;
skal0d19fbf2013-01-21 17:20:14 +0100961 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700962 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530963 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100964 ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530965 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700966 WebPDataClear(&frgm.bitstream);
967 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
968 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530969 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700970 err = WebPMuxPushFrame(mux, &frgm, 1);
971 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530972 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700973 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
skal0d19fbf2013-01-21 17:20:14 +0100974 ErrorString(err), i, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530975 }
976 }
977 break;
skal0d19fbf2013-01-21 17:20:14 +0100978 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530979
980 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700981 case FEATURE_EXIF:
skal0d19fbf2013-01-21 17:20:14 +0100982 case FEATURE_XMP: {
James Zern061263a2012-05-11 16:00:57 -0700983 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530984 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700985 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530986 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700987 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
988 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530989 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700990 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
991 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530992 }
993 break;
skal0d19fbf2013-01-21 17:20:14 +0100994 }
995 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530996 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
997 break;
skal0d19fbf2013-01-21 17:20:14 +0100998 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530999 }
1000 ok = WriteWebP(mux, config->output_);
1001 break;
skal0d19fbf2013-01-21 17:20:14 +01001002 }
1003 case ACTION_STRIP: {
James Zern061263a2012-05-11 16:00:57 -07001004 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301005 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -07001006 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
1007 feature->type_ == FEATURE_XMP) {
1008 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
1009 if (err != WEBP_MUX_OK) {
1010 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
1011 ErrorString(err), kDescriptions[feature->type_], Err2);
1012 }
1013 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301014 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1015 break;
1016 }
1017 ok = WriteWebP(mux, config->output_);
1018 break;
skal0d19fbf2013-01-21 17:20:14 +01001019 }
1020 case ACTION_INFO: {
James Zern061263a2012-05-11 16:00:57 -07001021 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301022 if (!ok) goto Err2;
1023 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1024 break;
skal0d19fbf2013-01-21 17:20:14 +01001025 }
1026 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301027 assert(0); // Invalid action.
1028 break;
skal0d19fbf2013-01-21 17:20:14 +01001029 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301030 }
1031
1032 Err2:
1033 WebPMuxDelete(mux);
1034 return ok;
1035}
1036
1037//------------------------------------------------------------------------------
1038// Main.
1039
1040int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301041 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001042 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301043 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001044 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301045 } else {
1046 PrintHelp();
1047 }
1048 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001049 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301050}
1051
1052//------------------------------------------------------------------------------