blob: 9b494451f545edf7337a48347076575e322d24c4 [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
Harald Weltef7f0c912013-02-11 09:26:36 +010048 union {
49 struct {
50 FILE *fh;
51 } file;
52 struct {
53 int fd;
54 } rtp;
55 } in;
56
57 union {
58 struct {
59 FILE *fh;
60 } file;
61 struct {
62 int fd;
63 } rtp;
64 } out;
Sylvain Munaut46049762010-11-11 22:58:57 +010065};
66
67
68static void
69print_help(char *progname)
70{
71 int i;
72
73 /* Header */
74 fprintf(stdout, "Usage: %s [options]\n", progname);
75 fprintf(stdout, "\n");
76 fprintf(stdout, "Options:\n");
77 fprintf(stdout, " -i, --input-file=FILE\t\tInput file\n");
78 fprintf(stdout, " -o, --output-file=FILE\tOutput file\n");
79 fprintf(stdout, " -f, --input-format=FMT\tInput format (see below)\n");
80 fprintf(stdout, " -g, --output-format=FMT\tOutput format (see below)\n");
81 fprintf(stdout, "\n");
82
83 /* Print all codecs */
84 fprintf(stdout, "Supported codecs:\n");
85 fprintf(stdout, " name\tfmt enc dec\tdescription\n");
86
87 for (i=CODEC_INVALID+1; i<_CODEC_MAX; i++) {
88 const struct codec_desc *codec = codec_get_from_type(i);
89 fprintf(stdout, " %s\t %c %c %c\t%s\n",
90 codec->name,
91 '*',
92 codec->codec_encode ? '*' : ' ',
93 codec->codec_decode ? '*' : ' ',
94 codec->description
95 );
96 }
97
98 fprintf(stdout, "\n");
99
100 /* Print all formats */
101 fprintf(stdout, "Supported formats:\n");
102
103 for (i=FMT_INVALID+1; i<_FMT_MAX; i++) {
104 const struct format_desc *fmt = fmt_get_from_type(i);
105 fprintf(stdout, " %s%s%s\t%s\n",
106 fmt->name,
107 strlen(fmt->name) < 7 ? "\t" : "",
108 strlen(fmt->name) < 15 ? "\t" : "",
109 fmt->description
110 );
111 }
112
113 fprintf(stdout, "\n");
114}
115
116static int
117parse_options(struct gapk_state *state, int argc, char *argv[])
118{
119 const struct option long_options[] = {
120 {"input-file", 1, 0, 'i'},
121 {"output-file", 1, 0, 'o'},
122 {"input-format", 1, 0, 'f'},
123 {"output-format", 1, 0, 'g'},
124 {"help", 0, 0, 'h'},
125 };
126 const char *short_options = "i:o:f:g:h";
127
128 struct gapk_options *opt = &state->opts;
129
130 /* Set some defaults */
131 memset(opt, 0x00, sizeof(*opt));
132
133 /* Parse */
134 while (1) {
135 int c;
136 int opt_idx;
137
138 c = getopt_long(
139 argc, argv, short_options, long_options, &opt_idx);
140 if (c == -1)
141 break;
142
143 switch (c) {
144 case 'i':
145 opt->fname_in = optarg;
146 break;
147
148 case 'o':
149 opt->fname_out = optarg;
150 break;
151
152 case 'f':
153 opt->fmt_in = fmt_get_from_name(optarg);
154 if (!opt->fmt_in) {
155 fprintf(stderr, "[!] Unsupported format: %s\n", optarg);
156 return -EINVAL;
157 }
158 break;
159
160 case 'g':
161 opt->fmt_out = fmt_get_from_name(optarg);
162 if (!opt->fmt_out) {
163 fprintf(stderr, "[!] Unsupported format: %s\n", optarg);
164 return -EINVAL;
165 }
166 break;
167
168 case 'h':
169 return 1;
170
171 default:
172 fprintf(stderr, "[+] Unknown option\n");
173 return -EINVAL;
174
175 }
176 }
177
178 return 0;
179}
180
181static int
182check_options(struct gapk_state *gs)
183{
184 /* Required formats */
185 if (!gs->opts.fmt_in || !gs->opts.fmt_out) {
186 fprintf(stderr, "[!] Input and output formats are required arguments !\n");
187 return -EINVAL;
188 }
189
190 /* Transcoding */
191 if (gs->opts.fmt_in->codec_type != gs->opts.fmt_out->codec_type) {
192 const struct codec_desc *codec;
193
194 /* Check source codec */
195 codec = codec_get_from_type(gs->opts.fmt_in->codec_type);
196 if (!codec) {
197 fprintf(stderr, "[!] Internal error: bad codec reference\n");
198 return -EINVAL;
199 }
200 if ((codec->type != CODEC_PCM) && !codec->codec_decode) {
201 fprintf(stderr, "[!] Decoding from '%s' codec is unsupported\n", codec->name);
202 return -ENOTSUP;
203 }
204
205 /* Check destination codec */
206 codec = codec_get_from_type(gs->opts.fmt_out->codec_type);
207 if (!codec) {
208 fprintf(stderr, "[!] Internal error: bad codec reference\n");
209 return -EINVAL;
210 }
211 if ((codec->type != CODEC_PCM) && !codec->codec_encode) {
212 fprintf(stderr, "[!] Encoding to '%s' codec is unsupported\n", codec->name);
213 return -ENOTSUP;
214 }
215 }
216
217 return 0;
218}
219
220static int
221files_open(struct gapk_state *gs)
222{
223 if (gs->opts.fname_in) {
Harald Weltef7f0c912013-02-11 09:26:36 +0100224 gs->in.file.fh = fopen(gs->opts.fname_in, "rb");
225 if (!gs->in.file.fh) {
Sylvain Munaut46049762010-11-11 22:58:57 +0100226 fprintf(stderr, "[!] Error while opening input file for reading\n");
227 perror("fopen");
228 return -errno;
229 }
230 } else
Harald Weltef7f0c912013-02-11 09:26:36 +0100231 gs->in.file.fh = stdin;
Sylvain Munaut46049762010-11-11 22:58:57 +0100232
233 if (gs->opts.fname_out) {
Harald Weltef7f0c912013-02-11 09:26:36 +0100234 gs->out.file.fh = fopen(gs->opts.fname_out, "wb");
235 if (!gs->out.file.fh) {
Sylvain Munaut46049762010-11-11 22:58:57 +0100236 fprintf(stderr, "[!] Error while opening output file for writing\n");
237 perror("fopen");
238 return -errno;
239 }
240 } else
Harald Weltef7f0c912013-02-11 09:26:36 +0100241 gs->out.file.fh = stdout;
Sylvain Munaut46049762010-11-11 22:58:57 +0100242
243 return 0;
244}
245
246static void
247files_close(struct gapk_state *gs)
248{
Harald Weltef7f0c912013-02-11 09:26:36 +0100249 if (gs->in.file.fh && gs->in.file.fh != stdin)
250 fclose(gs->in.file.fh);
251 if (gs->out.file.fh && gs->out.file.fh != stdout)
252 fclose(gs->out.file.fh);
Sylvain Munaut46049762010-11-11 22:58:57 +0100253}
254
255static int
256handle_headers(struct gapk_state *gs)
257{
258 int rv;
259 unsigned int len;
260
261 /* Input file header (remove & check it) */
262 len = gs->opts.fmt_in->header_len;
263 if (len) {
264 uint8_t *buf;
265
266 buf = malloc(len);
267 if (!buf)
268 return -ENOMEM;
269
Harald Weltef7f0c912013-02-11 09:26:36 +0100270 rv = fread(buf, len, 1, gs->in.file.fh);
Sylvain Munaut46049762010-11-11 22:58:57 +0100271 if ((rv != 1) ||
272 memcmp(buf, gs->opts.fmt_in->header, len))
273 {
274 free(buf);
275 fprintf(stderr, "[!] Invalid header in input file");
276 return -EINVAL;
277 }
278
279 free(buf);
280 }
281
282 /* Output file header (write it) */
283 len = gs->opts.fmt_out->header_len;
284 if (len) {
Harald Weltef7f0c912013-02-11 09:26:36 +0100285 rv = fwrite(gs->opts.fmt_out->header, len, 1, gs->out.file.fh);
Sylvain Munaut46049762010-11-11 22:58:57 +0100286 if (rv != 1)
287 return -ENOSPC;
288 }
289
290 return 0;
291}
292
293static int
294make_processing_chain(struct gapk_state *gs)
295{
296 const struct format_desc *fmt_in, *fmt_out;
297 const struct codec_desc *codec_in, *codec_out;
298
299 int need_dec, need_enc;
300
301 fmt_in = gs->opts.fmt_in;
302 fmt_out = gs->opts.fmt_out;
303
304 codec_in = codec_get_from_type(fmt_in->codec_type);
305 codec_out = codec_get_from_type(fmt_out->codec_type);
306
307 need_dec = (fmt_in->codec_type != CODEC_PCM) &&
308 (fmt_in->codec_type != fmt_out->codec_type);
309 need_enc = (fmt_out->codec_type != CODEC_PCM) &&
310 (fmt_out->codec_type != fmt_in->codec_type);
311
312 /* File read */
Harald Weltef7f0c912013-02-11 09:26:36 +0100313 pq_queue_file_input(gs->pq, gs->in.file.fh, fmt_in->frame_len);
Sylvain Munaut46049762010-11-11 22:58:57 +0100314
315 /* Decoding to PCM ? */
316 if (need_dec)
317 {
318 /* Convert input to decoder input fmt */
319 if (fmt_in->type != codec_in->codec_dec_format_type)
320 {
321 const struct format_desc *fmt_dec;
322
323 fmt_dec = fmt_get_from_type(codec_in->codec_dec_format_type);
324 if (!fmt_dec)
325 return -EINVAL;
326
327 pq_queue_fmt_convert(gs->pq, fmt_in, 0);
328 pq_queue_fmt_convert(gs->pq, fmt_dec, 1);
329 }
330
331 /* Do decoding */
332 pq_queue_codec(gs->pq, codec_in, 0);
333 }
334 else if (fmt_in->type != fmt_out->type)
335 {
336 /* Convert input to canonical fmt */
337 pq_queue_fmt_convert(gs->pq, fmt_in, 0);
338 }
339
340 /* Encoding from PCM ? */
341 if (need_enc)
342 {
343 /* Do encoding */
344 pq_queue_codec(gs->pq, codec_out, 1);
345
346 /* Convert encoder output to output fmt */
347 if (fmt_out->type != codec_out->codec_enc_format_type)
348 {
349 const struct format_desc *fmt_enc;
350
351 fmt_enc = fmt_get_from_type(codec_out->codec_enc_format_type);
352 if (!fmt_enc)
353 return -EINVAL;
354
355 pq_queue_fmt_convert(gs->pq, fmt_enc, 0);
356 pq_queue_fmt_convert(gs->pq, fmt_out, 1);
357 }
358 }
359 else if (fmt_in->type != fmt_out->type)
360 {
361 /* Convert canonical to output fmt */
362 pq_queue_fmt_convert(gs->pq, fmt_out, 1);
363 }
364
365 /* File write */
Harald Weltef7f0c912013-02-11 09:26:36 +0100366 pq_queue_file_output(gs->pq, gs->out.file.fh, fmt_out->frame_len);
Sylvain Munaut46049762010-11-11 22:58:57 +0100367
368 return 0;
369}
370
371static int
372run(struct gapk_state *gs)
373{
374 int rv, frames;
375
376 rv = pq_prepare(gs->pq);
377 if (rv)
378 return rv;
379
380 for (frames=0; !(rv = pq_execute(gs->pq)); frames++);
381
382 fprintf(stderr, "[+] Processed %d frames\n", frames);
383
384 return frames > 0 ? 0 : rv;
385}
386
Sylvain Munaut96b1c3b2010-10-24 10:22:06 +0200387int main(int argc, char *argv[])
388{
Sylvain Munaut46049762010-11-11 22:58:57 +0100389 struct gapk_state _gs, *gs = &_gs;
390 int rv;
391
392 /* Clear state */
393 memset(gs, 0x00, sizeof(struct gapk_state));
394
395 /* Parse / check options */
396 rv = parse_options(gs, argc, argv);
397 if (rv > 0) {
398 print_help(argv[0]);
399 return 0;
400 }
401 if (rv < 0)
402 return rv;
403
404 /* Check request */
405 rv = check_options(gs);
406 if (rv)
407 return rv;
408
409 /* Create processing queue */
410 gs->pq = pq_create();
411 if (!gs->pq) {
412 rv = -ENOMEM;
413 goto error;
414 }
415
416 /* Open source / destination files */
417 rv = files_open(gs);
418 if (rv)
419 goto error;
420
421 /* Handle input/output headers */
422 rv = handle_headers(gs);
423 if (rv)
424 goto error;
425
426 /* Make processing chain */
427 rv = make_processing_chain(gs);
428 if (rv)
429 goto error;
430
431 /* Run the processing queue */
432 rv = run(gs);
433
434error:
435 /* Close source / destination files */
436 files_close(gs);
437
438 /* Release processing queue */
439 pq_destroy(gs->pq);
440
441 return rv;
Sylvain Munaut96b1c3b2010-10-24 10:22:06 +0200442}