When I am on sip call, sometimes I want to send dtmf digits.
To do this I created a custom dial pad which when a key is pressed should play a sound of that key, but it is not playing that sound during a sip call (when there is no call, sound is played).
These sounds are played with functions from AudioToolbox.h
library (AudioServicesPlaySystemSound(soundID)
).
Is there some property that I need to set up in pjsip (pjsua) or in AudioToolbox library to enable a sound be played during a sip call?
I know this is possible (Bria has this, Groundwire also, not sure if they are using pjsip to implement sip).
This answer is combination of code snippets from these two links: PJSUA-API Media Manipulation and pjsipDll_PlayWav.cpp.
When pjsua makes a call it is using ports (conference ports) for transferring media from/to the call destination to your device speaker. You can have multiple ports opened at the same moment.
So what we are going to do to play our keypad button click sound is to open one more port and play a sound (in this case it is a wav file, and as you can notice there are also a pjsua function for streaming avi files).
To do this we are going to use this function:
pj_status_t pjsua_conf_connect (pjsua_conf_port_id source, pjsua_conf_port_id sink)
where our sink port is our device speaker port, in this case (and mostly) it is 0.
All functions below are added to pjsua_app.c file.
Before the place where they are used in the Objective-C class you have to add a line like this:
pj_status_t play_sound_during_call(pj_str_t sound_file);
To play a sound here is the function:
pj_status_t play_sound_during_call(pj_str_t sound_file)
{
pjsua_player_id player_id;
pj_status_t status;
status = pjsua_player_create(&sound_file, 0, &player_id);
if (status != PJ_SUCCESS)
return status;
pjmedia_port *player_media_port;
status = pjsua_player_get_port(player_id, &player_media_port);
if (status != PJ_SUCCESS)
{
return status;
}
pj_pool_t *pool = pjsua_pool_create("my_eof_data", 512, 512);
struct pjsua_player_eof_data *eof_data = PJ_POOL_ZALLOC_T(pool, struct pjsua_player_eof_data);
eof_data->pool = pool;
eof_data->player_id = player_id;
pjmedia_wav_player_set_eof_cb(player_media_port, eof_data, &on_pjsua_wav_file_end_callback);
status = pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0);
if (status != PJ_SUCCESS)
{
return status;
}
return status;
}
And here is the callback function that listens when your wav file reading (playing) has ended:
struct pjsua_player_eof_data
{
pj_pool_t *pool;
pjsua_player_id player_id;
};
static PJ_DEF(pj_status_t) on_pjsua_wav_file_end_callback(pjmedia_port* media_port, void* args)
{
pj_status_t status;
struct pjsua_player_eof_data *eof_data = (struct pjsua_player_eof_data *)args;
status = pjsua_player_destroy(eof_data->player_id);
PJ_LOG(3,(THIS_FILE, "End of Wav File, media_port: %d", media_port));
if (status == PJ_SUCCESS)
{
return -1;// Here it is important to return a value other than PJ_SUCCESS
//Check link below
}
return PJ_SUCCESS;
}
The reason why the pjmedia_wav_player_set_eof_cb callback function should return a value other then PJ_SUCCESS is because the documentation here pjmedia_wav_player_set_eof_cb says:
Note that if the application destroys the file port in the callback, it must return non-PJ_SUCCESS here.