Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions daemon/call_interfaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,12 @@ void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, h
case CSH_LOOKUP("file"):
out->file = s;
break;
case CSH_LOOKUP("audio-raw-rtp-file"):
out->audio_raw_rtp_file = s;
break;
case CSH_LOOKUP("audio-raw-rtp-codec"):
out->audio_raw_rtp_codec = s;
break;
case CSH_LOOKUP("frequency"):
case CSH_LOOKUP("frequencies"):
call_ng_flags_freqs(parser, value, out);
Expand Down Expand Up @@ -3870,6 +3876,8 @@ const char *call_play_media_ng(ng_command_ctx_t *ctx) {
.codec_set = flags.codec_set,
.file = flags.file,
.blob = flags.blob,
.audio_raw_rtp_file = flags.audio_raw_rtp_file,
.audio_raw_rtp_codec = flags.audio_raw_rtp_codec,
.db_id = flags.db_id,
);

Expand Down
85 changes: 85 additions & 0 deletions daemon/media_player.c
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,87 @@ static mp_cached_code __media_player_add_file(struct media_player *mp,
return MPC_OK;
}

struct rtp_payload_data{
int pt; // RTP payload type
const char ffmpeg_codec_name[6]; // Codec name (case-sensitive)
enum AVMediaType codec_type;// Media type
enum AVCodecID codec_id; // FFmpeg codec ID
int sample_rate; // Sample rate
int channels; // Default channels
};

const struct rtp_payload_data rtp_payload_types[] = {
{0, "mulaw", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1},
{8, "alaw", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1},
{9, "g722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_ADPCM_G722, 8000, 1},
{18, "g729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_G729, 8000, 1},
{-1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1}
};

// Helper function to find codec configuration
static const struct rtp_payload_data *find_rtp_payload_data(const str *codec_str) {
// Check static payload types
for (int i = 0; rtp_payload_types[i].pt != -1; i++) {
if (str_cmp(codec_str, rtp_payload_types[i].ffmpeg_codec_name) == 0) {
return &rtp_payload_types[i];
}
}
ilog(LOG_ERR, "Unsupported codec: '" STR_FORMAT "'", STR_FMT(codec_str));
return NULL;
}

static bool __media_player_open_audio_raw_rtp_file(struct media_player *mp, media_player_opts_t opts) {
// Validate codec
if (!opts.audio_raw_rtp_codec.len) {
ilog(LOG_ERR, "Raw RTP playback requires codec specification");
return false;
}

// Find codec configuration
const struct rtp_payload_data *payload_data = find_rtp_payload_data(&opts.audio_raw_rtp_codec);
if (!payload_data || payload_data->codec_id == AV_CODEC_ID_NONE) {
ilog(LOG_ERR, "Codec '" STR_FORMAT "' is not supported by FFmpeg", STR_FMT(&opts.audio_raw_rtp_codec));
return false;
}

// Convert file path
char file_path[PATH_MAX];
snprintf(file_path, sizeof(file_path), STR_FORMAT, STR_FMT(&opts.audio_raw_rtp_file));

AVInputFormat *iformat = av_find_input_format(opts.audio_raw_rtp_codec.s);
if (!iformat) {
ilog(LOG_ERR, "Failed to find input format:'" STR_FORMAT "'", STR_FMT(&opts.audio_raw_rtp_codec));
return false;
}

int ret = avformat_open_input(&mp->coder.fmtctx, file_path, iformat , NULL);
if (ret < 0) {
ilog(LOG_ERR, "Raw RTP playback failing in avformat_open_input");
return false;
}

if (!mp->coder.fmtctx->streams || !mp->coder.fmtctx->streams[0]) {
ilog(LOG_ERR, "No streams found in input file");
return false;
}
mp->coder.fmtctx->streams[0]->time_base = (AVRational){1, payload_data->sample_rate > 0 ? payload_data->sample_rate : 8000}; // Default for 8kHz audio;
mp->coder.fmtctx->streams[0]->codecpar->sample_rate = payload_data->sample_rate;

return true;
}

static bool media_player_play_audio_raw_rtp_file(struct media_player *mp, media_player_opts_t opts) {
const rtp_payload_type *dst_pt = media_player_play_init(mp);
if (!dst_pt)
return false;

if (!__media_player_open_audio_raw_rtp_file(mp, opts))
return false;

mp->coder.audio_raw_rtp_mode = true;
return media_player_play_start(mp, dst_pt, opts.codec_set);
}

// call->master_lock held in W
static bool media_player_play_file(struct media_player *mp, media_player_opts_t opts) {
const rtp_payload_type *dst_pt = media_player_play_init(mp);
Expand Down Expand Up @@ -1681,6 +1762,10 @@ const char * call_play_media_for_ml(struct call_monologue *ml,
if (!media_player_play_db(ml->player, opts))
return "Failed to start media playback from database";
}
else if (opts.audio_raw_rtp_file.len ) {
if (!media_player_play_audio_raw_rtp_file(ml->player, opts))
return "Failed to start audio raw RTP file playback";
}
else
return "No media file specified";
return NULL;
Expand Down
2 changes: 2 additions & 0 deletions include/call_interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ struct sdp_ng_flags {
str moh_file;
str blob;
str moh_blob;
str audio_raw_rtp_file;
str audio_raw_rtp_codec;
long long db_id;
long long moh_db_id;
long long duration;
Expand Down
5 changes: 5 additions & 0 deletions include/media_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ typedef struct {
unsigned int block_egress:1,
moh:1;
str file, blob;

str audio_raw_rtp_file; // Path to audio raw RTP file
str audio_raw_rtp_codec; // Codec for audio raw RTP (e.g., "PCMU")

long long db_id;
} media_player_opts_t;

Expand Down Expand Up @@ -58,6 +62,7 @@ struct media_player_coder {
str blob;
str read_pos;
struct codec_handler *handler;
unsigned int audio_raw_rtp_mode;
};

struct media_player {
Expand Down