1 /**
2 * @file aec.c
3 *
4 * @section LICENSE
5 * Copyright 2021 Mathis Rosenhauer, Moritz Hanke, Joerg Behrens, Luis Kornblueh
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * @section DESCRIPTION
33 *
34 * CLI frontend for Adaptive Entropy Coding library
35 *
36 */
37
38 #include <libaec.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #define CHUNK 10485760
44
get_param(unsigned int * param,int * iarg,char * argv[])45 int get_param(unsigned int *param, int *iarg, char *argv[])
46 {
47 if (strlen(argv[*iarg]) == 2) {
48 (*iarg)++;
49 if (argv[*iarg][0] == '-')
50 return 1;
51 else
52 *param = atoi(argv[*iarg]);
53 } else {
54 *param = atoi(&argv[*iarg][2]);
55 }
56 return 0;
57 }
58
usage(void)59 void usage(void)
60 {
61 fprintf(stderr, "NAME\n\taec - encode or decode files ");
62 fprintf(stderr, "with Adaptive Entropy Coding\n\n");
63 fprintf(stderr, "SYNOPSIS\n\taec [OPTION]... SOURCE DEST\n");
64 fprintf(stderr, "\nOPTIONS\n");
65 fprintf(stderr, "\t-3\n\t\t24 bit samples are stored in 3 bytes\n");
66 fprintf(stderr, "\t-N\n\t\tdisable pre/post processing\n");
67 fprintf(stderr, "\t-b size\n\t\tinternal buffer size in bytes\n");
68 fprintf(stderr, "\t-d\n\t\tdecode SOURCE. If -d is not used: encode.\n");
69 fprintf(stderr, "\t-j samples\n\t\tblock size in samples\n");
70 fprintf(stderr, "\t-m\n\t\tsamples are MSB first. Default is LSB\n");
71 fprintf(stderr, "\t-n bits\n\t\tbits per sample\n");
72 fprintf(stderr, "\t-p\n\t\tpad RSI to byte boundary\n");
73 fprintf(stderr, "\t-r blocks\n\t\treference sample interval in blocks\n");
74 fprintf(stderr, "\t-s\n\t\tsamples are signed. Default is unsigned\n");
75 fprintf(stderr, "\t-t\n\t\tuse restricted set of code options\n\n");
76 }
77
main(int argc,char * argv[])78 int main(int argc, char *argv[])
79 {
80 struct aec_stream strm;
81 unsigned char *in = NULL;
82 unsigned char *out = NULL;
83 size_t total_out;
84 unsigned int chunk;
85 int status = 0;
86 int input_avail, output_avail;
87 char *infn, *outfn;
88 FILE *infp, *outfp;
89 int dflag;
90 char *opt;
91 int iarg;
92
93 chunk = CHUNK;
94 strm.bits_per_sample = 8;
95 strm.block_size = 8;
96 strm.rsi = 2;
97 strm.flags = AEC_DATA_PREPROCESS;
98 dflag = 0;
99 iarg = 1;
100
101 while (iarg < argc - 2) {
102 opt = argv[iarg];
103 if (opt[0] != '-') {
104 usage();
105 goto DESTRUCT;
106 }
107 switch (opt[1]) {
108 case '3':
109 strm.flags |= AEC_DATA_3BYTE;
110 break;
111 case 'N':
112 strm.flags &= ~AEC_DATA_PREPROCESS;
113 break;
114 case 'b':
115 if (get_param(&chunk, &iarg, argv)) {
116 usage();
117 goto DESTRUCT;
118 }
119 break;
120 case 'd':
121 dflag = 1;
122 break;
123 case 'j':
124 if (get_param(&strm.block_size, &iarg, argv)) {
125 usage();
126 goto DESTRUCT;
127 }
128 break;
129 case 'm':
130 strm.flags |= AEC_DATA_MSB;
131 break;
132 case 'n':
133 if (get_param(&strm.bits_per_sample, &iarg, argv)) {
134 usage();
135 goto DESTRUCT;
136 }
137 break;
138 case 'p':
139 strm.flags |= AEC_PAD_RSI;
140 break;
141 case 'r':
142 if (get_param(&strm.rsi, &iarg, argv)) {
143 usage();
144 goto DESTRUCT;
145 }
146 break;
147 case 's':
148 strm.flags |= AEC_DATA_SIGNED;
149 break;
150 case 't':
151 strm.flags |= AEC_RESTRICTED;
152 break;
153 default:
154 usage();
155 goto DESTRUCT;
156 }
157 iarg++;
158 }
159
160 if (argc - iarg < 2) {
161 usage();
162 goto DESTRUCT;
163 }
164
165 infn = argv[iarg];
166 outfn = argv[iarg + 1];
167
168 if (strm.bits_per_sample > 16) {
169 if (strm.bits_per_sample <= 24 && strm.flags & AEC_DATA_3BYTE)
170 chunk *= 3;
171 else
172 chunk *= 4;
173 } else if (strm.bits_per_sample > 8) {
174 chunk *= 2;
175 }
176
177 out = (unsigned char *)malloc(chunk);
178 in = (unsigned char *)malloc(chunk);
179
180 if (in == NULL || out == NULL) {
181 status = 99;
182 goto DESTRUCT;
183 }
184
185 total_out = 0;
186 strm.avail_in = 0;
187 strm.avail_out = chunk;
188 strm.next_out = out;
189
190 input_avail = 1;
191 output_avail = 1;
192
193 if ((infp = fopen(infn, "rb")) == NULL) {
194 fprintf(stderr, "ERROR: cannot open input file %s\n", infn);
195 status = 99;
196 goto DESTRUCT;
197 }
198 if ((outfp = fopen(outfn, "wb")) == NULL) {
199 fprintf(stderr, "ERROR: cannot open output file %s\n", infn);
200 status = 99;
201 goto DESTRUCT;
202 }
203
204 if (dflag)
205 status = aec_decode_init(&strm);
206 else
207 status = aec_encode_init(&strm);
208
209 if (status != AEC_OK) {
210 fprintf(stderr, "ERROR: initialization failed (%d)\n", status);
211 goto DESTRUCT;
212 }
213
214 while(input_avail || output_avail) {
215 if (strm.avail_in == 0 && input_avail) {
216 strm.avail_in = fread(in, 1, chunk, infp);
217 if (strm.avail_in != chunk)
218 input_avail = 0;
219 strm.next_in = in;
220 }
221
222 if (dflag)
223 status = aec_decode(&strm, AEC_NO_FLUSH);
224 else
225 status = aec_encode(&strm, AEC_NO_FLUSH);
226
227 if (status != AEC_OK) {
228 fprintf(stderr, "ERROR: %i\n", status);
229 goto DESTRUCT;
230 }
231
232 if (strm.total_out - total_out > 0) {
233 fwrite(out, strm.total_out - total_out, 1, outfp);
234 total_out = strm.total_out;
235 output_avail = 1;
236 strm.next_out = out;
237 strm.avail_out = chunk;
238 } else {
239 output_avail = 0;
240 }
241
242 }
243
244 if (dflag) {
245 aec_decode_end(&strm);
246 } else {
247 if ((status = aec_encode(&strm, AEC_FLUSH)) != AEC_OK) {
248 fprintf(stderr, "ERROR: while flushing output (%i)\n", status);
249 goto DESTRUCT;
250 }
251
252 if (strm.total_out - total_out > 0)
253 fwrite(out, strm.total_out - total_out, 1, outfp);
254
255 aec_encode_end(&strm);
256 }
257
258 fclose(infp);
259 fclose(outfp);
260
261 DESTRUCT:
262 if (in)
263 free(in);
264 if (out)
265 free(out);
266 return status;
267 }
268