OSDN Git Service

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