1 // SPDX-License-Identifier: GPL-2.0
3 // container.c - an interface of parser/builder for formatted files.
5 // Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
7 // Licensed under the terms of the GNU General Public License, version 2.
17 static const char *const cntr_type_labels[] = {
18 [CONTAINER_TYPE_PARSER] = "parser",
19 [CONTAINER_TYPE_BUILDER] = "builder",
22 static const char *const cntr_format_labels[] = {
23 [CONTAINER_FORMAT_RIFF_WAVE] = "riff/wave",
24 [CONTAINER_FORMAT_AU] = "au",
27 static const char *const suffixes[] = {
28 [CONTAINER_FORMAT_RIFF_WAVE] = ".wav",
29 [CONTAINER_FORMAT_AU] = ".au",
33 const char *const container_suffix_from_format(enum container_format format)
35 return suffixes[format];
38 int container_recursive_read(struct container_context *cntr, void *buf,
39 unsigned int byte_count)
45 while (consumed < byte_count && !cntr->interrupted) {
46 result = read(cntr->fd, dst + consumed, byte_count - consumed);
48 // This descriptor was configured with non-blocking
49 // mode. EINTR is not cought when get any interrupts.
50 if (cntr->interrupted)
68 int container_recursive_write(struct container_context *cntr, void *buf,
69 unsigned int byte_count)
75 while (consumed < byte_count && !cntr->interrupted) {
76 result = write(cntr->fd, src + consumed, byte_count - consumed);
78 // This descriptor was configured with non-blocking
79 // mode. EINTR is not cought when get any interrupts.
80 if (cntr->interrupted)
93 enum container_format container_format_from_path(const char *path)
99 for (i = 0; i < ARRAY_SIZE(suffixes); ++i) {
100 suffix = suffixes[i];
102 // Check last part of the string.
103 pos = path + strlen(path) - strlen(suffix);
104 if (!strcmp(pos, suffix))
109 return CONTAINER_FORMAT_COUNT;
112 int container_seek_offset(struct container_context *cntr, off64_t offset)
116 pos = lseek64(cntr->fd, offset, SEEK_SET);
125 // To avoid blocking execution at system call iteration after receiving UNIX
127 static int set_nonblock_flag(int fd)
131 flags = fcntl(fd, F_GETFL);
136 if (fcntl(fd, F_SETFL, flags) < 0)
142 int container_parser_init(struct container_context *cntr,
143 const char *const path, unsigned int verbose)
145 const struct container_parser *parsers[] = {
146 [CONTAINER_FORMAT_RIFF_WAVE] = &container_parser_riff_wave,
147 [CONTAINER_FORMAT_AU] = &container_parser_au,
149 const struct container_parser *parser;
156 assert(path[0] != '\0');
158 // Detect forgotten to destruct.
159 assert(cntr->fd == 0);
160 assert(cntr->private_data == NULL);
162 memset(cntr, 0, sizeof(*cntr));
164 // Open a target descriptor.
165 if (!strcmp(path, "-")) {
166 cntr->fd = fileno(stdin);
167 err = set_nonblock_flag(cntr->fd);
172 cntr->fd = open(path, O_RDONLY | O_NONBLOCK);
177 // 4 bytes are enough to detect supported containers.
178 err = container_recursive_read(cntr, cntr->magic, sizeof(cntr->magic));
181 for (i = 0; i < ARRAY_SIZE(parsers); ++i) {
183 size = strlen(parser->magic);
186 if (!strncmp(cntr->magic, parser->magic, size))
190 // Don't forget that the first 4 bytes were already read for magic
192 cntr->magic_handled = false;
194 // Unless detected, use raw container.
195 if (i == ARRAY_SIZE(parsers))
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)
203 memset(cntr->private_data, 0, parser->private_size);
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;
216 int container_builder_init(struct container_context *cntr,
217 const char *const path, enum container_format format,
218 unsigned int verbose)
220 const struct container_builder *builders[] = {
221 [CONTAINER_FORMAT_RIFF_WAVE] = &container_builder_riff_wave,
222 [CONTAINER_FORMAT_AU] = &container_builder_au,
224 const struct container_builder *builder;
229 assert(path[0] != '\0');
231 // Detect forgotten to destruct.
232 assert(cntr->fd == 0);
233 assert(cntr->private_data == NULL);
235 memset(cntr, 0, sizeof(*cntr));
237 // Open a target descriptor.
238 if (path == NULL || *path == '\0')
240 if (!strcmp(path, "-")) {
241 cntr->fd = fileno(stdout);
242 err = set_nonblock_flag(cntr->fd);
247 cntr->fd = open(path, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC,
253 builder = builders[format];
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)
260 memset(cntr->private_data, 0, builder->private_size);
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;
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)
280 unsigned int bytes_per_frame;
285 assert(samples_per_frame);
286 assert(frames_per_second);
289 if (cntr->type == CONTAINER_TYPE_BUILDER)
290 byte_count = cntr->max_size;
292 if (cntr->ops->pre_process) {
293 err = cntr->ops->pre_process(cntr, format, samples_per_frame,
294 frames_per_second, &byte_count);
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);
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;
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;
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",
332 fprintf(stderr, " max frames: %lu\n",
340 int container_context_process_frames(struct container_context *cntr,
342 unsigned int *frame_count)
344 char *buf = frame_buffer;
345 unsigned int bytes_per_frame;
346 unsigned int byte_count;
351 assert(frame_buffer);
354 bytes_per_frame = cntr->bytes_per_sample * cntr->samples_per_frame;
355 byte_count = *frame_count * bytes_per_frame;
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;
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);
369 cntr->handled_byte_count += byte_count;
370 if (cntr->handled_byte_count == cntr->max_size)
373 *frame_count = byte_count / bytes_per_frame;
378 int container_context_post_process(struct container_context *cntr,
379 uint64_t *frame_count)
386 if (cntr->verbose && cntr->handled_byte_count > 0) {
387 fprintf(stderr, " Handled bytes: %lu\n",
388 cntr->handled_byte_count);
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;
397 err = cntr->ops->post_process(cntr, cntr->handled_byte_count);
400 // Ensure to perform write-back from disk cache.
401 if (cntr->type == CONTAINER_TYPE_BUILDER)
407 if (cntr->bytes_per_sample == 0 || cntr->samples_per_frame == 0) {
410 *frame_count = cntr->handled_byte_count /
411 cntr->bytes_per_sample /
412 cntr->samples_per_frame;
418 void container_context_destroy(struct container_context *cntr)
423 if (cntr->private_data)
424 free(cntr->private_data);
427 cntr->private_data = NULL;