blob: 7ce1dbf4581a0dc1c48c731cb525735069361eb4 [file] [log] [blame]
Sylvain Munaut96b1c3b2010-10-24 10:22:06 +02001/* Main */
2
3/*
4 * This file is part of gapk (GSM Audio Pocket Knife).
5 *
6 * gapk is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * gapk is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with gapk. If not, see <http://www.gnu.org/licenses/>.
18 */
19
Sylvain Munaut46049762010-11-11 22:58:57 +010020#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <stdint.h>
24#include <string.h>
25#include <unistd.h>
26#include <getopt.h>
27
28#include <gapk/codecs.h>
29#include <gapk/formats.h>
30#include <gapk/procqueue.h>
31
32
33struct gapk_options
34{
35 const char *fname_in;
36 const struct format_desc *fmt_in;
37
38 const char *fname_out;
39 const struct format_desc *fmt_out;
40};
41
42struct gapk_state
43{
44 struct gapk_options opts;
45
46 struct pq *pq;
47
48 FILE *fh_in;
49 FILE *fh_out;
50};
51
52
53static void
54print_help(char *progname)
55{
56 int i;
57
58 /* Header */
59 fprintf(stdout, "Usage: %s [options]\n", progname);
60 fprintf(stdout, "\n");
61 fprintf(stdout, "Options:\n");
62 fprintf(stdout, " -i, --input-file=FILE\t\tInput file\n");
63 fprintf(stdout, " -o, --output-file=FILE\tOutput file\n");
64 fprintf(stdout, " -f, --input-format=FMT\tInput format (see below)\n");
65 fprintf(stdout, " -g, --output-format=FMT\tOutput format (see below)\n");
66 fprintf(stdout, "\n");
67
68 /* Print all codecs */
69 fprintf(stdout, "Supported codecs:\n");
70 fprintf(stdout, " name\tfmt enc dec\tdescription\n");
71
72 for (i=CODEC_INVALID+1; i<_CODEC_MAX; i++) {
73 const struct codec_desc *codec = codec_get_from_type(i);
74 fprintf(stdout, " %s\t %c %c %c\t%s\n",
75 codec->name,
76 '*',
77 codec->codec_encode ? '*' : ' ',
78 codec->codec_decode ? '*' : ' ',
79 codec->description
80 );
81 }
82
83 fprintf(stdout, "\n");
84
85 /* Print all formats */
86 fprintf(stdout, "Supported formats:\n");
87
88 for (i=FMT_INVALID+1; i<_FMT_MAX; i++) {
89 const struct format_desc *fmt = fmt_get_from_type(i);
90 fprintf(stdout, " %s%s%s\t%s\n",
91 fmt->name,
92 strlen(fmt->name) < 7 ? "\t" : "",
93 strlen(fmt->name) < 15 ? "\t" : "",
94 fmt->description
95 );
96 }
97
98 fprintf(stdout, "\n");
99}
100
101static int
102parse_options(struct gapk_state *state, int argc, char *argv[])
103{
104 const struct option long_options[] = {
105 {"input-file", 1, 0, 'i'},
106 {"output-file", 1, 0, 'o'},
107 {"input-format", 1, 0, 'f'},
108 {"output-format", 1, 0, 'g'},
109 {"help", 0, 0, 'h'},
110 };
111 const char *short_options = "i:o:f:g:h";
112
113 struct gapk_options *opt = &state->opts;
114
115 /* Set some defaults */
116 memset(opt, 0x00, sizeof(*opt));
117
118 /* Parse */
119 while (1) {
120 int c;
121 int opt_idx;
122
123 c = getopt_long(
124 argc, argv, short_options, long_options, &opt_idx);
125 if (c == -1)
126 break;
127
128 switch (c) {
129 case 'i':
130 opt->fname_in = optarg;
131 break;
132
133 case 'o':
134 opt->fname_out = optarg;
135 break;
136
137 case 'f':
138 opt->fmt_in = fmt_get_from_name(optarg);
139 if (!opt->fmt_in) {
140 fprintf(stderr, "[!] Unsupported format: %s\n", optarg);
141 return -EINVAL;
142 }
143 break;
144
145 case 'g':
146 opt->fmt_out = fmt_get_from_name(optarg);
147 if (!opt->fmt_out) {
148 fprintf(stderr, "[!] Unsupported format: %s\n", optarg);
149 return -EINVAL;
150 }
151 break;
152
153 case 'h':
154 return 1;
155
156 default:
157 fprintf(stderr, "[+] Unknown option\n");
158 return -EINVAL;
159
160 }
161 }
162
163 return 0;
164}
165
166static int
167check_options(struct gapk_state *gs)
168{
169 /* Required formats */
170 if (!gs->opts.fmt_in || !gs->opts.fmt_out) {
171 fprintf(stderr, "[!] Input and output formats are required arguments !\n");
172 return -EINVAL;
173 }
174
175 /* Transcoding */
176 if (gs->opts.fmt_in->codec_type != gs->opts.fmt_out->codec_type) {
177 const struct codec_desc *codec;
178
179 /* Check source codec */
180 codec = codec_get_from_type(gs->opts.fmt_in->codec_type);
181 if (!codec) {
182 fprintf(stderr, "[!] Internal error: bad codec reference\n");
183 return -EINVAL;
184 }
185 if ((codec->type != CODEC_PCM) && !codec->codec_decode) {
186 fprintf(stderr, "[!] Decoding from '%s' codec is unsupported\n", codec->name);
187 return -ENOTSUP;
188 }
189
190 /* Check destination codec */
191 codec = codec_get_from_type(gs->opts.fmt_out->codec_type);
192 if (!codec) {
193 fprintf(stderr, "[!] Internal error: bad codec reference\n");
194 return -EINVAL;
195 }
196 if ((codec->type != CODEC_PCM) && !codec->codec_encode) {
197 fprintf(stderr, "[!] Encoding to '%s' codec is unsupported\n", codec->name);
198 return -ENOTSUP;
199 }
200 }
201
202 return 0;
203}
204
205static int
206files_open(struct gapk_state *gs)
207{
208 if (gs->opts.fname_in) {
209 gs->fh_in = fopen(gs->opts.fname_in, "rb");
210 if (!gs->fh_in) {
211 fprintf(stderr, "[!] Error while opening input file for reading\n");
212 perror("fopen");
213 return -errno;
214 }
215 } else
216 gs->fh_in = stdin;
217
218 if (gs->opts.fname_out) {
219 gs->fh_out = fopen(gs->opts.fname_out, "wb");
220 if (!gs->fh_out) {
221 fprintf(stderr, "[!] Error while opening output file for writing\n");
222 perror("fopen");
223 return -errno;
224 }
225 } else
226 gs->fh_out = stdout;
227
228 return 0;
229}
230
231static void
232files_close(struct gapk_state *gs)
233{
234 if (gs->fh_in && gs->fh_in != stdin)
235 fclose(gs->fh_in);
236 if (gs->fh_out && gs->fh_out != stdout)
237 fclose(gs->fh_out);
238}
239
240static int
241handle_headers(struct gapk_state *gs)
242{
243 int rv;
244 unsigned int len;
245
246 /* Input file header (remove & check it) */
247 len = gs->opts.fmt_in->header_len;
248 if (len) {
249 uint8_t *buf;
250
251 buf = malloc(len);
252 if (!buf)
253 return -ENOMEM;
254
255 rv = fread(buf, len, 1, gs->fh_in);
256 if ((rv != 1) ||
257 memcmp(buf, gs->opts.fmt_in->header, len))
258 {
259 free(buf);
260 fprintf(stderr, "[!] Invalid header in input file");
261 return -EINVAL;
262 }
263
264 free(buf);
265 }
266
267 /* Output file header (write it) */
268 len = gs->opts.fmt_out->header_len;
269 if (len) {
270 rv = fwrite(gs->opts.fmt_out->header, len, 1, gs->fh_out);
271 if (rv != 1)
272 return -ENOSPC;
273 }
274
275 return 0;
276}
277
278static int
279make_processing_chain(struct gapk_state *gs)
280{
281 const struct format_desc *fmt_in, *fmt_out;
282 const struct codec_desc *codec_in, *codec_out;
283
284 int need_dec, need_enc;
285
286 fmt_in = gs->opts.fmt_in;
287 fmt_out = gs->opts.fmt_out;
288
289 codec_in = codec_get_from_type(fmt_in->codec_type);
290 codec_out = codec_get_from_type(fmt_out->codec_type);
291
292 need_dec = (fmt_in->codec_type != CODEC_PCM) &&
293 (fmt_in->codec_type != fmt_out->codec_type);
294 need_enc = (fmt_out->codec_type != CODEC_PCM) &&
295 (fmt_out->codec_type != fmt_in->codec_type);
296
297 /* File read */
298 pq_queue_file_input(gs->pq, gs->fh_in, fmt_in->frame_len);
299
300 /* Decoding to PCM ? */
301 if (need_dec)
302 {
303 /* Convert input to decoder input fmt */
304 if (fmt_in->type != codec_in->codec_dec_format_type)
305 {
306 const struct format_desc *fmt_dec;
307
308 fmt_dec = fmt_get_from_type(codec_in->codec_dec_format_type);
309 if (!fmt_dec)
310 return -EINVAL;
311
312 pq_queue_fmt_convert(gs->pq, fmt_in, 0);
313 pq_queue_fmt_convert(gs->pq, fmt_dec, 1);
314 }
315
316 /* Do decoding */
317 pq_queue_codec(gs->pq, codec_in, 0);
318 }
319 else if (fmt_in->type != fmt_out->type)
320 {
321 /* Convert input to canonical fmt */
322 pq_queue_fmt_convert(gs->pq, fmt_in, 0);
323 }
324
325 /* Encoding from PCM ? */
326 if (need_enc)
327 {
328 /* Do encoding */
329 pq_queue_codec(gs->pq, codec_out, 1);
330
331 /* Convert encoder output to output fmt */
332 if (fmt_out->type != codec_out->codec_enc_format_type)
333 {
334 const struct format_desc *fmt_enc;
335
336 fmt_enc = fmt_get_from_type(codec_out->codec_enc_format_type);
337 if (!fmt_enc)
338 return -EINVAL;
339
340 pq_queue_fmt_convert(gs->pq, fmt_enc, 0);
341 pq_queue_fmt_convert(gs->pq, fmt_out, 1);
342 }
343 }
344 else if (fmt_in->type != fmt_out->type)
345 {
346 /* Convert canonical to output fmt */
347 pq_queue_fmt_convert(gs->pq, fmt_out, 1);
348 }
349
350 /* File write */
351 pq_queue_file_output(gs->pq, gs->fh_out, fmt_out->frame_len);
352
353 return 0;
354}
355
356static int
357run(struct gapk_state *gs)
358{
359 int rv, frames;
360
361 rv = pq_prepare(gs->pq);
362 if (rv)
363 return rv;
364
365 for (frames=0; !(rv = pq_execute(gs->pq)); frames++);
366
367 fprintf(stderr, "[+] Processed %d frames\n", frames);
368
369 return frames > 0 ? 0 : rv;
370}
371
Sylvain Munaut96b1c3b2010-10-24 10:22:06 +0200372int main(int argc, char *argv[])
373{
Sylvain Munaut46049762010-11-11 22:58:57 +0100374 struct gapk_state _gs, *gs = &_gs;
375 int rv;
376
377 /* Clear state */
378 memset(gs, 0x00, sizeof(struct gapk_state));
379
380 /* Parse / check options */
381 rv = parse_options(gs, argc, argv);
382 if (rv > 0) {
383 print_help(argv[0]);
384 return 0;
385 }
386 if (rv < 0)
387 return rv;
388
389 /* Check request */
390 rv = check_options(gs);
391 if (rv)
392 return rv;
393
394 /* Create processing queue */
395 gs->pq = pq_create();
396 if (!gs->pq) {
397 rv = -ENOMEM;
398 goto error;
399 }
400
401 /* Open source / destination files */
402 rv = files_open(gs);
403 if (rv)
404 goto error;
405
406 /* Handle input/output headers */
407 rv = handle_headers(gs);
408 if (rv)
409 goto error;
410
411 /* Make processing chain */
412 rv = make_processing_chain(gs);
413 if (rv)
414 goto error;
415
416 /* Run the processing queue */
417 rv = run(gs);
418
419error:
420 /* Close source / destination files */
421 files_close(gs);
422
423 /* Release processing queue */
424 pq_destroy(gs->pq);
425
426 return rv;
Sylvain Munaut96b1c3b2010-10-24 10:22:06 +0200427}