blob: 4707c3db9792d83bea0677a0284e2c0aab3edf4e [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple command-line to create a WebP container file and to extract or strip
9// relevant data from the container file.
10//
Urvang Joshia4f32ca2011-09-30 11:07:01 +053011// Authors: Vikas (vikaas.arora@gmail.com),
12// Urvang (urvang@google.com)
13
14/* Usage examples:
15
16 Create container WebP file:
skal48600082012-11-14 06:19:31 +010017 webpmux -frame anim_1.webp +100+10+10 \
18 -frame anim_2.webp +100+25+25+1 \
19 -frame anim_3.webp +100+50+50+1 \
20 -frame anim_4.webp +100 \
21 -loop 10 -bgcolor 128,255,255,255 \
Urvang Joshia4f32ca2011-09-30 11:07:01 +053022 -o out_animation_container.webp
23
24 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070025 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
26 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053027
Urvang Joshia4f32ca2011-09-30 11:07:01 +053028 Extract relevant data from WebP container file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070029 webpmux -get frgm n in.webp -o out_fragment.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053030 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053031 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshif903cba2012-10-31 16:30:41 -070032 webpmux -get exif in.webp -o image_metadata.exif
33 webpmux -get xmp in.webp -o image_metadata.xmp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053034
Urvang Joshia4f32ca2011-09-30 11:07:01 +053035 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070036 webpmux -strip icc in.webp -o out.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070037 webpmux -strip exif in.webp -o out.webp
38 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053039
40 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053041 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070042 webpmux [ -h | -help ]
Urvang Joshia5042a32013-02-26 14:22:06 -080043 webpmux -version
Urvang Joshia4f32ca2011-09-30 11:07:01 +053044*/
45
Urvang Joshi5dbd4032013-03-15 14:46:12 -070046#ifdef HAVE_CONFIG_H
47#include "config.h"
48#endif
49
Urvang Joshia4f32ca2011-09-30 11:07:01 +053050#include <assert.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070055#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053056
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057//------------------------------------------------------------------------------
58// Config object to parse command-line arguments.
59
60typedef enum {
61 NIL_ACTION = 0,
62 ACTION_GET,
63 ACTION_SET,
64 ACTION_STRIP,
65 ACTION_INFO,
66 ACTION_HELP
67} ActionType;
68
69typedef enum {
70 NIL_SUBTYPE = 0,
Urvang Joshifa30c862012-11-01 15:34:46 -070071 SUBTYPE_ANMF,
72 SUBTYPE_LOOP,
73 SUBTYPE_BGCOLOR
Urvang Joshia4f32ca2011-09-30 11:07:01 +053074} FeatureSubType;
75
76typedef struct {
77 FeatureSubType subtype_;
78 const char* filename_;
79 const char* params_;
80} FeatureArg;
81
82typedef enum {
83 NIL_FEATURE = 0,
Urvang Joshif903cba2012-10-31 16:30:41 -070084 FEATURE_EXIF,
85 FEATURE_XMP,
Urvang Joshia4f32ca2011-09-30 11:07:01 +053086 FEATURE_ICCP,
Urvang Joshia00a3da2012-10-31 17:49:15 -070087 FEATURE_ANMF,
88 FEATURE_FRGM,
Urvang Joshif903cba2012-10-31 16:30:41 -070089 LAST_FEATURE
Urvang Joshia4f32ca2011-09-30 11:07:01 +053090} FeatureType;
91
Urvang Joshif903cba2012-10-31 16:30:41 -070092static const char* const kFourccList[LAST_FEATURE] = {
93 NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
94};
95
96static const char* const kDescriptions[LAST_FEATURE] = {
97 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Urvang Joshia00a3da2012-10-31 17:49:15 -070098 "Animation frame", "Image fragment"
Urvang Joshif903cba2012-10-31 16:30:41 -070099};
100
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530101typedef struct {
102 FeatureType type_;
103 FeatureArg* args_;
104 int arg_count_;
105} Feature;
106
107typedef struct {
108 ActionType action_type_;
109 const char* input_;
110 const char* output_;
111 Feature feature_;
112} WebPMuxConfig;
113
114//------------------------------------------------------------------------------
115// Helper functions.
116
James Zern04e84cf2011-11-04 15:20:08 -0700117static int CountOccurrences(const char* arglist[], int list_length,
118 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530119 int i;
120 int num_occurences = 0;
121
122 for (i = 0; i < list_length; ++i) {
123 if (!strcmp(arglist[i], arg)) {
124 ++num_occurences;
125 }
126 }
127 return num_occurences;
128}
129
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530130static const char* const kErrorMessages[] = {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530131 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
132 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530133};
134
135static const char* ErrorString(WebPMuxError err) {
Urvang Joshia4b9b1c2012-07-06 17:33:59 +0530136 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530137 return kErrorMessages[-err];
138}
139
James Zerna0b27362012-01-27 17:39:47 -0800140#define RETURN_IF_ERROR(ERR_MSG) \
141 if (err != WEBP_MUX_OK) { \
142 fprintf(stderr, ERR_MSG); \
143 return err; \
144 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530145
James Zerna0b27362012-01-27 17:39:47 -0800146#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
147 if (err != WEBP_MUX_OK) { \
148 fprintf(stderr, ERR_MSG, FORMAT_STR); \
149 return err; \
150 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530151
Urvang Joshid0c79f02012-08-23 16:28:36 +0530152#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
153 if (err != WEBP_MUX_OK) { \
154 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
155 return err; \
156 }
157
James Zerna0b27362012-01-27 17:39:47 -0800158#define ERROR_GOTO1(ERR_MSG, LABEL) \
159 do { \
160 fprintf(stderr, ERR_MSG); \
161 ok = 0; \
162 goto LABEL; \
163 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530164
James Zerna0b27362012-01-27 17:39:47 -0800165#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
166 do { \
167 fprintf(stderr, ERR_MSG, FORMAT_STR); \
168 ok = 0; \
169 goto LABEL; \
170 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530171
James Zerna0b27362012-01-27 17:39:47 -0800172#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
Urvang Joshi6393fe42013-04-26 15:55:42 -0700173 do { \
174 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
175 ok = 0; \
176 goto LABEL; \
177 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530178
179static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshifffefd12013-05-02 13:54:25 -0700180 int width, height;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530181 uint32_t flag;
182
Urvang Joshifffefd12013-05-02 13:54:25 -0700183 WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
184 RETURN_IF_ERROR("Failed to retrieve canvas width/height.\n");
185 printf("Canvas size: %d x %d\n", width, height);
186
187 err = WebPMuxGetFeatures(mux, &flag);
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700188#ifndef WEBP_EXPERIMENTAL_FEATURES
189 if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
190#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530191 RETURN_IF_ERROR("Failed to retrieve features\n");
192
193 if (flag == 0) {
194 fprintf(stderr, "No features present.\n");
195 return err;
196 }
197
198 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800199 printf("Features present:");
200 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700201 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zernd8dc72a2013-03-13 14:04:20 -0700202 if (flag & ICCP_FLAG) printf(" ICC profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700203 if (flag & EXIF_FLAG) printf(" EXIF metadata");
204 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800205 if (flag & ALPHA_FLAG) printf(" transparency");
206 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530207
Urvang Joshia00a3da2012-10-31 17:49:15 -0700208 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530209 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700210 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700211 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800212 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530213
Urvang Joshid0c79f02012-08-23 16:28:36 +0530214 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700215 WebPMuxAnimParams params;
216 err = WebPMuxGetAnimationParams(mux, &params);
217 RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
218 printf("Background color : 0x%.8X Loop Count : %d\n",
219 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530220 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530221
Urvang Joshid0c79f02012-08-23 16:28:36 +0530222 err = WebPMuxNumChunks(mux, id, &nFrames);
223 RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
224
225 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530226 if (nFrames > 0) {
227 int i;
Urvang Joshid0c79f02012-08-23 16:28:36 +0530228 printf("No.: x_offset y_offset ");
Urvang Joshifa30c862012-11-01 15:34:46 -0700229 if (is_anim) printf("duration dispose ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530230 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530231 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530232 WebPMuxFrameInfo frame;
233 err = WebPMuxGetFrame(mux, i, &frame);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530234 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia0770722012-10-30 14:54:46 -0700235 printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
Urvang Joshifa30c862012-11-01 15:34:46 -0700236 if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
James Zern14d42af2013-03-20 16:59:35 -0700237 printf("%10d\n", (int)frame.bitstream.size);
Urvang Joshia0770722012-10-30 14:54:46 -0700238 WebPDataClear(&frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530239 }
240 }
241 }
242
243 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800244 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530245 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshif903cba2012-10-31 16:30:41 -0700246 RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
James Zern14d42af2013-03-20 16:59:35 -0700247 printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530248 }
249
Urvang Joshif903cba2012-10-31 16:30:41 -0700250 if (flag & EXIF_FLAG) {
251 WebPData exif;
252 err = WebPMuxGetChunk(mux, "EXIF", &exif);
253 RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700254 printf("Size of the EXIF metadata: %d\n", (int)exif.size);
Urvang Joshif903cba2012-10-31 16:30:41 -0700255 }
256
257 if (flag & XMP_FLAG) {
258 WebPData xmp;
259 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
260 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700261 printf("Size of the XMP metadata: %d\n", (int)xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530262 }
263
Urvang Joshia00a3da2012-10-31 17:49:15 -0700264 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530265 WebPMuxFrameInfo image;
266 err = WebPMuxGetFrame(mux, 1, &image);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530267 RETURN_IF_ERROR("Failed to retrieve the image\n");
James Zern14d42af2013-03-20 16:59:35 -0700268 printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530269 }
270
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530271 return WEBP_MUX_OK;
272}
273
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700274static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800275 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
276 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
277 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700278#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700279 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700280#endif
Urvang Joshifa30c862012-11-01 15:34:46 -0700281 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
282 "\n");
283 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800284 printf(" webpmux -info INPUT\n");
285 printf(" webpmux [-h|-help]\n");
Urvang Joshia5042a32013-02-26 14:22:06 -0800286 printf(" webpmux -version\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530287
James Zern974aaff2012-01-24 12:46:46 -0800288 printf("\n");
289 printf("GET_OPTIONS:\n");
290 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700291 printf(" icc Get ICC profile.\n");
292 printf(" exif Get EXIF metadata.\n");
293 printf(" xmp Get XMP metadata.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700294#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700295 printf(" frgm n Get nth fragment.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700296#endif
James Zern974aaff2012-01-24 12:46:46 -0800297 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530298
James Zern974aaff2012-01-24 12:46:46 -0800299 printf("\n");
300 printf("SET_OPTIONS:\n");
301 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700302 printf(" icc file.icc Set ICC profile.\n");
303 printf(" exif file.exif Set EXIF metadata.\n");
304 printf(" xmp file.xmp Set XMP metadata.\n");
305 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
306 printf(" 'file.exif' contains the EXIF metadata to be set\n");
307 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530308
James Zern974aaff2012-01-24 12:46:46 -0800309 printf("\n");
310 printf("STRIP_OPTIONS:\n");
311 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700312 printf(" icc Strip ICC profile.\n");
313 printf(" exif Strip EXIF metadata.\n");
314 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530315
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700316#ifdef WEBP_EXPERIMENTAL_FEATURES
James Zern974aaff2012-01-24 12:46:46 -0800317 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700318 printf("FRAGMENT_OPTIONS(i):\n");
319 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800320 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700321 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
322 printf(" 'xi','yi' specify the image offset for this fragment."
323 "\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700324#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530325
James Zern974aaff2012-01-24 12:46:46 -0800326 printf("\n");
327 printf("FRAME_OPTIONS(i):\n");
328 printf(" Create animation.\n");
James Zern8fab1612013-03-07 19:15:37 -0800329 printf(" file_i +di+xi+yi+mi\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700330 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800331 printf(" 'di' is the pause duration before next frame.\n");
James Zern8fab1612013-03-07 19:15:37 -0800332 printf(" 'xi','yi' specify the image offset for this frame.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700333 printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
334
335 printf("\n");
336 printf("LOOP_COUNT:\n");
337 printf(" Number of times to repeat the animation.\n");
338 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
339
340 printf("\n");
341 printf("BACKGROUND_COLOR:\n");
342 printf(" Background color of the canvas.\n");
343 printf(" A,R,G,B\n");
344 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
345 "specifying\n");
346 printf(" the Alpha, Red, Green and Blue component values "
347 "respectively\n");
348 printf(" [Default: 255,255,255,255].\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530349
Urvang Joshif903cba2012-10-31 16:30:41 -0700350 printf("\nINPUT & OUTPUT are in WebP format.\n");
351
Urvang Joshifa30c862012-11-01 15:34:46 -0700352 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
353 printf(" and is assumed to be\nvalid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530354}
355
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530356static int ReadFileToWebPData(const char* const filename,
357 WebPData* const webp_data) {
358 const uint8_t* data;
359 size_t size;
360 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700361 webp_data->bytes = data;
362 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530363 return 1;
364}
365
James Zern061263a2012-05-11 16:00:57 -0700366static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530367 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530368 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530369 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530370 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700371 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530372 if (*mux != NULL) return 1;
373 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530374 return 0;
375}
376
James Zern0f7820e2012-01-24 14:08:27 -0800377static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530378 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700379 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530380 if (!fout) {
381 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
382 return 0;
383 }
Urvang Joshia0770722012-10-30 14:54:46 -0700384 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530385 fprintf(stderr, "Error writing file %s!\n", filename);
386 } else {
James Zern14d42af2013-03-20 16:59:35 -0700387 fprintf(stderr, "Saved file %s (%d bytes)\n",
388 filename, (int)webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530389 ok = 1;
390 }
391 if (fout != stdout) fclose(fout);
392 return ok;
393}
394
395static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530396 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530397 WebPData webp_data;
398 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530399 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530400 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530401 return 0;
402 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530403 ok = WriteData(filename, &webp_data);
404 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530405 return ok;
406}
407
Urvang Joshiab3234a2012-08-23 15:18:51 +0530408static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100409 int dispose_method, dummy;
410 const int num_args = sscanf(args, "+%d+%d+%d+%d+%d",
411 &info->duration, &info->x_offset, &info->y_offset,
412 &dispose_method, &dummy);
413 switch (num_args) {
414 case 1:
415 info->x_offset = info->y_offset = 0; // fall through
416 case 3:
417 dispose_method = 0; // fall through
418 case 4:
419 break;
420 default:
421 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700422 }
423 // Note: The sanity of the following conversion is checked by
424 // WebPMuxSetAnimationParams().
425 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
426 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530427}
428
Urvang Joshia00a3da2012-10-31 17:49:15 -0700429static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700430 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530431}
432
Urvang Joshifa30c862012-11-01 15:34:46 -0700433static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
434 uint32_t a, r, g, b;
435 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
436 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
437 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
438 return 1;
439}
440
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530441//------------------------------------------------------------------------------
442// Clean-up.
443
444static void DeleteConfig(WebPMuxConfig* config) {
445 if (config != NULL) {
446 free(config->feature_.args_);
447 free(config);
448 }
449}
450
451//------------------------------------------------------------------------------
452// Parsing.
453
454// Basic syntactic checks on the command-line arguments.
455// Returns 1 on valid, 0 otherwise.
456// Also fills up num_feature_args to be number of feature arguments given.
457// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
458static int ValidateCommandLine(int argc, const char* argv[],
459 int* num_feature_args) {
460 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700461 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530462 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700463 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530464 int ok = 1;
465
466 assert(num_feature_args != NULL);
467 *num_feature_args = 0;
468
469 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700470 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530471 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
472 }
James Zern04e84cf2011-11-04 15:20:08 -0700473 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530474 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
475 }
James Zern04e84cf2011-11-04 15:20:08 -0700476 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530477 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
478 }
James Zern04e84cf2011-11-04 15:20:08 -0700479 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530480 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
481 }
James Zern04e84cf2011-11-04 15:20:08 -0700482 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530483 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
484 }
485
486 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700487 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700488 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700489 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700490 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530491
492 if (num_loop_args > 1) {
493 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
494 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700495 if (num_bgcolor_args > 1) {
496 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
497 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530498
Urvang Joshifa30c862012-11-01 15:34:46 -0700499 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
500 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
501 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530502 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700503 if (num_frame_args > 0 && num_frgm_args > 0) {
504 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
505 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530506 }
507
508 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700509 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700510 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530511 *num_feature_args = 1;
512 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700513 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530514 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700515 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530516 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700517 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530518 }
519 }
520
521 ErrValidate:
522 return ok;
523}
524
525#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
526
527#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
528
529#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
530 if (argc < i + (NUM)) { \
531 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
532 goto LABEL; \
533 }
534
535#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
536 if (argc != i + (NUM)) { \
537 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
538 goto LABEL; \
539 }
540
541// Parses command-line arguments to fill up config object. Also performs some
542// semantic checks.
543static int ParseCommandLine(int argc, const char* argv[],
544 WebPMuxConfig* config) {
545 int i = 0;
546 int feature_arg_index = 0;
547 int ok = 1;
548
549 while (i < argc) {
550 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700551 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530552 if (argv[i][0] == '-') { // One of the action types or output.
553 if (!strcmp(argv[i], "-set")) {
554 if (ACTION_IS_NIL) {
555 config->action_type_ = ACTION_SET;
556 } else {
557 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
558 }
559 ++i;
560 } else if (!strcmp(argv[i], "-get")) {
561 if (ACTION_IS_NIL) {
562 config->action_type_ = ACTION_GET;
563 } else {
564 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
565 }
566 ++i;
567 } else if (!strcmp(argv[i], "-strip")) {
568 if (ACTION_IS_NIL) {
569 config->action_type_ = ACTION_STRIP;
570 feature->arg_count_ = 0;
571 } else {
572 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
573 }
574 ++i;
575 } else if (!strcmp(argv[i], "-frame")) {
576 CHECK_NUM_ARGS_LESS(3, ErrParse);
577 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
578 config->action_type_ = ACTION_SET;
579 } else {
580 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
581 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700582 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
583 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530584 } else {
585 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
586 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700587 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530588 arg->filename_ = argv[i + 1];
589 arg->params_ = argv[i + 2];
590 ++feature_arg_index;
591 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700592 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530593 CHECK_NUM_ARGS_LESS(2, ErrParse);
594 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
595 config->action_type_ = ACTION_SET;
596 } else {
597 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
598 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700599 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
600 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530601 } else {
602 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
603 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700604 arg->subtype_ =
605 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530606 arg->params_ = argv[i + 1];
607 ++feature_arg_index;
608 i += 2;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700609#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700610 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530611 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_FRGM) {
618 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530619 } else {
620 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
621 }
622 arg->filename_ = argv[i + 1];
623 arg->params_ = argv[i + 2];
624 ++feature_arg_index;
625 i += 3;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700626#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530627 } else if (!strcmp(argv[i], "-o")) {
628 CHECK_NUM_ARGS_LESS(2, ErrParse);
629 config->output_ = argv[i + 1];
630 i += 2;
631 } else if (!strcmp(argv[i], "-info")) {
632 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
633 if (config->action_type_ != NIL_ACTION) {
634 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
635 } else {
636 config->action_type_ = ACTION_INFO;
637 feature->arg_count_ = 0;
638 config->input_ = argv[i + 1];
639 }
640 i += 2;
641 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
642 PrintHelp();
643 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800644 exit(0);
Urvang Joshia5042a32013-02-26 14:22:06 -0800645 } else if (!strcmp(argv[i], "-version")) {
646 const int version = WebPGetMuxVersion();
647 printf("%d.%d.%d\n",
648 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
649 DeleteConfig(config);
650 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530651 } else {
652 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
653 }
654 } else { // One of the feature types or input.
655 if (ACTION_IS_NIL) {
656 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
657 ErrParse);
658 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700659 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
660 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530661 if (FEATURETYPE_IS_NIL) {
662 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700663 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530664 } else {
665 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
666 }
667 if (config->action_type_ == ACTION_SET) {
668 CHECK_NUM_ARGS_LESS(2, ErrParse);
669 arg->filename_ = argv[i + 1];
670 ++feature_arg_index;
671 i += 2;
672 } else {
673 ++i;
674 }
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700675#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530676 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700677 !strcmp(argv[i], "frgm")) &&
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700678#else
679 } else if (!strcmp(argv[i], "frame") &&
680#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530681 (config->action_type_ == ACTION_GET)) {
682 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700683 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
684 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530685 arg->params_ = argv[i + 1];
686 ++feature_arg_index;
687 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700688 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530689 if (config->input_ == NULL) {
690 config->input_ = argv[i];
691 } else {
692 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
693 argv[i], ErrParse);
694 }
695 ++i;
696 }
697 }
698 }
699 ErrParse:
700 return ok;
701}
702
James Zern04e84cf2011-11-04 15:20:08 -0700703// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530704static int ValidateConfig(WebPMuxConfig* config) {
705 int ok = 1;
706 Feature* const feature = &config->feature_;
707
708 // Action.
709 if (ACTION_IS_NIL) {
710 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
711 }
712
713 // Feature type.
714 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
715 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
716 }
717
718 // Input file.
719 if (config->input_ == NULL) {
720 if (config->action_type_ != ACTION_SET) {
721 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700722 } else if (feature->type_ != FEATURE_ANMF &&
723 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530724 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
725 }
726 }
727
728 // Output file.
729 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
730 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
731 }
732
733 ErrValidate2:
734 return ok;
735}
736
737// Create config object from command-line arguments.
738static int InitializeConfig(int argc, const char* argv[],
739 WebPMuxConfig** config) {
740 int num_feature_args = 0;
741 int ok = 1;
742
743 assert(config != NULL);
744 *config = NULL;
745
746 // Validate command-line arguments.
747 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
748 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
749 }
750
751 // Allocate memory.
752 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
753 if (*config == NULL) {
754 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
755 }
756 (*config)->feature_.arg_count_ = num_feature_args;
757 (*config)->feature_.args_ =
758 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
759 if ((*config)->feature_.args_ == NULL) {
760 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
761 }
762
763 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700764 if (!ParseCommandLine(argc, argv, *config) ||
765 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530766 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
767 }
768
769 Err1:
770 return ok;
771}
772
773#undef ACTION_IS_NIL
774#undef FEATURETYPE_IS_NIL
775#undef CHECK_NUM_ARGS_LESS
776#undef CHECK_NUM_ARGS_MORE
777
778//------------------------------------------------------------------------------
779// Processing.
780
Urvang Joshia00a3da2012-10-31 17:49:15 -0700781static int GetFrameFragment(const WebPMux* mux,
Urvang Joshi7681bb92013-03-13 18:10:56 -0700782 const WebPMuxConfig* config, int is_frame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530783 WebPMuxError err = WEBP_MUX_OK;
784 WebPMux* mux_single = NULL;
785 long num = 0;
786 int ok = 1;
Urvang Joshi7681bb92013-03-13 18:10:56 -0700787 const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530788 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700789 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530790
791 num = strtol(config->feature_.args_[0].params_, NULL, 10);
792 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700793 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530794 }
795
Urvang Joshid0c79f02012-08-23 16:28:36 +0530796 err = WebPMuxGetFrame(mux, num, &info);
797 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
798 if (err != WEBP_MUX_OK) {
799 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
800 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530801 }
802
803 mux_single = WebPMuxNew();
804 if (mux_single == NULL) {
805 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530806 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
807 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530808 }
Urvang Joshia0770722012-10-30 14:54:46 -0700809 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530810 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530811 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
812 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530813 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530814
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530815 ok = WriteWebP(mux_single, config->output_);
816
817 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700818 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530819 WebPMuxDelete(mux_single);
820 return ok;
821}
822
823// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700824static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530825 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700826 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530827 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530828 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700829 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530830
James Zern04e84cf2011-11-04 15:20:08 -0700831 switch (config->action_type_) {
skal0d19fbf2013-01-21 17:20:14 +0100832 case ACTION_GET: {
James Zern061263a2012-05-11 16:00:57 -0700833 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530834 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700835 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700836 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800837 case FEATURE_FRGM:
838 ok = GetFrameFragment(mux, config,
839 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530840 break;
841
842 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700843 case FEATURE_EXIF:
844 case FEATURE_XMP:
845 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530846 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700847 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
848 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530849 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700850 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530851 break;
852
853 default:
854 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
855 break;
856 }
857 break;
skal0d19fbf2013-01-21 17:20:14 +0100858 }
859 case ACTION_SET: {
James Zern04e84cf2011-11-04 15:20:08 -0700860 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700861 case FEATURE_ANMF: {
skal0d19fbf2013-01-21 17:20:14 +0100862 int i;
Urvang Joshifa30c862012-11-01 15:34:46 -0700863 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530864 mux = WebPMuxNew();
865 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530866 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
867 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530868 }
skal0d19fbf2013-01-21 17:20:14 +0100869 for (i = 0; i < feature->arg_count_; ++i) {
870 switch (feature->args_[i].subtype_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700871 case SUBTYPE_BGCOLOR: {
872 uint32_t bgcolor;
skal0d19fbf2013-01-21 17:20:14 +0100873 ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
Urvang Joshifa30c862012-11-01 15:34:46 -0700874 if (!ok) {
875 ERROR_GOTO1("ERROR: Could not parse the background color \n",
876 Err2);
877 }
878 params.bgcolor = bgcolor;
879 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530880 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700881 case SUBTYPE_LOOP: {
882 const long loop_count =
skal0d19fbf2013-01-21 17:20:14 +0100883 strtol(feature->args_[i].params_, NULL, 10);
Urvang Joshifa30c862012-11-01 15:34:46 -0700884 if (loop_count != (int)loop_count) {
885 // Note: This is only a 'necessary' condition for loop_count
886 // to be valid. The 'sufficient' conditioned in checked in
887 // WebPMuxSetAnimationParams() method called later.
888 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
889 "65535.\n", Err2);
890 }
891 params.loop_count = (int)loop_count;
892 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530893 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700894 case SUBTYPE_ANMF: {
895 WebPMuxFrameInfo frame;
896 frame.id = WEBP_CHUNK_ANMF;
skal0d19fbf2013-01-21 17:20:14 +0100897 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshifa30c862012-11-01 15:34:46 -0700898 &frame.bitstream);
899 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100900 ok = ParseFrameArgs(feature->args_[i].params_, &frame);
Urvang Joshifa30c862012-11-01 15:34:46 -0700901 if (!ok) {
902 WebPDataClear(&frame.bitstream);
903 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
904 Err2);
905 }
906 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700907 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700908 if (err != WEBP_MUX_OK) {
909 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
skal0d19fbf2013-01-21 17:20:14 +0100910 "\n", ErrorString(err), i, Err2);
Urvang Joshifa30c862012-11-01 15:34:46 -0700911 }
912 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530913 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700914 default: {
915 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
916 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530917 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530918 }
919 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700920 err = WebPMuxSetAnimationParams(mux, &params);
921 if (err != WEBP_MUX_OK) {
922 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
923 ErrorString(err), Err2);
924 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530925 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700926 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530927
skal0d19fbf2013-01-21 17:20:14 +0100928 case FEATURE_FRGM: {
929 int i;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530930 mux = WebPMuxNew();
931 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530932 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
933 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530934 }
skal0d19fbf2013-01-21 17:20:14 +0100935 for (i = 0; i < feature->arg_count_; ++i) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700936 WebPMuxFrameInfo frgm;
937 frgm.id = WEBP_CHUNK_FRGM;
skal0d19fbf2013-01-21 17:20:14 +0100938 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700939 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100941 ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530942 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700943 WebPDataClear(&frgm.bitstream);
944 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
945 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530946 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700947 err = WebPMuxPushFrame(mux, &frgm, 1);
948 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530949 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700950 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
skal0d19fbf2013-01-21 17:20:14 +0100951 ErrorString(err), i, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530952 }
953 }
954 break;
skal0d19fbf2013-01-21 17:20:14 +0100955 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530956
957 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700958 case FEATURE_EXIF:
skal0d19fbf2013-01-21 17:20:14 +0100959 case FEATURE_XMP: {
James Zern061263a2012-05-11 16:00:57 -0700960 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530961 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700962 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530963 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700964 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
965 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530966 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700967 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
968 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530969 }
970 break;
skal0d19fbf2013-01-21 17:20:14 +0100971 }
972 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530973 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
974 break;
skal0d19fbf2013-01-21 17:20:14 +0100975 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530976 }
977 ok = WriteWebP(mux, config->output_);
978 break;
skal0d19fbf2013-01-21 17:20:14 +0100979 }
980 case ACTION_STRIP: {
James Zern061263a2012-05-11 16:00:57 -0700981 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530982 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700983 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
984 feature->type_ == FEATURE_XMP) {
985 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
986 if (err != WEBP_MUX_OK) {
987 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
988 ErrorString(err), kDescriptions[feature->type_], Err2);
989 }
990 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530991 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
992 break;
993 }
994 ok = WriteWebP(mux, config->output_);
995 break;
skal0d19fbf2013-01-21 17:20:14 +0100996 }
997 case ACTION_INFO: {
James Zern061263a2012-05-11 16:00:57 -0700998 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530999 if (!ok) goto Err2;
1000 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1001 break;
skal0d19fbf2013-01-21 17:20:14 +01001002 }
1003 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301004 assert(0); // Invalid action.
1005 break;
skal0d19fbf2013-01-21 17:20:14 +01001006 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301007 }
1008
1009 Err2:
1010 WebPMuxDelete(mux);
1011 return ok;
1012}
1013
1014//------------------------------------------------------------------------------
1015// Main.
1016
1017int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301018 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001019 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301020 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001021 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301022 } else {
1023 PrintHelp();
1024 }
1025 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001026 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301027}
1028
1029//------------------------------------------------------------------------------