mirror of https://github.com/sipwise/rtpengine.git
Similar to the existing media_player, but instead of simply producing its own standalone output media stream, the audio_player takes over the entire media stream flowing to the receiver, including media forwarded from the opposite side of the call, as well as media produced by the media_player. Change-Id: Ic34ecf08fc73b04210cfffb1a7d795b462ece5eapull/1627/head
parent
b13ce91c89
commit
ddab4a71e4
@ -0,0 +1,204 @@
|
||||
#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;
|
||||
struct timeval 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) {
|
||||
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 struct rtp_payload_type *dst_pt,
|
||||
unsigned int size_ms, unsigned int delay_ms)
|
||||
{
|
||||
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)
|
||||
// TODO: determine dest sample format from created encoder
|
||||
struct rtp_payload_type src_pt = {
|
||||
.payload_type = -1,
|
||||
.encoding = STR_CONST_INIT("PCM-S16LE"), // 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_slice_alloc0(sizeof(*m->audio_player));
|
||||
|
||||
if (mp)
|
||||
media_player_stop(mp);
|
||||
else
|
||||
mp = ap->mp = media_player_new(m->monologue);
|
||||
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))
|
||||
goto error;
|
||||
|
||||
bufsize_ms = MAX(bufsize_ms, ptime_ms * 2); // make sure the buf size is at least 2 frames
|
||||
|
||||
mix_buffer_init(&ap->mb, AV_SAMPLE_FMT_S16, clockrate, dst_pt->channels, bufsize_ms, delay_ms);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
audio_player_free(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 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.tv_sec) // already running?
|
||||
return;
|
||||
|
||||
ilogs(transcoding, LOG_DEBUG, "Starting audio player");
|
||||
|
||||
ap->last_run = rtpe_now;
|
||||
|
||||
mp->next_run = rtpe_now;
|
||||
timeval_add_usec(&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");
|
||||
}
|
||||
|
||||
|
||||
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.tv_sec)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool audio_player_pt_match(struct call_media *m, const struct 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_slice_free1(sizeof(*ap), ap);
|
||||
m->audio_player = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,42 @@
|
||||
#ifndef _AUDIO_PLAYER_H_
|
||||
#define _AUDIO_PLAYER_H_
|
||||
|
||||
#ifdef WITH_TRANSCODING
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/*
|
||||
* Similar to the existing media_player, but instead of simply producing
|
||||
* its own standalone output media stream, the audio_player takes over the
|
||||
* entire media stream flowing to the receiver, including media forwarded
|
||||
* from the opposite side of the call, as well as media produced by the
|
||||
* media_player.
|
||||
*/
|
||||
|
||||
struct audio_player;
|
||||
struct call_media;
|
||||
struct rtp_payload_type;
|
||||
|
||||
bool audio_player_setup(struct call_media *, const struct rtp_payload_type *,
|
||||
unsigned int size_ms, unsigned int delay_ms);
|
||||
void audio_player_free(struct call_media *);
|
||||
|
||||
void audio_player_start(struct call_media *);
|
||||
void audio_player_stop(struct call_media *);
|
||||
bool audio_player_is_active(struct call_media *);
|
||||
bool audio_player_pt_match(struct call_media *, const struct rtp_payload_type *);
|
||||
|
||||
void audio_player_add_frame(struct audio_player *, uint32_t ssrc, AVFrame *);
|
||||
|
||||
#else
|
||||
|
||||
INLINE void audio_player_start(struct call_media *m) { }
|
||||
INLINE void audio_player_free(struct call_media *m) { }
|
||||
INLINE void audio_player_stop(struct call_media *m) { }
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Loading…
Reference in new issue