summaryrefslogtreecommitdiff
path: root/src/audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio.c')
-rw-r--r--src/audio.c212
1 files changed, 166 insertions, 46 deletions
diff --git a/src/audio.c b/src/audio.c
index 17eb5f6..b2b3cca 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -81,23 +81,24 @@ struct autx {
struct auenc_state *enc; /**< Audio encoder state (optional) */
struct aubuf *aubuf; /**< Packetize outgoing stream */
size_t aubuf_maxsz; /**< Maximum aubuf size in [bytes] */
- volatile bool aubuf_started;
+ volatile bool aubuf_started; /**< Aubuf was started flag */
struct auresamp resamp; /**< Optional resampler for DSP */
struct list filtl; /**< Audio filters in encoding order */
struct mbuf *mb; /**< Buffer for outgoing RTP packets */
char device[64]; /**< Audio source device name */
- int16_t *sampv; /**< Sample buffer */
+ void *sampv; /**< Sample buffer */
int16_t *sampv_rs; /**< Sample buffer for resampler */
uint32_t ptime; /**< Packet time for sending */
uint64_t ts_ext; /**< Ext. Timestamp for outgoing RTP */
- uint32_t ts_base;
+ uint32_t ts_base; /**< First timestamp sent */
uint32_t ts_tel; /**< Timestamp for Telephony Events */
size_t psize; /**< Packet size for sending */
bool marker; /**< Marker bit for outgoing RTP */
bool muted; /**< Audio source is muted */
int cur_key; /**< Currently transmitted event */
- enum aufmt src_fmt;
- bool need_conv;
+ enum aufmt src_fmt; /**< Sample format for audio source */
+ enum aufmt enc_fmt; /**< Sample format for encoder */
+ bool need_conv; /**< Sample format conversion needed */
struct {
uint64_t aubuf_overrun;
@@ -137,24 +138,25 @@ struct aurx {
struct audec_state *dec; /**< Audio decoder state (optional) */
struct aubuf *aubuf; /**< Incoming audio buffer */
size_t aubuf_maxsz; /**< Maximum aubuf size in [bytes] */
- volatile bool aubuf_started;
+ volatile bool aubuf_started; /**< Aubuf was started flag */
struct auresamp resamp; /**< Optional resampler for DSP */
struct list filtl; /**< Audio filters in decoding order */
char device[64]; /**< Audio player device name */
- int16_t *sampv; /**< Sample buffer */
+ void *sampv; /**< Sample buffer */
int16_t *sampv_rs; /**< Sample buffer for resampler */
uint32_t ptime; /**< Packet time for receiving */
int pt; /**< Payload type for incoming RTP */
double level_last;
bool level_set;
- enum aufmt play_fmt;
- bool need_conv;
- struct timestamp_recv ts_recv;
- uint64_t n_discard;
+ enum aufmt play_fmt; /**< Sample format for audio playback*/
+ enum aufmt dec_fmt; /**< Sample format for decoder */
+ bool need_conv; /**< Sample format conversion needed */
+ struct timestamp_recv ts_recv;/**< Receive timestamp state */
struct {
uint64_t aubuf_overrun;
uint64_t aubuf_underrun;
+ uint64_t n_discard;
} stats;
};
@@ -309,7 +311,7 @@ static inline double calc_ptime(size_t nsamp, uint32_t srate, uint8_t channels)
/**
- * Get the DSP samplerate for an audio-codec (exception for G.722 and MPA)
+ * Get the DSP samplerate for an audio-codec
*/
static inline uint32_t get_srate(const struct aucodec *ac)
{
@@ -448,7 +450,9 @@ static void encode_rtp_send(struct audio *a, struct autx *tx,
len = mbuf_get_space(tx->mb);
- err = tx->ac->ench(tx->enc, mbuf_buf(tx->mb), &len, sampv, sampc);
+ err = tx->ac->ench(tx->enc, mbuf_buf(tx->mb), &len,
+ tx->enc_fmt, sampv, sampc);
+
if ((err & 0xffff0000) == 0x00010000) {
/* MPA needs some special treatment here */
tx->ts_ext = err & 0xffff;
@@ -514,11 +518,12 @@ static void poll_aubuf_tx(struct audio *a)
/* timed read from audio-buffer */
- if (tx->src_fmt == AUFMT_S16LE) {
+ if (tx->src_fmt == tx->enc_fmt) {
aubuf_read(tx->aubuf, (uint8_t *)tx->sampv, num_bytes);
}
- else {
+ else if (tx->enc_fmt == AUFMT_S16LE) {
+
/* Convert from ausrc format to 16-bit format */
void *tmp_sampv;
@@ -540,11 +545,23 @@ static void poll_aubuf_tx(struct audio *a)
mem_deref(tmp_sampv);
}
+ else {
+ warning("audio: tx: invalid sample formats (%s -> %s)\n",
+ aufmt_name(tx->src_fmt),
+ aufmt_name(tx->enc_fmt));
+ }
/* optional resampler */
if (tx->resamp.resample) {
size_t sampc_rs = AUDIO_SAMPSZ;
+ if (tx->enc_fmt != AUFMT_S16LE) {
+ warning("audio: skipping resampler due to"
+ " incompatible format (%s)\n",
+ aufmt_name(tx->enc_fmt));
+ return;
+ }
+
err = auresamp(&tx->resamp,
tx->sampv_rs, &sampc_rs,
tx->sampv, sampc);
@@ -555,15 +572,23 @@ static void poll_aubuf_tx(struct audio *a)
sampc = sampc_rs;
}
- /* Process exactly one audio-frame in list order */
- for (le = tx->filtl.head; le; le = le->next) {
- struct aufilt_enc_st *st = le->data;
+ if (tx->enc_fmt == AUFMT_S16LE) {
+
+ /* Process exactly one audio-frame in list order */
+ for (le = tx->filtl.head; le; le = le->next) {
+ struct aufilt_enc_st *st = le->data;
- if (st->af && st->af->ench)
- err |= st->af->ench(st, sampv, &sampc);
+ if (st->af && st->af->ench)
+ err |= st->af->ench(st, sampv, &sampc);
+ }
+ if (err) {
+ warning("audio: aufilter encode: %m\n", err);
+ }
}
- if (err) {
- warning("audio: aufilter encode: %m\n", err);
+ else if (!list_isempty(&tx->filtl)) {
+ warning("audio: skipping audio-filters due to"
+ " incompatible format (%s)\n",
+ aufmt_name(tx->enc_fmt));
}
/* Encode and send */
@@ -623,8 +648,10 @@ static void auplay_write_handler(void *sampv, size_t sampc, void *arg)
++rx->stats.aubuf_underrun;
+#if 0
debug("audio: rx aubuf underrun (total %llu)\n",
rx->stats.aubuf_underrun);
+#endif
}
aubuf_read(rx->aubuf, sampv, num_bytes);
@@ -726,7 +753,7 @@ static void handle_telev(struct audio *a, struct mbuf *mb)
static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
{
size_t sampc = AUDIO_SAMPSZ;
- int16_t *sampv;
+ void *sampv;
struct le *le;
int err = 0;
@@ -735,13 +762,16 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
return 0;
if (mbuf_get_left(mb)) {
- err = rx->ac->dech(rx->dec, rx->sampv, &sampc,
+
+ err = rx->ac->dech(rx->dec,
+ rx->dec_fmt, rx->sampv, &sampc,
mbuf_buf(mb), mbuf_get_left(mb));
+
}
- else if (rx->ac->plch) {
+ else if (rx->ac->plch && rx->dec_fmt == AUFMT_S16LE) {
sampc = rx->ac->srate * rx->ac->ch * rx->ptime / 1000;
- err = rx->ac->plch(rx->dec, rx->sampv, &sampc);
+ err = rx->ac->plch(rx->dec, rx->dec_fmt, rx->sampv, &sampc);
}
else {
/* no PLC in the codec, might be done in filters below */
@@ -754,12 +784,19 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
goto out;
}
- /* Process exactly one audio-frame in reverse list order */
- for (le = rx->filtl.tail; le; le = le->prev) {
- struct aufilt_dec_st *st = le->data;
+ if (rx->dec_fmt == AUFMT_S16LE) {
+ /* Process exactly one audio-frame in reverse list order */
+ for (le = rx->filtl.tail; le; le = le->prev) {
+ struct aufilt_dec_st *st = le->data;
- if (st->af && st->af->dech)
- err |= st->af->dech(st, rx->sampv, &sampc);
+ if (st->af && st->af->dech)
+ err |= st->af->dech(st, rx->sampv, &sampc);
+ }
+ }
+ else if (!list_isempty(&rx->filtl)) {
+ warning("audio: skipping audio-filters due to"
+ " incompatible format (%s)\n",
+ aufmt_name(rx->dec_fmt));
}
if (!rx->aubuf)
@@ -771,6 +808,13 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
if (rx->resamp.resample) {
size_t sampc_rs = AUDIO_SAMPSZ;
+ if (rx->dec_fmt != AUFMT_S16LE) {
+ warning("audio: skipping resampler due to"
+ " incompatible format (%s)\n",
+ aufmt_name(rx->dec_fmt));
+ return ENOTSUP;
+ }
+
err = auresamp(&rx->resamp,
rx->sampv_rs, &sampc_rs,
rx->sampv, sampc);
@@ -789,15 +833,17 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
rx->stats.aubuf_overrun);
}
- if (rx->play_fmt == AUFMT_S16LE) {
- err = aubuf_write_samp(rx->aubuf, sampv, sampc);
+ if (rx->play_fmt == rx->dec_fmt) {
+
+ size_t num_bytes = sampc * aufmt_sample_size(rx->play_fmt);
+
+ err = aubuf_write(rx->aubuf, sampv, num_bytes);
if (err)
goto out;
}
- else {
+ else if (rx->dec_fmt == AUFMT_S16LE) {
/* Convert from 16-bit to auplay format */
-
void *tmp_sampv;
size_t num_bytes = sampc * aufmt_sample_size(rx->play_fmt);
@@ -822,6 +868,11 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
if (err)
goto out;
}
+ else {
+ warning("audio: decode: invalid sample formats (%s -> %s)\n",
+ aufmt_name(rx->dec_fmt),
+ aufmt_name(rx->play_fmt));
+ }
rx->aubuf_started = true;
@@ -945,7 +996,7 @@ static void stream_recv_handler(const struct rtp_header *hdr,
#endif
if (discard) {
- ++a->rx.n_discard;
+ ++rx->stats.n_discard;
return;
}
@@ -1057,6 +1108,9 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
tx->src_fmt = cfg->audio.src_fmt;
rx->play_fmt = cfg->audio.play_fmt;
+ tx->enc_fmt = cfg->audio.enc_fmt;
+ rx->dec_fmt = cfg->audio.dec_fmt;
+
err = stream_alloc(&a->strm, stream_prm, &cfg->avt, call, sdp_sess,
"audio", label,
mnat, mnat_sess, menc, menc_sess,
@@ -1101,8 +1155,11 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
}
tx->mb = mbuf_alloc(STREAM_PRESZ + 4096);
- tx->sampv = mem_zalloc(AUDIO_SAMPSZ * sizeof(int16_t), NULL);
- rx->sampv = mem_zalloc(AUDIO_SAMPSZ * sizeof(int16_t), NULL);
+ tx->sampv = mem_zalloc(AUDIO_SAMPSZ * aufmt_sample_size(tx->enc_fmt),
+ NULL);
+
+ rx->sampv = mem_zalloc(AUDIO_SAMPSZ * aufmt_sample_size(rx->dec_fmt),
+ NULL);
if (!tx->mb || !tx->sampv || !rx->sampv) {
err = ENOMEM;
goto out;
@@ -1480,6 +1537,10 @@ static int start_source(struct autx *tx, struct audio *a)
}
switch (a->cfg.txmode) {
+
+ case AUDIO_MODE_POLL:
+ break;
+
#ifdef HAVE_PTHREAD
case AUDIO_MODE_THREAD:
if (!tx->u.thr.run) {
@@ -1495,7 +1556,9 @@ static int start_source(struct autx *tx, struct audio *a)
#endif
default:
- break;
+ warning("audio: tx mode not supported (%d)\n",
+ a->cfg.txmode);
+ return ENOTSUP;
}
tx->ausrc_prm = prm;
@@ -1602,6 +1665,7 @@ int audio_encoder_set(struct audio *a, const struct aucodec *ac,
struct auenc_param prm;
prm.ptime = tx->ptime;
+ prm.bitrate = 0; /* auto */
err = ac->encupdh(&tx->enc, ac, &prm, params);
if (err) {
@@ -1891,9 +1955,10 @@ int audio_debug(struct re_printf *pf, const struct audio *a)
err = re_hprintf(pf, "\n--- Audio stream ---\n");
- err |= re_hprintf(pf, " tx: %H ptime=%ums\n",
+ err |= re_hprintf(pf, " tx: encode: %H ptime=%ums %s\n",
aucodec_print, tx->ac,
- tx->ptime);
+ tx->ptime,
+ aufmt_name(tx->enc_fmt));
err |= re_hprintf(pf, " aubuf: %H"
" (cur %.2fms, max %.2fms, or %llu, ur %llu)\n",
aubuf_debug, tx->aubuf,
@@ -1905,14 +1970,15 @@ int audio_debug(struct re_printf *pf, const struct audio *a)
tx->ausrc_prm.ch),
tx->stats.aubuf_overrun,
tx->stats.aubuf_underrun);
-
+ err |= re_hprintf(pf, " source: %s\n",
+ aufmt_name(tx->src_fmt));
err |= re_hprintf(pf, " time = %.3f sec\n",
autx_calc_seconds(tx));
err |= re_hprintf(pf,
- " rx: %H\n"
+ " rx: decode: %H %s\n"
" ptime=%ums pt=%d\n",
- aucodec_print, rx->ac,
+ aucodec_print, rx->ac, aufmt_name(rx->dec_fmt),
rx->ptime, rx->pt);
err |= re_hprintf(pf, " aubuf: %H"
" (cur %.2fms, max %.2fms, or %llu, ur %llu)\n",
@@ -1926,9 +1992,9 @@ int audio_debug(struct re_printf *pf, const struct audio *a)
rx->stats.aubuf_overrun,
rx->stats.aubuf_underrun
);
-
+ err |= re_hprintf(pf, " player: %s\n", aufmt_name(rx->play_fmt));
err |= re_hprintf(pf, " n_discard:%llu\n",
- rx->n_discard);
+ rx->stats.n_discard);
if (rx->level_set) {
err |= re_hprintf(pf, " level %.3f dBov\n",
rx->level_last);
@@ -2079,3 +2145,57 @@ int audio_print_rtpstat(struct re_printf *pf, const struct audio *a)
return err;
}
+
+
+int audio_set_bitrate(struct audio *au, uint32_t bitrate)
+{
+ struct autx *tx;
+ const struct aucodec *ac;
+ int err;
+
+ if (!au)
+ return EINVAL;
+
+ tx = &au->tx;
+
+ ac = tx->ac;
+
+ info("audio: set bitrate for encoder '%s' to %u bits/s\n",
+ ac ? ac->name : "?",
+ bitrate);
+
+ if (ac) {
+
+ if (ac->encupdh) {
+ struct auenc_param prm;
+
+ prm.ptime = tx->ptime;
+ prm.bitrate = bitrate;
+
+ err = ac->encupdh(&tx->enc, ac, &prm, NULL);
+ if (err) {
+ warning("audio: encupdh error: %m\n", err);
+ return err;
+ }
+ }
+
+ }
+ else {
+ info("audio: set_bitrate: no audio encoder\n");
+ }
+
+ return 0;
+}
+
+
+bool audio_rxaubuf_started(struct audio *au)
+{
+ struct aurx *rx;
+
+ if (!au)
+ return false;
+
+ rx = &au->rx;
+
+ return rx->aubuf_started;
+}