blob: 0ba4ca76771824465cbd01d51e0443c958d450c1 [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple command-line to create a WebP container file and to extract or strip
9// relevant data from the container file.
10//
James Zern04e84cf2011-11-04 15:20:08 -070011// Compile with: gcc -o webpmux webpmux.c -lwebpmux -lwebp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053012//
13//
14// Authors: Vikas (vikaas.arora@gmail.com),
15// Urvang (urvang@google.com)
16
17/* Usage examples:
18
19 Create container WebP file:
skal48600082012-11-14 06:19:31 +010020 webpmux -frame anim_1.webp +100+10+10 \
21 -frame anim_2.webp +100+25+25+1 \
22 -frame anim_3.webp +100+50+50+1 \
23 -frame anim_4.webp +100 \
24 -loop 10 -bgcolor 128,255,255,255 \
Urvang Joshia4f32ca2011-09-30 11:07:01 +053025 -o out_animation_container.webp
26
27 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070028 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
29 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053030
Urvang Joshia4f32ca2011-09-30 11:07:01 +053031 Extract relevant data from WebP container file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070032 webpmux -get frgm n in.webp -o out_fragment.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053033 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053034 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshif903cba2012-10-31 16:30:41 -070035 webpmux -get exif in.webp -o image_metadata.exif
36 webpmux -get xmp in.webp -o image_metadata.xmp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037
Urvang Joshia4f32ca2011-09-30 11:07:01 +053038 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070039 webpmux -strip icc in.webp -o out.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070040 webpmux -strip exif in.webp -o out.webp
41 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053042
43 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053044 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070045 webpmux [ -h | -help ]
Urvang Joshia5042a32013-02-26 14:22:06 -080046 webpmux -version
Urvang Joshia4f32ca2011-09-30 11:07:01 +053047*/
48
Urvang Joshi5dbd4032013-03-15 14:46:12 -070049#ifdef HAVE_CONFIG_H
50#include "config.h"
51#endif
52
Urvang Joshia4f32ca2011-09-30 11:07:01 +053053#include <assert.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#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 Joshia4f32ca2011-09-30 11:07:01 +0530183 uint32_t flag;
184
185 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700186#ifndef WEBP_EXPERIMENTAL_FEATURES
187 if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
188#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530189 RETURN_IF_ERROR("Failed to retrieve features\n");
190
191 if (flag == 0) {
192 fprintf(stderr, "No features present.\n");
193 return err;
194 }
195
196 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800197 printf("Features present:");
198 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700199 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zernd8dc72a2013-03-13 14:04:20 -0700200 if (flag & ICCP_FLAG) printf(" ICC profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700201 if (flag & EXIF_FLAG) printf(" EXIF metadata");
202 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800203 if (flag & ALPHA_FLAG) printf(" transparency");
204 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530205
Urvang Joshia00a3da2012-10-31 17:49:15 -0700206 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530207 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700208 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700209 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800210 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530211
Urvang Joshid0c79f02012-08-23 16:28:36 +0530212 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700213 WebPMuxAnimParams params;
214 err = WebPMuxGetAnimationParams(mux, &params);
215 RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
216 printf("Background color : 0x%.8X Loop Count : %d\n",
217 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530218 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530219
Urvang Joshid0c79f02012-08-23 16:28:36 +0530220 err = WebPMuxNumChunks(mux, id, &nFrames);
221 RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
222
223 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530224 if (nFrames > 0) {
225 int i;
Urvang Joshid0c79f02012-08-23 16:28:36 +0530226 printf("No.: x_offset y_offset ");
Urvang Joshifa30c862012-11-01 15:34:46 -0700227 if (is_anim) printf("duration dispose ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530228 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530229 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530230 WebPMuxFrameInfo frame;
231 err = WebPMuxGetFrame(mux, i, &frame);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530232 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia0770722012-10-30 14:54:46 -0700233 printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
Urvang Joshifa30c862012-11-01 15:34:46 -0700234 if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
James Zern14d42af2013-03-20 16:59:35 -0700235 printf("%10d\n", (int)frame.bitstream.size);
Urvang Joshia0770722012-10-30 14:54:46 -0700236 WebPDataClear(&frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530237 }
238 }
239 }
240
241 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800242 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530243 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshif903cba2012-10-31 16:30:41 -0700244 RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
James Zern14d42af2013-03-20 16:59:35 -0700245 printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530246 }
247
Urvang Joshif903cba2012-10-31 16:30:41 -0700248 if (flag & EXIF_FLAG) {
249 WebPData exif;
250 err = WebPMuxGetChunk(mux, "EXIF", &exif);
251 RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700252 printf("Size of the EXIF metadata: %d\n", (int)exif.size);
Urvang Joshif903cba2012-10-31 16:30:41 -0700253 }
254
255 if (flag & XMP_FLAG) {
256 WebPData xmp;
257 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
258 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern14d42af2013-03-20 16:59:35 -0700259 printf("Size of the XMP metadata: %d\n", (int)xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530260 }
261
Urvang Joshia00a3da2012-10-31 17:49:15 -0700262 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530263 WebPMuxFrameInfo image;
264 err = WebPMuxGetFrame(mux, 1, &image);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530265 RETURN_IF_ERROR("Failed to retrieve the image\n");
James Zern14d42af2013-03-20 16:59:35 -0700266 printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530267 }
268
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530269 return WEBP_MUX_OK;
270}
271
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700272static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800273 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
274 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
275 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700276#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700277 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700278#endif
Urvang Joshifa30c862012-11-01 15:34:46 -0700279 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
280 "\n");
281 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800282 printf(" webpmux -info INPUT\n");
283 printf(" webpmux [-h|-help]\n");
Urvang Joshia5042a32013-02-26 14:22:06 -0800284 printf(" webpmux -version\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530285
James Zern974aaff2012-01-24 12:46:46 -0800286 printf("\n");
287 printf("GET_OPTIONS:\n");
288 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700289 printf(" icc Get ICC profile.\n");
290 printf(" exif Get EXIF metadata.\n");
291 printf(" xmp Get XMP metadata.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700292#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700293 printf(" frgm n Get nth fragment.\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700294#endif
James Zern974aaff2012-01-24 12:46:46 -0800295 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530296
James Zern974aaff2012-01-24 12:46:46 -0800297 printf("\n");
298 printf("SET_OPTIONS:\n");
299 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700300 printf(" icc file.icc Set ICC profile.\n");
301 printf(" exif file.exif Set EXIF metadata.\n");
302 printf(" xmp file.xmp Set XMP metadata.\n");
303 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
304 printf(" 'file.exif' contains the EXIF metadata to be set\n");
305 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530306
James Zern974aaff2012-01-24 12:46:46 -0800307 printf("\n");
308 printf("STRIP_OPTIONS:\n");
309 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700310 printf(" icc Strip ICC profile.\n");
311 printf(" exif Strip EXIF metadata.\n");
312 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530313
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700314#ifdef WEBP_EXPERIMENTAL_FEATURES
James Zern974aaff2012-01-24 12:46:46 -0800315 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700316 printf("FRAGMENT_OPTIONS(i):\n");
317 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800318 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700319 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
320 printf(" 'xi','yi' specify the image offset for this fragment."
321 "\n");
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700322#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530323
James Zern974aaff2012-01-24 12:46:46 -0800324 printf("\n");
325 printf("FRAME_OPTIONS(i):\n");
326 printf(" Create animation.\n");
James Zern8fab1612013-03-07 19:15:37 -0800327 printf(" file_i +di+xi+yi+mi\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700328 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800329 printf(" 'di' is the pause duration before next frame.\n");
James Zern8fab1612013-03-07 19:15:37 -0800330 printf(" 'xi','yi' specify the image offset for this frame.\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700331 printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
332
333 printf("\n");
334 printf("LOOP_COUNT:\n");
335 printf(" Number of times to repeat the animation.\n");
336 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
337
338 printf("\n");
339 printf("BACKGROUND_COLOR:\n");
340 printf(" Background color of the canvas.\n");
341 printf(" A,R,G,B\n");
342 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
343 "specifying\n");
344 printf(" the Alpha, Red, Green and Blue component values "
345 "respectively\n");
346 printf(" [Default: 255,255,255,255].\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530347
Urvang Joshif903cba2012-10-31 16:30:41 -0700348 printf("\nINPUT & OUTPUT are in WebP format.\n");
349
Urvang Joshifa30c862012-11-01 15:34:46 -0700350 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
351 printf(" and is assumed to be\nvalid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530352}
353
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530354static int ReadFileToWebPData(const char* const filename,
355 WebPData* const webp_data) {
356 const uint8_t* data;
357 size_t size;
358 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700359 webp_data->bytes = data;
360 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530361 return 1;
362}
363
James Zern061263a2012-05-11 16:00:57 -0700364static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530365 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530366 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530367 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530368 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700369 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530370 if (*mux != NULL) return 1;
371 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530372 return 0;
373}
374
James Zern0f7820e2012-01-24 14:08:27 -0800375static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530376 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700377 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530378 if (!fout) {
379 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
380 return 0;
381 }
Urvang Joshia0770722012-10-30 14:54:46 -0700382 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530383 fprintf(stderr, "Error writing file %s!\n", filename);
384 } else {
James Zern14d42af2013-03-20 16:59:35 -0700385 fprintf(stderr, "Saved file %s (%d bytes)\n",
386 filename, (int)webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530387 ok = 1;
388 }
389 if (fout != stdout) fclose(fout);
390 return ok;
391}
392
393static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530394 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530395 WebPData webp_data;
396 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530397 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530398 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530399 return 0;
400 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530401 ok = WriteData(filename, &webp_data);
402 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530403 return ok;
404}
405
Urvang Joshiab3234a2012-08-23 15:18:51 +0530406static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100407 int dispose_method, dummy;
408 const int num_args = sscanf(args, "+%d+%d+%d+%d+%d",
409 &info->duration, &info->x_offset, &info->y_offset,
410 &dispose_method, &dummy);
411 switch (num_args) {
412 case 1:
413 info->x_offset = info->y_offset = 0; // fall through
414 case 3:
415 dispose_method = 0; // fall through
416 case 4:
417 break;
418 default:
419 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700420 }
421 // Note: The sanity of the following conversion is checked by
422 // WebPMuxSetAnimationParams().
423 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
424 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530425}
426
Urvang Joshia00a3da2012-10-31 17:49:15 -0700427static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700428 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530429}
430
Urvang Joshifa30c862012-11-01 15:34:46 -0700431static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
432 uint32_t a, r, g, b;
433 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
434 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
435 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
436 return 1;
437}
438
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530439//------------------------------------------------------------------------------
440// Clean-up.
441
442static void DeleteConfig(WebPMuxConfig* config) {
443 if (config != NULL) {
444 free(config->feature_.args_);
445 free(config);
446 }
447}
448
449//------------------------------------------------------------------------------
450// Parsing.
451
452// Basic syntactic checks on the command-line arguments.
453// Returns 1 on valid, 0 otherwise.
454// Also fills up num_feature_args to be number of feature arguments given.
455// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
456static int ValidateCommandLine(int argc, const char* argv[],
457 int* num_feature_args) {
458 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700459 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530460 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700461 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530462 int ok = 1;
463
464 assert(num_feature_args != NULL);
465 *num_feature_args = 0;
466
467 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700468 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530469 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
470 }
James Zern04e84cf2011-11-04 15:20:08 -0700471 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530472 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
473 }
James Zern04e84cf2011-11-04 15:20:08 -0700474 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530475 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
476 }
James Zern04e84cf2011-11-04 15:20:08 -0700477 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530478 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
479 }
James Zern04e84cf2011-11-04 15:20:08 -0700480 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530481 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
482 }
483
484 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700485 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700486 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700487 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700488 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530489
490 if (num_loop_args > 1) {
491 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
492 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700493 if (num_bgcolor_args > 1) {
494 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
495 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530496
Urvang Joshifa30c862012-11-01 15:34:46 -0700497 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
498 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
499 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530500 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700501 if (num_frame_args > 0 && num_frgm_args > 0) {
502 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
503 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530504 }
505
506 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700507 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700508 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530509 *num_feature_args = 1;
510 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700511 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530512 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700513 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530514 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700515 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530516 }
517 }
518
519 ErrValidate:
520 return ok;
521}
522
523#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
524
525#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
526
527#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
528 if (argc < i + (NUM)) { \
529 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
530 goto LABEL; \
531 }
532
533#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
534 if (argc != i + (NUM)) { \
535 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
536 goto LABEL; \
537 }
538
539// Parses command-line arguments to fill up config object. Also performs some
540// semantic checks.
541static int ParseCommandLine(int argc, const char* argv[],
542 WebPMuxConfig* config) {
543 int i = 0;
544 int feature_arg_index = 0;
545 int ok = 1;
546
547 while (i < argc) {
548 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700549 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530550 if (argv[i][0] == '-') { // One of the action types or output.
551 if (!strcmp(argv[i], "-set")) {
552 if (ACTION_IS_NIL) {
553 config->action_type_ = ACTION_SET;
554 } else {
555 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
556 }
557 ++i;
558 } else if (!strcmp(argv[i], "-get")) {
559 if (ACTION_IS_NIL) {
560 config->action_type_ = ACTION_GET;
561 } else {
562 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
563 }
564 ++i;
565 } else if (!strcmp(argv[i], "-strip")) {
566 if (ACTION_IS_NIL) {
567 config->action_type_ = ACTION_STRIP;
568 feature->arg_count_ = 0;
569 } else {
570 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
571 }
572 ++i;
573 } else if (!strcmp(argv[i], "-frame")) {
574 CHECK_NUM_ARGS_LESS(3, ErrParse);
575 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
576 config->action_type_ = ACTION_SET;
577 } else {
578 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
579 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700580 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
581 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530582 } else {
583 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
584 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700585 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530586 arg->filename_ = argv[i + 1];
587 arg->params_ = argv[i + 2];
588 ++feature_arg_index;
589 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700590 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530591 CHECK_NUM_ARGS_LESS(2, ErrParse);
592 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
593 config->action_type_ = ACTION_SET;
594 } else {
595 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
596 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700597 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
598 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530599 } else {
600 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
601 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700602 arg->subtype_ =
603 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530604 arg->params_ = argv[i + 1];
605 ++feature_arg_index;
606 i += 2;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700607#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia00a3da2012-10-31 17:49:15 -0700608 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530609 CHECK_NUM_ARGS_LESS(3, ErrParse);
610 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
611 config->action_type_ = ACTION_SET;
612 } else {
613 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
614 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700615 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
616 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530617 } else {
618 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
619 }
620 arg->filename_ = argv[i + 1];
621 arg->params_ = argv[i + 2];
622 ++feature_arg_index;
623 i += 3;
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700624#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530625 } else if (!strcmp(argv[i], "-o")) {
626 CHECK_NUM_ARGS_LESS(2, ErrParse);
627 config->output_ = argv[i + 1];
628 i += 2;
629 } else if (!strcmp(argv[i], "-info")) {
630 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
631 if (config->action_type_ != NIL_ACTION) {
632 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
633 } else {
634 config->action_type_ = ACTION_INFO;
635 feature->arg_count_ = 0;
636 config->input_ = argv[i + 1];
637 }
638 i += 2;
639 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
640 PrintHelp();
641 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800642 exit(0);
Urvang Joshia5042a32013-02-26 14:22:06 -0800643 } else if (!strcmp(argv[i], "-version")) {
644 const int version = WebPGetMuxVersion();
645 printf("%d.%d.%d\n",
646 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
647 DeleteConfig(config);
648 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530649 } else {
650 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
651 }
652 } else { // One of the feature types or input.
653 if (ACTION_IS_NIL) {
654 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
655 ErrParse);
656 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700657 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
658 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530659 if (FEATURETYPE_IS_NIL) {
660 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700661 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530662 } else {
663 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
664 }
665 if (config->action_type_ == ACTION_SET) {
666 CHECK_NUM_ARGS_LESS(2, ErrParse);
667 arg->filename_ = argv[i + 1];
668 ++feature_arg_index;
669 i += 2;
670 } else {
671 ++i;
672 }
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700673#ifdef WEBP_EXPERIMENTAL_FEATURES
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530674 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700675 !strcmp(argv[i], "frgm")) &&
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700676#else
677 } else if (!strcmp(argv[i], "frame") &&
678#endif
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530679 (config->action_type_ == ACTION_GET)) {
680 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700681 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
682 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530683 arg->params_ = argv[i + 1];
684 ++feature_arg_index;
685 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700686 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530687 if (config->input_ == NULL) {
688 config->input_ = argv[i];
689 } else {
690 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
691 argv[i], ErrParse);
692 }
693 ++i;
694 }
695 }
696 }
697 ErrParse:
698 return ok;
699}
700
James Zern04e84cf2011-11-04 15:20:08 -0700701// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530702static int ValidateConfig(WebPMuxConfig* config) {
703 int ok = 1;
704 Feature* const feature = &config->feature_;
705
706 // Action.
707 if (ACTION_IS_NIL) {
708 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
709 }
710
711 // Feature type.
712 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
713 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
714 }
715
716 // Input file.
717 if (config->input_ == NULL) {
718 if (config->action_type_ != ACTION_SET) {
719 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700720 } else if (feature->type_ != FEATURE_ANMF &&
721 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530722 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
723 }
724 }
725
726 // Output file.
727 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
728 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
729 }
730
731 ErrValidate2:
732 return ok;
733}
734
735// Create config object from command-line arguments.
736static int InitializeConfig(int argc, const char* argv[],
737 WebPMuxConfig** config) {
738 int num_feature_args = 0;
739 int ok = 1;
740
741 assert(config != NULL);
742 *config = NULL;
743
744 // Validate command-line arguments.
745 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
746 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
747 }
748
749 // Allocate memory.
750 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
751 if (*config == NULL) {
752 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
753 }
754 (*config)->feature_.arg_count_ = num_feature_args;
755 (*config)->feature_.args_ =
756 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
757 if ((*config)->feature_.args_ == NULL) {
758 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
759 }
760
761 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700762 if (!ParseCommandLine(argc, argv, *config) ||
763 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530764 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
765 }
766
767 Err1:
768 return ok;
769}
770
771#undef ACTION_IS_NIL
772#undef FEATURETYPE_IS_NIL
773#undef CHECK_NUM_ARGS_LESS
774#undef CHECK_NUM_ARGS_MORE
775
776//------------------------------------------------------------------------------
777// Processing.
778
Urvang Joshia00a3da2012-10-31 17:49:15 -0700779static int GetFrameFragment(const WebPMux* mux,
Urvang Joshi7681bb92013-03-13 18:10:56 -0700780 const WebPMuxConfig* config, int is_frame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530781 WebPMuxError err = WEBP_MUX_OK;
782 WebPMux* mux_single = NULL;
783 long num = 0;
784 int ok = 1;
Urvang Joshi7681bb92013-03-13 18:10:56 -0700785 const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530786 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700787 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530788
789 num = strtol(config->feature_.args_[0].params_, NULL, 10);
790 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700791 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530792 }
793
Urvang Joshid0c79f02012-08-23 16:28:36 +0530794 err = WebPMuxGetFrame(mux, num, &info);
795 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
796 if (err != WEBP_MUX_OK) {
797 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
798 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530799 }
800
801 mux_single = WebPMuxNew();
802 if (mux_single == NULL) {
803 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530804 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
805 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530806 }
Urvang Joshia0770722012-10-30 14:54:46 -0700807 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530808 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530809 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
810 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530811 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530812
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530813 ok = WriteWebP(mux_single, config->output_);
814
815 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700816 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530817 WebPMuxDelete(mux_single);
818 return ok;
819}
820
821// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700822static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530823 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700824 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530825 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530826 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700827 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530828
James Zern04e84cf2011-11-04 15:20:08 -0700829 switch (config->action_type_) {
skal0d19fbf2013-01-21 17:20:14 +0100830 case ACTION_GET: {
James Zern061263a2012-05-11 16:00:57 -0700831 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530832 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700833 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700834 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800835 case FEATURE_FRGM:
836 ok = GetFrameFragment(mux, config,
837 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530838 break;
839
840 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700841 case FEATURE_EXIF:
842 case FEATURE_XMP:
843 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530844 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700845 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
846 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530847 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700848 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530849 break;
850
851 default:
852 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
853 break;
854 }
855 break;
skal0d19fbf2013-01-21 17:20:14 +0100856 }
857 case ACTION_SET: {
James Zern04e84cf2011-11-04 15:20:08 -0700858 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700859 case FEATURE_ANMF: {
skal0d19fbf2013-01-21 17:20:14 +0100860 int i;
Urvang Joshifa30c862012-11-01 15:34:46 -0700861 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530862 mux = WebPMuxNew();
863 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530864 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
865 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530866 }
skal0d19fbf2013-01-21 17:20:14 +0100867 for (i = 0; i < feature->arg_count_; ++i) {
868 switch (feature->args_[i].subtype_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700869 case SUBTYPE_BGCOLOR: {
870 uint32_t bgcolor;
skal0d19fbf2013-01-21 17:20:14 +0100871 ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
Urvang Joshifa30c862012-11-01 15:34:46 -0700872 if (!ok) {
873 ERROR_GOTO1("ERROR: Could not parse the background color \n",
874 Err2);
875 }
876 params.bgcolor = bgcolor;
877 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530878 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700879 case SUBTYPE_LOOP: {
880 const long loop_count =
skal0d19fbf2013-01-21 17:20:14 +0100881 strtol(feature->args_[i].params_, NULL, 10);
Urvang Joshifa30c862012-11-01 15:34:46 -0700882 if (loop_count != (int)loop_count) {
883 // Note: This is only a 'necessary' condition for loop_count
884 // to be valid. The 'sufficient' conditioned in checked in
885 // WebPMuxSetAnimationParams() method called later.
886 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
887 "65535.\n", Err2);
888 }
889 params.loop_count = (int)loop_count;
890 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530891 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700892 case SUBTYPE_ANMF: {
893 WebPMuxFrameInfo frame;
894 frame.id = WEBP_CHUNK_ANMF;
skal0d19fbf2013-01-21 17:20:14 +0100895 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshifa30c862012-11-01 15:34:46 -0700896 &frame.bitstream);
897 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100898 ok = ParseFrameArgs(feature->args_[i].params_, &frame);
Urvang Joshifa30c862012-11-01 15:34:46 -0700899 if (!ok) {
900 WebPDataClear(&frame.bitstream);
901 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
902 Err2);
903 }
904 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700905 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700906 if (err != WEBP_MUX_OK) {
907 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
skal0d19fbf2013-01-21 17:20:14 +0100908 "\n", ErrorString(err), i, Err2);
Urvang Joshifa30c862012-11-01 15:34:46 -0700909 }
910 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530911 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700912 default: {
913 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
914 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530915 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530916 }
917 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700918 err = WebPMuxSetAnimationParams(mux, &params);
919 if (err != WEBP_MUX_OK) {
920 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
921 ErrorString(err), Err2);
922 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530923 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700924 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530925
skal0d19fbf2013-01-21 17:20:14 +0100926 case FEATURE_FRGM: {
927 int i;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530928 mux = WebPMuxNew();
929 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530930 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
931 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530932 }
skal0d19fbf2013-01-21 17:20:14 +0100933 for (i = 0; i < feature->arg_count_; ++i) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700934 WebPMuxFrameInfo frgm;
935 frgm.id = WEBP_CHUNK_FRGM;
skal0d19fbf2013-01-21 17:20:14 +0100936 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700937 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530938 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100939 ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700941 WebPDataClear(&frgm.bitstream);
942 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
943 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530944 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700945 err = WebPMuxPushFrame(mux, &frgm, 1);
946 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530947 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700948 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
skal0d19fbf2013-01-21 17:20:14 +0100949 ErrorString(err), i, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530950 }
951 }
952 break;
skal0d19fbf2013-01-21 17:20:14 +0100953 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530954
955 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700956 case FEATURE_EXIF:
skal0d19fbf2013-01-21 17:20:14 +0100957 case FEATURE_XMP: {
James Zern061263a2012-05-11 16:00:57 -0700958 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530959 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700960 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530961 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700962 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
963 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530964 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700965 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
966 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530967 }
968 break;
skal0d19fbf2013-01-21 17:20:14 +0100969 }
970 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530971 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
972 break;
skal0d19fbf2013-01-21 17:20:14 +0100973 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530974 }
975 ok = WriteWebP(mux, config->output_);
976 break;
skal0d19fbf2013-01-21 17:20:14 +0100977 }
978 case ACTION_STRIP: {
James Zern061263a2012-05-11 16:00:57 -0700979 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530980 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700981 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
982 feature->type_ == FEATURE_XMP) {
983 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
984 if (err != WEBP_MUX_OK) {
985 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
986 ErrorString(err), kDescriptions[feature->type_], Err2);
987 }
988 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530989 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
990 break;
991 }
992 ok = WriteWebP(mux, config->output_);
993 break;
skal0d19fbf2013-01-21 17:20:14 +0100994 }
995 case ACTION_INFO: {
James Zern061263a2012-05-11 16:00:57 -0700996 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530997 if (!ok) goto Err2;
998 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
999 break;
skal0d19fbf2013-01-21 17:20:14 +01001000 }
1001 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301002 assert(0); // Invalid action.
1003 break;
skal0d19fbf2013-01-21 17:20:14 +01001004 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301005 }
1006
1007 Err2:
1008 WebPMuxDelete(mux);
1009 return ok;
1010}
1011
1012//------------------------------------------------------------------------------
1013// Main.
1014
1015int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301016 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001017 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301018 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001019 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301020 } else {
1021 PrintHelp();
1022 }
1023 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001024 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301025}
1026
1027//------------------------------------------------------------------------------