summaryrefslogtreecommitdiff
path: root/audio/resampler.c
blob: 6906d052641f7e79337516dfcb498cdc80417a7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <asm-generic/errno-base.h>
#include <assert.h>
#include <libavcodec/avcodec.h>
#include <libavutil/error.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>
#include "resampler.h"

#define MICROSECOND 1000000

static inline uint64_t div_uint64_ceil(uint64_t num, uint64_t den)
{
	return (num / den) + (num % den != 0);
}

static int resampler_init (struct Resampler *resampler, const AVChannelLayout *out_chlayout, enum AVSampleFormat out_sample_fmt, int64_t out_sample_rate, uint64_t out_frame_size)
{
	/* Let me tell you something; you --don't want-- CAN'T configure a
	 * SwrContext manually, GOOD ****ING LUCK DOING THAT. The documentation is
	 * so bad on that matter, it only referes to "tHe PaRaMeTeRs" or show off a
	 * list of deprecated attribute which looks nothing like what's actually
	 * used in their API. I tried to copy what's being done in their a API but
	 * whatever I do would either not work or be liable to change since it's not
	 * documented for shit (even the documented stuff doesn't appears stable, so
	 * wtf am I saying anyway). A ****ing joke, I tell you that.
	 *
	 * Pardon my french.
	 *
	 * Anywho, instead of configuring the input parameter directly, we shove
	 * them in resampler so we can recall them before calling ffmpeg's SWR
	 * allocator.
	 */
	resampler->out_chlayout    = out_chlayout;
	resampler->out_sample_fmt  = out_sample_fmt;
	resampler->out_sample_rate = out_sample_rate;
	resampler->out_frame_size  = out_frame_size;
	resampler->swr = NULL;
	return 0;
}

static int resampler_conf (struct Resampler *resampler, const AVChannelLayout *in_chlayout, enum AVSampleFormat in_sample_fmt, int64_t in_sample_rate)
{
	int err;

	err = swr_alloc_set_opts2(
		&resampler->swr,
		resampler->out_chlayout,
		resampler->out_sample_fmt,
		resampler->out_sample_rate,
		in_chlayout,
		in_sample_fmt,
		in_sample_rate,
		0, 0);
	if (err) return err;

	if (!swr_is_initialized(resampler->swr))
		if ((err = swr_init(resampler->swr)))
			return err;

	resampler->in_sample_rate = in_sample_rate;
	resampler->needed = div_uint64_ceil(resampler->out_frame_size * in_sample_rate, resampler->out_sample_rate);

	return 0;
}

int resampler_send_frame (struct Resampler *resampler, AVFrame *in)
{
	int err;

	if ((err = swr_convert(resampler->swr, NULL, 0, (const uint8_t **)in->extended_data, in->nb_samples)) < 0)
		return err;
	resampler->stored += in->nb_samples;
	return 0;
}

int resampler_send_empty (struct Resampler *resampler, unsigned int microsecond)
{
	// TODO: Check for downcasting
	int nb_samples;
	nb_samples = div_uint64_ceil(microsecond * resampler->in_sample_rate, 1000);
	return swr_inject_silence(resampler->swr, nb_samples);
}

int resampler_convert (struct Resampler *resampler, AVFrame *out)
{
	int err;
	assert(out != NULL);
	assert(resampler->swr != NULL);

	if (resampler->stored < resampler->needed) {
		return AVERROR(EAGAIN);
		/*
		err = swr_inject_silence(resampler->swr, resampler->needed - resampler->stored);
		if (err < 0) return err;
		*/
	}

	err = swr_convert(resampler->swr, out->extended_data, out->nb_samples, NULL, 0);
	if (err < 0) return err;
	resampler->stored = 0;

	return 0;
}

void resampler_free (struct Resampler *resampler)
{
	if (resampler == NULL) return;
	swr_free(&resampler->swr);
}

int resampler_init_for_encoder (struct Resampler *resampler, const struct Encoder *encoder)
{
	AVCodecContext *avctx = encoder->avctx;
	return resampler_init(resampler, &avctx->ch_layout, avctx->sample_fmt, avctx->sample_rate, avctx->frame_size);
}

int resampler_conf_for_decoder (struct Resampler *resampler, const struct Decoder *decoder)
{
	AVCodecContext *avctx = decoder->avctx;
	return resampler_conf(resampler, &avctx->ch_layout, avctx->sample_fmt, avctx->sample_rate);
}