blob: f10dc0d7f047d438416121ec70f0ba5c8eeb10e6 [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>
Urvang Joshi0e6747f2013-09-16 15:29:24 -070056#include "webp/decode.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070058#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053059
Urvang Joshia4f32ca2011-09-30 11:07:01 +053060//------------------------------------------------------------------------------
61// Config object to parse command-line arguments.
62
63typedef enum {
64 NIL_ACTION = 0,
65 ACTION_GET,
66 ACTION_SET,
67 ACTION_STRIP,
68 ACTION_INFO,
69 ACTION_HELP
70} ActionType;
71
72typedef enum {
73 NIL_SUBTYPE = 0,
Urvang Joshifa30c862012-11-01 15:34:46 -070074 SUBTYPE_ANMF,
75 SUBTYPE_LOOP,
76 SUBTYPE_BGCOLOR
Urvang Joshia4f32ca2011-09-30 11:07:01 +053077} FeatureSubType;
78
79typedef struct {
80 FeatureSubType subtype_;
81 const char* filename_;
82 const char* params_;
83} FeatureArg;
84
85typedef enum {
86 NIL_FEATURE = 0,
Urvang Joshif903cba2012-10-31 16:30:41 -070087 FEATURE_EXIF,
88 FEATURE_XMP,
Urvang Joshia4f32ca2011-09-30 11:07:01 +053089 FEATURE_ICCP,
Urvang Joshia00a3da2012-10-31 17:49:15 -070090 FEATURE_ANMF,
91 FEATURE_FRGM,
Urvang Joshif903cba2012-10-31 16:30:41 -070092 LAST_FEATURE
Urvang Joshia4f32ca2011-09-30 11:07:01 +053093} FeatureType;
94
Urvang Joshif903cba2012-10-31 16:30:41 -070095static const char* const kFourccList[LAST_FEATURE] = {
96 NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
97};
98
99static const char* const kDescriptions[LAST_FEATURE] = {
100 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Urvang Joshia00a3da2012-10-31 17:49:15 -0700101 "Animation frame", "Image fragment"
Urvang Joshif903cba2012-10-31 16:30:41 -0700102};
103
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530104typedef struct {
105 FeatureType type_;
106 FeatureArg* args_;
107 int arg_count_;
108} Feature;
109
110typedef struct {
111 ActionType action_type_;
112 const char* input_;
113 const char* output_;
114 Feature feature_;
115} WebPMuxConfig;
116
117//------------------------------------------------------------------------------
118// Helper functions.
119
James Zern04e84cf2011-11-04 15:20:08 -0700120static int CountOccurrences(const char* arglist[], int list_length,
121 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530122 int i;
123 int num_occurences = 0;
124
125 for (i = 0; i < list_length; ++i) {
126 if (!strcmp(arglist[i], arg)) {
127 ++num_occurences;
128 }
129 }
130 return num_occurences;
131}
132
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530133static const char* const kErrorMessages[] = {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530134 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
135 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530136};
137
138static const char* ErrorString(WebPMuxError err) {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530139 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530140 return kErrorMessages[-err];
141}
142
James Zerna0b27362012-01-27 17:39:47 -0800143#define RETURN_IF_ERROR(ERR_MSG) \
144 if (err != WEBP_MUX_OK) { \
145 fprintf(stderr, ERR_MSG); \
146 return err; \
147 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530148
James Zerna0b27362012-01-27 17:39:47 -0800149#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
150 if (err != WEBP_MUX_OK) { \
151 fprintf(stderr, ERR_MSG, FORMAT_STR); \
152 return err; \
153 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530154
Urvang Joshid0c79f02012-08-23 16:28:36 +0530155#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
156 if (err != WEBP_MUX_OK) { \
157 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
158 return err; \
159 }
160
James Zerna0b27362012-01-27 17:39:47 -0800161#define ERROR_GOTO1(ERR_MSG, LABEL) \
162 do { \
163 fprintf(stderr, ERR_MSG); \
164 ok = 0; \
165 goto LABEL; \
166 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530167
James Zerna0b27362012-01-27 17:39:47 -0800168#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
169 do { \
170 fprintf(stderr, ERR_MSG, FORMAT_STR); \
171 ok = 0; \
172 goto LABEL; \
173 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530174
James Zerna0b27362012-01-27 17:39:47 -0800175#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
Urvang Joshi6393fe42013-04-26 15:55:42 -0700176 do { \
177 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
178 ok = 0; \
179 goto LABEL; \
180 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530181
182static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshifffefd12013-05-02 13:54:25 -0700183 int width, height;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530184 uint32_t flag;
185
Urvang Joshifffefd12013-05-02 13:54:25 -0700186 WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
187 RETURN_IF_ERROR("Failed to retrieve canvas width/height.\n");
188 printf("Canvas size: %d x %d\n", width, height);
189
190 err = WebPMuxGetFeatures(mux, &flag);
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700191#ifndef WEBP_EXPERIMENTAL_FEATURES
192 if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
193#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530194 RETURN_IF_ERROR("Failed to retrieve features\n");
195
196 if (flag == 0) {
197 fprintf(stderr, "No features present.\n");
198 return err;
199 }
200
201 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800202 printf("Features present:");
203 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700204 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zernd8dc72a2013-03-13 14:04:20 -0700205 if (flag & ICCP_FLAG) printf(" ICC profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700206 if (flag & EXIF_FLAG) printf(" EXIF metadata");
207 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800208 if (flag & ALPHA_FLAG) printf(" transparency");
209 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530210
Urvang Joshia00a3da2012-10-31 17:49:15 -0700211 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530212 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700213 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700214 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800215 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530216
Urvang Joshid0c79f02012-08-23 16:28:36 +0530217 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700218 WebPMuxAnimParams params;
219 err = WebPMuxGetAnimationParams(mux, &params);
220 RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
221 printf("Background color : 0x%.8X Loop Count : %d\n",
222 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530223 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530224
Urvang Joshid0c79f02012-08-23 16:28:36 +0530225 err = WebPMuxNumChunks(mux, id, &nFrames);
226 RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
227
228 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530229 if (nFrames > 0) {
230 int i;
Urvang Joshi0e6747f2013-09-16 15:29:24 -0700231 printf("No.: width height alpha x_offset y_offset ");
232 if (is_anim) printf("duration dispose blend ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530233 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530234 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530235 WebPMuxFrameInfo frame;
236 err = WebPMuxGetFrame(mux, i, &frame);
skal3e59a742013-05-22 00:58:53 +0200237 if (err == WEBP_MUX_OK) {
Urvang Joshi0e6747f2013-09-16 15:29:24 -0700238 WebPBitstreamFeatures features;
239 const VP8StatusCode status = WebPGetFeatures(
240 frame.bitstream.bytes, frame.bitstream.size, &features);
241 assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate().
242 (void)status;
243 printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
244 features.height, features.has_alpha ? "yes" : "no",
245 frame.x_offset, frame.y_offset);
Urvang Joshie81fac82013-08-26 18:04:52 -0700246 if (is_anim) {
Urvang Joshi0e6747f2013-09-16 15:29:24 -0700247 const char* const dispose =
248 (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
249 : "background";
250 const char* const blend =
251 (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
252 printf("%8d %10s %5s ", frame.duration, dispose, blend);
Urvang Joshie81fac82013-08-26 18:04:52 -0700253 }
skal3e59a742013-05-22 00:58:53 +0200254 printf("%10d\n", (int)frame.bitstream.size);
255 }
Urvang Joshia0770722012-10-30 14:54:46 -0700256 WebPDataClear(&frame.bitstream);
skal3e59a742013-05-22 00:58:53 +0200257 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530258 }
259 }
260 }
261
262 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800263 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530264 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshif903cba2012-10-31 16:30:41 -0700265 RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
James Zern14d42af2013-03-20 16:59:35 -0700266 printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530267 }
268
Urvang Joshif903cba2012-10-31 16:30:41 -0700269 if (flag & EXIF_FLAG) {
270 WebPData exif;
271 err = WebPMuxGetChunk(mux, "EXIF", &exif);
272 RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700273 printf("Size of the EXIF metadata: %d\n", (int)exif.size);
Urvang Joshif903cba2012-10-31 16:30:41 -0700274 }
275
276 if (flag & XMP_FLAG) {
277 WebPData xmp;
278 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
279 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700280 printf("Size of the XMP metadata: %d\n", (int)xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530281 }
282
Urvang Joshia00a3da2012-10-31 17:49:15 -0700283 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530284 WebPMuxFrameInfo image;
285 err = WebPMuxGetFrame(mux, 1, &image);
skal3e59a742013-05-22 00:58:53 +0200286 if (err == WEBP_MUX_OK) {
287 printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
288 }
289 WebPDataClear(&image.bitstream);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530290 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530291 }
292
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530293 return WEBP_MUX_OK;
294}
295
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700296static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800297 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
298 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
299 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700300#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700301 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700302#endif
Urvang Joshifa30c862012-11-01 15:34:46 -0700303 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
304 "\n");
305 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800306 printf(" webpmux -info INPUT\n");
307 printf(" webpmux [-h|-help]\n");
Urvang Joshia5042a32013-02-26 14:22:06 -0800308 printf(" webpmux -version\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530309
James Zern974aaff2012-01-24 12:46:46 -0800310 printf("\n");
311 printf("GET_OPTIONS:\n");
312 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700313 printf(" icc Get ICC profile.\n");
314 printf(" exif Get EXIF metadata.\n");
315 printf(" xmp Get XMP metadata.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700316#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700317 printf(" frgm n Get nth fragment.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700318#endif
James Zern974aaff2012-01-24 12:46:46 -0800319 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530320
James Zern974aaff2012-01-24 12:46:46 -0800321 printf("\n");
322 printf("SET_OPTIONS:\n");
323 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700324 printf(" icc file.icc Set ICC profile.\n");
325 printf(" exif file.exif Set EXIF metadata.\n");
326 printf(" xmp file.xmp Set XMP metadata.\n");
327 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
328 printf(" 'file.exif' contains the EXIF metadata to be set\n");
329 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530330
James Zern974aaff2012-01-24 12:46:46 -0800331 printf("\n");
332 printf("STRIP_OPTIONS:\n");
333 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700334 printf(" icc Strip ICC profile.\n");
335 printf(" exif Strip EXIF metadata.\n");
336 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530337
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700338#ifdef WEBP_EXPERIMENTAL_FEATURES
James Zern974aaff2012-01-24 12:46:46 -0800339 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700340 printf("FRAGMENT_OPTIONS(i):\n");
341 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800342 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700343 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
344 printf(" 'xi','yi' specify the image offset for this fragment."
345 "\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700346#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530347
James Zern974aaff2012-01-24 12:46:46 -0800348 printf("\n");
349 printf("FRAME_OPTIONS(i):\n");
350 printf(" Create animation.\n");
Urvang Joshie81fac82013-08-26 18:04:52 -0700351 printf(" file_i +di+[xi+yi[+mi[bi]]]\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700352 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800353 printf(" 'di' is the pause duration before next frame.\n");
James Zern8fab1612013-03-07 19:15:37 -0800354 printf(" 'xi','yi' specify the image offset for this frame.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700355 printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
Urvang Joshie81fac82013-08-26 18:04:52 -0700356 printf(" 'bi' is the blending method for this frame (+b or -b)."
357 "\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700358
359 printf("\n");
360 printf("LOOP_COUNT:\n");
361 printf(" Number of times to repeat the animation.\n");
362 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
363
364 printf("\n");
365 printf("BACKGROUND_COLOR:\n");
366 printf(" Background color of the canvas.\n");
367 printf(" A,R,G,B\n");
368 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
369 "specifying\n");
370 printf(" the Alpha, Red, Green and Blue component values "
371 "respectively\n");
372 printf(" [Default: 255,255,255,255].\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530373
Urvang Joshif903cba2012-10-31 16:30:41 -0700374 printf("\nINPUT & OUTPUT are in WebP format.\n");
375
Urvang Joshifa30c862012-11-01 15:34:46 -0700376 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
377 printf(" and is assumed to be\nvalid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530378}
379
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530380static int ReadFileToWebPData(const char* const filename,
381 WebPData* const webp_data) {
382 const uint8_t* data;
383 size_t size;
384 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700385 webp_data->bytes = data;
386 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530387 return 1;
388}
389
James Zern061263a2012-05-11 16:00:57 -0700390static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530391 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530392 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530393 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530394 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700395 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530396 if (*mux != NULL) return 1;
397 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530398 return 0;
399}
400
James Zern0f7820e2012-01-24 14:08:27 -0800401static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530402 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700403 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530404 if (!fout) {
405 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
406 return 0;
407 }
Urvang Joshia0770722012-10-30 14:54:46 -0700408 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530409 fprintf(stderr, "Error writing file %s!\n", filename);
410 } else {
James Zern14d42af2013-03-20 16:59:35 -0700411 fprintf(stderr, "Saved file %s (%d bytes)\n",
412 filename, (int)webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530413 ok = 1;
414 }
415 if (fout != stdout) fclose(fout);
416 return ok;
417}
418
419static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530420 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530421 WebPData webp_data;
422 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530423 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530424 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530425 return 0;
426 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530427 ok = WriteData(filename, &webp_data);
428 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530429 return ok;
430}
431
Urvang Joshiab3234a2012-08-23 15:18:51 +0530432static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100433 int dispose_method, dummy;
Urvang Joshie81fac82013-08-26 18:04:52 -0700434 char plus_minus, blend_method;
435 const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
436 &info->x_offset, &info->y_offset, &dispose_method,
437 &plus_minus, &blend_method, &dummy);
skal48600082012-11-14 06:19:31 +0100438 switch (num_args) {
439 case 1:
440 info->x_offset = info->y_offset = 0; // fall through
441 case 3:
442 dispose_method = 0; // fall through
443 case 4:
Urvang Joshie81fac82013-08-26 18:04:52 -0700444 plus_minus = '+';
445 blend_method = 'b'; // fall through
446 case 6:
skal48600082012-11-14 06:19:31 +0100447 break;
Urvang Joshie81fac82013-08-26 18:04:52 -0700448 case 2:
449 case 5:
skal48600082012-11-14 06:19:31 +0100450 default:
451 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700452 }
453 // Note: The sanity of the following conversion is checked by
Urvang Joshie81fac82013-08-26 18:04:52 -0700454 // WebPMuxPushFrame().
Urvang Joshifa30c862012-11-01 15:34:46 -0700455 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
Urvang Joshie81fac82013-08-26 18:04:52 -0700456
457 if (blend_method != 'b') return 0;
458 if (plus_minus != '-' && plus_minus != '+') return 0;
459 info->blend_method =
460 (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
Urvang Joshifa30c862012-11-01 15:34:46 -0700461 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530462}
463
Urvang Joshia00a3da2012-10-31 17:49:15 -0700464static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700465 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530466}
467
Urvang Joshifa30c862012-11-01 15:34:46 -0700468static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
469 uint32_t a, r, g, b;
470 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
471 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
472 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
473 return 1;
474}
475
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530476//------------------------------------------------------------------------------
477// Clean-up.
478
479static void DeleteConfig(WebPMuxConfig* config) {
480 if (config != NULL) {
481 free(config->feature_.args_);
482 free(config);
483 }
484}
485
486//------------------------------------------------------------------------------
487// Parsing.
488
489// Basic syntactic checks on the command-line arguments.
490// Returns 1 on valid, 0 otherwise.
491// Also fills up num_feature_args to be number of feature arguments given.
492// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
493static int ValidateCommandLine(int argc, const char* argv[],
494 int* num_feature_args) {
495 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700496 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530497 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700498 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530499 int ok = 1;
500
501 assert(num_feature_args != NULL);
502 *num_feature_args = 0;
503
504 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700505 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530506 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
507 }
James Zern04e84cf2011-11-04 15:20:08 -0700508 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530509 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
510 }
James Zern04e84cf2011-11-04 15:20:08 -0700511 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530512 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
513 }
James Zern04e84cf2011-11-04 15:20:08 -0700514 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530515 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
516 }
James Zern04e84cf2011-11-04 15:20:08 -0700517 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530518 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
519 }
520
521 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700522 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700523 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700524 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700525 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530526
527 if (num_loop_args > 1) {
528 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
529 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700530 if (num_bgcolor_args > 1) {
531 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
532 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530533
Urvang Joshifa30c862012-11-01 15:34:46 -0700534 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
535 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
536 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530537 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700538 if (num_frame_args > 0 && num_frgm_args > 0) {
539 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
540 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530541 }
542
543 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700544 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700545 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530546 *num_feature_args = 1;
547 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700548 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530549 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700550 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530551 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700552 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530553 }
554 }
555
556 ErrValidate:
557 return ok;
558}
559
560#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
561
562#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
563
564#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
565 if (argc < i + (NUM)) { \
566 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
567 goto LABEL; \
568 }
569
570#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
571 if (argc != i + (NUM)) { \
572 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
573 goto LABEL; \
574 }
575
576// Parses command-line arguments to fill up config object. Also performs some
577// semantic checks.
578static int ParseCommandLine(int argc, const char* argv[],
579 WebPMuxConfig* config) {
580 int i = 0;
581 int feature_arg_index = 0;
582 int ok = 1;
583
584 while (i < argc) {
585 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700586 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530587 if (argv[i][0] == '-') { // One of the action types or output.
588 if (!strcmp(argv[i], "-set")) {
589 if (ACTION_IS_NIL) {
590 config->action_type_ = ACTION_SET;
591 } else {
592 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
593 }
594 ++i;
595 } else if (!strcmp(argv[i], "-get")) {
596 if (ACTION_IS_NIL) {
597 config->action_type_ = ACTION_GET;
598 } else {
599 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
600 }
601 ++i;
602 } else if (!strcmp(argv[i], "-strip")) {
603 if (ACTION_IS_NIL) {
604 config->action_type_ = ACTION_STRIP;
605 feature->arg_count_ = 0;
606 } else {
607 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
608 }
609 ++i;
610 } else if (!strcmp(argv[i], "-frame")) {
611 CHECK_NUM_ARGS_LESS(3, ErrParse);
612 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
613 config->action_type_ = ACTION_SET;
614 } else {
615 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
616 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700617 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
618 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530619 } else {
620 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
621 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700622 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530623 arg->filename_ = argv[i + 1];
624 arg->params_ = argv[i + 2];
625 ++feature_arg_index;
626 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700627 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530628 CHECK_NUM_ARGS_LESS(2, ErrParse);
629 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
630 config->action_type_ = ACTION_SET;
631 } else {
632 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
633 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700634 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
635 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530636 } else {
637 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
638 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700639 arg->subtype_ =
640 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530641 arg->params_ = argv[i + 1];
642 ++feature_arg_index;
643 i += 2;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700644#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700645 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530646 CHECK_NUM_ARGS_LESS(3, ErrParse);
647 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
648 config->action_type_ = ACTION_SET;
649 } else {
650 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
651 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700652 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
653 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530654 } else {
655 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
656 }
657 arg->filename_ = argv[i + 1];
658 arg->params_ = argv[i + 2];
659 ++feature_arg_index;
660 i += 3;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700661#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530662 } else if (!strcmp(argv[i], "-o")) {
663 CHECK_NUM_ARGS_LESS(2, ErrParse);
664 config->output_ = argv[i + 1];
665 i += 2;
666 } else if (!strcmp(argv[i], "-info")) {
667 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
668 if (config->action_type_ != NIL_ACTION) {
669 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
670 } else {
671 config->action_type_ = ACTION_INFO;
672 feature->arg_count_ = 0;
673 config->input_ = argv[i + 1];
674 }
675 i += 2;
676 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
677 PrintHelp();
678 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800679 exit(0);
Urvang Joshia5042a32013-02-26 14:22:06 -0800680 } else if (!strcmp(argv[i], "-version")) {
681 const int version = WebPGetMuxVersion();
682 printf("%d.%d.%d\n",
683 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
684 DeleteConfig(config);
685 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530686 } else {
687 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
688 }
689 } else { // One of the feature types or input.
690 if (ACTION_IS_NIL) {
691 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
692 ErrParse);
693 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700694 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
695 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530696 if (FEATURETYPE_IS_NIL) {
697 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700698 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530699 } else {
700 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
701 }
702 if (config->action_type_ == ACTION_SET) {
703 CHECK_NUM_ARGS_LESS(2, ErrParse);
704 arg->filename_ = argv[i + 1];
705 ++feature_arg_index;
706 i += 2;
707 } else {
708 ++i;
709 }
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700710#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530711 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700712 !strcmp(argv[i], "frgm")) &&
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700713#else
714 } else if (!strcmp(argv[i], "frame") &&
715#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530716 (config->action_type_ == ACTION_GET)) {
717 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700718 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
719 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530720 arg->params_ = argv[i + 1];
721 ++feature_arg_index;
722 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700723 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530724 if (config->input_ == NULL) {
725 config->input_ = argv[i];
726 } else {
727 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
728 argv[i], ErrParse);
729 }
730 ++i;
731 }
732 }
733 }
734 ErrParse:
735 return ok;
736}
737
James Zern04e84cf2011-11-04 15:20:08 -0700738// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530739static int ValidateConfig(WebPMuxConfig* config) {
740 int ok = 1;
741 Feature* const feature = &config->feature_;
742
743 // Action.
744 if (ACTION_IS_NIL) {
745 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
746 }
747
748 // Feature type.
749 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
750 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
751 }
752
753 // Input file.
754 if (config->input_ == NULL) {
755 if (config->action_type_ != ACTION_SET) {
756 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700757 } else if (feature->type_ != FEATURE_ANMF &&
758 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530759 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
760 }
761 }
762
763 // Output file.
764 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
765 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
766 }
767
768 ErrValidate2:
769 return ok;
770}
771
772// Create config object from command-line arguments.
773static int InitializeConfig(int argc, const char* argv[],
774 WebPMuxConfig** config) {
775 int num_feature_args = 0;
776 int ok = 1;
777
778 assert(config != NULL);
779 *config = NULL;
780
781 // Validate command-line arguments.
782 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
783 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
784 }
785
786 // Allocate memory.
787 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
788 if (*config == NULL) {
789 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
790 }
791 (*config)->feature_.arg_count_ = num_feature_args;
792 (*config)->feature_.args_ =
793 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
794 if ((*config)->feature_.args_ == NULL) {
795 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
796 }
797
798 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700799 if (!ParseCommandLine(argc, argv, *config) ||
800 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530801 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
802 }
803
804 Err1:
805 return ok;
806}
807
808#undef ACTION_IS_NIL
809#undef FEATURETYPE_IS_NIL
810#undef CHECK_NUM_ARGS_LESS
811#undef CHECK_NUM_ARGS_MORE
812
813//------------------------------------------------------------------------------
814// Processing.
815
Urvang Joshia00a3da2012-10-31 17:49:15 -0700816static int GetFrameFragment(const WebPMux* mux,
Urvang Joshi7681bb92013-03-13 18:10:56 -0700817 const WebPMuxConfig* config, int is_frame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530818 WebPMuxError err = WEBP_MUX_OK;
819 WebPMux* mux_single = NULL;
820 long num = 0;
821 int ok = 1;
Urvang Joshi7681bb92013-03-13 18:10:56 -0700822 const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530823 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700824 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530825
826 num = strtol(config->feature_.args_[0].params_, NULL, 10);
827 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700828 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530829 }
830
Urvang Joshid0c79f02012-08-23 16:28:36 +0530831 err = WebPMuxGetFrame(mux, num, &info);
832 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
833 if (err != WEBP_MUX_OK) {
834 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
835 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530836 }
837
838 mux_single = WebPMuxNew();
839 if (mux_single == NULL) {
840 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530841 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
842 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530843 }
Urvang Joshia0770722012-10-30 14:54:46 -0700844 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530845 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530846 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
847 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530848 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530849
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530850 ok = WriteWebP(mux_single, config->output_);
851
852 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700853 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530854 WebPMuxDelete(mux_single);
855 return ok;
856}
857
858// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700859static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530860 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700861 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530862 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530863 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700864 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530865
James Zern04e84cf2011-11-04 15:20:08 -0700866 switch (config->action_type_) {
skal0d19fbf2013-01-21 17:20:14 +0100867 case ACTION_GET: {
James Zern061263a2012-05-11 16:00:57 -0700868 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700870 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700871 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800872 case FEATURE_FRGM:
873 ok = GetFrameFragment(mux, config,
874 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530875 break;
876
877 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700878 case FEATURE_EXIF:
879 case FEATURE_XMP:
880 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530881 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700882 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
883 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530884 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700885 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530886 break;
887
888 default:
889 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
890 break;
891 }
892 break;
skal0d19fbf2013-01-21 17:20:14 +0100893 }
894 case ACTION_SET: {
James Zern04e84cf2011-11-04 15:20:08 -0700895 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700896 case FEATURE_ANMF: {
skal0d19fbf2013-01-21 17:20:14 +0100897 int i;
Urvang Joshifa30c862012-11-01 15:34:46 -0700898 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530899 mux = WebPMuxNew();
900 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530901 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
902 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530903 }
skal0d19fbf2013-01-21 17:20:14 +0100904 for (i = 0; i < feature->arg_count_; ++i) {
905 switch (feature->args_[i].subtype_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700906 case SUBTYPE_BGCOLOR: {
907 uint32_t bgcolor;
skal0d19fbf2013-01-21 17:20:14 +0100908 ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
Urvang Joshifa30c862012-11-01 15:34:46 -0700909 if (!ok) {
910 ERROR_GOTO1("ERROR: Could not parse the background color \n",
911 Err2);
912 }
913 params.bgcolor = bgcolor;
914 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530915 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700916 case SUBTYPE_LOOP: {
917 const long loop_count =
skal0d19fbf2013-01-21 17:20:14 +0100918 strtol(feature->args_[i].params_, NULL, 10);
Urvang Joshifa30c862012-11-01 15:34:46 -0700919 if (loop_count != (int)loop_count) {
920 // Note: This is only a 'necessary' condition for loop_count
921 // to be valid. The 'sufficient' conditioned in checked in
922 // WebPMuxSetAnimationParams() method called later.
923 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
924 "65535.\n", Err2);
925 }
926 params.loop_count = (int)loop_count;
927 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530928 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700929 case SUBTYPE_ANMF: {
930 WebPMuxFrameInfo frame;
931 frame.id = WEBP_CHUNK_ANMF;
skal0d19fbf2013-01-21 17:20:14 +0100932 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshifa30c862012-11-01 15:34:46 -0700933 &frame.bitstream);
934 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100935 ok = ParseFrameArgs(feature->args_[i].params_, &frame);
Urvang Joshifa30c862012-11-01 15:34:46 -0700936 if (!ok) {
937 WebPDataClear(&frame.bitstream);
938 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
939 Err2);
940 }
941 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700942 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700943 if (err != WEBP_MUX_OK) {
944 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
skal0d19fbf2013-01-21 17:20:14 +0100945 "\n", ErrorString(err), i, Err2);
Urvang Joshifa30c862012-11-01 15:34:46 -0700946 }
947 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530948 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700949 default: {
950 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
951 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530952 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530953 }
954 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700955 err = WebPMuxSetAnimationParams(mux, &params);
956 if (err != WEBP_MUX_OK) {
957 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
958 ErrorString(err), Err2);
959 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530960 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700961 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530962
skal0d19fbf2013-01-21 17:20:14 +0100963 case FEATURE_FRGM: {
964 int i;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530965 mux = WebPMuxNew();
966 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530967 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
968 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530969 }
skal0d19fbf2013-01-21 17:20:14 +0100970 for (i = 0; i < feature->arg_count_; ++i) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700971 WebPMuxFrameInfo frgm;
972 frgm.id = WEBP_CHUNK_FRGM;
skal0d19fbf2013-01-21 17:20:14 +0100973 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700974 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530975 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100976 ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530977 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700978 WebPDataClear(&frgm.bitstream);
979 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
980 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530981 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700982 err = WebPMuxPushFrame(mux, &frgm, 1);
983 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530984 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700985 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
skal0d19fbf2013-01-21 17:20:14 +0100986 ErrorString(err), i, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530987 }
988 }
989 break;
skal0d19fbf2013-01-21 17:20:14 +0100990 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530991
992 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700993 case FEATURE_EXIF:
skal0d19fbf2013-01-21 17:20:14 +0100994 case FEATURE_XMP: {
James Zern061263a2012-05-11 16:00:57 -0700995 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530996 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700997 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530998 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700999 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
1000 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301001 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -07001002 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
1003 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301004 }
1005 break;
skal0d19fbf2013-01-21 17:20:14 +01001006 }
1007 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301008 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
1009 break;
skal0d19fbf2013-01-21 17:20:14 +01001010 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301011 }
1012 ok = WriteWebP(mux, config->output_);
1013 break;
skal0d19fbf2013-01-21 17:20:14 +01001014 }
1015 case ACTION_STRIP: {
James Zern061263a2012-05-11 16:00:57 -07001016 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301017 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -07001018 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
1019 feature->type_ == FEATURE_XMP) {
1020 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
1021 if (err != WEBP_MUX_OK) {
1022 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
1023 ErrorString(err), kDescriptions[feature->type_], Err2);
1024 }
1025 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301026 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1027 break;
1028 }
1029 ok = WriteWebP(mux, config->output_);
1030 break;
skal0d19fbf2013-01-21 17:20:14 +01001031 }
1032 case ACTION_INFO: {
James Zern061263a2012-05-11 16:00:57 -07001033 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301034 if (!ok) goto Err2;
1035 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1036 break;
skal0d19fbf2013-01-21 17:20:14 +01001037 }
1038 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301039 assert(0); // Invalid action.
1040 break;
skal0d19fbf2013-01-21 17:20:14 +01001041 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301042 }
1043
1044 Err2:
1045 WebPMuxDelete(mux);
1046 return ok;
1047}
1048
1049//------------------------------------------------------------------------------
1050// Main.
1051
1052int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301053 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001054 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301055 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001056 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301057 } else {
1058 PrintHelp();
1059 }
1060 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001061 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301062}
1063
1064//------------------------------------------------------------------------------