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