bluez5: asha-codec-g722: Drop the dependency on FFmpeg

We use the G722 encoder from Android itself and drop
the dependency on FFmpeg/libav.
This commit is contained in:
Sanchayan Maity 2025-02-13 18:26:10 +05:30
parent 27e0338f24
commit dbed1bdf3d
4 changed files with 547 additions and 120 deletions

View file

@ -6,14 +6,9 @@
#include <spa/utils/dict.h>
#include <spa/debug/log.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include "rtp.h"
#include "media-codecs.h"
#include "g722/g722_enc_dec.h"
#define ASHA_HEADER_SZ 1 /* 1 byte sequence number */
#define ASHA_ENCODED_PKT_SZ 160
@ -21,11 +16,7 @@
static struct spa_log *spalog;
struct impl {
const AVCodec *avcodec;
AVCodecContext *ctx;
AVFrame *frame;
AVPacket *pkt;
g722_encode_state_t encode;
unsigned int codesize;
};
@ -96,14 +87,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
static void codec_deinit(void *data)
{
struct impl *this = data;
if (this->frame)
av_frame_free(&this->frame);
if (this->pkt)
av_packet_free(&this->pkt);
if (this->ctx)
avcodec_free_context(&this->ctx);
return;
}
static void *codec_init(const struct media_codec *codec, uint32_t flags,
@ -111,64 +95,11 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
void *props, size_t mtu)
{
struct impl *this;
const AVCodec *avcodec;
AVCodecContext *c = NULL;
AVFrame *frame = NULL;
AVPacket *pkt = NULL;
if ((this = calloc(1, sizeof(struct impl))) == NULL)
goto error;
return NULL;
avcodec = avcodec_find_encoder(AV_CODEC_ID_ADPCM_G722);
if (!avcodec) {
spa_log_error(spalog, "Codec not found");
goto error;
}
c = avcodec_alloc_context3(avcodec);
if (!c) {
spa_log_error(spalog, "Codec context allocation failed");
goto error;
}
/* https://source.android.com/docs/core/connect/bluetooth/asha#audio-packet-format-and-timing */
c->bit_rate = 64000;
c->sample_rate = 16000;
c->sample_fmt = AV_SAMPLE_FMT_S16;
av_channel_layout_copy(&c->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO);
spa_log_info(spalog, "Opening codec, format: %d, rate: %d, layout: %d", c->sample_fmt, c->sample_rate, c->ch_layout.nb_channels);
if (avcodec_open2(c, avcodec, NULL) < 0) {
spa_log_error(spalog, "Could not open codec");
goto error;
}
frame = av_frame_alloc();
if (!frame) {
spa_log_error(spalog, "Could not allocate frame");
goto error;
}
frame->nb_samples = c->frame_size;
frame->format = c->sample_fmt;
if (av_channel_layout_copy (&frame->ch_layout, &c->ch_layout)) {
spa_log_error(spalog, "Failed to copy channel layout");
goto error;
}
spa_log_info(spalog, "Frame info, samples: %d, format: %d, layout: %d", frame->nb_samples, frame->format, frame->ch_layout.nb_channels);
if (av_frame_get_buffer(frame, 0)) {
spa_log_error(spalog, "Failed to allocate buffer for frame");
goto error;
}
pkt = av_packet_alloc();
if (!pkt) {
spa_log_error(spalog, "Could not allocate packet");
goto error;
}
g722_encode_init(&this->encode, 64000, G722_PACKED);
/*
* G722 has a compression ratio of 4. Considering 160 bytes of encoded
@ -176,22 +107,9 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
*/
this->codesize = ASHA_ENCODED_PKT_SZ * 4;
this->avcodec = avcodec;
this->ctx = c;
this->frame = frame;
this->pkt = pkt;
spa_log_debug(spalog, "Codec initialized");
return this;
error:
if (frame)
av_frame_free(&frame);
if (c)
avcodec_free_context(&c);
return NULL;
}
static int codec_encode(void *data,
@ -200,8 +118,6 @@ static int codec_encode(void *data,
size_t *dst_out, int *need_flush)
{
struct impl *this = data;
AVFrame *frame = this->frame;
AVPacket *pkt = this->pkt;
size_t src_sz;
int ret;
@ -209,45 +125,21 @@ static int codec_encode(void *data,
spa_log_trace(spalog, "Insufficient bytes for encoding, %zd", src_size);
return 0;
}
if (dst_size < (ASHA_HEADER_SZ + ASHA_ENCODED_PKT_SZ)) {
spa_log_trace(spalog, "No space for encoded output, %zd", dst_size);
return 0;
}
spa_log_trace(spalog, "%zd bytes to encode", src_size);
ret = av_frame_make_writable(this->frame);
if (ret < 0) {
spa_log_error(spalog, "Failed to make frame writable");
return -EIO;
}
src_sz = (src_size > this->codesize) ? this->codesize : src_size;
frame->data[0] = (uint8_t *)src;
frame->linesize[0] = src_sz;
spa_log_trace(spalog, "Encoding %zd bytes", src_sz);
ret = avcodec_send_frame(this->ctx, frame);
ret = g722_encode(&this->encode, dst, src, src_sz / 2 /* S16LE */);
if (ret < 0) {
spa_log_error(spalog, "Failed to send frame: %d", ret);
return ret;
}
ret = avcodec_receive_packet(this->ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
spa_log_warn(spalog, "Receive packet EOF/EAGAIN: %d", ret);
return 0;
}
if (ret < 0) {
spa_log_error(spalog, "Receive packet error: %d", ret);
spa_log_error(spalog, "encode error: %d", ret);
return -EIO;
}
memcpy(dst, pkt->data, pkt->size);
*dst_out = pkt->size;
*dst_out = ret;
*need_flush = NEED_FLUSH_ALL;
return src_sz;

View file

@ -0,0 +1,148 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* g722.h - The ITU G.722 codec.
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005 Steve Underwood
*
* Despite my general liking of the GPL, I place my own contributions
* to this code in the public domain for the benefit of all mankind -
* even the slimy ones who might try to proprietize my work and use it
* to my detriment.
*
* Based on a single channel G.722 codec which is:
*
***** Copyright (c) CMU 1993 *****
* Computer Science, Speech Group
* Chengxiang Lu and Alex Hauptmann
*
* $Id: g722.h 48959 2006-12-25 06:42:15Z rizzo $
*/
/*! \file */
#if !defined(_G722_H_)
#define _G722_H_
#include <stdint.h>
/*! \page g722_page G.722 encoding and decoding
\section g722_page_sec_1 What does it do?
The G.722 module is a bit exact implementation of the ITU G.722 specification for all three
specified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests.
To allow fast and flexible interworking with narrow band telephony, the encoder and decoder
support an option for the linear audio to be an 8k samples/second stream. In this mode the
codec is considerably faster, and still fully compatible with wideband terminals using G.722.
\section g722_page_sec_2 How does it work?
???.
*/
/* Format DAC12 is added to decode directly into samples suitable for
a 12-bit DAC using offset binary representation. */
enum {
G722_SAMPLE_RATE_8000 = 0x0001,
G722_PACKED = 0x0002,
G722_FORMAT_DAC12 = 0x0004,
};
#ifdef BUILD_FEATURE_DAC
#define NLDECOMPRESS_APPLY_GAIN(s, g) (((s) * (int32_t)(g)) >> 16)
// Equivalent to shift 16, add 0x8000, shift 4
#define NLDECOMPRESS_APPLY_GAIN_CONVERTED_DAC(s, g) \
(uint16_t)((uint16_t)(((s) * (int32_t)(g)) >> 20) + 0x800)
#else
#define NLDECOMPRESS_APPLY_GAIN(s, g) (((int32_t)(s) * (int32_t)(g)) >> 16)
#endif
#ifdef BUILD_FEATURE_DAC
#define NLDECOMPRESS_PREPROCESS_PCM_SAMPLE_WITH_GAIN(s, g) \
NLDECOMPRESS_APPLY_GAIN_CONVERTED_DAC((s), (g))
#define NLDECOMPRESS_PREPROCESS_SAMPLE_WITH_GAIN(s, g) ((int16_t)NLDECOMPRESS_APPLY_GAIN((s), (g)))
#else
#define NLDECOMPRESS_PREPROCESS_PCM_SAMPLE_WITH_GAIN NLDECOMPRESS_PREPROCESS_SAMPLE_WITH_GAIN
#define NLDECOMPRESS_PREPROCESS_SAMPLE_WITH_GAIN(s, g) \
((int16_t)(NLDECOMPRESS_APPLY_GAIN((s), (g))))
#endif
typedef struct {
int s;
int sp;
int sz;
int r[3];
int a[3];
int ap[3];
int p[3];
int d[7];
int b[7];
int bp[7];
int nb;
int det;
} g722_band_t;
typedef struct {
/*! TRUE if the operating in the special ITU test mode, with the band split filters disabled. */
int itu_test_mode;
/*! TRUE if the G.722 data is packed */
int packed;
/*! TRUE if encode from 8k samples/second */
int eight_k;
/*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */
int bits_per_sample;
/*! Signal history for the QMF */
int x[24];
g722_band_t band[2];
unsigned int in_buffer;
int in_bits;
unsigned int out_buffer;
int out_bits;
} g722_encode_state_t;
typedef struct {
/*! TRUE if the operating in the special ITU test mode, with the band split filters disabled. */
int itu_test_mode;
/*! TRUE if the G.722 data is packed */
int packed;
/*! TRUE if decode to 8k samples/second */
int eight_k;
/*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */
int bits_per_sample;
/*! TRUE if offset binary for a 12-bit DAC */
int dac_pcm;
/*! Signal history for the QMF */
int x[24];
g722_band_t band[2];
unsigned int in_buffer;
int in_bits;
unsigned int out_buffer;
int out_bits;
} g722_decode_state_t;
#ifdef __cplusplus
extern "C" {
#endif
g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, unsigned int rate, int options);
int g722_encode_release(g722_encode_state_t *s);
int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len);
g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, unsigned int rate, int options);
int g722_decode_release(g722_decode_state_t *s);
uint32_t g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len,
uint16_t aGain);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,387 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* g722_encode.c - The ITU G.722 codec, encode part.
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005 Steve Underwood
*
* All rights reserved.
*
* Despite my general liking of the GPL, I place my own contributions
* to this code in the public domain for the benefit of all mankind -
* even the slimy ones who might try to proprietize my work and use it
* to my detriment.
*
* Based on a single channel 64kbps only G.722 codec which is:
*
***** Copyright (c) CMU 1993 *****
* Computer Science, Speech Group
* Chengxiang Lu and Alex Hauptmann
*
* $Id: g722_encode.c,v 1.14 2006/07/07 16:37:49 steveu Exp $
*/
/*! \file */
#include <stdlib.h>
#include <string.h>
#include "g722_enc_dec.h"
#if !defined(FALSE)
#define FALSE 0
#endif
#if !defined(TRUE)
#define TRUE (!FALSE)
#endif
#define PACKED_OUTPUT (0)
#define BITS_PER_SAMPLE (8)
#ifndef BUILD_FEATURE_G722_USE_INTRINSIC_SAT
static __inline int16_t saturate(int32_t amp) {
int16_t amp16;
/* Hopefully this is optimised for the common case - not clipping */
amp16 = (int16_t)amp;
if (amp == amp16) {
return amp16;
}
if (amp > 0x7FFF) {
return 0x7FFF;
}
return 0x8000;
}
#else
static __inline int16_t saturate(int32_t val) {
register int32_t res;
__asm volatile("SSAT %0, #16, %1\n\t" : "=r"(res) : "r"(val) :);
return (int16_t)res;
}
#endif
/*- End of function --------------------------------------------------------*/
static void block4(g722_band_t *band, int d) {
int wd1;
int wd2;
int wd3;
int i;
int sg[7];
int ap1, ap2;
int sg0, sgi;
int sz;
/* Block 4, RECONS */
band->d[0] = d;
band->r[0] = saturate(band->s + d);
/* Block 4, PARREC */
band->p[0] = saturate(band->sz + d);
/* Block 4, UPPOL2 */
for (i = 0; i < 3; i++) {
sg[i] = band->p[i] >> 15;
}
wd1 = saturate(band->a[1] << 2);
wd2 = (sg[0] == sg[1]) ? -wd1 : wd1;
if (wd2 > 32767) {
wd2 = 32767;
}
ap2 = (wd2 >> 7) + ((sg[0] == sg[2]) ? 128 : -128);
ap2 += (band->a[2] * 32512) >> 15;
if (ap2 > 12288) {
ap2 = 12288;
} else if (ap2 < -12288) {
ap2 = -12288;
}
band->ap[2] = ap2;
/* Block 4, UPPOL1 */
sg[0] = band->p[0] >> 15;
sg[1] = band->p[1] >> 15;
wd1 = (sg[0] == sg[1]) ? 192 : -192;
wd2 = (band->a[1] * 32640) >> 15;
ap1 = saturate(wd1 + wd2);
wd3 = saturate(15360 - band->ap[2]);
if (ap1 > wd3) {
ap1 = wd3;
} else if (ap1 < -wd3) {
ap1 = -wd3;
}
band->ap[1] = ap1;
/* Block 4, UPZERO */
/* Block 4, FILTEZ */
wd1 = (d == 0) ? 0 : 128;
sg0 = sg[0] = d >> 15;
for (i = 1; i < 7; i++) {
sgi = band->d[i] >> 15;
wd2 = (sgi == sg0) ? wd1 : -wd1;
wd3 = (band->b[i] * 32640) >> 15;
band->bp[i] = saturate(wd2 + wd3);
}
/* Block 4, DELAYA */
sz = 0;
for (i = 6; i > 0; i--) {
int bi;
band->d[i] = band->d[i - 1];
bi = band->b[i] = band->bp[i];
wd1 = saturate(band->d[i] + band->d[i]);
sz += (bi * wd1) >> 15;
}
band->sz = sz;
for (i = 2; i > 0; i--) {
band->r[i] = band->r[i - 1];
band->p[i] = band->p[i - 1];
band->a[i] = band->ap[i];
}
/* Block 4, FILTEP */
wd1 = saturate(band->r[1] + band->r[1]);
wd1 = (band->a[1] * wd1) >> 15;
wd2 = saturate(band->r[2] + band->r[2]);
wd2 = (band->a[2] * wd2) >> 15;
band->sp = saturate(wd1 + wd2);
/* Block 4, PREDIC */
band->s = saturate(band->sp + band->sz);
}
/*- End of function --------------------------------------------------------*/
g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, unsigned int rate, int options) {
if (s == NULL) {
#ifdef G722_SUPPORT_MALLOC
if ((s = (g722_encode_state_t *)malloc(sizeof(*s))) == NULL)
#endif
return NULL;
}
memset(s, 0, sizeof(*s));
if (rate == 48000) {
s->bits_per_sample = 6;
} else if (rate == 56000) {
s->bits_per_sample = 7;
} else {
s->bits_per_sample = 8;
}
s->band[0].det = 32;
s->band[1].det = 8;
return s;
}
/*- End of function --------------------------------------------------------*/
int g722_encode_release(g722_encode_state_t *s) {
free(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
/* WebRtc, tlegrand:
* Only define the following if bit-exactness with reference implementation
* is needed. Will only have any effect if input signal is saturated.
*/
// #define RUN_LIKE_REFERENCE_G722
#ifdef RUN_LIKE_REFERENCE_G722
int16_t limitValues(int16_t rl) {
int16_t yl;
yl = (rl > 16383) ? 16383 : ((rl < -16384) ? -16384 : rl);
return yl;
}
/*- End of function --------------------------------------------------------*/
#endif
static int16_t q6[32] = {0, 35, 72, 110, 150, 190, 233, 276, 323, 370, 422,
473, 530, 587, 650, 714, 786, 858, 940, 1023, 1121, 1219,
1339, 1458, 1612, 1765, 1980, 2195, 2557, 2919, 0, 0};
static int16_t iln[32] = {0, 63, 62, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 0};
static int16_t ilp[32] = {0, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47,
46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 0};
static int16_t wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042};
static int16_t rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0};
static int16_t ilb[32] = {2048, 2093, 2139, 2186, 2233, 2282, 2332, 2383, 2435, 2489, 2543,
2599, 2656, 2714, 2774, 2834, 2896, 2960, 3025, 3091, 3158, 3228,
3298, 3371, 3444, 3520, 3597, 3676, 3756, 3838, 3922, 4008};
static int16_t qm4[16] = {0, -20456, -12896, -8968, -6288, -4240, -2584, -1200,
20456, 12896, 8968, 6288, 4240, 2584, 1200, 0};
static int16_t qm2[4] = {-7408, -1616, 7408, 1616};
static int16_t qmf_coeffs[12] = {
3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11,
};
static int16_t ihn[3] = {0, 1, 0};
static int16_t ihp[3] = {0, 3, 2};
static int16_t wh[3] = {0, -214, 798};
static int16_t rh2[4] = {2, 1, 2, 1};
int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len) {
int dlow;
int dhigh;
int el;
int wd;
int wd1;
int ril;
int wd2;
int il4;
int ih2;
int wd3;
int eh;
int mih;
int i;
int j;
/* Low and high band PCM from the QMF */
int xlow;
int xhigh;
int g722_bytes;
/* Even and odd tap accumulators */
int sumeven;
int sumodd;
int ihigh;
int ilow;
int code;
g722_bytes = 0;
xhigh = 0;
for (j = 0; j < len;) {
if (s->itu_test_mode) {
xlow = xhigh = amp[j++] >> 1;
} else {
{
/* Apply the transmit QMF */
/* Shuffle the buffer down */
for (i = 0; i < 22; i++) {
s->x[i] = s->x[i + 2];
}
// TODO: if len is odd, then this can be a buffer overrun
s->x[22] = amp[j++];
s->x[23] = amp[j++];
/* Discard every other QMF output */
sumeven = 0;
sumodd = 0;
for (i = 0; i < 12; i++) {
sumodd += s->x[2 * i] * qmf_coeffs[i];
sumeven += s->x[2 * i + 1] * qmf_coeffs[11 - i];
}
/* We shift by 12 to allow for the QMF filters (DC gain = 4096), plus 1
to allow for us summing two filters, plus 1 to allow for the 15 bit
input to the G.722 algorithm. */
xlow = (sumeven + sumodd) >> 14;
xhigh = (sumeven - sumodd) >> 14;
#ifdef RUN_LIKE_REFERENCE_G722
/* The following lines are only used to verify bit-exactness
* with reference implementation of G.722. Higher precision
* is achieved without limiting the values.
*/
xlow = limitValues(xlow);
xhigh = limitValues(xhigh);
#endif
}
}
/* Block 1L, SUBTRA */
el = saturate(xlow - s->band[0].s);
/* Block 1L, QUANTL */
wd = (el >= 0) ? el : -(el + 1);
for (i = 1; i < 30; i++) {
wd1 = (q6[i] * s->band[0].det) >> 12;
if (wd < wd1) {
break;
}
}
ilow = (el < 0) ? iln[i] : ilp[i];
/* Block 2L, INVQAL */
ril = ilow >> 2;
wd2 = qm4[ril];
dlow = (s->band[0].det * wd2) >> 15;
/* Block 3L, LOGSCL */
il4 = rl42[ril];
wd = (s->band[0].nb * 127) >> 7;
s->band[0].nb = wd + wl[il4];
if (s->band[0].nb < 0) {
s->band[0].nb = 0;
} else if (s->band[0].nb > 18432) {
s->band[0].nb = 18432;
}
/* Block 3L, SCALEL */
wd1 = (s->band[0].nb >> 6) & 31;
wd2 = 8 - (s->band[0].nb >> 11);
wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
s->band[0].det = wd3 << 2;
block4(&s->band[0], dlow);
{
int nb;
/* Block 1H, SUBTRA */
eh = saturate(xhigh - s->band[1].s);
/* Block 1H, QUANTH */
wd = (eh >= 0) ? eh : -(eh + 1);
wd1 = (564 * s->band[1].det) >> 12;
mih = (wd >= wd1) ? 2 : 1;
ihigh = (eh < 0) ? ihn[mih] : ihp[mih];
/* Block 2H, INVQAH */
wd2 = qm2[ihigh];
dhigh = (s->band[1].det * wd2) >> 15;
/* Block 3H, LOGSCH */
ih2 = rh2[ihigh];
wd = (s->band[1].nb * 127) >> 7;
nb = wd + wh[ih2];
if (nb < 0) {
nb = 0;
} else if (nb > 22528) {
nb = 22528;
}
s->band[1].nb = nb;
/* Block 3H, SCALEH */
wd1 = (s->band[1].nb >> 6) & 31;
wd2 = 10 - (s->band[1].nb >> 11);
wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
s->band[1].det = wd3 << 2;
block4(&s->band[1], dhigh);
#if BITS_PER_SAMPLE == 8
code = ((ihigh << 6) | ilow);
#elif BITS_PER_SAMPLE == 7
code = ((ihigh << 6) | ilow) >> 1;
#elif BITS_PER_SAMPLE == 6
code = ((ihigh << 6) | ilow) >> 2;
#endif
}
#if PACKED_OUTPUT == 1
/* Pack the code bits */
s->out_buffer |= (code << s->out_bits);
s->out_bits += s->bits_per_sample;
if (s->out_bits >= 8) {
g722_data[g722_bytes++] = (uint8_t)(s->out_buffer & 0xFF);
s->out_bits -= 8;
s->out_buffer >>= 8;
}
#else
g722_data[g722_bytes++] = (uint8_t)code;
#endif
}
return g722_bytes;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View file

@ -173,12 +173,12 @@ if get_option('bluez5-codec-lc3').allowed() and lc3_dep.found()
install_dir : spa_plugindir / 'bluez5')
endif
if get_option('bluez5-codec-g722').allowed() and ffmpeg.allowed()
if get_option('bluez5-codec-g722').allowed()
bluez_codec_g722 = shared_library('spa-codec-bluez5-g722',
[ 'asha-codec-g722.c', 'media-codecs.c' ],
[ 'g722/g722_encode.c', 'asha-codec-g722.c', 'media-codecs.c' ],
include_directories : [ configinc ],
c_args : codec_args,
dependencies : [ spa_dep, avcodec_dep, avformat_dep, avutil_dep ],
dependencies : [ spa_dep ],
install : true,
install_dir : spa_plugindir / 'bluez5')
endif