OSDN Git Service

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