OSDN Git Service

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