Blame axfer/subcmd-transfer.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
//
Packit Service a9274b
// subcmd-transfer.c - operations for transfer sub command.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "xfer.h"
Packit Service a9274b
#include "subcmd.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
#include <signal.h>
Packit Service a9274b
#include <inttypes.h>
Packit Service a9274b
Packit Service a9274b
struct context {
Packit Service a9274b
	struct xfer_context xfer;
Packit Service a9274b
	struct mapper_context mapper;
Packit Service a9274b
	struct container_context *cntrs;
Packit Service a9274b
	unsigned int cntr_count;
Packit Service a9274b
Packit Service a9274b
	// NOTE: To handling Unix signal.
Packit Service a9274b
	bool interrupted;
Packit Service a9274b
	int signal;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// NOTE: To handling Unix signal.
Packit Service a9274b
static struct context *ctx_ptr;
Packit Service a9274b
Packit Service a9274b
static void handle_unix_signal_for_finish(int sig)
Packit Service a9274b
{
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ctx_ptr->cntr_count; ++i)
Packit Service a9274b
		ctx_ptr->cntrs[i].interrupted = true;
Packit Service a9274b
Packit Service a9274b
	ctx_ptr->signal = sig;
Packit Service a9274b
	ctx_ptr->interrupted = true;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void handle_unix_signal_for_suspend(int sig)
Packit Service a9274b
{
Packit Service a9274b
	sigset_t curr, prev;
Packit Service a9274b
	struct sigaction sa = {0};
Packit Service a9274b
Packit Service a9274b
	// 1. suspend substream.
Packit Service a9274b
	xfer_context_pause(&ctx_ptr->xfer, true);
Packit Service a9274b
Packit Service a9274b
	// 2. Prepare for default handler(SIG_DFL) of SIGTSTP to stop this
Packit Service a9274b
	// process.
Packit Service a9274b
	if (sigaction(SIGTSTP, NULL, &sa) < 0) {
Packit Service a9274b
		fprintf(stderr, "sigaction(2)\n");
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
	if (sa.sa_handler == SIG_ERR)
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	if (sa.sa_handler == handle_unix_signal_for_suspend)
Packit Service a9274b
		sa.sa_handler = SIG_DFL;
Packit Service a9274b
	if (sigaction(SIGTSTP, &sa, NULL) < 0) {
Packit Service a9274b
		fprintf(stderr, "sigaction(2)\n");
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Queue SIGTSTP.
Packit Service a9274b
	raise(SIGTSTP);
Packit Service a9274b
Packit Service a9274b
	// Release the queued signal from being blocked. This causes an
Packit Service a9274b
	// additional interrupt for the default handler.
Packit Service a9274b
	sigemptyset(&curr);
Packit Service a9274b
	sigaddset(&curr, SIGTSTP);
Packit Service a9274b
	if (sigprocmask(SIG_UNBLOCK, &curr, &prev) < 0) {
Packit Service a9274b
		fprintf(stderr, "sigprocmask(2)\n");
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// 3. SIGCONT is cought and rescheduled. Recover blocking status of
Packit Service a9274b
	// UNIX signals.
Packit Service a9274b
	if (sigprocmask(SIG_SETMASK, &prev, NULL) < 0) {
Packit Service a9274b
		fprintf(stderr, "sigprocmask(2)\n");
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Reconfigure this handler for SIGTSTP, instead of default one.
Packit Service a9274b
	sigemptyset(&sa.sa_mask);
Packit Service a9274b
	sa.sa_flags = SA_RESTART;
Packit Service a9274b
	sa.sa_handler = handle_unix_signal_for_suspend;
Packit Service a9274b
	if (sigaction(SIGTSTP, &sa, NULL) < 0) {
Packit Service a9274b
		fprintf(stderr, "sigaction(2)\n");
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// 4. Continue the PCM substream.
Packit Service a9274b
	xfer_context_pause(&ctx_ptr->xfer, false);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int prepare_signal_handler(struct context *ctx)
Packit Service a9274b
{
Packit Service a9274b
	struct sigaction sa = {0};
Packit Service a9274b
Packit Service a9274b
	sigemptyset(&sa.sa_mask);
Packit Service a9274b
	sa.sa_flags = 0;
Packit Service a9274b
	sa.sa_handler = handle_unix_signal_for_finish;
Packit Service a9274b
Packit Service a9274b
	if (sigaction(SIGINT, &sa, NULL) < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
	if (sigaction(SIGTERM, &sa, NULL) < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
Packit Service a9274b
	sigemptyset(&sa.sa_mask);
Packit Service a9274b
	sa.sa_flags = 0;
Packit Service a9274b
	sa.sa_handler = handle_unix_signal_for_suspend;
Packit Service a9274b
	if (sigaction(SIGTSTP, &sa, NULL) < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
Packit Service a9274b
	ctx_ptr = ctx;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int context_init(struct context *ctx, snd_pcm_stream_t direction,
Packit Service a9274b
			int argc, char *const *argv)
Packit Service a9274b
{
Packit Service a9274b
	const char *xfer_type_literal;
Packit Service a9274b
	enum xfer_type xfer_type;
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	// Decide transfer backend before option parser runs.
Packit Service a9274b
	xfer_type_literal = NULL;
Packit Service a9274b
	for (i = 0; i < argc; ++i) {
Packit Service a9274b
		if (strstr(argv[i], "--xfer-type") != argv[i])
Packit Service a9274b
			continue;
Packit Service a9274b
		xfer_type_literal = argv[i] + 12;
Packit Service a9274b
	}
Packit Service a9274b
	if (xfer_type_literal == NULL) {
Packit Service a9274b
		xfer_type = XFER_TYPE_LIBASOUND;
Packit Service a9274b
	} else {
Packit Service a9274b
		xfer_type = xfer_type_from_label(xfer_type_literal);
Packit Service a9274b
		if (xfer_type == XFER_TYPE_UNSUPPORTED) {
Packit Service a9274b
			fprintf(stderr, "The '%s' xfer type is not supported\n",
Packit Service a9274b
				xfer_type_literal);
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Initialize transfer.
Packit Service a9274b
	return xfer_context_init(&ctx->xfer, xfer_type, direction, argc, argv);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int capture_pre_process(struct context *ctx, snd_pcm_access_t *access,
Packit Service a9274b
			       snd_pcm_uframes_t *frames_per_buffer,
Packit Service a9274b
			       uint64_t *total_frame_count)
Packit Service a9274b
{
Packit Service a9274b
	snd_pcm_format_t sample_format = SND_PCM_FORMAT_UNKNOWN;
Packit Service a9274b
	unsigned int samples_per_frame = 0;
Packit Service a9274b
	unsigned int frames_per_second = 0;
Packit Service a9274b
	unsigned int channels;
Packit Service a9274b
	int i;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = xfer_context_pre_process(&ctx->xfer, &sample_format,
Packit Service a9274b
				       &samples_per_frame, &frames_per_second,
Packit Service a9274b
				       access, frames_per_buffer);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	// Prepare for containers.
Packit Service a9274b
	ctx->cntrs = calloc(ctx->xfer.path_count, sizeof(*ctx->cntrs));
Packit Service a9274b
	if (ctx->cntrs == NULL)
Packit Service a9274b
		return -ENOMEM;
Packit Service a9274b
	ctx->cntr_count = ctx->xfer.path_count;
Packit Service a9274b
Packit Service a9274b
	if (ctx->cntr_count > 1)
Packit Service a9274b
		channels = 1;
Packit Service a9274b
	else
Packit Service a9274b
		channels = samples_per_frame;
Packit Service a9274b
Packit Service a9274b
	*total_frame_count = 0;
Packit Service a9274b
	for (i = 0; i < ctx->cntr_count; ++i) {
Packit Service a9274b
		uint64_t frame_count;
Packit Service a9274b
Packit Service a9274b
		err = container_builder_init(ctx->cntrs + i,
Packit Service a9274b
					     ctx->xfer.paths[i],
Packit Service a9274b
					     ctx->xfer.cntr_format,
Packit Service a9274b
					     ctx->xfer.verbose > 1);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
Packit Service a9274b
		err = container_context_pre_process(ctx->cntrs + i,
Packit Service a9274b
						    &sample_format, &channels,
Packit Service a9274b
						    &frames_per_second,
Packit Service a9274b
						    &frame_count);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
Packit Service a9274b
		if (*total_frame_count == 0)
Packit Service a9274b
			*total_frame_count = frame_count;
Packit Service a9274b
		if (frame_count < *total_frame_count)
Packit Service a9274b
			*total_frame_count = frame_count;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int playback_pre_process(struct context *ctx, snd_pcm_access_t *access,
Packit Service a9274b
				snd_pcm_uframes_t *frames_per_buffer,
Packit Service a9274b
				uint64_t *total_frame_count)
Packit Service a9274b
{
Packit Service a9274b
	snd_pcm_format_t sample_format = SND_PCM_FORMAT_UNKNOWN;
Packit Service a9274b
	unsigned int samples_per_frame = 0;
Packit Service a9274b
	unsigned int frames_per_second = 0;
Packit Service a9274b
	int i;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// Prepare for containers.
Packit Service a9274b
	ctx->cntrs = calloc(ctx->xfer.path_count, sizeof(*ctx->cntrs));
Packit Service a9274b
	if (ctx->cntrs == NULL)
Packit Service a9274b
		return -ENOMEM;
Packit Service a9274b
	ctx->cntr_count = ctx->xfer.path_count;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ctx->cntr_count; ++i) {
Packit Service a9274b
		snd_pcm_format_t format;
Packit Service a9274b
		unsigned int channels;
Packit Service a9274b
		unsigned int rate;
Packit Service a9274b
		uint64_t frame_count;
Packit Service a9274b
Packit Service a9274b
		err = container_parser_init(ctx->cntrs + i,
Packit Service a9274b
					    ctx->xfer.paths[i],
Packit Service a9274b
					    ctx->xfer.verbose > 1);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
Packit Service a9274b
		if (i == 0) {
Packit Service a9274b
			// For a raw container.
Packit Service a9274b
			format = ctx->xfer.sample_format;
Packit Service a9274b
			channels = ctx->xfer.samples_per_frame;
Packit Service a9274b
			rate = ctx->xfer.frames_per_second;
Packit Service a9274b
		} else {
Packit Service a9274b
			format = sample_format;
Packit Service a9274b
			channels = samples_per_frame;
Packit Service a9274b
			rate = frames_per_second;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		err = container_context_pre_process(ctx->cntrs + i, &format,
Packit Service a9274b
						    &channels, &rate,
Packit Service a9274b
						    &frame_count);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
Packit Service a9274b
		if (format == SND_PCM_FORMAT_UNKNOWN || channels == 0 ||
Packit Service a9274b
		    rate == 0) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"Sample format, channels and rate should be "
Packit Service a9274b
				"indicated for given files.\n");
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		if (i == 0) {
Packit Service a9274b
			sample_format = format;
Packit Service a9274b
			samples_per_frame = channels;
Packit Service a9274b
			frames_per_second = rate;
Packit Service a9274b
			*total_frame_count = frame_count;
Packit Service a9274b
		} else {
Packit Service a9274b
			if (format != sample_format) {
Packit Service a9274b
				fprintf(stderr,
Packit Service a9274b
					"When using several files, they "
Packit Service a9274b
					"should include the same sample "
Packit Service a9274b
					"format.\n");
Packit Service a9274b
				return -EINVAL;
Packit Service a9274b
			}
Packit Service a9274b
Packit Service a9274b
			// No need to check channels to handle multiple
Packit Service a9274b
			// containers.
Packit Service a9274b
			if (rate != frames_per_second) {
Packit Service a9274b
				fprintf(stderr,
Packit Service a9274b
					"When using several files, they "
Packit Service a9274b
					"should include samples at the same "
Packit Service a9274b
					"sampling rate.\n");
Packit Service a9274b
				return -EINVAL;
Packit Service a9274b
			}
Packit Service a9274b
			if (frame_count < *total_frame_count)
Packit Service a9274b
				*total_frame_count = frame_count;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (ctx->cntr_count > 1)
Packit Service a9274b
		samples_per_frame = ctx->cntr_count;
Packit Service a9274b
Packit Service a9274b
	// Configure hardware with these parameters.
Packit Service a9274b
	return xfer_context_pre_process(&ctx->xfer, &sample_format,
Packit Service a9274b
					&samples_per_frame, &frames_per_second,
Packit Service a9274b
					access, frames_per_buffer);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int context_pre_process(struct context *ctx, snd_pcm_stream_t direction,
Packit Service a9274b
			       uint64_t *total_frame_count)
Packit Service a9274b
{
Packit Service a9274b
	snd_pcm_access_t access;
Packit Service a9274b
	snd_pcm_uframes_t frames_per_buffer = 0;
Packit Service a9274b
	unsigned int bytes_per_sample = 0;
Packit Service a9274b
	enum mapper_type mapper_type;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	if (direction == SND_PCM_STREAM_CAPTURE) {
Packit Service a9274b
		mapper_type = MAPPER_TYPE_DEMUXER;
Packit Service a9274b
		err = capture_pre_process(ctx, &access, &frames_per_buffer,
Packit Service a9274b
					  total_frame_count);
Packit Service a9274b
	} else {
Packit Service a9274b
		mapper_type = MAPPER_TYPE_MUXER;
Packit Service a9274b
		err = playback_pre_process(ctx, &access, &frames_per_buffer,
Packit Service a9274b
					   total_frame_count);
Packit Service a9274b
	}
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	// Prepare for mapper.
Packit Service a9274b
	err = mapper_context_init(&ctx->mapper, mapper_type, ctx->cntr_count,
Packit Service a9274b
				  ctx->xfer.verbose > 1);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	bytes_per_sample =
Packit Service a9274b
		snd_pcm_format_physical_width(ctx->xfer.sample_format) / 8;
Packit Service a9274b
	if (bytes_per_sample <= 0)
Packit Service a9274b
		return -ENXIO;
Packit Service a9274b
	err = mapper_context_pre_process(&ctx->mapper, access, bytes_per_sample,
Packit Service a9274b
					 ctx->xfer.samples_per_frame,
Packit Service a9274b
					 frames_per_buffer, ctx->cntrs);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	xfer_options_calculate_duration(&ctx->xfer, total_frame_count);
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int context_process_frames(struct context *ctx,
Packit Service a9274b
				  snd_pcm_stream_t direction,
Packit Service a9274b
				  uint64_t expected_frame_count,
Packit Service a9274b
				  uint64_t *actual_frame_count)
Packit Service a9274b
{
Packit Service a9274b
	bool verbose = ctx->xfer.verbose > 2;
Packit Service a9274b
	unsigned int frame_count;
Packit Service a9274b
	int i;
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	if (!ctx->xfer.quiet) {
Packit Service a9274b
		fprintf(stderr,
Packit Service a9274b
			"%s: Format '%s', Rate %u Hz, Channels ",
Packit Service a9274b
			snd_pcm_stream_name(direction),
Packit Service a9274b
			snd_pcm_format_description(ctx->xfer.sample_format),
Packit Service a9274b
			ctx->xfer.frames_per_second);
Packit Service a9274b
		if (ctx->xfer.samples_per_frame == 1)
Packit Service a9274b
			fprintf(stderr, "'monaural'");
Packit Service a9274b
		else if (ctx->xfer.samples_per_frame == 2)
Packit Service a9274b
			fprintf(stderr, "'Stereo'");
Packit Service a9274b
		else
Packit Service a9274b
			fprintf(stderr, "%u", ctx->xfer.samples_per_frame);
Packit Service a9274b
		fprintf(stderr, "\n");
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	*actual_frame_count = 0;
Packit Service a9274b
	while (!ctx->interrupted) {
Packit Service a9274b
		struct container_context *cntr;
Packit Service a9274b
Packit Service a9274b
		// Tell remains to expected frame count.
Packit Service a9274b
		frame_count = expected_frame_count - *actual_frame_count;
Packit Service a9274b
		err = xfer_context_process_frames(&ctx->xfer, &ctx->mapper,
Packit Service a9274b
						  ctx->cntrs, &frame_count);
Packit Service a9274b
		if (err < 0) {
Packit Service a9274b
			if (err == -EAGAIN || err == -EINTR)
Packit Service a9274b
				continue;
Packit Service a9274b
			break;
Packit Service a9274b
		}
Packit Service a9274b
		if (verbose) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"  handled: %u\n", frame_count);
Packit Service a9274b
		}
Packit Service a9274b
		for (i = 0; i < ctx->cntr_count; ++i) {
Packit Service a9274b
			cntr = &ctx->cntrs[i];
Packit Service a9274b
			if (cntr->eof)
Packit Service a9274b
				break;
Packit Service a9274b
		}
Packit Service a9274b
		if (i < ctx->cntr_count)
Packit Service a9274b
			break;
Packit Service a9274b
Packit Service a9274b
		*actual_frame_count += frame_count;
Packit Service a9274b
		if (*actual_frame_count >= expected_frame_count)
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (!ctx->xfer.quiet) {
Packit Service a9274b
		fprintf(stderr,
Packit Service a9274b
			"%s: Expected %" PRIu64 "frames, "
Packit Service a9274b
			"Actual %" PRIu64 "frames\n",
Packit Service a9274b
			snd_pcm_stream_name(direction), expected_frame_count,
Packit Service a9274b
			*actual_frame_count);
Packit Service a9274b
		if (ctx->interrupted) {
Packit Service a9274b
			fprintf(stderr, "Aborted by signal: %s\n",
Packit Service a9274b
			       strsignal(ctx->signal));
Packit Service a9274b
			return 0;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void context_post_process(struct context *ctx,
Packit Service a9274b
				 uint64_t accumulated_frame_count)
Packit Service a9274b
{
Packit Service a9274b
	uint64_t total_frame_count;
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	xfer_context_post_process(&ctx->xfer);
Packit Service a9274b
Packit Service a9274b
	if (ctx->cntrs) {
Packit Service a9274b
		for (i = 0; i < ctx->cntr_count; ++i) {
Packit Service a9274b
			container_context_post_process(ctx->cntrs + i,
Packit Service a9274b
						       &total_frame_count);
Packit Service a9274b
			container_context_destroy(ctx->cntrs + i);
Packit Service a9274b
		}
Packit Service a9274b
		free(ctx->cntrs);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	mapper_context_post_process(&ctx->mapper);
Packit Service a9274b
	mapper_context_destroy(&ctx->mapper);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void context_destroy(struct context *ctx)
Packit Service a9274b
{
Packit Service a9274b
	xfer_context_destroy(&ctx->xfer);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int subcmd_transfer(int argc, char *const *argv, snd_pcm_stream_t direction)
Packit Service a9274b
{
Packit Service a9274b
	struct context ctx = {0};
Packit Service a9274b
	uint64_t expected_frame_count = 0;
Packit Service a9274b
	uint64_t actual_frame_count = 0;
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	err = prepare_signal_handler(&ctx;;
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	err = context_init(&ctx, direction, argc, argv);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		goto end;
Packit Service a9274b
	if (ctx.xfer.help || ctx.xfer.dump_hw_params)
Packit Service a9274b
		goto end;
Packit Service a9274b
Packit Service a9274b
	err = context_pre_process(&ctx, direction, &expected_frame_count);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		goto end;
Packit Service a9274b
Packit Service a9274b
	err = context_process_frames(&ctx, direction, expected_frame_count,
Packit Service a9274b
				     &actual_frame_count);
Packit Service a9274b
end:
Packit Service a9274b
	context_post_process(&ctx, actual_frame_count);
Packit Service a9274b
Packit Service a9274b
	context_destroy(&ctx;;
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}