From de0d8ab5d5bb35ff78713e9e8e82c1656b26290b Mon Sep 17 00:00:00 2001 From: Farzaneh Soltanzadeh Date: Wed, 20 Aug 2025 14:50:39 +0330 Subject: [PATCH 1/2] playback audio from raw rtp file --- daemon/call_interfaces.c | 8 ++ daemon/media_player.c | 167 +++++++++++++++++++++++++++++++++++--- include/call_interfaces.h | 2 + include/media_player.h | 11 +++ 4 files changed, 178 insertions(+), 10 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 70d5e3ac7..74b0c12c8 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -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); @@ -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, ); diff --git a/daemon/media_player.c b/daemon/media_player.c index ef369f1ee..022447b63 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -1104,6 +1104,57 @@ static int media_player_find_file_begin(struct media_player *mp) { static bool media_player_read_packet(struct media_player *mp) { if (!mp->coder.fmtctx) return true; + // Handle raw RTP file playback + if (mp->coder.audio_raw_rtp_mode) { + // Check if we have more data + if (mp->coder.audio_raw_rtp_pos >= mp->coder.audio_raw_rtp_data.len) { + ilog(LOG_DEBUG, "End of raw RTP file reached"); + return true; + } + + // Calculate bytes to read (one frame) + size_t bytes_to_read = MIN(mp->coder.audio_raw_rtp_frame_size, + mp->coder.audio_raw_rtp_data.len - mp->coder.audio_raw_rtp_pos); + // Allocate packet + mp->coder.pkt = av_packet_alloc(); + if (!mp->coder.pkt) { + ilog(LOG_ERR, "Failed to allocate packet"); + return true; + } + + // Fill packet with raw RTP data + if (av_new_packet(mp->coder.pkt, bytes_to_read) < 0) { + ilog(LOG_ERR, "Failed to create packet"); + av_packet_free(&mp->coder.pkt); + return true; + } + if (bytes_to_read > 0) { + memcpy(mp->coder.pkt->data, + mp->coder.audio_raw_rtp_data.s + mp->coder.audio_raw_rtp_pos, + bytes_to_read); + } + if (bytes_to_read < mp->coder.audio_raw_rtp_frame_size) { + // Pad with silence if needed + memset(mp->coder.pkt->data + bytes_to_read, + mp->coder.silence_byte, + mp->coder.audio_raw_rtp_frame_size - bytes_to_read); + ilog(LOG_DEBUG, "Padding %zu bytes of silence", + mp->coder.audio_raw_rtp_frame_size - bytes_to_read); + } + mp->coder.audio_raw_rtp_pos += bytes_to_read; + + // Simulate packet timing (20ms per frame) + mp->coder.pkt->pts = mp->last_frame_ts; + mp->coder.pkt->duration = 160; + mp->last_frame_ts += mp->coder.pkt->duration; + // Process packet + media_player_coder_add_packet(&mp->coder, media_player_add_packet, mp); + av_packet_free(&mp->coder.pkt); + + // Schedule next read in 20ms + mp->next_run = rtpe_now + 20000; + return false; + } int ret = av_read_frame(mp->coder.fmtctx, mp->coder.pkt); if (ret < 0) { @@ -1232,17 +1283,18 @@ static bool media_player_play_start(struct media_player *mp, const rtp_payload_t return true; mp->next_run = rtpe_now; - // give ourselves a bit of a head start with decoding - mp->next_run -= 50000; - - // if start_pos is positive, try to seek to that position - if (mp->opts.start_pos > 0) { - ilog(LOG_DEBUG, "Seeking to position %lli", mp->opts.start_pos); - av_seek_frame(mp->coder.fmtctx, 0, mp->opts.start_pos, 0); + if (!mp->coder.audio_raw_rtp_mode) { + // give ourselves a bit of a head start with decoding + mp->next_run -= 50000; + + // if start_pos is positive, try to seek to that position + if (mp->opts.start_pos > 0) { + ilog(LOG_DEBUG, "Seeking to position %lli", mp->opts.start_pos); + av_seek_frame(mp->coder.fmtctx, 0, mp->opts.start_pos, 0); + } + else // in case this is a repeated start + av_seek_frame(mp->coder.fmtctx, 0, 0, 0); } - else // in case this is a repeated start - av_seek_frame(mp->coder.fmtctx, 0, 0, 0); - media_player_read_packet(mp); return true; @@ -1475,6 +1527,97 @@ static mp_cached_code __media_player_add_file(struct media_player *mp, return MPC_OK; } +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; + } + + // Convert file path + char file_path[PATH_MAX]; + snprintf(file_path, sizeof(file_path), STR_FORMAT, STR_FMT(&opts.audio_raw_rtp_file)); + + // Open file + FILE *f = fopen(file_path, "rb"); + if (!f) { + ilog(LOG_ERR, "Failed to open raw RTP file: %s", file_path); + return false; + } + + // Get file size + fseek(f, 0, SEEK_END); + long file_size = ftell(f); + fseek(f, 0, SEEK_SET); + // Read entire file into memory + char *file_data = malloc(file_size); + if (!file_data || fread(file_data, 1, file_size, f) != file_size) { + ilog(LOG_ERR, "Failed to read raw RTP file"); + fclose(f); + free(file_data); + return false; + } + fclose(f); + + // Store in player context + mp->coder.audio_raw_rtp_data.s = file_data; + mp->coder.audio_raw_rtp_data.len = file_size; + mp->coder.audio_raw_rtp_pos = 0; + // Set codec parameters based on input + if (opts.audio_raw_rtp_codec.len == 4 && strncasecmp(opts.audio_raw_rtp_codec.s, "PCMU", 4) == 0) { + mp->coder.audio_raw_rtp_codec = AV_CODEC_ID_PCM_MULAW; + mp->coder.audio_raw_rtp_frame_size = 160; // 20ms frames + mp->coder.silence_byte = 0xFF; // μ-law silence + mp->coder.time_base = (AVRational){1, 8000}; // Default for 8kHz audio + } + else if (opts.audio_raw_rtp_codec.len == 4 && strncasecmp(opts.audio_raw_rtp_codec.s, "PCMA", 4) == 0) { + mp->coder.audio_raw_rtp_codec = AV_CODEC_ID_PCM_ALAW; + mp->coder.audio_raw_rtp_frame_size = 160; // 20ms frames + mp->coder.silence_byte = 0x55; // A-law silence + mp->coder.time_base = (AVRational){1, 8000}; // Default for 8kHz audio + } + else { + ilog(LOG_ERR, "Unsupported raw RTP codec: " STR_FORMAT, STR_FMT(&opts.audio_raw_rtp_codec)); + free(file_data); + return false; + } + + 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; + + // Set up fake format context + mp->coder.fmtctx = avformat_alloc_context(); + if (!mp->coder.fmtctx) { + ilog(LOG_ERR, "Failed to alloc format context"); + return false; + } + + // Create a dummy stream + AVStream *stream = avformat_new_stream(mp->coder.fmtctx, NULL); + if (!stream) { + ilog(LOG_ERR, "Failed to create stream"); + return false; + } + + // Set codec parameters + stream->time_base = mp->coder.time_base; + stream->codecpar->codec_id = mp->coder.audio_raw_rtp_codec; + stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + stream->codecpar->channels = 1; + stream->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + + 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); @@ -1681,6 +1824,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; diff --git a/include/call_interfaces.h b/include/call_interfaces.h index e7e97b499..b846a3bca 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -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; diff --git a/include/media_player.h b/include/media_player.h index 91cc74f85..4827cc695 100644 --- a/include/media_player.h +++ b/include/media_player.h @@ -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; @@ -58,6 +62,13 @@ struct media_player_coder { str blob; str read_pos; struct codec_handler *handler; + unsigned int audio_raw_rtp_mode:1; + str audio_raw_rtp_data; + size_t audio_raw_rtp_pos; + int audio_raw_rtp_frame_size; + unsigned char silence_byte; + enum AVCodecID audio_raw_rtp_codec; + AVRational time_base; }; struct media_player { From 31e3c03f70fe7d0daff00626eaf5e2a0a58acb42 Mon Sep 17 00:00:00 2001 From: Farzaneh Soltanzadeh Date: Sun, 7 Sep 2025 15:29:26 +0330 Subject: [PATCH 2/2] change logic to use ffmpeg funcs to open raw rtp file --- daemon/media_player.c | 188 ++++++++++++++--------------------------- include/media_player.h | 8 +- 2 files changed, 64 insertions(+), 132 deletions(-) diff --git a/daemon/media_player.c b/daemon/media_player.c index 022447b63..fbabe8a51 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -1104,57 +1104,6 @@ static int media_player_find_file_begin(struct media_player *mp) { static bool media_player_read_packet(struct media_player *mp) { if (!mp->coder.fmtctx) return true; - // Handle raw RTP file playback - if (mp->coder.audio_raw_rtp_mode) { - // Check if we have more data - if (mp->coder.audio_raw_rtp_pos >= mp->coder.audio_raw_rtp_data.len) { - ilog(LOG_DEBUG, "End of raw RTP file reached"); - return true; - } - - // Calculate bytes to read (one frame) - size_t bytes_to_read = MIN(mp->coder.audio_raw_rtp_frame_size, - mp->coder.audio_raw_rtp_data.len - mp->coder.audio_raw_rtp_pos); - // Allocate packet - mp->coder.pkt = av_packet_alloc(); - if (!mp->coder.pkt) { - ilog(LOG_ERR, "Failed to allocate packet"); - return true; - } - - // Fill packet with raw RTP data - if (av_new_packet(mp->coder.pkt, bytes_to_read) < 0) { - ilog(LOG_ERR, "Failed to create packet"); - av_packet_free(&mp->coder.pkt); - return true; - } - if (bytes_to_read > 0) { - memcpy(mp->coder.pkt->data, - mp->coder.audio_raw_rtp_data.s + mp->coder.audio_raw_rtp_pos, - bytes_to_read); - } - if (bytes_to_read < mp->coder.audio_raw_rtp_frame_size) { - // Pad with silence if needed - memset(mp->coder.pkt->data + bytes_to_read, - mp->coder.silence_byte, - mp->coder.audio_raw_rtp_frame_size - bytes_to_read); - ilog(LOG_DEBUG, "Padding %zu bytes of silence", - mp->coder.audio_raw_rtp_frame_size - bytes_to_read); - } - mp->coder.audio_raw_rtp_pos += bytes_to_read; - - // Simulate packet timing (20ms per frame) - mp->coder.pkt->pts = mp->last_frame_ts; - mp->coder.pkt->duration = 160; - mp->last_frame_ts += mp->coder.pkt->duration; - // Process packet - media_player_coder_add_packet(&mp->coder, media_player_add_packet, mp); - av_packet_free(&mp->coder.pkt); - - // Schedule next read in 20ms - mp->next_run = rtpe_now + 20000; - return false; - } int ret = av_read_frame(mp->coder.fmtctx, mp->coder.pkt); if (ret < 0) { @@ -1283,18 +1232,17 @@ static bool media_player_play_start(struct media_player *mp, const rtp_payload_t return true; mp->next_run = rtpe_now; - if (!mp->coder.audio_raw_rtp_mode) { - // give ourselves a bit of a head start with decoding - mp->next_run -= 50000; - - // if start_pos is positive, try to seek to that position - if (mp->opts.start_pos > 0) { - ilog(LOG_DEBUG, "Seeking to position %lli", mp->opts.start_pos); - av_seek_frame(mp->coder.fmtctx, 0, mp->opts.start_pos, 0); - } - else // in case this is a repeated start - av_seek_frame(mp->coder.fmtctx, 0, 0, 0); + // give ourselves a bit of a head start with decoding + mp->next_run -= 50000; + + // if start_pos is positive, try to seek to that position + if (mp->opts.start_pos > 0) { + ilog(LOG_DEBUG, "Seeking to position %lli", mp->opts.start_pos); + av_seek_frame(mp->coder.fmtctx, 0, mp->opts.start_pos, 0); } + else // in case this is a repeated start + av_seek_frame(mp->coder.fmtctx, 0, 0, 0); + media_player_read_packet(mp); return true; @@ -1527,61 +1475,72 @@ 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; } - - // Convert file path - char file_path[PATH_MAX]; - snprintf(file_path, sizeof(file_path), STR_FORMAT, STR_FMT(&opts.audio_raw_rtp_file)); - // Open file - FILE *f = fopen(file_path, "rb"); - if (!f) { - ilog(LOG_ERR, "Failed to open raw RTP file: %s", file_path); + // 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)); - // Get file size - fseek(f, 0, SEEK_END); - long file_size = ftell(f); - fseek(f, 0, SEEK_SET); - // Read entire file into memory - char *file_data = malloc(file_size); - if (!file_data || fread(file_data, 1, file_size, f) != file_size) { - ilog(LOG_ERR, "Failed to read raw RTP file"); - fclose(f); - free(file_data); + 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; } - fclose(f); - - // Store in player context - mp->coder.audio_raw_rtp_data.s = file_data; - mp->coder.audio_raw_rtp_data.len = file_size; - mp->coder.audio_raw_rtp_pos = 0; - // Set codec parameters based on input - if (opts.audio_raw_rtp_codec.len == 4 && strncasecmp(opts.audio_raw_rtp_codec.s, "PCMU", 4) == 0) { - mp->coder.audio_raw_rtp_codec = AV_CODEC_ID_PCM_MULAW; - mp->coder.audio_raw_rtp_frame_size = 160; // 20ms frames - mp->coder.silence_byte = 0xFF; // μ-law silence - mp->coder.time_base = (AVRational){1, 8000}; // Default for 8kHz audio - } - else if (opts.audio_raw_rtp_codec.len == 4 && strncasecmp(opts.audio_raw_rtp_codec.s, "PCMA", 4) == 0) { - mp->coder.audio_raw_rtp_codec = AV_CODEC_ID_PCM_ALAW; - mp->coder.audio_raw_rtp_frame_size = 160; // 20ms frames - mp->coder.silence_byte = 0x55; // A-law silence - mp->coder.time_base = (AVRational){1, 8000}; // Default for 8kHz audio - } - else { - ilog(LOG_ERR, "Unsupported raw RTP codec: " STR_FORMAT, STR_FMT(&opts.audio_raw_rtp_codec)); - free(file_data); + + 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; } @@ -1591,28 +1550,7 @@ static bool media_player_play_audio_raw_rtp_file(struct media_player *mp, media_ return false; if (!__media_player_open_audio_raw_rtp_file(mp, opts)) - return false; - - // Set up fake format context - mp->coder.fmtctx = avformat_alloc_context(); - if (!mp->coder.fmtctx) { - ilog(LOG_ERR, "Failed to alloc format context"); - return false; - } - - // Create a dummy stream - AVStream *stream = avformat_new_stream(mp->coder.fmtctx, NULL); - if (!stream) { - ilog(LOG_ERR, "Failed to create stream"); - return false; - } - - // Set codec parameters - stream->time_base = mp->coder.time_base; - stream->codecpar->codec_id = mp->coder.audio_raw_rtp_codec; - stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - stream->codecpar->channels = 1; - stream->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + return false; mp->coder.audio_raw_rtp_mode = true; return media_player_play_start(mp, dst_pt, opts.codec_set); diff --git a/include/media_player.h b/include/media_player.h index 4827cc695..4a50ee218 100644 --- a/include/media_player.h +++ b/include/media_player.h @@ -62,13 +62,7 @@ struct media_player_coder { str blob; str read_pos; struct codec_handler *handler; - unsigned int audio_raw_rtp_mode:1; - str audio_raw_rtp_data; - size_t audio_raw_rtp_pos; - int audio_raw_rtp_frame_size; - unsigned char silence_byte; - enum AVCodecID audio_raw_rtp_codec; - AVRational time_base; + unsigned int audio_raw_rtp_mode; }; struct media_player {