blob: 6ce0844d8dc93d0453564a722256f82ff4977d7a [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
James Zernd6406142013-06-06 23:05:58 -07003// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05308// -----------------------------------------------------------------------------
9//
10// Simple command-line to create a WebP container file and to extract or strip
11// relevant data from the container file.
12//
Urvang Joshia4f32ca2011-09-30 11:07:01 +053013// Authors: Vikas (vikaas.arora@gmail.com),
14// Urvang (urvang@google.com)
15
16/* Usage examples:
17
18 Create container WebP file:
skal48600082012-11-14 06:19:31 +010019 webpmux -frame anim_1.webp +100+10+10 \
20 -frame anim_2.webp +100+25+25+1 \
21 -frame anim_3.webp +100+50+50+1 \
22 -frame anim_4.webp +100 \
23 -loop 10 -bgcolor 128,255,255,255 \
Urvang Joshia4f32ca2011-09-30 11:07:01 +053024 -o out_animation_container.webp
25
26 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070027 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
28 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053029
Urvang Joshia4f32ca2011-09-30 11:07:01 +053030 Extract relevant data from WebP container file:
Urvang Joshia00a3da2012-10-31 17:49:15 -070031 webpmux -get frgm n in.webp -o out_fragment.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053032 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053033 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshif903cba2012-10-31 16:30:41 -070034 webpmux -get exif in.webp -o image_metadata.exif
35 webpmux -get xmp in.webp -o image_metadata.xmp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070038 webpmux -strip icc in.webp -o out.webp
Urvang Joshif903cba2012-10-31 16:30:41 -070039 webpmux -strip exif in.webp -o out.webp
40 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053041
42 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053043 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070044 webpmux [ -h | -help ]
Urvang Joshia5042a32013-02-26 14:22:06 -080045 webpmux -version
Urvang Joshia4f32ca2011-09-30 11:07:01 +053046*/
47
Urvang Joshi5dbd4032013-03-15 14:46:12 -070048#ifdef HAVE_CONFIG_H
James Zern32b31372014-06-10 17:53:44 -070049#include "webp/config.h"
Urvang Joshi5dbd4032013-03-15 14:46:12 -070050#endif
51
Urvang Joshia4f32ca2011-09-30 11:07:01 +053052#include <assert.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
Urvang Joshi0e6747f2013-09-16 15:29:24 -070056#include "webp/decode.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057#include "webp/mux.h"
James Zern061263a2012-05-11 16:00:57 -070058#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053059
Urvang Joshia4f32ca2011-09-30 11:07:01 +053060//------------------------------------------------------------------------------
61// Config object to parse command-line arguments.
62
63typedef enum {
64 NIL_ACTION = 0,
65 ACTION_GET,
66 ACTION_SET,
67 ACTION_STRIP,
68 ACTION_INFO,
69 ACTION_HELP
70} ActionType;
71
72typedef enum {
73 NIL_SUBTYPE = 0,
Urvang Joshifa30c862012-11-01 15:34:46 -070074 SUBTYPE_ANMF,
75 SUBTYPE_LOOP,
76 SUBTYPE_BGCOLOR
Urvang Joshia4f32ca2011-09-30 11:07:01 +053077} FeatureSubType;
78
79typedef struct {
80 FeatureSubType subtype_;
81 const char* filename_;
82 const char* params_;
83} FeatureArg;
84
85typedef enum {
86 NIL_FEATURE = 0,
Urvang Joshif903cba2012-10-31 16:30:41 -070087 FEATURE_EXIF,
88 FEATURE_XMP,
Urvang Joshia4f32ca2011-09-30 11:07:01 +053089 FEATURE_ICCP,
Urvang Joshia00a3da2012-10-31 17:49:15 -070090 FEATURE_ANMF,
91 FEATURE_FRGM,
Urvang Joshif903cba2012-10-31 16:30:41 -070092 LAST_FEATURE
Urvang Joshia4f32ca2011-09-30 11:07:01 +053093} FeatureType;
94
Urvang Joshif903cba2012-10-31 16:30:41 -070095static const char* const kFourccList[LAST_FEATURE] = {
96 NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
97};
98
99static const char* const kDescriptions[LAST_FEATURE] = {
100 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Urvang Joshia00a3da2012-10-31 17:49:15 -0700101 "Animation frame", "Image fragment"
Urvang Joshif903cba2012-10-31 16:30:41 -0700102};
103
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530104typedef struct {
105 FeatureType type_;
106 FeatureArg* args_;
107 int arg_count_;
108} Feature;
109
110typedef struct {
111 ActionType action_type_;
112 const char* input_;
113 const char* output_;
114 Feature feature_;
115} WebPMuxConfig;
116
117//------------------------------------------------------------------------------
118// Helper functions.
119
James Zern04e84cf2011-11-04 15:20:08 -0700120static int CountOccurrences(const char* arglist[], int list_length,
121 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530122 int i;
123 int num_occurences = 0;
124
125 for (i = 0; i < list_length; ++i) {
126 if (!strcmp(arglist[i], arg)) {
127 ++num_occurences;
128 }
129 }
130 return num_occurences;
131}
132
skal0b747b12014-07-21 15:44:43 +0200133static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
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
Urvang Joshid0c79f02012-08-23 16:28:36 +0530149#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
150 if (err != WEBP_MUX_OK) { \
151 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
152 return err; \
153 }
154
James Zerna0b27362012-01-27 17:39:47 -0800155#define ERROR_GOTO1(ERR_MSG, LABEL) \
156 do { \
157 fprintf(stderr, ERR_MSG); \
158 ok = 0; \
159 goto LABEL; \
160 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530161
James Zerna0b27362012-01-27 17:39:47 -0800162#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
163 do { \
164 fprintf(stderr, ERR_MSG, FORMAT_STR); \
165 ok = 0; \
166 goto LABEL; \
167 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530168
James Zerna0b27362012-01-27 17:39:47 -0800169#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
Urvang Joshi6393fe42013-04-26 15:55:42 -0700170 do { \
171 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
172 ok = 0; \
173 goto LABEL; \
174 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530175
176static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshifffefd12013-05-02 13:54:25 -0700177 int width, height;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530178 uint32_t flag;
179
Urvang Joshifffefd12013-05-02 13:54:25 -0700180 WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
Urvang Joshi54b8e3f2013-09-18 11:44:24 -0700181 assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier.
Urvang Joshifffefd12013-05-02 13:54:25 -0700182 printf("Canvas size: %d x %d\n", width, height);
183
184 err = WebPMuxGetFeatures(mux, &flag);
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700185 if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530186 RETURN_IF_ERROR("Failed to retrieve features\n");
187
188 if (flag == 0) {
189 fprintf(stderr, "No features present.\n");
190 return err;
191 }
192
193 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800194 printf("Features present:");
195 if (flag & ANIMATION_FLAG) printf(" animation");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700196 if (flag & FRAGMENTS_FLAG) printf(" image fragments");
James Zernd8dc72a2013-03-13 14:04:20 -0700197 if (flag & ICCP_FLAG) printf(" ICC profile");
Urvang Joshif903cba2012-10-31 16:30:41 -0700198 if (flag & EXIF_FLAG) printf(" EXIF metadata");
199 if (flag & XMP_FLAG) printf(" XMP metadata");
James Zern974aaff2012-01-24 12:46:46 -0800200 if (flag & ALPHA_FLAG) printf(" transparency");
201 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530202
Urvang Joshia00a3da2012-10-31 17:49:15 -0700203 if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530204 const int is_anim = !!(flag & ANIMATION_FLAG);
Urvang Joshi92f80592012-10-30 12:14:10 -0700205 const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700206 const char* const type_str = is_anim ? "frame" : "fragment";
James Zerneec4b872012-01-07 12:44:01 -0800207 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530208
Urvang Joshid0c79f02012-08-23 16:28:36 +0530209 if (is_anim) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700210 WebPMuxAnimParams params;
211 err = WebPMuxGetAnimationParams(mux, &params);
Urvang Joshi54b8e3f2013-09-18 11:44:24 -0700212 assert(err == WEBP_MUX_OK);
Urvang Joshifa30c862012-11-01 15:34:46 -0700213 printf("Background color : 0x%.8X Loop Count : %d\n",
214 params.bgcolor, params.loop_count);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530215 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530216
Urvang Joshid0c79f02012-08-23 16:28:36 +0530217 err = WebPMuxNumChunks(mux, id, &nFrames);
Urvang Joshi54b8e3f2013-09-18 11:44:24 -0700218 assert(err == WEBP_MUX_OK);
Urvang Joshid0c79f02012-08-23 16:28:36 +0530219
220 printf("Number of %ss: %d\n", type_str, nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530221 if (nFrames > 0) {
222 int i;
Urvang Joshi0e6747f2013-09-16 15:29:24 -0700223 printf("No.: width height alpha x_offset y_offset ");
224 if (is_anim) printf("duration dispose blend ");
Urvang Joshid0c79f02012-08-23 16:28:36 +0530225 printf("image_size\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530226 for (i = 1; i <= nFrames; i++) {
Urvang Joshiab3234a2012-08-23 15:18:51 +0530227 WebPMuxFrameInfo frame;
228 err = WebPMuxGetFrame(mux, i, &frame);
skal3e59a742013-05-22 00:58:53 +0200229 if (err == WEBP_MUX_OK) {
Urvang Joshi0e6747f2013-09-16 15:29:24 -0700230 WebPBitstreamFeatures features;
231 const VP8StatusCode status = WebPGetFeatures(
232 frame.bitstream.bytes, frame.bitstream.size, &features);
233 assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate().
234 (void)status;
235 printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
236 features.height, features.has_alpha ? "yes" : "no",
237 frame.x_offset, frame.y_offset);
Urvang Joshie81fac82013-08-26 18:04:52 -0700238 if (is_anim) {
Urvang Joshi0e6747f2013-09-16 15:29:24 -0700239 const char* const dispose =
240 (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
241 : "background";
242 const char* const blend =
243 (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
244 printf("%8d %10s %5s ", frame.duration, dispose, blend);
Urvang Joshie81fac82013-08-26 18:04:52 -0700245 }
skal3e59a742013-05-22 00:58:53 +0200246 printf("%10d\n", (int)frame.bitstream.size);
247 }
Urvang Joshia0770722012-10-30 14:54:46 -0700248 WebPDataClear(&frame.bitstream);
skal3e59a742013-05-22 00:58:53 +0200249 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530250 }
251 }
252 }
253
254 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800255 WebPData icc_profile;
Urvang Joshi1c04a0d2012-08-23 15:28:20 +0530256 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Urvang Joshi54b8e3f2013-09-18 11:44:24 -0700257 assert(err == WEBP_MUX_OK);
James Zern14d42af2013-03-20 16:59:35 -0700258 printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530259 }
260
Urvang Joshif903cba2012-10-31 16:30:41 -0700261 if (flag & EXIF_FLAG) {
262 WebPData exif;
263 err = WebPMuxGetChunk(mux, "EXIF", &exif);
Urvang Joshi54b8e3f2013-09-18 11:44:24 -0700264 assert(err == WEBP_MUX_OK);
James Zern14d42af2013-03-20 16:59:35 -0700265 printf("Size of the EXIF metadata: %d\n", (int)exif.size);
Urvang Joshif903cba2012-10-31 16:30:41 -0700266 }
267
268 if (flag & XMP_FLAG) {
269 WebPData xmp;
270 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
Urvang Joshi54b8e3f2013-09-18 11:44:24 -0700271 assert(err == WEBP_MUX_OK);
James Zern14d42af2013-03-20 16:59:35 -0700272 printf("Size of the XMP metadata: %d\n", (int)xmp.size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530273 }
274
Urvang Joshia00a3da2012-10-31 17:49:15 -0700275 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
Urvang Joshid0c79f02012-08-23 16:28:36 +0530276 WebPMuxFrameInfo image;
277 err = WebPMuxGetFrame(mux, 1, &image);
skal3e59a742013-05-22 00:58:53 +0200278 if (err == WEBP_MUX_OK) {
279 printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
280 }
281 WebPDataClear(&image.bitstream);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530282 RETURN_IF_ERROR("Failed to retrieve the image\n");
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530283 }
284
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530285 return WEBP_MUX_OK;
286}
287
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700288static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800289 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
290 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
291 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700292 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
293 "\n");
294 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
James Zern974aaff2012-01-24 12:46:46 -0800295 printf(" webpmux -info INPUT\n");
296 printf(" webpmux [-h|-help]\n");
Urvang Joshia5042a32013-02-26 14:22:06 -0800297 printf(" webpmux -version\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530298
James Zern974aaff2012-01-24 12:46:46 -0800299 printf("\n");
300 printf("GET_OPTIONS:\n");
skal0a8b8862014-06-18 16:45:34 +0200301 printf(" Extract relevant data:\n");
302 printf(" icc get ICC profile\n");
303 printf(" exif get EXIF metadata\n");
304 printf(" xmp get XMP metadata\n");
skal0a8b8862014-06-18 16:45:34 +0200305 printf(" frame n get nth frame\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530306
James Zern974aaff2012-01-24 12:46:46 -0800307 printf("\n");
308 printf("SET_OPTIONS:\n");
skal0a8b8862014-06-18 16:45:34 +0200309 printf(" Set color profile/metadata:\n");
310 printf(" icc file.icc set ICC profile\n");
311 printf(" exif file.exif set EXIF metadata\n");
312 printf(" xmp file.xmp set XMP metadata\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700313 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
314 printf(" 'file.exif' contains the EXIF metadata to be set\n");
315 printf(" 'file.xmp' contains the XMP metadata to be set\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530316
James Zern974aaff2012-01-24 12:46:46 -0800317 printf("\n");
318 printf("STRIP_OPTIONS:\n");
skal0a8b8862014-06-18 16:45:34 +0200319 printf(" Strip color profile/metadata:\n");
320 printf(" icc strip ICC profile\n");
321 printf(" exif strip EXIF metadata\n");
322 printf(" xmp strip XMP metadata\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530323
James Zern974aaff2012-01-24 12:46:46 -0800324 printf("\n");
325 printf("FRAME_OPTIONS(i):\n");
skal0a8b8862014-06-18 16:45:34 +0200326 printf(" Create animation:\n");
Urvang Joshie81fac82013-08-26 18:04:52 -0700327 printf(" file_i +di+[xi+yi[+mi[bi]]]\n");
Urvang Joshif903cba2012-10-31 16:30:41 -0700328 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
skal0a8b8862014-06-18 16:45:34 +0200329 printf(" 'di' is the pause duration before next frame,\n");
330 printf(" 'xi','yi' specify the image offset for this frame,\n");
331 printf(" 'mi' is the dispose method for this frame (0 or 1),\n");
332 printf(" 'bi' is the blending method for this frame (+b or -b)"
Urvang Joshie81fac82013-08-26 18:04:52 -0700333 "\n");
Urvang Joshifa30c862012-11-01 15:34:46 -0700334
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");
skal0a8b8862014-06-18 16:45:34 +0200348 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
James Zernc0726632014-06-17 23:21:49 -0700356static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
357 if ((info->x_offset | info->y_offset) & 1) {
358 fprintf(stderr, "Warning: odd offsets will be snapped to even values"
359 " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
360 info->x_offset & ~1, info->y_offset & ~1);
361 }
362}
363
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530364static int ReadFileToWebPData(const char* const filename,
365 WebPData* const webp_data) {
366 const uint8_t* data;
367 size_t size;
368 if (!ExUtilReadFile(filename, &data, &size)) return 0;
Urvang Joshia0770722012-10-30 14:54:46 -0700369 webp_data->bytes = data;
370 webp_data->size = size;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530371 return 1;
372}
373
James Zern061263a2012-05-11 16:00:57 -0700374static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530375 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530376 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530377 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530378 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700379 free((void*)bitstream.bytes);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530380 if (*mux != NULL) return 1;
381 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530382 return 0;
383}
384
James Zern0f7820e2012-01-24 14:08:27 -0800385static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530386 int ok = 0;
James Zerne80eab12014-08-29 19:11:41 -0700387 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb")
388 : ExUtilSetBinaryMode(stdout);
389 if (fout == NULL) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530390 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
391 return 0;
392 }
Urvang Joshia0770722012-10-30 14:54:46 -0700393 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530394 fprintf(stderr, "Error writing file %s!\n", filename);
395 } else {
James Zern14d42af2013-03-20 16:59:35 -0700396 fprintf(stderr, "Saved file %s (%d bytes)\n",
397 filename, (int)webpdata->size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530398 ok = 1;
399 }
400 if (fout != stdout) fclose(fout);
401 return ok;
402}
403
404static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530405 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530406 WebPData webp_data;
407 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530408 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530409 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530410 return 0;
411 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530412 ok = WriteData(filename, &webp_data);
413 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530414 return ok;
415}
416
Urvang Joshiab3234a2012-08-23 15:18:51 +0530417static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
skal48600082012-11-14 06:19:31 +0100418 int dispose_method, dummy;
Urvang Joshie81fac82013-08-26 18:04:52 -0700419 char plus_minus, blend_method;
420 const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
421 &info->x_offset, &info->y_offset, &dispose_method,
422 &plus_minus, &blend_method, &dummy);
skal48600082012-11-14 06:19:31 +0100423 switch (num_args) {
424 case 1:
425 info->x_offset = info->y_offset = 0; // fall through
426 case 3:
427 dispose_method = 0; // fall through
428 case 4:
Urvang Joshie81fac82013-08-26 18:04:52 -0700429 plus_minus = '+';
430 blend_method = 'b'; // fall through
431 case 6:
skal48600082012-11-14 06:19:31 +0100432 break;
Urvang Joshie81fac82013-08-26 18:04:52 -0700433 case 2:
434 case 5:
skal48600082012-11-14 06:19:31 +0100435 default:
436 return 0;
Urvang Joshifa30c862012-11-01 15:34:46 -0700437 }
James Zernc0726632014-06-17 23:21:49 -0700438
439 WarnAboutOddOffset(info);
440
Urvang Joshifa30c862012-11-01 15:34:46 -0700441 // Note: The sanity of the following conversion is checked by
Urvang Joshie81fac82013-08-26 18:04:52 -0700442 // WebPMuxPushFrame().
Urvang Joshifa30c862012-11-01 15:34:46 -0700443 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
Urvang Joshie81fac82013-08-26 18:04:52 -0700444
445 if (blend_method != 'b') return 0;
446 if (plus_minus != '-' && plus_minus != '+') return 0;
447 info->blend_method =
448 (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
Urvang Joshifa30c862012-11-01 15:34:46 -0700449 return 1;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530450}
451
Urvang Joshia00a3da2012-10-31 17:49:15 -0700452static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
James Zernc0726632014-06-17 23:21:49 -0700453 const int ok =
454 (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
455 if (ok) WarnAboutOddOffset(info);
456 return ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530457}
458
Urvang Joshifa30c862012-11-01 15:34:46 -0700459static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
460 uint32_t a, r, g, b;
461 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
462 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
463 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
464 return 1;
465}
466
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530467//------------------------------------------------------------------------------
468// Clean-up.
469
470static void DeleteConfig(WebPMuxConfig* config) {
471 if (config != NULL) {
472 free(config->feature_.args_);
James Zernc0a462c2014-09-12 00:03:28 -0700473 memset(config, 0, sizeof(*config));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530474 }
475}
476
477//------------------------------------------------------------------------------
478// Parsing.
479
480// Basic syntactic checks on the command-line arguments.
481// Returns 1 on valid, 0 otherwise.
482// Also fills up num_feature_args to be number of feature arguments given.
483// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
484static int ValidateCommandLine(int argc, const char* argv[],
485 int* num_feature_args) {
486 int num_frame_args;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700487 int num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530488 int num_loop_args;
Urvang Joshifa30c862012-11-01 15:34:46 -0700489 int num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530490 int ok = 1;
491
492 assert(num_feature_args != NULL);
493 *num_feature_args = 0;
494
495 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700496 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530497 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
498 }
James Zern04e84cf2011-11-04 15:20:08 -0700499 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530500 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
501 }
James Zern04e84cf2011-11-04 15:20:08 -0700502 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530503 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
504 }
James Zern04e84cf2011-11-04 15:20:08 -0700505 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530506 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
507 }
James Zern04e84cf2011-11-04 15:20:08 -0700508 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530509 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
510 }
511
512 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700513 num_frame_args = CountOccurrences(argv, argc, "-frame");
Urvang Joshia00a3da2012-10-31 17:49:15 -0700514 num_frgm_args = CountOccurrences(argv, argc, "-frgm");
James Zern04e84cf2011-11-04 15:20:08 -0700515 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshifa30c862012-11-01 15:34:46 -0700516 num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530517
518 if (num_loop_args > 1) {
519 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
520 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700521 if (num_bgcolor_args > 1) {
522 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
523 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530524
Urvang Joshifa30c862012-11-01 15:34:46 -0700525 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
526 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
527 "case of animation.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530528 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700529 if (num_frame_args > 0 && num_frgm_args > 0) {
530 ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
531 "time.\n", ErrValidate);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530532 }
533
534 assert(ok == 1);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700535 if (num_frame_args == 0 && num_frgm_args == 0) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700536 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530537 *num_feature_args = 1;
538 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700539 // Multiple arguments ('set' action for animation or fragmented image).
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530540 if (num_frame_args > 0) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700541 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530542 } else {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700543 *num_feature_args = num_frgm_args;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530544 }
545 }
546
547 ErrValidate:
548 return ok;
549}
550
551#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
552
553#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
554
555#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
556 if (argc < i + (NUM)) { \
557 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
558 goto LABEL; \
559 }
560
561#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
562 if (argc != i + (NUM)) { \
563 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
564 goto LABEL; \
565 }
566
567// Parses command-line arguments to fill up config object. Also performs some
568// semantic checks.
569static int ParseCommandLine(int argc, const char* argv[],
570 WebPMuxConfig* config) {
571 int i = 0;
572 int feature_arg_index = 0;
573 int ok = 1;
574
575 while (i < argc) {
576 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700577 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530578 if (argv[i][0] == '-') { // One of the action types or output.
579 if (!strcmp(argv[i], "-set")) {
580 if (ACTION_IS_NIL) {
581 config->action_type_ = ACTION_SET;
582 } else {
583 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
584 }
585 ++i;
586 } else if (!strcmp(argv[i], "-get")) {
587 if (ACTION_IS_NIL) {
588 config->action_type_ = ACTION_GET;
589 } else {
590 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
591 }
592 ++i;
593 } else if (!strcmp(argv[i], "-strip")) {
594 if (ACTION_IS_NIL) {
595 config->action_type_ = ACTION_STRIP;
596 feature->arg_count_ = 0;
597 } else {
598 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
599 }
600 ++i;
601 } else if (!strcmp(argv[i], "-frame")) {
602 CHECK_NUM_ARGS_LESS(3, ErrParse);
603 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
604 config->action_type_ = ACTION_SET;
605 } else {
606 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
607 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700608 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
609 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530610 } else {
611 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
612 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700613 arg->subtype_ = SUBTYPE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530614 arg->filename_ = argv[i + 1];
615 arg->params_ = argv[i + 2];
616 ++feature_arg_index;
617 i += 3;
Urvang Joshifa30c862012-11-01 15:34:46 -0700618 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530619 CHECK_NUM_ARGS_LESS(2, ErrParse);
620 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
621 config->action_type_ = ACTION_SET;
622 } else {
623 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
624 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700625 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
626 feature->type_ = FEATURE_ANMF;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530627 } else {
628 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
629 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700630 arg->subtype_ =
631 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530632 arg->params_ = argv[i + 1];
633 ++feature_arg_index;
634 i += 2;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530635 } else if (!strcmp(argv[i], "-o")) {
636 CHECK_NUM_ARGS_LESS(2, ErrParse);
637 config->output_ = argv[i + 1];
638 i += 2;
639 } else if (!strcmp(argv[i], "-info")) {
640 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
641 if (config->action_type_ != NIL_ACTION) {
642 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
643 } else {
644 config->action_type_ = ACTION_INFO;
645 feature->arg_count_ = 0;
646 config->input_ = argv[i + 1];
647 }
648 i += 2;
649 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
650 PrintHelp();
651 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800652 exit(0);
Urvang Joshia5042a32013-02-26 14:22:06 -0800653 } else if (!strcmp(argv[i], "-version")) {
654 const int version = WebPGetMuxVersion();
655 printf("%d.%d.%d\n",
656 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
657 DeleteConfig(config);
658 exit(0);
James Zernb4fa0a42013-12-17 20:50:46 -0800659 } else if (!strcmp(argv[i], "--")) {
660 if (i < argc - 1) {
661 ++i;
662 if (config->input_ == NULL) {
663 config->input_ = argv[i];
664 } else {
665 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
666 argv[i], ErrParse);
667 }
668 }
669 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530670 } else {
671 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
672 }
673 } else { // One of the feature types or input.
674 if (ACTION_IS_NIL) {
675 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
676 ErrParse);
677 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700678 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
679 !strcmp(argv[i], "xmp")) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530680 if (FEATURETYPE_IS_NIL) {
681 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Urvang Joshif903cba2012-10-31 16:30:41 -0700682 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530683 } else {
684 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
685 }
686 if (config->action_type_ == ACTION_SET) {
687 CHECK_NUM_ARGS_LESS(2, ErrParse);
688 arg->filename_ = argv[i + 1];
689 ++feature_arg_index;
690 i += 2;
691 } else {
692 ++i;
693 }
Urvang Joshi5dbd4032013-03-15 14:46:12 -0700694 } else if (!strcmp(argv[i], "frame") &&
James Zernac79ed12015-01-08 20:03:51 -0800695 (config->action_type_ == ACTION_GET)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530696 CHECK_NUM_ARGS_LESS(2, ErrParse);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700697 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
698 FEATURE_FRGM;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530699 arg->params_ = argv[i + 1];
700 ++feature_arg_index;
701 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700702 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530703 if (config->input_ == NULL) {
704 config->input_ = argv[i];
705 } else {
706 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
707 argv[i], ErrParse);
708 }
709 ++i;
710 }
711 }
712 }
713 ErrParse:
714 return ok;
715}
716
James Zern04e84cf2011-11-04 15:20:08 -0700717// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530718static int ValidateConfig(WebPMuxConfig* config) {
719 int ok = 1;
720 Feature* const feature = &config->feature_;
721
722 // Action.
723 if (ACTION_IS_NIL) {
724 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
725 }
726
727 // Feature type.
728 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
729 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
730 }
731
732 // Input file.
733 if (config->input_ == NULL) {
734 if (config->action_type_ != ACTION_SET) {
735 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Urvang Joshia00a3da2012-10-31 17:49:15 -0700736 } else if (feature->type_ != FEATURE_ANMF &&
737 feature->type_ != FEATURE_FRGM) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530738 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
739 }
740 }
741
742 // Output file.
743 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
744 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
745 }
746
747 ErrValidate2:
748 return ok;
749}
750
751// Create config object from command-line arguments.
752static int InitializeConfig(int argc, const char* argv[],
James Zernc0a462c2014-09-12 00:03:28 -0700753 WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530754 int num_feature_args = 0;
755 int ok = 1;
756
757 assert(config != NULL);
James Zernc0a462c2014-09-12 00:03:28 -0700758 memset(config, 0, sizeof(*config));
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530759
760 // Validate command-line arguments.
761 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
762 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
763 }
764
James Zernc0a462c2014-09-12 00:03:28 -0700765 config->feature_.arg_count_ = num_feature_args;
766 config->feature_.args_ =
767 (FeatureArg*)calloc(num_feature_args, sizeof(*config->feature_.args_));
768 if (config->feature_.args_ == NULL) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530769 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
770 }
771
772 // Parse command-line.
James Zernc0a462c2014-09-12 00:03:28 -0700773 if (!ParseCommandLine(argc, argv, config) || !ValidateConfig(config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530774 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
775 }
776
777 Err1:
778 return ok;
779}
780
781#undef ACTION_IS_NIL
782#undef FEATURETYPE_IS_NIL
783#undef CHECK_NUM_ARGS_LESS
784#undef CHECK_NUM_ARGS_MORE
785
786//------------------------------------------------------------------------------
787// Processing.
788
Urvang Joshia00a3da2012-10-31 17:49:15 -0700789static int GetFrameFragment(const WebPMux* mux,
Urvang Joshi7681bb92013-03-13 18:10:56 -0700790 const WebPMuxConfig* config, int is_frame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530791 WebPMuxError err = WEBP_MUX_OK;
792 WebPMux* mux_single = NULL;
793 long num = 0;
794 int ok = 1;
James Zern96d43a82014-09-10 23:35:48 -0700795 int parse_error = 0;
Urvang Joshi7681bb92013-03-13 18:10:56 -0700796 const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
Urvang Joshiab3234a2012-08-23 15:18:51 +0530797 WebPMuxFrameInfo info;
Urvang Joshia0770722012-10-30 14:54:46 -0700798 WebPDataInit(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530799
James Zern96d43a82014-09-10 23:35:48 -0700800 num = ExUtilGetInt(config->feature_.args_[0].params_, 10, &parse_error);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530801 if (num < 0) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700802 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530803 }
James Zern96d43a82014-09-10 23:35:48 -0700804 if (parse_error) goto ErrGet;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530805
Urvang Joshid0c79f02012-08-23 16:28:36 +0530806 err = WebPMuxGetFrame(mux, num, &info);
807 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
808 if (err != WEBP_MUX_OK) {
809 ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
810 ErrorString(err), num, ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530811 }
812
813 mux_single = WebPMuxNew();
814 if (mux_single == NULL) {
815 err = WEBP_MUX_MEMORY_ERROR;
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530816 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
817 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530818 }
Urvang Joshia0770722012-10-30 14:54:46 -0700819 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530820 if (err != WEBP_MUX_OK) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530821 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
822 ErrorString(err), ErrGet);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530823 }
Urvang Joshid0c79f02012-08-23 16:28:36 +0530824
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530825 ok = WriteWebP(mux_single, config->output_);
826
827 ErrGet:
Urvang Joshia0770722012-10-30 14:54:46 -0700828 WebPDataClear(&info.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530829 WebPMuxDelete(mux_single);
James Zernf89e1692014-09-11 23:36:12 -0700830 return ok && !parse_error;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530831}
832
833// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700834static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530835 WebPMux* mux = NULL;
Urvang Joshif903cba2012-10-31 16:30:41 -0700836 WebPData chunk;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530837 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530838 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700839 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530840
James Zern04e84cf2011-11-04 15:20:08 -0700841 switch (config->action_type_) {
skal0d19fbf2013-01-21 17:20:14 +0100842 case ACTION_GET: {
James Zern061263a2012-05-11 16:00:57 -0700843 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530844 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700845 switch (feature->type_) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700846 case FEATURE_ANMF:
Urvang Joshi7caab1d2012-11-07 16:04:08 -0800847 case FEATURE_FRGM:
848 ok = GetFrameFragment(mux, config,
849 (feature->type_ == FEATURE_ANMF) ? 1 : 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530850 break;
851
852 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700853 case FEATURE_EXIF:
854 case FEATURE_XMP:
855 err = WebPMuxGetChunk(mux, kFourccList[feature->type_], &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530856 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700857 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
858 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530859 }
Urvang Joshif903cba2012-10-31 16:30:41 -0700860 ok = WriteData(config->output_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530861 break;
862
863 default:
864 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
865 break;
866 }
867 break;
skal0d19fbf2013-01-21 17:20:14 +0100868 }
869 case ACTION_SET: {
James Zern04e84cf2011-11-04 15:20:08 -0700870 switch (feature->type_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700871 case FEATURE_ANMF: {
skal0d19fbf2013-01-21 17:20:14 +0100872 int i;
Urvang Joshifa30c862012-11-01 15:34:46 -0700873 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530874 mux = WebPMuxNew();
875 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530876 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
877 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530878 }
skal0d19fbf2013-01-21 17:20:14 +0100879 for (i = 0; i < feature->arg_count_; ++i) {
880 switch (feature->args_[i].subtype_) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700881 case SUBTYPE_BGCOLOR: {
882 uint32_t bgcolor;
skal0d19fbf2013-01-21 17:20:14 +0100883 ok = ParseBgcolorArgs(feature->args_[i].params_, &bgcolor);
Urvang Joshifa30c862012-11-01 15:34:46 -0700884 if (!ok) {
885 ERROR_GOTO1("ERROR: Could not parse the background color \n",
886 Err2);
887 }
888 params.bgcolor = bgcolor;
889 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530890 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700891 case SUBTYPE_LOOP: {
James Zern96d43a82014-09-10 23:35:48 -0700892 int parse_error = 0;
893 const int loop_count =
894 ExUtilGetInt(feature->args_[i].params_, 10, &parse_error);
James Zern0e23c482014-09-11 15:26:11 -0700895 if (loop_count < 0 || loop_count > 65535) {
Urvang Joshifa30c862012-11-01 15:34:46 -0700896 // Note: This is only a 'necessary' condition for loop_count
897 // to be valid. The 'sufficient' conditioned in checked in
898 // WebPMuxSetAnimationParams() method called later.
899 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
900 "65535.\n", Err2);
901 }
James Zernf89e1692014-09-11 23:36:12 -0700902 ok = !parse_error;
903 if (!ok) goto Err2;
James Zern6986bb52014-09-11 23:53:05 -0700904 params.loop_count = loop_count;
Urvang Joshifa30c862012-11-01 15:34:46 -0700905 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530906 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700907 case SUBTYPE_ANMF: {
908 WebPMuxFrameInfo frame;
909 frame.id = WEBP_CHUNK_ANMF;
skal0d19fbf2013-01-21 17:20:14 +0100910 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshifa30c862012-11-01 15:34:46 -0700911 &frame.bitstream);
912 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100913 ok = ParseFrameArgs(feature->args_[i].params_, &frame);
Urvang Joshifa30c862012-11-01 15:34:46 -0700914 if (!ok) {
915 WebPDataClear(&frame.bitstream);
916 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
917 Err2);
918 }
919 err = WebPMuxPushFrame(mux, &frame, 1);
Urvang Joshia0770722012-10-30 14:54:46 -0700920 WebPDataClear(&frame.bitstream);
Urvang Joshifa30c862012-11-01 15:34:46 -0700921 if (err != WEBP_MUX_OK) {
922 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
skal0d19fbf2013-01-21 17:20:14 +0100923 "\n", ErrorString(err), i, Err2);
Urvang Joshifa30c862012-11-01 15:34:46 -0700924 }
925 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530926 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700927 default: {
928 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
929 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530930 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530931 }
932 }
Urvang Joshifa30c862012-11-01 15:34:46 -0700933 err = WebPMuxSetAnimationParams(mux, &params);
934 if (err != WEBP_MUX_OK) {
935 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
936 ErrorString(err), Err2);
937 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530938 break;
Urvang Joshifa30c862012-11-01 15:34:46 -0700939 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940
skal0d19fbf2013-01-21 17:20:14 +0100941 case FEATURE_FRGM: {
942 int i;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530943 mux = WebPMuxNew();
944 if (mux == NULL) {
Urvang Joshid11f6fc2012-06-27 16:55:01 +0530945 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
946 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530947 }
skal0d19fbf2013-01-21 17:20:14 +0100948 for (i = 0; i < feature->arg_count_; ++i) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700949 WebPMuxFrameInfo frgm;
950 frgm.id = WEBP_CHUNK_FRGM;
skal0d19fbf2013-01-21 17:20:14 +0100951 ok = ReadFileToWebPData(feature->args_[i].filename_,
Urvang Joshia00a3da2012-10-31 17:49:15 -0700952 &frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530953 if (!ok) goto Err2;
skal0d19fbf2013-01-21 17:20:14 +0100954 ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530955 if (!ok) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700956 WebPDataClear(&frgm.bitstream);
957 ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
958 Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530959 }
Urvang Joshia00a3da2012-10-31 17:49:15 -0700960 err = WebPMuxPushFrame(mux, &frgm, 1);
961 WebPDataClear(&frgm.bitstream);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530962 if (err != WEBP_MUX_OK) {
Urvang Joshia00a3da2012-10-31 17:49:15 -0700963 ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
skal0d19fbf2013-01-21 17:20:14 +0100964 ErrorString(err), i, Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530965 }
966 }
967 break;
skal0d19fbf2013-01-21 17:20:14 +0100968 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530969
970 case FEATURE_ICCP:
Urvang Joshif903cba2012-10-31 16:30:41 -0700971 case FEATURE_EXIF:
skal0d19fbf2013-01-21 17:20:14 +0100972 case FEATURE_XMP: {
James Zern061263a2012-05-11 16:00:57 -0700973 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530974 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700975 ok = ReadFileToWebPData(feature->args_[0].filename_, &chunk);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530976 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700977 err = WebPMuxSetChunk(mux, kFourccList[feature->type_], &chunk, 1);
978 free((void*)chunk.bytes);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530979 if (err != WEBP_MUX_OK) {
Urvang Joshif903cba2012-10-31 16:30:41 -0700980 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
981 ErrorString(err), kDescriptions[feature->type_], Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530982 }
983 break;
skal0d19fbf2013-01-21 17:20:14 +0100984 }
985 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530986 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
987 break;
skal0d19fbf2013-01-21 17:20:14 +0100988 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530989 }
990 ok = WriteWebP(mux, config->output_);
991 break;
skal0d19fbf2013-01-21 17:20:14 +0100992 }
993 case ACTION_STRIP: {
James Zern061263a2012-05-11 16:00:57 -0700994 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530995 if (!ok) goto Err2;
Urvang Joshif903cba2012-10-31 16:30:41 -0700996 if (feature->type_ == FEATURE_ICCP || feature->type_ == FEATURE_EXIF ||
997 feature->type_ == FEATURE_XMP) {
998 err = WebPMuxDeleteChunk(mux, kFourccList[feature->type_]);
999 if (err != WEBP_MUX_OK) {
1000 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
1001 ErrorString(err), kDescriptions[feature->type_], Err2);
1002 }
1003 } else {
James Zern6986bb52014-09-11 23:53:05 -07001004 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1005 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301006 }
1007 ok = WriteWebP(mux, config->output_);
1008 break;
skal0d19fbf2013-01-21 17:20:14 +01001009 }
1010 case ACTION_INFO: {
James Zern061263a2012-05-11 16:00:57 -07001011 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301012 if (!ok) goto Err2;
1013 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1014 break;
skal0d19fbf2013-01-21 17:20:14 +01001015 }
1016 default: {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301017 assert(0); // Invalid action.
1018 break;
skal0d19fbf2013-01-21 17:20:14 +01001019 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301020 }
1021
1022 Err2:
1023 WebPMuxDelete(mux);
1024 return ok;
1025}
1026
1027//------------------------------------------------------------------------------
1028// Main.
1029
1030int main(int argc, const char* argv[]) {
James Zernc0a462c2014-09-12 00:03:28 -07001031 WebPMuxConfig config;
James Zern974aaff2012-01-24 12:46:46 -08001032 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301033 if (ok) {
James Zernc0a462c2014-09-12 00:03:28 -07001034 ok = Process(&config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301035 } else {
1036 PrintHelp();
1037 }
James Zernc0a462c2014-09-12 00:03:28 -07001038 DeleteConfig(&config);
James Zern974aaff2012-01-24 12:46:46 -08001039 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301040}
1041
1042//------------------------------------------------------------------------------