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