1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // container.c - an interface of parser/builder for formatted files.
4 //
5 // Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6 //
7 // Licensed under the terms of the GNU General Public License, version 2.
8
9 #include "container.h"
10 #include "misc.h"
11
12 #include <stdio.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <inttypes.h>
17
18 static const char *const cntr_type_labels[] = {
19 [CONTAINER_TYPE_PARSER] = "parser",
20 [CONTAINER_TYPE_BUILDER] = "builder",
21 };
22
23 static const char *const cntr_format_labels[] = {
24 [CONTAINER_FORMAT_RIFF_WAVE] = "riff/wave",
25 [CONTAINER_FORMAT_AU] = "au",
26 [CONTAINER_FORMAT_VOC] = "voc",
27 [CONTAINER_FORMAT_RAW] = "raw",
28 };
29
30 static const char *const suffixes[] = {
31 [CONTAINER_FORMAT_RIFF_WAVE] = ".wav",
32 [CONTAINER_FORMAT_AU] = ".au",
33 [CONTAINER_FORMAT_VOC] = ".voc",
34 [CONTAINER_FORMAT_RAW] = "",
35 };
36
container_suffix_from_format(enum container_format format)37 const char *const container_suffix_from_format(enum container_format format)
38 {
39 return suffixes[format];
40 }
41
container_recursive_read(struct container_context * cntr,void * buf,unsigned int byte_count)42 int container_recursive_read(struct container_context *cntr, void *buf,
43 unsigned int byte_count)
44 {
45 char *dst = buf;
46 ssize_t result;
47 size_t consumed = 0;
48
49 while (consumed < byte_count && !cntr->interrupted) {
50 result = read(cntr->fd, dst + consumed, byte_count - consumed);
51 if (result < 0) {
52 // This descriptor was configured with non-blocking
53 // mode. EINTR is not cought when get any interrupts.
54 if (cntr->interrupted)
55 return -EINTR;
56 if (errno == EAGAIN)
57 continue;
58 return -errno;
59 }
60 // Reach EOF.
61 if (result == 0) {
62 cntr->eof = true;
63 return 0;
64 }
65
66 consumed += result;
67 }
68
69 return 0;
70 }
71
container_recursive_write(struct container_context * cntr,void * buf,unsigned int byte_count)72 int container_recursive_write(struct container_context *cntr, void *buf,
73 unsigned int byte_count)
74 {
75 char *src = buf;
76 ssize_t result;
77 size_t consumed = 0;
78
79 while (consumed < byte_count && !cntr->interrupted) {
80 result = write(cntr->fd, src + consumed, byte_count - consumed);
81 if (result < 0) {
82 // This descriptor was configured with non-blocking
83 // mode. EINTR is not cought when get any interrupts.
84 if (cntr->interrupted)
85 return -EINTR;
86 if (errno == EAGAIN)
87 continue;
88 return -errno;
89 }
90
91 consumed += result;
92 }
93
94 return 0;
95 }
96
container_format_from_path(const char * path)97 enum container_format container_format_from_path(const char *path)
98 {
99 const char *suffix;
100 const char *pos;
101 int i;
102
103 for (i = 0; i < ARRAY_SIZE(suffixes); ++i) {
104 suffix = suffixes[i];
105
106 // Check last part of the string.
107 pos = path + strlen(path) - strlen(suffix);
108 if (!strcmp(pos, suffix))
109 return i;
110 }
111
112 // Unsupported.
113 return CONTAINER_FORMAT_RAW;
114 }
115
container_seek_offset(struct container_context * cntr,off64_t offset)116 int container_seek_offset(struct container_context *cntr, off64_t offset)
117 {
118 off64_t pos;
119
120 pos = lseek64(cntr->fd, offset, SEEK_SET);
121 if (pos < 0)
122 return -errno;
123 if (pos != offset)
124 return -EIO;
125
126 return 0;
127 }
128
129 // To avoid blocking execution at system call iteration after receiving UNIX
130 // signals.
set_nonblock_flag(int fd)131 static int set_nonblock_flag(int fd)
132 {
133 int flags;
134
135 flags = fcntl(fd, F_GETFL);
136 if (flags < 0)
137 return -errno;
138
139 flags |= O_NONBLOCK;
140 if (fcntl(fd, F_SETFL, flags) < 0)
141 return -errno;
142
143 return 0;
144 }
145
container_parser_init(struct container_context * cntr,int fd,unsigned int verbose)146 int container_parser_init(struct container_context *cntr, int fd,
147 unsigned int verbose)
148 {
149 const struct container_parser *parsers[] = {
150 [CONTAINER_FORMAT_RIFF_WAVE] = &container_parser_riff_wave,
151 [CONTAINER_FORMAT_AU] = &container_parser_au,
152 [CONTAINER_FORMAT_VOC] = &container_parser_voc,
153 };
154 const struct container_parser *parser;
155 unsigned int size;
156 int i;
157 int err;
158
159 assert(cntr);
160 assert(fd >= 0);
161
162 // Detect forgotten to destruct.
163 assert(cntr->fd == 0);
164 assert(cntr->private_data == NULL);
165
166 memset(cntr, 0, sizeof(*cntr));
167
168 cntr->fd = fd;
169
170 cntr->stdio = (cntr->fd == fileno(stdin));
171 if (cntr->stdio) {
172 if (isatty(cntr->fd)) {
173 fprintf(stderr,
174 "A terminal is referred for standard input. "
175 "Output from any process or shell redirection "
176 "should be referred instead.\n");
177 return -EIO;
178 }
179 }
180
181 err = set_nonblock_flag(cntr->fd);
182 if (err < 0)
183 return err;
184
185 // 4 bytes are enough to detect supported containers.
186 err = container_recursive_read(cntr, cntr->magic, sizeof(cntr->magic));
187 if (err < 0)
188 return err;
189 for (i = 0; i < ARRAY_SIZE(parsers); ++i) {
190 parser = parsers[i];
191 size = strlen(parser->magic);
192 if (size > 4)
193 size = 4;
194 if (!strncmp(cntr->magic, parser->magic, size))
195 break;
196 }
197
198 // Don't forget that the first 4 bytes were already read for magic
199 // bytes.
200 cntr->magic_handled = false;
201
202 // Unless detected, use raw container.
203 if (i == ARRAY_SIZE(parsers))
204 parser = &container_parser_raw;
205
206 // Allocate private data for the parser.
207 if (parser->private_size > 0) {
208 cntr->private_data = malloc(parser->private_size);
209 if (cntr->private_data == NULL)
210 return -ENOMEM;
211 memset(cntr->private_data, 0, parser->private_size);
212 }
213
214 cntr->type = CONTAINER_TYPE_PARSER;
215 cntr->process_bytes = container_recursive_read;
216 cntr->format = parser->format;
217 cntr->ops = &parser->ops;
218 cntr->max_size = parser->max_size;
219 cntr->verbose = verbose;
220
221 return 0;
222 }
223
container_builder_init(struct container_context * cntr,int fd,enum container_format format,unsigned int verbose)224 int container_builder_init(struct container_context *cntr, int fd,
225 enum container_format format, unsigned int verbose)
226 {
227 const struct container_builder *builders[] = {
228 [CONTAINER_FORMAT_RIFF_WAVE] = &container_builder_riff_wave,
229 [CONTAINER_FORMAT_AU] = &container_builder_au,
230 [CONTAINER_FORMAT_VOC] = &container_builder_voc,
231 [CONTAINER_FORMAT_RAW] = &container_builder_raw,
232 };
233 const struct container_builder *builder;
234 int err;
235
236 assert(cntr);
237 assert(fd >= 0);
238
239 // Detect forgotten to destruct.
240 assert(cntr->fd == 0);
241 assert(cntr->private_data == NULL);
242
243 memset(cntr, 0, sizeof(*cntr));
244
245 cntr->fd = fd;
246
247 cntr->stdio = (cntr->fd == fileno(stdout));
248 if (cntr->stdio) {
249 if (isatty(cntr->fd)) {
250 fprintf(stderr,
251 "A terminal is referred for standard output. "
252 "Input to any process or shell redirection "
253 "should be referred instead.\n");
254 return -EIO;
255 }
256 }
257
258 err = set_nonblock_flag(cntr->fd);
259 if (err < 0)
260 return err;
261
262 builder = builders[format];
263
264 // Allocate private data for the builder.
265 if (builder->private_size > 0) {
266 cntr->private_data = malloc(builder->private_size);
267 if (cntr->private_data == NULL)
268 return -ENOMEM;
269 memset(cntr->private_data, 0, builder->private_size);
270 }
271
272 cntr->type = CONTAINER_TYPE_BUILDER;
273 cntr->process_bytes = container_recursive_write;
274 cntr->format = builder->format;
275 cntr->ops = &builder->ops;
276 cntr->max_size = builder->max_size;
277 cntr->verbose = verbose;
278
279 return 0;
280 }
281
container_context_pre_process(struct container_context * cntr,snd_pcm_format_t * format,unsigned int * samples_per_frame,unsigned int * frames_per_second,uint64_t * frame_count)282 int container_context_pre_process(struct container_context *cntr,
283 snd_pcm_format_t *format,
284 unsigned int *samples_per_frame,
285 unsigned int *frames_per_second,
286 uint64_t *frame_count)
287 {
288 uint64_t byte_count = 0;
289 unsigned int bytes_per_frame;
290 int err;
291
292 assert(cntr);
293 assert(format);
294 assert(samples_per_frame);
295 assert(frames_per_second);
296 assert(frame_count);
297
298 if (cntr->type == CONTAINER_TYPE_BUILDER)
299 byte_count = cntr->max_size;
300
301 if (cntr->ops->pre_process) {
302 err = cntr->ops->pre_process(cntr, format, samples_per_frame,
303 frames_per_second, &byte_count);
304 if (err < 0)
305 return err;
306 if (cntr->eof)
307 return 0;
308 }
309
310 if (cntr->format == CONTAINER_FORMAT_RAW) {
311 if (*format == SND_PCM_FORMAT_UNKNOWN ||
312 *samples_per_frame == 0 || *frames_per_second == 0) {
313 fprintf(stderr,
314 "Any file format is not detected. Need to "
315 "indicate all of sample format, channels and "
316 "rate explicitly.\n");
317 return -EINVAL;
318 }
319 }
320 assert(*format >= SND_PCM_FORMAT_S8);
321 assert(*format <= SND_PCM_FORMAT_LAST);
322 assert(*samples_per_frame > 0);
323 assert(*frames_per_second > 0);
324 assert(byte_count > 0);
325
326 cntr->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
327 cntr->samples_per_frame = *samples_per_frame;
328 cntr->frames_per_second = *frames_per_second;
329
330 bytes_per_frame = cntr->bytes_per_sample * *samples_per_frame;
331 *frame_count = byte_count / bytes_per_frame;
332 cntr->max_size -= cntr->max_size / bytes_per_frame;
333
334 if (cntr->verbose > 0) {
335 fprintf(stderr, "Container: %s\n",
336 cntr_type_labels[cntr->type]);
337 fprintf(stderr, " format: %s\n",
338 cntr_format_labels[cntr->format]);
339 fprintf(stderr, " sample format: %s\n",
340 snd_pcm_format_name(*format));
341 fprintf(stderr, " bytes/sample: %u\n",
342 cntr->bytes_per_sample);
343 fprintf(stderr, " samples/frame: %u\n",
344 cntr->samples_per_frame);
345 fprintf(stderr, " frames/second: %u\n",
346 cntr->frames_per_second);
347 if (cntr->type == CONTAINER_TYPE_PARSER) {
348 fprintf(stderr, " frames: %" PRIu64 "\n",
349 *frame_count);
350 } else {
351 fprintf(stderr, " max frames: %" PRIu64 "\n",
352 *frame_count);
353 }
354 }
355
356 return 0;
357 }
358
container_context_process_frames(struct container_context * cntr,void * frame_buffer,unsigned int * frame_count)359 int container_context_process_frames(struct container_context *cntr,
360 void *frame_buffer,
361 unsigned int *frame_count)
362 {
363 char *buf = frame_buffer;
364 unsigned int bytes_per_frame;
365 unsigned int byte_count;
366 unsigned int target_byte_count;
367 int err;
368
369 assert(cntr);
370 assert(!cntr->eof);
371 assert(frame_buffer);
372 assert(frame_count);
373
374 bytes_per_frame = cntr->bytes_per_sample * cntr->samples_per_frame;
375 target_byte_count = *frame_count * bytes_per_frame;
376
377 // A parser of cotainers already read first 4 bytes to detect format
378 // of container, however they includes PCM frames when any format was
379 // undetected. Surely to write out them.
380 byte_count = target_byte_count;
381 if (cntr->format == CONTAINER_FORMAT_RAW &&
382 cntr->type == CONTAINER_TYPE_PARSER && !cntr->magic_handled) {
383 memcpy(buf, cntr->magic, sizeof(cntr->magic));
384 buf += sizeof(cntr->magic);
385 byte_count -= sizeof(cntr->magic);
386 cntr->magic_handled = true;
387 }
388
389 // Each container has limitation for its volume for sample data.
390 if (cntr->handled_byte_count > cntr->max_size - byte_count)
391 byte_count = cntr->max_size - cntr->handled_byte_count;
392
393 // All of supported containers include interleaved PCM frames.
394 // TODO: process frames for truncate case.
395 err = cntr->process_bytes(cntr, buf, byte_count);
396 if (err < 0) {
397 *frame_count = 0;
398 return err;
399 }
400
401 cntr->handled_byte_count += target_byte_count;
402 if (cntr->handled_byte_count == cntr->max_size)
403 cntr->eof = true;
404
405 *frame_count = target_byte_count / bytes_per_frame;
406
407 return 0;
408 }
409
container_context_post_process(struct container_context * cntr,uint64_t * frame_count)410 int container_context_post_process(struct container_context *cntr,
411 uint64_t *frame_count)
412 {
413 int err = 0;
414
415 assert(cntr);
416 assert(frame_count);
417
418 if (cntr->verbose && cntr->handled_byte_count > 0) {
419 fprintf(stderr, " Handled bytes: %" PRIu64 "\n",
420 cntr->handled_byte_count);
421 }
422
423 // NOTE* we cannot seek when using standard input/output.
424 if (!cntr->stdio && cntr->ops && cntr->ops->post_process) {
425 // Usually, need to write out processed bytes in container
426 // header even it this program is interrupted.
427 cntr->interrupted = false;
428
429 err = cntr->ops->post_process(cntr, cntr->handled_byte_count);
430 }
431
432 // Ensure to perform write-back from disk cache.
433 if (cntr->type == CONTAINER_TYPE_BUILDER)
434 fsync(cntr->fd);
435
436 if (err < 0)
437 return err;
438
439 if (cntr->bytes_per_sample == 0 || cntr->samples_per_frame == 0) {
440 *frame_count = 0;
441 } else {
442 *frame_count = cntr->handled_byte_count /
443 cntr->bytes_per_sample /
444 cntr->samples_per_frame;
445 }
446
447 return 0;
448 }
449
container_context_destroy(struct container_context * cntr)450 void container_context_destroy(struct container_context *cntr)
451 {
452 assert(cntr);
453
454 if (cntr->private_data)
455 free(cntr->private_data);
456
457 cntr->fd = 0;
458 cntr->private_data = NULL;
459 }
460