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",
26 static const char *const suffixes[] = {
27 [CONTAINER_FORMAT_RIFF_WAVE] = ".wav",
31 const char *const container_suffix_from_format(enum container_format format)
33 return suffixes[format];
36 int container_recursive_read(struct container_context *cntr, void *buf,
37 unsigned int byte_count)
43 while (consumed < byte_count && !cntr->interrupted) {
44 result = read(cntr->fd, dst + consumed, byte_count - consumed);
46 // This descriptor was configured with non-blocking
47 // mode. EINTR is not cought when get any interrupts.
48 if (cntr->interrupted)
66 int container_recursive_write(struct container_context *cntr, void *buf,
67 unsigned int byte_count)
73 while (consumed < byte_count && !cntr->interrupted) {
74 result = write(cntr->fd, src + consumed, byte_count - consumed);
76 // This descriptor was configured with non-blocking
77 // mode. EINTR is not cought when get any interrupts.
78 if (cntr->interrupted)
91 enum container_format container_format_from_path(const char *path)
97 for (i = 0; i < ARRAY_SIZE(suffixes); ++i) {
100 // Check last part of the string.
101 pos = path + strlen(path) - strlen(suffix);
102 if (!strcmp(pos, suffix))
107 return CONTAINER_FORMAT_COUNT;
110 int container_seek_offset(struct container_context *cntr, off64_t offset)
114 pos = lseek64(cntr->fd, offset, SEEK_SET);
123 // To avoid blocking execution at system call iteration after receiving UNIX
125 static int set_nonblock_flag(int fd)
129 flags = fcntl(fd, F_GETFL);
134 if (fcntl(fd, F_SETFL, flags) < 0)
140 int container_parser_init(struct container_context *cntr,
141 const char *const path, unsigned int verbose)
143 const struct container_parser *parsers[] = {
144 [CONTAINER_FORMAT_RIFF_WAVE] = &container_parser_riff_wave,
146 const struct container_parser *parser;
153 assert(path[0] != '\0');
155 // Detect forgotten to destruct.
156 assert(cntr->fd == 0);
157 assert(cntr->private_data == NULL);
159 memset(cntr, 0, sizeof(*cntr));
161 // Open a target descriptor.
162 if (!strcmp(path, "-")) {
163 cntr->fd = fileno(stdin);
164 err = set_nonblock_flag(cntr->fd);
169 cntr->fd = open(path, O_RDONLY | O_NONBLOCK);
174 // 4 bytes are enough to detect supported containers.
175 err = container_recursive_read(cntr, cntr->magic, sizeof(cntr->magic));
178 for (i = 0; i < ARRAY_SIZE(parsers); ++i) {
180 size = strlen(parser->magic);
183 if (!strncmp(cntr->magic, parser->magic, size))
187 // Don't forget that the first 4 bytes were already read for magic
189 cntr->magic_handled = false;
191 // Unless detected, use raw container.
192 if (i == ARRAY_SIZE(parsers))
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)
200 memset(cntr->private_data, 0, parser->private_size);
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;
213 int container_builder_init(struct container_context *cntr,
214 const char *const path, enum container_format format,
215 unsigned int verbose)
217 const struct container_builder *builders[] = {
218 [CONTAINER_FORMAT_RIFF_WAVE] = &container_builder_riff_wave,
220 const struct container_builder *builder;
225 assert(path[0] != '\0');
227 // Detect forgotten to destruct.
228 assert(cntr->fd == 0);
229 assert(cntr->private_data == NULL);
231 memset(cntr, 0, sizeof(*cntr));
233 // Open a target descriptor.
234 if (path == NULL || *path == '\0')
236 if (!strcmp(path, "-")) {
237 cntr->fd = fileno(stdout);
238 err = set_nonblock_flag(cntr->fd);
243 cntr->fd = open(path, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC,
249 builder = builders[format];
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)
256 memset(cntr->private_data, 0, builder->private_size);
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;
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)
276 unsigned int bytes_per_frame;
281 assert(samples_per_frame);
282 assert(frames_per_second);
285 if (cntr->type == CONTAINER_TYPE_BUILDER)
286 byte_count = cntr->max_size;
288 if (cntr->ops->pre_process) {
289 err = cntr->ops->pre_process(cntr, format, samples_per_frame,
290 frames_per_second, &byte_count);
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);
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;
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;
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",
328 fprintf(stderr, " max frames: %lu\n",
336 int container_context_process_frames(struct container_context *cntr,
338 unsigned int *frame_count)
340 char *buf = frame_buffer;
341 unsigned int bytes_per_frame;
342 unsigned int byte_count;
347 assert(frame_buffer);
350 bytes_per_frame = cntr->bytes_per_sample * cntr->samples_per_frame;
351 byte_count = *frame_count * bytes_per_frame;
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;
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);
365 cntr->handled_byte_count += byte_count;
366 if (cntr->handled_byte_count == cntr->max_size)
369 *frame_count = byte_count / bytes_per_frame;
374 int container_context_post_process(struct container_context *cntr,
375 uint64_t *frame_count)
382 if (cntr->verbose && cntr->handled_byte_count > 0) {
383 fprintf(stderr, " Handled bytes: %lu\n",
384 cntr->handled_byte_count);
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;
393 err = cntr->ops->post_process(cntr, cntr->handled_byte_count);
396 // Ensure to perform write-back from disk cache.
397 if (cntr->type == CONTAINER_TYPE_BUILDER)
403 if (cntr->bytes_per_sample == 0 || cntr->samples_per_frame == 0) {
406 *frame_count = cntr->handled_byte_count /
407 cntr->bytes_per_sample /
408 cntr->samples_per_frame;
414 void container_context_destroy(struct container_context *cntr)
419 if (cntr->private_data)
420 free(cntr->private_data);
423 cntr->private_data = NULL;