MT#55283 add support for codec-set + play_media

Relevant to #1841

Change-Id: I62e6d2053c60ce7ad05afe21687539e40f7d448f
pull/1853/head
Richard Fuchs 10 months ago
parent 0dc615e3ce
commit c271a8321d

@ -51,7 +51,7 @@ static bool audio_player_run(struct media_player *mp) {
// 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)
unsigned int size_ms, unsigned int delay_ms, str_case_value_ht codec_set)
{
if (!dst_pt)
return false;
@ -123,7 +123,7 @@ bool audio_player_setup(struct call_media *m, const rtp_payload_type *dst_pt,
ap->ptime_us = ptime_us;
ap->ptime = ptime_smp;
if (media_player_setup(mp, &src_pt, dst_pt))
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

@ -2600,7 +2600,7 @@ static void __update_init_subscribers(struct call_media *media, struct stream_pa
ice_update(media->ice_agent, sp, opmode == OP_OFFER); /* sp == NULL: update in case rtcp-mux changed */
recording_setup_media(media);
t38_gateway_start(media->t38_gateway);
t38_gateway_start(media->t38_gateway, flags ? flags->codec_set : str_case_value_ht_null());
audio_player_start(media);
if (mqtt_publish_scope() == MPS_MEDIA)
@ -2710,6 +2710,7 @@ static void __call_monologue_init_from_flags(struct call_monologue *ml, struct c
.repeat = flags->repeat_times,
.start_pos = flags->start_pos,
.block_egress = !!flags->block_egress,
.codec_set = flags->codec_set,
);
if (flags->file.len)
ret = media_player_init_file(ml->rec_player, &flags->file, opts);

@ -1313,7 +1313,9 @@ void call_ng_codec_flags(ng_parser_ctx_t *ctx, str *key, parser_arg value, helpe
return;
}
#ifdef WITH_TRANSCODING
if (out->opmode == OP_OFFER || out->opmode == OP_REQUEST || out->opmode == OP_PUBLISH) {
if (out->opmode == OP_OFFER || out->opmode == OP_REQUEST || out->opmode == OP_PUBLISH
|| out->opmode == OP_PLAY_MEDIA)
{
switch (__csh_lookup(key)) {
case CSH_LOOKUP("accept"):
call_ng_flags_str_list(ctx, value, call_ng_flags_esc_str_list, &out->codec_accept);
@ -3486,6 +3488,7 @@ const char *call_play_media_ng(ng_parser_ctx_t *ctx) {
.repeat = flags.repeat_times,
.start_pos = flags.start_pos,
.block_egress = !!flags.block_egress,
.codec_set = flags.codec_set,
);
if (flags.file.len) {

@ -58,6 +58,7 @@ static rtp_payload_type *codec_store_find_compatible(struct codec_store *cs,
const rtp_payload_type *pt);
static void __rtp_payload_type_add_name(codec_names_ht, rtp_payload_type *pt);
static void codec_calc_lost(struct ssrc_ctx *ssrc, uint16_t seq);
static void __codec_options_set(call_t *call, rtp_payload_type *pt, str_case_value_ht codec_set);
static struct codec_handler codec_handler_stub = {
@ -560,10 +561,11 @@ static void __make_audio_player_decoder(struct codec_handler *handler, rtp_paylo
// used for generic playback (audio_player, t38_gateway)
struct codec_handler *codec_handler_make_playback(const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt, unsigned long last_ts, struct call_media *media,
uint32_t ssrc)
uint32_t ssrc, str_case_value_ht codec_set)
{
struct codec_handler *handler = __handler_new(src_pt, media, NULL);
rtp_payload_type_copy(&handler->dest_pt, dst_pt);
__codec_options_set(media ? media->call : NULL, &handler->dest_pt, codec_set);
handler->handler_func = handler_func_playback;
handler->ssrc_handler = (void *) __ssrc_handler_transcode_new(handler);
if (!handler->ssrc_handler) {
@ -588,9 +590,9 @@ struct codec_handler *codec_handler_make_playback(const rtp_payload_type *src_pt
// used for "play media" player
struct codec_handler *codec_handler_make_media_player(const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt, unsigned long last_ts, struct call_media *media,
uint32_t ssrc)
uint32_t ssrc, str_case_value_ht codec_set)
{
struct codec_handler *h = codec_handler_make_playback(src_pt, dst_pt, last_ts, media, ssrc);
struct codec_handler *h = codec_handler_make_playback(src_pt, dst_pt, last_ts, media, ssrc, codec_set);
if (!h)
return NULL;
if (audio_player_is_active(media)) {
@ -603,10 +605,12 @@ struct codec_handler *codec_handler_make_media_player(const rtp_payload_type *sr
}
return h;
}
struct codec_handler *codec_handler_make_dummy(const rtp_payload_type *dst_pt, struct call_media *media)
struct codec_handler *codec_handler_make_dummy(const rtp_payload_type *dst_pt, struct call_media *media,
str_case_value_ht codec_set)
{
struct codec_handler *handler = __handler_new(NULL, media, NULL);
rtp_payload_type_copy(&handler->dest_pt, dst_pt);
__codec_options_set(media->call, &handler->dest_pt, codec_set);
return handler;
}
@ -1523,7 +1527,8 @@ next:
}
audio_player_setup(sink, pref_dest_codec, rtpe_config.audio_buffer_length,
rtpe_config.audio_buffer_delay);
rtpe_config.audio_buffer_delay,
a.flags ? a.flags->codec_set : str_case_value_ht_null());
if (a.flags && (a.flags->early_media || a.flags->opmode == OP_ANSWER))
audio_player_activate(sink);
}

@ -572,7 +572,7 @@ static void media_player_kernel_player_start(struct media_player *mp) {
media_player_kernel_player_start_now(mp);
}
static void media_player_cached_reader_start(struct media_player *mp) {
static void media_player_cached_reader_start(struct media_player *mp, str_case_value_ht codec_set) {
struct media_player_cache_entry *entry = mp->cache_entry;
const rtp_payload_type *dst_pt = &entry->coder.handler->dest_pt;
@ -583,7 +583,7 @@ static void media_player_cached_reader_start(struct media_player *mp) {
// create dummy codec handler and start timer
mp->coder.handler = codec_handler_make_dummy(&entry->coder.handler->dest_pt, mp->media);
mp->coder.handler = codec_handler_make_dummy(&entry->coder.handler->dest_pt, mp->media, codec_set);
mp->run_func = media_player_read_decoded_packet;
mp->next_run = rtpe_now;
@ -611,7 +611,7 @@ static void cache_packet_free(struct media_player_cache_packet *p) {
// returns: true = entry exists, decoding handled separately, use entry for playback
// false = no entry exists, OR entry is a new one, proceed to open decoder, then call _play_start
static bool media_player_cache_get_entry(struct media_player *mp,
const rtp_payload_type *dst_pt)
const rtp_payload_type *dst_pt, str_case_value_ht codec_set)
{
if (!rtpe_config.player_cache)
return false;
@ -630,7 +630,7 @@ static bool media_player_cache_get_entry(struct media_player *mp,
bool ret = true; // entry exists, use cached data
if (entry) {
media_player_cached_reader_start(mp);
media_player_cached_reader_start(mp, codec_set);
goto out;
}
@ -789,7 +789,9 @@ static int media_player_packet_cache(encoder_t *enc, void *u1, void *u2) {
// do have a cache entry, initialise it, set up the thread, take over decoding, and then proceed as a
// media player consuming the data from the decoder thread.
// returns: false = continue normally decode in-thread, true = take data from other thread
static bool media_player_cache_entry_init(struct media_player *mp, const rtp_payload_type *dst_pt) {
static bool media_player_cache_entry_init(struct media_player *mp, const rtp_payload_type *dst_pt,
str_case_value_ht codec_set)
{
struct media_player_cache_entry *entry = mp->cache_entry;
if (!entry)
return false;
@ -804,7 +806,7 @@ static bool media_player_cache_entry_init(struct media_player *mp, const rtp_pay
// use low priority (10 nice)
thread_create_detach_prio(media_player_cache_entry_decoder_thread, entry, NULL, 10, "mp decoder");
media_player_cached_reader_start(mp);
media_player_cached_reader_start(mp, codec_set);
return true;
}
@ -870,7 +872,7 @@ static int media_player_setup_common(struct media_player *mp, const rtp_payload_
// used for generic playback (audio_player, t38_gateway)
int media_player_setup(struct media_player *mp, const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt)
const rtp_payload_type *dst_pt, str_case_value_ht codec_set)
{
int ret = media_player_setup_common(mp, src_pt, &dst_pt);
if (ret)
@ -878,7 +880,7 @@ int media_player_setup(struct media_player *mp, const rtp_payload_type *src_pt,
if (!mp->coder.handler)
mp->coder.handler = codec_handler_make_playback(src_pt, dst_pt, mp->sync_ts, mp->media,
mp->ssrc_out->parent->h.ssrc);
mp->ssrc_out->parent->h.ssrc, codec_set);
if (!mp->coder.handler)
return -1;
@ -886,7 +888,7 @@ int media_player_setup(struct media_player *mp, const rtp_payload_type *src_pt,
}
// used for "play media" player
static int __media_player_setup_internal(struct media_player *mp, const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt)
const rtp_payload_type *dst_pt, str_case_value_ht codec_set)
{
int ret = media_player_setup_common(mp, src_pt, &dst_pt);
if (ret)
@ -894,14 +896,16 @@ static int __media_player_setup_internal(struct media_player *mp, const rtp_payl
if (!mp->coder.handler)
mp->coder.handler = codec_handler_make_media_player(src_pt, dst_pt, mp->sync_ts, mp->media,
mp->ssrc_out->parent->h.ssrc);
mp->ssrc_out->parent->h.ssrc, codec_set);
if (!mp->coder.handler)
return -1;
return 0;
}
static int __ensure_codec_handler(struct media_player *mp, const rtp_payload_type *dst_pt) {
static int __ensure_codec_handler(struct media_player *mp, const rtp_payload_type *dst_pt,
str_case_value_ht codec_set)
{
if (mp->coder.handler)
return 0;
@ -917,7 +921,7 @@ static int __ensure_codec_handler(struct media_player *mp, const rtp_payload_typ
src_pt.clock_rate = mp->coder.avstream->CODECPAR->sample_rate;
codec_init_payload_type(&src_pt, MT_AUDIO);
if (__media_player_setup_internal(mp, &src_pt, dst_pt))
if (__media_player_setup_internal(mp, &src_pt, dst_pt, codec_set))
return -1;
mp->coder.duration = mp->coder.avstream->duration * 1000 * mp->coder.avstream->time_base.num
@ -1064,7 +1068,9 @@ static const rtp_payload_type *media_player_play_init(struct media_player *mp) {
// call->master_lock held in W
static bool media_player_play_start(struct media_player *mp, const rtp_payload_type *dst_pt) {
static bool media_player_play_start(struct media_player *mp, const rtp_payload_type *dst_pt,
str_case_value_ht codec_set)
{
// needed to have usable duration for some formats. ignore errors.
if (!mp->coder.fmtctx->streams || !mp->coder.fmtctx->streams[0])
avformat_find_stream_info(mp->coder.fmtctx, NULL);
@ -1075,13 +1081,13 @@ static bool media_player_play_start(struct media_player *mp, const rtp_payload_t
return false;
}
if (__ensure_codec_handler(mp, dst_pt))
if (__ensure_codec_handler(mp, dst_pt, codec_set))
return false;
if (mp->opts.block_egress)
MEDIA_SET(mp->media, BLOCK_EGRESS);
if (media_player_cache_entry_init(mp, dst_pt))
if (media_player_cache_entry_init(mp, dst_pt, codec_set))
return true;
mp->next_run = rtpe_now;
@ -1114,7 +1120,7 @@ static mp_cached_code __media_player_init_file(struct media_player *mp, const st
mp->opts = opts;
if (media_player_cache_get_entry(mp, dst_pt))
if (media_player_cache_get_entry(mp, dst_pt, opts.codec_set))
return MPC_CACHED;
char file_s[PATH_MAX];
@ -1145,7 +1151,7 @@ bool media_player_play_file(struct media_player *mp, const str *file, media_play
if (ret == MPC_ERR)
return false;
return media_player_play_start(mp, dst_pt);
return media_player_play_start(mp, dst_pt, opts.codec_set);
#else
return false;
#endif
@ -1219,14 +1225,14 @@ static mp_cached_code __media_player_init_blob_id(struct media_player *mp, const
mp->cache_index.type = MP_DB;
mp->cache_index.db_id = db_id;
if (media_player_cache_get_entry(mp, dst_pt))
if (media_player_cache_get_entry(mp, dst_pt, opts.codec_set))
return MPC_CACHED;
}
else {
mp->cache_index.type = MP_BLOB;
mp->cache_index.file = str_dup_str(blob);
if (media_player_cache_get_entry(mp, dst_pt))
if (media_player_cache_get_entry(mp, dst_pt, opts.codec_set))
return MPC_CACHED;
}
@ -1282,7 +1288,7 @@ bool media_player_play_blob(struct media_player *mp, const str *blob, media_play
if (ret == MPC_ERR)
return false;
return media_player_play_start(mp, dst_pt);
return media_player_play_start(mp, dst_pt, opts.codec_set);
}
// call->master_lock held in W
@ -1389,7 +1395,7 @@ bool media_player_play_db(struct media_player *mp, long long id, media_player_op
if (ret == MPC_ERR)
return false;
return media_player_play_start(mp, dst_pt);
return media_player_play_start(mp, dst_pt, opts.codec_set);
}
// call->master_lock held in W
@ -1513,7 +1519,7 @@ bool media_player_start(struct media_player *mp) {
if (!dst_pt)
return false;
return media_player_play_start(mp, dst_pt);
return media_player_play_start(mp, dst_pt, str_case_value_ht_null());
#else
return false;
#endif

@ -457,13 +457,13 @@ err:
// call is locked in W
void t38_gateway_start(struct t38_gateway *tg) {
void t38_gateway_start(struct t38_gateway *tg, str_case_value_ht codec_set) {
if (!tg)
return;
// set up our player first
media_player_set_media(tg->pcm_player, tg->pcm_media);
if (media_player_setup(tg->pcm_player, &tg->pcm_pt, NULL))
if (media_player_setup(tg->pcm_player, &tg->pcm_pt, NULL, codec_set))
return;
// now start our player if we can or should

@ -1974,6 +1974,8 @@ Starts playback of a provided media file to the selected call participant. The f
can be anything that is supported by *ffmpeg*, for example a `.wav` or `.mp3` file. It will automatically
be resampled and transcoded to the appropriate sampling rate and codec. The selected participant's first
listed (preferred) codec that is supported will be chosen for this purpose.
Encoder parameters such as bit rate can be set via the `codec-set` list
described above.
Media files can be provided through one of these keys:

@ -21,7 +21,7 @@ struct audio_player;
struct call_media;
bool audio_player_setup(struct call_media *, const rtp_payload_type *,
unsigned int size_ms, unsigned int delay_ms);
unsigned int size_ms, unsigned int delay_ms, str_case_value_ht codec_set);
void audio_player_activate(struct call_media *);
void audio_player_free(struct call_media *);

@ -105,10 +105,13 @@ struct codec_handler *codec_handler_get(struct call_media *, int payload_type, s
struct sink_handler *);
void codec_handlers_free(struct call_media *);
struct codec_handler *codec_handler_make_playback(const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt, unsigned long ts, struct call_media *, uint32_t ssrc);
const rtp_payload_type *dst_pt, unsigned long ts, struct call_media *, uint32_t ssrc,
str_case_value_ht codec_set);
struct codec_handler *codec_handler_make_media_player(const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt, unsigned long ts, struct call_media *, uint32_t ssrc);
struct codec_handler *codec_handler_make_dummy(const rtp_payload_type *dst_pt, struct call_media *media);
const rtp_payload_type *dst_pt, unsigned long ts, struct call_media *, uint32_t ssrc,
str_case_value_ht codec_set);
struct codec_handler *codec_handler_make_dummy(const rtp_payload_type *dst_pt, struct call_media *media,
str_case_value_ht codec_set);
void codec_calc_jitter(struct ssrc_ctx *, unsigned long ts, unsigned int clockrate, const struct timeval *);
void codec_update_all_handlers(struct call_monologue *ml);
void codec_update_all_source_handlers(struct call_monologue *ml, const sdp_ng_flags *flags);

@ -20,6 +20,7 @@ struct media_player;
typedef struct {
long long start_pos;
int repeat;
str_case_value_ht codec_set;
unsigned int block_egress:1;
} media_player_opts_t;
@ -126,7 +127,7 @@ long long media_player_stop(struct media_player *);
bool media_player_is_active(struct call_monologue *);
int media_player_setup(struct media_player *mp, const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt);
const rtp_payload_type *dst_pt, str_case_value_ht codec_set);
void media_player_set_media(struct media_player *mp, struct call_media *media);
bool media_player_pt_match(const struct media_player *mp, const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt);

@ -69,7 +69,7 @@ struct t38_gateway {
void t38_init(void);
int t38_gateway_pair(struct call_media *t38_media, struct call_media *pcm_media, const struct t38_options *);
void t38_gateway_start(struct t38_gateway *);
void t38_gateway_start(struct t38_gateway *, str_case_value_ht codec_set);
int t38_gateway_input_samples(struct t38_gateway *, int16_t amp[], int len);
int t38_gateway_input_udptl(struct t38_gateway *, const str *);
void t38_gateway_stop(struct t38_gateway *);
@ -88,7 +88,7 @@ INLINE void t38_gateway_put(struct t38_gateway **tp) {
// stubs
INLINE void t38_init(void) { }
INLINE void t38_gateway_start(struct t38_gateway *tg) { }
INLINE void t38_gateway_start(struct t38_gateway *tg, str_case_value_ht codec_set) { }
INLINE void t38_gateway_stop(struct t38_gateway *tg) { }
INLINE void t38_gateway_put(struct t38_gateway **tp) { }

Loading…
Cancel
Save