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