blob: 7fb90173dd777005cdb8aa8f321f56e2cd5539dc [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple command-line to create a WebP container file and to extract or strip
9// relevant data from the container file.
10//
James Zern04e84cf2011-11-04 15:20:08 -070011// Compile with: gcc -o webpmux webpmux.c -lwebpmux -lwebp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053012//
13//
14// Authors: Vikas (vikaas.arora@gmail.com),
15// Urvang (urvang@google.com)
16
17/* Usage examples:
18
19 Create container WebP file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070020 webpmux -frgm fragment_1.webp +0+0 \
21 -frgm fragment_2.webp +960+0 \
22 -frgm fragment_3.webp +0+576 \
23 -frgm fragment_4.webp +960+576 \
24 -o out_fragment_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053025
26 webpmux -frame anim_1.webp +0+0+0 \
27 -frame anim_2.webp +25+25+100 \
28 -frame anim_3.webp +50+50+100 \
29 -frame anim_4.webp +0+0+100 \
30 -loop 10 \
31 -o out_animation_container.webp
32
33 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070034 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
35 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 Extract relevant data from WebP container file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070038 webpmux -get frgm n in.webp -o out_fragment.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053039 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053040 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshif903cba2012-10-31 16:30:41 -070041 webpmux -get exif in.webp -o image_metadata.exif
42 webpmux -get xmp in.webp -o image_metadata.xmp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053043
Urvang Joshia4f32ca2011-09-30 11:07:01 +053044 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070045 webpmux -strip icc in.webp -o out.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070046 webpmux -strip exif in.webp -o out.webp
47 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053048
49 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053050 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070051 webpmux [ -h | -help ]
Urvang Joshia4f32ca2011-09-30 11:07:01 +053052*/
53
54#include <assert.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070059#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053060
Urvang Joshia4f32ca2011-09-30 11:07:01 +053061//------------------------------------------------------------------------------
62// Config object to parse command-line arguments.
63
64typedef enum {
65 NIL_ACTION = 0,
66 ACTION_GET,
67 ACTION_SET,
68 ACTION_STRIP,
69 ACTION_INFO,
70 ACTION_HELP
71} ActionType;
72
73typedef enum {
74 NIL_SUBTYPE = 0,
75 SUBTYPE_FRM,
76 SUBTYPE_LOOP
77} 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
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530143static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700144 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530145}
146
James Zerna0b27362012-01-27 17:39:47 -0800147#define RETURN_IF_ERROR(ERR_MSG) \
148 if (err != WEBP_MUX_OK) { \
149 fprintf(stderr, ERR_MSG); \
150 return err; \
151 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530152
James Zerna0b27362012-01-27 17:39:47 -0800153#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
154 if (err != WEBP_MUX_OK) { \
155 fprintf(stderr, ERR_MSG, FORMAT_STR); \
156 return err; \
157 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530158
Urvang Joshid0c79f02012-08-23 16:28:36 +0530159#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
160 if (err != WEBP_MUX_OK) { \
161 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
162 return err; \
163 }
164
James Zerna0b27362012-01-27 17:39:47 -0800165#define ERROR_GOTO1(ERR_MSG, LABEL) \
166 do { \
167 fprintf(stderr, ERR_MSG); \
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_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
173 do { \
174 fprintf(stderr, ERR_MSG, FORMAT_STR); \
175 ok = 0; \
176 goto LABEL; \
177 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530178
James Zerna0b27362012-01-27 17:39:47 -0800179#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
180 do { \
181 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
182 ok = 0; \
183 goto LABEL; \
184 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530185
186static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530187 uint32_t flag;
188
189 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
190 RETURN_IF_ERROR("Failed to retrieve features\n");
191
192 if (flag == 0) {
193 fprintf(stderr, "No features present.\n");
194 return err;
195 }
196
197 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800198 printf("Features present:");
199 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700200 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zern974aaff2012-01-24 12:46:46 -0800201 if (flag & ICCP_FLAG) printf(" icc profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700202 if (flag & EXIF_FLAG) printf(" EXIF metadata");
203 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800204 if (flag & ALPHA_FLAG) printf(" transparency");
205 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530206
Urvang Joshia00a3da2012-10-31 17:49:15 -0700207 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530208 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700209 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700210 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800211 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530212
Urvang Joshid0c79f02012-08-23 16:28:36 +0530213 if (is_anim) {
214 int loop_count;
215 err = WebPMuxGetLoopCount(mux, &loop_count);
216 RETURN_IF_ERROR("Failed to retrieve loop count\n");
217 printf("Loop Count : %d\n", loop_count);
218 }
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 ");
227 if (is_anim) printf("duration ");
228 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);
234 if (is_anim) printf("%8d ", frame.duration);
235 printf("%10zu\n", frame.bitstream.size);
236 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");
245 printf("Size of the ICC profile data: %zu\n", 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");
252 printf("Size of the EXIF metadata: %zu\n", exif.size);
253 }
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");
259 printf("Size of the XMP metadata: %zu\n", 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");
Urvang Joshia0770722012-10-30 14:54:46 -0700266 printf("Size of the image (with alpha): %zu\n", 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 Joshia00a3da2012-10-31 17:49:15 -0700276 printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800277 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
278 printf(" -loop LOOP_COUNT -o OUTPUT\n");
279 printf(" webpmux -info INPUT\n");
280 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530281
James Zern974aaff2012-01-24 12:46:46 -0800282 printf("\n");
283 printf("GET_OPTIONS:\n");
284 printf(" Extract relevant data.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700285 printf(" icc Get ICC profile.\n");
286 printf(" exif Get EXIF metadata.\n");
287 printf(" xmp Get XMP metadata.\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700288 printf(" frgm n Get nth fragment.\n");
James Zern974aaff2012-01-24 12:46:46 -0800289 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530290
James Zern974aaff2012-01-24 12:46:46 -0800291 printf("\n");
292 printf("SET_OPTIONS:\n");
293 printf(" Set color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700294 printf(" icc file.icc Set ICC profile.\n");
295 printf(" exif file.exif Set EXIF metadata.\n");
296 printf(" xmp file.xmp Set XMP metadata.\n");
297 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
298 printf(" 'file.exif' contains the EXIF metadata to be set\n");
299 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530300
James Zern974aaff2012-01-24 12:46:46 -0800301 printf("\n");
302 printf("STRIP_OPTIONS:\n");
303 printf(" Strip color profile/metadata.\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700304 printf(" icc Strip ICC profile.\n");
305 printf(" exif Strip EXIF metadata.\n");
306 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530307
James Zern974aaff2012-01-24 12:46:46 -0800308 printf("\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700309 printf("FRAGMENT_OPTIONS(i):\n");
310 printf(" Create fragmented image.\n");
James Zern974aaff2012-01-24 12:46:46 -0800311 printf(" file_i +xi+yi\n");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700312 printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
313 printf(" 'xi','yi' specify the image offset for this fragment."
314 "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530315
James Zern974aaff2012-01-24 12:46:46 -0800316 printf("\n");
317 printf("FRAME_OPTIONS(i):\n");
318 printf(" Create animation.\n");
319 printf(" file_i +xi+yi+di\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700320 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
James Zern974aaff2012-01-24 12:46:46 -0800321 printf(" 'xi','yi' specify the image offset for this frame.\n");
322 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530323
Urvang Joshif903cba2012-10-31 16:30:41 -0700324 printf("\nINPUT & OUTPUT are in WebP format.\n");
325
326 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked and is "
327 "assumed to be valid.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530328}
329
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530330static int ReadFileToWebPData(const char* const filename,
331 WebPData* const webp_data) {
332 const uint8_t* data;
333 size_t size;
334 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700335 webp_data->bytes = data;
336 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530337 return 1;
338}
339
James Zern061263a2012-05-11 16:00:57 -0700340static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530341 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530342 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530343 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530344 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700345 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530346 if (*mux != NULL) return 1;
347 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530348 return 0;
349}
350
James Zern0f7820e2012-01-24 14:08:27 -0800351static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530352 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700353 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530354 if (!fout) {
355 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
356 return 0;
357 }
Urvang Joshia0770722012-10-30 14:54:46 -0700358 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530359 fprintf(stderr, "Error writing file %s!\n", filename);
360 } else {
Urvang Joshia0770722012-10-30 14:54:46 -0700361 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530362 ok = 1;
363 }
364 if (fout != stdout) fclose(fout);
365 return ok;
366}
367
368static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530369 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530370 WebPData webp_data;
371 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530372 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530373 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530374 return 0;
375 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530376 ok = WriteData(filename, &webp_data);
377 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530378 return ok;
379}
380
Urvang Joshiab3234a2012-08-23 15:18:51 +0530381static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
382 return (sscanf(args, "+%d+%d+%d",
Urvang Joshia0770722012-10-30 14:54:46 -0700383 &info->x_offset, &info->y_offset, &info->duration) == 3);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530384}
385
Urvang Joshia00a3da2012-10-31 17:49:15 -0700386static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
Urvang Joshia0770722012-10-30 14:54:46 -0700387 return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530388}
389
390//------------------------------------------------------------------------------
391// Clean-up.
392
393static void DeleteConfig(WebPMuxConfig* config) {
394 if (config != NULL) {
395 free(config->feature_.args_);
396 free(config);
397 }
398}
399
400//------------------------------------------------------------------------------
401// Parsing.
402
403// Basic syntactic checks on the command-line arguments.
404// Returns 1 on valid, 0 otherwise.
405// Also fills up num_feature_args to be number of feature arguments given.
406// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
407static int ValidateCommandLine(int argc, const char* argv[],
408 int* num_feature_args) {
409 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700410 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530411 int num_loop_args;
412 int ok = 1;
413
414 assert(num_feature_args != NULL);
415 *num_feature_args = 0;
416
417 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700418 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530419 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
420 }
James Zern04e84cf2011-11-04 15:20:08 -0700421 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530422 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
423 }
James Zern04e84cf2011-11-04 15:20:08 -0700424 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530425 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
426 }
James Zern04e84cf2011-11-04 15:20:08 -0700427 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530428 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
429 }
James Zern04e84cf2011-11-04 15:20:08 -0700430 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530431 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
432 }
433
434 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700435 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700436 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700437 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530438
439 if (num_loop_args > 1) {
440 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
441 }
442
443 if (IsNotCompatible(num_frame_args, num_loop_args)) {
444 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
445 ErrValidate);
446 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700447 if (num_frame_args > 0 && num_frgm_args > 0) {
448 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
449 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530450 }
451
452 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700453 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700454 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530455 *num_feature_args = 1;
456 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700457 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530458 if (num_frame_args > 0) {
459 *num_feature_args = num_frame_args + num_loop_args;
460 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700461 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530462 }
463 }
464
465 ErrValidate:
466 return ok;
467}
468
469#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
470
471#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
472
473#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
474 if (argc < i + (NUM)) { \
475 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
476 goto LABEL; \
477 }
478
479#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
480 if (argc != i + (NUM)) { \
481 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
482 goto LABEL; \
483 }
484
485// Parses command-line arguments to fill up config object. Also performs some
486// semantic checks.
487static int ParseCommandLine(int argc, const char* argv[],
488 WebPMuxConfig* config) {
489 int i = 0;
490 int feature_arg_index = 0;
491 int ok = 1;
492
493 while (i < argc) {
494 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700495 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530496 if (argv[i][0] == '-') { // One of the action types or output.
497 if (!strcmp(argv[i], "-set")) {
498 if (ACTION_IS_NIL) {
499 config->action_type_ = ACTION_SET;
500 } else {
501 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
502 }
503 ++i;
504 } else if (!strcmp(argv[i], "-get")) {
505 if (ACTION_IS_NIL) {
506 config->action_type_ = ACTION_GET;
507 } else {
508 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
509 }
510 ++i;
511 } else if (!strcmp(argv[i], "-strip")) {
512 if (ACTION_IS_NIL) {
513 config->action_type_ = ACTION_STRIP;
514 feature->arg_count_ = 0;
515 } else {
516 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
517 }
518 ++i;
519 } else if (!strcmp(argv[i], "-frame")) {
520 CHECK_NUM_ARGS_LESS(3, ErrParse);
521 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
522 config->action_type_ = ACTION_SET;
523 } else {
524 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
525 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700526 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
527 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530528 } else {
529 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
530 }
531 arg->subtype_ = SUBTYPE_FRM;
532 arg->filename_ = argv[i + 1];
533 arg->params_ = argv[i + 2];
534 ++feature_arg_index;
535 i += 3;
536 } else if (!strcmp(argv[i], "-loop")) {
537 CHECK_NUM_ARGS_LESS(2, ErrParse);
538 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
539 config->action_type_ = ACTION_SET;
540 } else {
541 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
542 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700543 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
544 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530545 } else {
546 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
547 }
548 arg->subtype_ = SUBTYPE_LOOP;
549 arg->params_ = argv[i + 1];
550 ++feature_arg_index;
551 i += 2;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700552 } else if (!strcmp(argv[i], "-frgm")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530553 CHECK_NUM_ARGS_LESS(3, ErrParse);
554 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
555 config->action_type_ = ACTION_SET;
556 } else {
557 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
558 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700559 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
560 feature->type_ = FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530561 } else {
562 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
563 }
564 arg->filename_ = argv[i + 1];
565 arg->params_ = argv[i + 2];
566 ++feature_arg_index;
567 i += 3;
568 } else if (!strcmp(argv[i], "-o")) {
569 CHECK_NUM_ARGS_LESS(2, ErrParse);
570 config->output_ = argv[i + 1];
571 i += 2;
572 } else if (!strcmp(argv[i], "-info")) {
573 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
574 if (config->action_type_ != NIL_ACTION) {
575 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
576 } else {
577 config->action_type_ = ACTION_INFO;
578 feature->arg_count_ = 0;
579 config->input_ = argv[i + 1];
580 }
581 i += 2;
582 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
583 PrintHelp();
584 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800585 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530586 } else {
587 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
588 }
589 } else { // One of the feature types or input.
590 if (ACTION_IS_NIL) {
591 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
592 ErrParse);
593 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700594 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
595 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530596 if (FEATURETYPE_IS_NIL) {
597 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700598 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530599 } else {
600 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
601 }
602 if (config->action_type_ == ACTION_SET) {
603 CHECK_NUM_ARGS_LESS(2, ErrParse);
604 arg->filename_ = argv[i + 1];
605 ++feature_arg_index;
606 i += 2;
607 } else {
608 ++i;
609 }
610 } else if ((!strcmp(argv[i], "frame") ||
Urvang Joshia00a3da2012-10-31 17:49:15 -0700611 !strcmp(argv[i], "frgm")) &&
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530612 (config->action_type_ == ACTION_GET)) {
613 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700614 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
615 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530616 arg->params_ = argv[i + 1];
617 ++feature_arg_index;
618 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700619 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530620 if (config->input_ == NULL) {
621 config->input_ = argv[i];
622 } else {
623 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
624 argv[i], ErrParse);
625 }
626 ++i;
627 }
628 }
629 }
630 ErrParse:
631 return ok;
632}
633
James Zern04e84cf2011-11-04 15:20:08 -0700634// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530635static int ValidateConfig(WebPMuxConfig* config) {
636 int ok = 1;
637 Feature* const feature = &config->feature_;
638
639 // Action.
640 if (ACTION_IS_NIL) {
641 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
642 }
643
644 // Feature type.
645 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
646 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
647 }
648
649 // Input file.
650 if (config->input_ == NULL) {
651 if (config->action_type_ != ACTION_SET) {
652 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700653 } else if (feature->type_ != FEATURE_ANMF &&
654 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530655 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
656 }
657 }
658
659 // Output file.
660 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
661 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
662 }
663
664 ErrValidate2:
665 return ok;
666}
667
668// Create config object from command-line arguments.
669static int InitializeConfig(int argc, const char* argv[],
670 WebPMuxConfig** config) {
671 int num_feature_args = 0;
672 int ok = 1;
673
674 assert(config != NULL);
675 *config = NULL;
676
677 // Validate command-line arguments.
678 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
679 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
680 }
681
682 // Allocate memory.
683 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
684 if (*config == NULL) {
685 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
686 }
687 (*config)->feature_.arg_count_ = num_feature_args;
688 (*config)->feature_.args_ =
689 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
690 if ((*config)->feature_.args_ == NULL) {
691 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
692 }
693
694 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700695 if (!ParseCommandLine(argc, argv, *config) ||
696 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530697 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
698 }
699
700 Err1:
701 return ok;
702}
703
704#undef ACTION_IS_NIL
705#undef FEATURETYPE_IS_NIL
706#undef CHECK_NUM_ARGS_LESS
707#undef CHECK_NUM_ARGS_MORE
708
709//------------------------------------------------------------------------------
710// Processing.
711
Urvang Joshia00a3da2012-10-31 17:49:15 -0700712static int GetFrameFragment(const WebPMux* mux,
713 const WebPMuxConfig* config, int isFrame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530714 WebPMuxError err = WEBP_MUX_OK;
715 WebPMux* mux_single = NULL;
716 long num = 0;
717 int ok = 1;
Urvang Joshi92f80592012-10-30 12:14:10 -0700718 const WebPChunkId id = isFrame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530719 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700720 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530721
722 num = strtol(config->feature_.args_[0].params_, NULL, 10);
723 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700724 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530725 }
726
Urvang Joshid0c79f02012-08-23 16:28:36 +0530727 err = WebPMuxGetFrame(mux, num, &info);
728 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
729 if (err != WEBP_MUX_OK) {
730 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
731 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530732 }
733
734 mux_single = WebPMuxNew();
735 if (mux_single == NULL) {
736 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530737 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
738 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530739 }
Urvang Joshia0770722012-10-30 14:54:46 -0700740 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530741 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530742 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
743 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530744 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530745
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530746 ok = WriteWebP(mux_single, config->output_);
747
748 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700749 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530750 WebPMuxDelete(mux_single);
751 return ok;
752}
753
754// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700755static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530756 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700757 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530758 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530759 int index = 0;
760 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700761 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530762
James Zern04e84cf2011-11-04 15:20:08 -0700763 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530764 case ACTION_GET:
James Zern061263a2012-05-11 16:00:57 -0700765 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530766 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700767 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700768 case FEATURE_ANMF:
769 case FEATURE_FRGM:
770 ok = GetFrameFragment(mux, config,
771 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530772 break;
773
774 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700775 case FEATURE_EXIF:
776 case FEATURE_XMP:
777 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530778 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700779 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
780 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530781 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700782 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530783 break;
784
785 default:
786 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
787 break;
788 }
789 break;
790
791 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700792 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700793 case FEATURE_ANMF:
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530794 mux = WebPMuxNew();
795 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530796 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
797 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530798 }
799 for (index = 0; index < feature->arg_count_; ++index) {
800 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
James Zern0f7820e2012-01-24 14:08:27 -0800801 const long num = strtol(feature->args_[index].params_, NULL, 10);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530802 if (num < 0) {
803 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530804 }
James Zern0f7820e2012-01-24 14:08:27 -0800805 err = WebPMuxSetLoopCount(mux, num);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530806 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530807 ERROR_GOTO2("ERROR (%s): Could not set loop count.\n",
808 ErrorString(err), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530809 }
810 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530811 WebPMuxFrameInfo frame;
Urvang Joshib74ed6e2012-06-20 10:59:40 -0700812 ok = ReadFileToWebPData(feature->args_[index].filename_,
Urvang Joshia0770722012-10-30 14:54:46 -0700813 &frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530814 if (!ok) goto Err2;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530815 ok = ParseFrameArgs(feature->args_[index].params_, &frame);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530816 if (!ok) {
Urvang Joshia0770722012-10-30 14:54:46 -0700817 WebPDataClear(&frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530818 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
819 }
Urvang Joshi92f80592012-10-30 12:14:10 -0700820 frame.id = WEBP_CHUNK_ANMF;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530821 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700822 WebPDataClear(&frame.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530823 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530824 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d.\n",
825 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530826 }
827 } else {
828 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
829 }
830 }
831 break;
832
Urvang Joshia00a3da2012-10-31 17:49:15 -0700833 case FEATURE_FRGM:
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530834 mux = WebPMuxNew();
835 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530836 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
837 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530838 }
839 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700840 WebPMuxFrameInfo frgm;
841 frgm.id = WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530842 ok = ReadFileToWebPData(feature->args_[index].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700843 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530844 if (!ok) goto Err2;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700845 ok = ParseFragmentArgs(feature->args_[index].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530846 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700847 WebPDataClear(&frgm.bitstream);
848 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
849 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530850 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700851 err = WebPMuxPushFrame(mux, &frgm, 1);
852 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530853 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700854 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530855 ErrorString(err), index, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530856 }
857 }
858 break;
859
860 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700861 case FEATURE_EXIF:
862 case FEATURE_XMP:
James Zern061263a2012-05-11 16:00:57 -0700863 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530864 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700865 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530866 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700867 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
868 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700870 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
871 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530872 }
873 break;
874
875 default:
876 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
877 break;
878 }
879 ok = WriteWebP(mux, config->output_);
880 break;
881
882 case ACTION_STRIP:
James Zern061263a2012-05-11 16:00:57 -0700883 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530884 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700885 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
886 feature->type_ == FEATURE_XMP) {
887 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
888 if (err != WEBP_MUX_OK) {
889 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
890 ErrorString(err), kDescriptions[feature->type_], Err2);
891 }
892 } else {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530893 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
894 break;
895 }
896 ok = WriteWebP(mux, config->output_);
897 break;
898
899 case ACTION_INFO:
James Zern061263a2012-05-11 16:00:57 -0700900 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530901 if (!ok) goto Err2;
902 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
903 break;
904
905 default:
906 assert(0); // Invalid action.
907 break;
908 }
909
910 Err2:
911 WebPMuxDelete(mux);
912 return ok;
913}
914
915//------------------------------------------------------------------------------
916// Main.
917
918int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530919 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -0800920 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530921 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -0800922 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530923 } else {
924 PrintHelp();
925 }
926 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800927 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530928}
929
930//------------------------------------------------------------------------------