mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
5.0 KiB
224 lines
5.0 KiB
#ifdef WITH_TRANSCODING
|
|
|
|
#include "audio_player.h"
|
|
#include "call.h"
|
|
#include "media_player.h"
|
|
#include "mix_buffer.h"
|
|
#include "codec.h"
|
|
|
|
|
|
struct audio_player {
|
|
struct media_player *mp;
|
|
struct mix_buffer mb;
|
|
int64_t last_run;
|
|
|
|
unsigned int ptime_us;
|
|
unsigned int ptime; // in samples
|
|
|
|
unsigned long long pts;
|
|
};
|
|
|
|
|
|
// call is locked in R and mp is locked
|
|
static bool audio_player_run(struct media_player *mp) {
|
|
if (!mp || !mp->media)
|
|
return false;
|
|
|
|
struct audio_player *ap = mp->media->audio_player;
|
|
if (!ap || !ap->ptime_us)
|
|
return false;
|
|
|
|
ap->last_run = rtpe_now; // equals mp->next_run
|
|
|
|
unsigned int size;
|
|
void *buf = mix_buffer_read_fast(&ap->mb, ap->ptime, &size);
|
|
if (!buf) {
|
|
if (!size) {
|
|
// error or not active: just reschedule
|
|
mp->next_run += ap->ptime_us;
|
|
timerthread_obj_schedule_abs(&mp->tt_obj, mp->next_run);
|
|
return false;
|
|
}
|
|
buf = g_alloca(size);
|
|
mix_buffer_read_slow(&ap->mb, buf, ap->ptime);
|
|
}
|
|
|
|
media_player_add_packet(mp, buf, size, ap->ptime_us, ap->pts);
|
|
ap->pts += ap->ptime;
|
|
|
|
return false;
|
|
}
|
|
|
|
// call locked in W
|
|
bool audio_player_setup(struct call_media *m, const rtp_payload_type *dst_pt,
|
|
unsigned int size_ms, unsigned int delay_ms, str_case_value_ht codec_set)
|
|
{
|
|
if (!dst_pt)
|
|
return false;
|
|
unsigned int bufsize_ms = size_ms;
|
|
if (!bufsize_ms)
|
|
bufsize_ms = rtpe_config.audio_buffer_length;
|
|
if (!bufsize_ms)
|
|
return false;
|
|
|
|
unsigned int clockrate = fraction_mult(dst_pt->clock_rate, &dst_pt->codec_def->default_clockrate_fact);
|
|
|
|
unsigned int ptime_ms = m->ptime;
|
|
if (!ptime_ms)
|
|
ptime_ms = 20;
|
|
unsigned int ptime_us = ptime_ms * 1000;
|
|
unsigned int ptime_smp = ptime_ms * clockrate / 1000; // in samples
|
|
|
|
// TODO: shortcut this to avoid the detour of avframe -> avpacket -> avframe (all in s16)
|
|
rtp_payload_type src_pt = {
|
|
.payload_type = -1,
|
|
.encoding = STR_CONST("X-L16"), // XXX support flp
|
|
.channels = dst_pt->channels,
|
|
.clock_rate = clockrate,
|
|
.ptime = ptime_ms,
|
|
};
|
|
|
|
struct audio_player *ap;
|
|
struct media_player *mp = NULL;
|
|
|
|
// check if objects exists and parameters are still the same
|
|
|
|
if ((ap = m->audio_player) && (mp = ap->mp)) {
|
|
if (!media_player_pt_match(mp, &src_pt, dst_pt))
|
|
{ /* do reset below */ }
|
|
if (ap->ptime != ptime_smp || ap->ptime_us != ptime_us)
|
|
{ /* do reset below */ }
|
|
else // everything matched
|
|
return true;
|
|
|
|
ilogs(transcoding, LOG_DEBUG, "Resetting audio player for new parameters");
|
|
}
|
|
else
|
|
ilogs(transcoding, LOG_DEBUG, "Creating new audio player");
|
|
|
|
// create ap and mp objects, or reset them if needed
|
|
|
|
if (ap) {
|
|
mix_buffer_destroy(&ap->mb);
|
|
ZERO(ap->mb);
|
|
}
|
|
else
|
|
ap = m->audio_player = g_new0(__typeof(*m->audio_player), 1);
|
|
|
|
if (mp)
|
|
media_player_stop(mp);
|
|
else {
|
|
media_player_new(&mp, m->monologue, NULL, NULL);
|
|
ap->mp = mp;
|
|
}
|
|
if (!mp)
|
|
goto error;
|
|
|
|
// set everything up
|
|
|
|
src_pt.codec_def = codec_find_by_av(AV_CODEC_ID_PCM_S16LE), // XXX shortcut this?
|
|
|
|
mp->run_func = audio_player_run;
|
|
|
|
ap->ptime_us = ptime_us;
|
|
ap->ptime = ptime_smp;
|
|
|
|
if (media_player_setup(mp, &src_pt, dst_pt, codec_set))
|
|
goto error;
|
|
|
|
bufsize_ms = MAX(bufsize_ms, ptime_ms * 2); // make sure the buf size is at least 2 frames
|
|
|
|
mix_buffer_init_active(&ap->mb, AV_SAMPLE_FMT_S16, clockrate, dst_pt->channels, bufsize_ms, delay_ms,
|
|
false);
|
|
|
|
return true;
|
|
|
|
error:
|
|
audio_player_free(m);
|
|
return false;
|
|
}
|
|
|
|
|
|
void audio_player_activate(struct call_media *m) {
|
|
if (!m)
|
|
return;
|
|
struct audio_player *ap = m->audio_player;
|
|
if (!ap)
|
|
return;
|
|
mix_buffer_activate(&ap->mb);
|
|
}
|
|
|
|
|
|
// call locked in W
|
|
void audio_player_start(struct call_media *m) {
|
|
struct audio_player *ap;
|
|
|
|
if (!m || !(ap = m->audio_player))
|
|
return;
|
|
|
|
struct media_player *mp = ap->mp;
|
|
if (!mp)
|
|
return;
|
|
|
|
media_player_set_media(mp, m);
|
|
|
|
if (mp->next_run) // already running?
|
|
return;
|
|
|
|
ilogs(transcoding, LOG_DEBUG, "Starting audio player");
|
|
|
|
ap->last_run = rtpe_now;
|
|
|
|
mp->next_run = rtpe_now;
|
|
mp->next_run += ap->ptime_us;
|
|
timerthread_obj_schedule_abs(&mp->tt_obj, mp->next_run);
|
|
|
|
}
|
|
|
|
|
|
void audio_player_add_frame(struct audio_player *ap, uint32_t ssrc, AVFrame *frame) {
|
|
bool ret = mix_buffer_write(&ap->mb, ssrc, frame->extended_data[0], frame->nb_samples);
|
|
if (!ret)
|
|
ilogs(transcoding, LOG_WARN | LOG_FLAG_LIMIT, "Failed to add samples to mix buffer");
|
|
av_frame_free(&frame);
|
|
}
|
|
|
|
|
|
void audio_player_stop(struct call_media *m) {
|
|
struct audio_player *ap = m->audio_player;
|
|
if (!ap)
|
|
return;
|
|
ilogs(transcoding, LOG_DEBUG, "Stopping audio player");
|
|
media_player_stop(ap->mp);
|
|
media_player_put(&ap->mp);
|
|
}
|
|
|
|
|
|
bool audio_player_is_active(struct call_media *m) {
|
|
if (!m->audio_player)
|
|
return false;
|
|
if (!m->audio_player->mp)
|
|
return false;
|
|
if (!m->audio_player->mp->next_run)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool audio_player_pt_match(struct call_media *m, const rtp_payload_type *pt) {
|
|
return rtp_payload_type_eq_exact(&m->audio_player->mp->coder.handler->dest_pt, pt);
|
|
}
|
|
|
|
|
|
void audio_player_free(struct call_media *m) {
|
|
struct audio_player *ap = m->audio_player;
|
|
if (!ap)
|
|
return;
|
|
mix_buffer_destroy(&ap->mb);
|
|
media_player_put(&ap->mp);
|
|
g_free(ap);
|
|
m->audio_player = NULL;
|
|
}
|
|
|
|
#endif
|