fixing a stackoverflow.com post about pjsip

F
folkert
Tue, Apr 30, 2019 7:24 PM

Hi,

There's an interesting posting at stackoverflow.com about how to stream
audio from a sip-session without audio-hardware.
https://stackoverflow.com/questions/46243029/how-to-get-the-audio-stream-from-pjsip-when-there-is-no-audio-hardware-device
Now no-one responded to it so I took it on me to fix it - answer the
post (I also want to be able to stream audio without audio-device).

Some fixes were easy: the callbacks were missing and a loop waiting for
the call to be set-up was missing as well.
If I start the program, my phone starts ringing and I can pick it up.
Only then I get:

09:24:29.022          pjsua_media.c  ......pjsua_aud_channel_update() failed for call_id 0 media 0: Invalid operation (PJ_EINVALIDOP)
09:24:29.022          pjsua_media.c  ......Error updating media call00:0: Invalid operation (PJ_EINVALIDOP)
09:24:29.022          pjsua_call.c  .....Unable to create media session: No active media stream after negotiation (PJMEDIA_SDPNEG_ENOMEDIA) [status=220048]

which is a bit strange as all codecs are allowed in the asterisk setup
it is talking against.

Any ideas?

#include <pjlib.h>
#include <pjlib-util.h>
#include <pjnath.h>
#include <pjsip.h>
#include <pjsip_ua.h>
#include <pjsip_simple.h>
#include <pjsua-lib/pjsua.h>
#include <pjmedia.h>
#include <pjmedia-codec.h>
#include <pj/log.h>
#include <pj/os.h>
#include <unistd.h>

pj_status_t test_rec_cb(void *user_data, pjmedia_frame *frame)
{
return PJ_SUCCESS;
}

pj_status_t test_play_cb(void *user_data, pjmedia_frame *frame)
{
return PJ_SUCCESS;
}

int main(int, char **)
{
// Create pjsua first!
pj_status_t status = pjsua_create();
if (status != PJ_SUCCESS)
{
fprintf(stderr,"pjsua_create error\n");
return -1;
}

// Init pjsua 
pjsua_config cfg;
pjsua_logging_config log_cfg;

pjsua_config_default(&cfg);

pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 1;

status = pjsua_init(&cfg, &log_cfg, NULL);
if (status != PJ_SUCCESS)
{
	fprintf(stderr,"pjsua_init error\n");
	return -1;
}


// Proactively list known audio devices so we are sure there are NONE
pjmedia_aud_dev_info info[64];
unsigned info_count = 64;
pjsua_enum_aud_devs(info, &info_count);

fprintf(stderr,"Listing known sound devices, total of [%u]\n", info_count);
for (unsigned i = 0; i<info_count; ++i)
	fprintf(stderr,"Name [%s]\n", info[i].name);

// Add transport
pjsua_transport_config tcfg;
pjsua_transport_id trans_id;
pjsua_transport_config_default(&tcfg);
tcfg.port = 5060;
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &tcfg, &trans_id);
if (status != PJ_SUCCESS)
{
	fprintf(stderr, "pjsua_transport_create error\n");
	return -1;
}

// Initialization is done, now start pjsua 
status = pjsua_start();
if (status != PJ_SUCCESS)
{
	fprintf(stderr, "pjsua_start error\n");
	return -1;
}

// Set NULL sound

#if 1
pjmedia_port *port = pjsua_set_no_snd_dev();
#else
status = pjsua_set_null_snd_dev();
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjsua_set_null_snd_dev error");
return -1;
}
#endif

// Register to a SIP server by creating SIP account, I happen use use Asterisk 
pjsua_acc_id acc_id;
fprintf(stderr, "Setting up SIP server registration\n");
if (1)
{
	pjsua_acc_config cfg;
	pjsua_acc_config_default(&cfg);
	cfg.id = pj_str("sip:1000@192.168.64.1");
	cfg.reg_uri = cfg.id; // same as ID
	cfg.cred_count = 1;

	cfg.cred_info[0].realm = pj_str("*");
	cfg.cred_info[0].scheme = pj_str("digest"); 
	cfg.cred_info[0].username = pj_str("1000");
	cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
	cfg.cred_info[0].data = pj_str("1234");

	status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
	if (status != PJ_SUCCESS)
	{
		fprintf(stderr, "pjsua_acc_add error\n");
		return -1;
	}
}

fprintf(stderr, "Waiting for SIP server registration to complete....\n");

sleep(2); // sleep 2 seconds

// Call extension 9 on my Asterisk server at 10.0.0.21:5060
pj_str_t sip_target(pj_str("sip:6001@192.168.64.1"));
fprintf(stderr, "Making call to [%s]\n", sip_target.ptr);

pjsua_call_id call_id;
status = pjsua_call_make_call(acc_id, &sip_target, 0, NULL, NULL, &call_id);
if (status != PJ_SUCCESS)
{
	fprintf(stderr, "pjsua_call_make_call error\n");
	return -1;
}

while(pjsua_call_is_active(call_id) == false) {
	printf("Not active\n");
	sleep(1);
}

pj_pool_t * pool = nullptr;
pjmedia_port * wav = nullptr;
pjmedia_aud_stream *strm = nullptr;
pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav-audio", 1000, 1000, NULL);

if (nullptr == pool)
{
	fprintf(stderr,"Pool creation failed\n");
	return -1;
}

// 8kHz, single channel 16bit MS WAV format file
status = pjmedia_wav_writer_port_create(pool, "test.wav", 8000, 1, 320, 16, PJMEDIA_FILE_WRITE_PCM, 0, &wav);
if (status != PJ_SUCCESS)
{
	fprintf(stderr, "Error creating WAV file\n");
	return -1;
}

pjmedia_aud_param param; 
//////////////////////////////////////////////////////
// FAILURE HERE : This is the function call which returns PJMEDIA_AUD_INVALID_DEV
//////////////////////////////////////////////////////
status = pjmedia_aud_dev_default_param(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, &param); 
if (status != PJ_SUCCESS) 
{
	fprintf(stderr, "pjmedia_aud_dev_default_param()");
	return -1;
}

param.dir = PJMEDIA_DIR_CAPTURE;
param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info);
param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
param.channel_count = PJMEDIA_PIA_CCNT(&wav->info);
param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info);

status = pjmedia_aud_stream_create(&param, test_rec_cb, test_play_cb, wav, &strm);
if (status != PJ_SUCCESS)
{
	fprintf(stderr, "Error opening the sound stream");
	return -1;
}

status = pjmedia_aud_stream_start(strm);
if (status != PJ_SUCCESS)
{
	fprintf(stderr, "Error starting the sound device");
	return -1;
}

// Spend some time allowing the called party to pick up and recording to proceed
sleep(10); // sleep 10 seconds

// Clean up code omitted
return 0;

}

Folkert van Heusden

--

Hi, There's an interesting posting at stackoverflow.com about how to stream audio from a sip-session without audio-hardware. https://stackoverflow.com/questions/46243029/how-to-get-the-audio-stream-from-pjsip-when-there-is-no-audio-hardware-device Now no-one responded to it so I took it on me to fix it - answer the post (I also want to be able to stream audio without audio-device). Some fixes were easy: the callbacks were missing and a loop waiting for the call to be set-up was missing as well. If I start the program, my phone starts ringing and I can pick it up. Only then I get: 09:24:29.022 pjsua_media.c ......pjsua_aud_channel_update() failed for call_id 0 media 0: Invalid operation (PJ_EINVALIDOP) 09:24:29.022 pjsua_media.c ......Error updating media call00:0: Invalid operation (PJ_EINVALIDOP) 09:24:29.022 pjsua_call.c .....Unable to create media session: No active media stream after negotiation (PJMEDIA_SDPNEG_ENOMEDIA) [status=220048] which is a bit strange as all codecs are allowed in the asterisk setup it is talking against. Any ideas? #include <pjlib.h> #include <pjlib-util.h> #include <pjnath.h> #include <pjsip.h> #include <pjsip_ua.h> #include <pjsip_simple.h> #include <pjsua-lib/pjsua.h> #include <pjmedia.h> #include <pjmedia-codec.h> #include <pj/log.h> #include <pj/os.h> #include <unistd.h> pj_status_t test_rec_cb(void *user_data, pjmedia_frame *frame) { return PJ_SUCCESS; } pj_status_t test_play_cb(void *user_data, pjmedia_frame *frame) { return PJ_SUCCESS; } int main(int, char **) { // Create pjsua first! pj_status_t status = pjsua_create(); if (status != PJ_SUCCESS) { fprintf(stderr,"pjsua_create error\n"); return -1; } // Init pjsua pjsua_config cfg; pjsua_logging_config log_cfg; pjsua_config_default(&cfg); pjsua_logging_config_default(&log_cfg); log_cfg.console_level = 1; status = pjsua_init(&cfg, &log_cfg, NULL); if (status != PJ_SUCCESS) { fprintf(stderr,"pjsua_init error\n"); return -1; } // Proactively list known audio devices so we are sure there are NONE pjmedia_aud_dev_info info[64]; unsigned info_count = 64; pjsua_enum_aud_devs(info, &info_count); fprintf(stderr,"Listing known sound devices, total of [%u]\n", info_count); for (unsigned i = 0; i<info_count; ++i) fprintf(stderr,"Name [%s]\n", info[i].name); // Add transport pjsua_transport_config tcfg; pjsua_transport_id trans_id; pjsua_transport_config_default(&tcfg); tcfg.port = 5060; status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &tcfg, &trans_id); if (status != PJ_SUCCESS) { fprintf(stderr, "pjsua_transport_create error\n"); return -1; } // Initialization is done, now start pjsua status = pjsua_start(); if (status != PJ_SUCCESS) { fprintf(stderr, "pjsua_start error\n"); return -1; } // Set NULL sound #if 1 pjmedia_port *port = pjsua_set_no_snd_dev(); #else status = pjsua_set_null_snd_dev(); if (status != PJ_SUCCESS) { fprintf(stderr, "pjsua_set_null_snd_dev error"); return -1; } #endif // Register to a SIP server by creating SIP account, I happen use use Asterisk pjsua_acc_id acc_id; fprintf(stderr, "Setting up SIP server registration\n"); if (1) { pjsua_acc_config cfg; pjsua_acc_config_default(&cfg); cfg.id = pj_str("sip:1000@192.168.64.1"); cfg.reg_uri = cfg.id; // same as ID cfg.cred_count = 1; cfg.cred_info[0].realm = pj_str("*"); cfg.cred_info[0].scheme = pj_str("digest"); cfg.cred_info[0].username = pj_str("1000"); cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cfg.cred_info[0].data = pj_str("1234"); status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id); if (status != PJ_SUCCESS) { fprintf(stderr, "pjsua_acc_add error\n"); return -1; } } fprintf(stderr, "Waiting for SIP server registration to complete....\n"); sleep(2); // sleep 2 seconds // Call extension 9 on my Asterisk server at 10.0.0.21:5060 pj_str_t sip_target(pj_str("sip:6001@192.168.64.1")); fprintf(stderr, "Making call to [%s]\n", sip_target.ptr); pjsua_call_id call_id; status = pjsua_call_make_call(acc_id, &sip_target, 0, NULL, NULL, &call_id); if (status != PJ_SUCCESS) { fprintf(stderr, "pjsua_call_make_call error\n"); return -1; } while(pjsua_call_is_active(call_id) == false) { printf("Not active\n"); sleep(1); } pj_pool_t * pool = nullptr; pjmedia_port * wav = nullptr; pjmedia_aud_stream *strm = nullptr; pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav-audio", 1000, 1000, NULL); if (nullptr == pool) { fprintf(stderr,"Pool creation failed\n"); return -1; } // 8kHz, single channel 16bit MS WAV format file status = pjmedia_wav_writer_port_create(pool, "test.wav", 8000, 1, 320, 16, PJMEDIA_FILE_WRITE_PCM, 0, &wav); if (status != PJ_SUCCESS) { fprintf(stderr, "Error creating WAV file\n"); return -1; } pjmedia_aud_param param; ////////////////////////////////////////////////////// // FAILURE HERE : This is the function call which returns PJMEDIA_AUD_INVALID_DEV ////////////////////////////////////////////////////// status = pjmedia_aud_dev_default_param(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, &param); if (status != PJ_SUCCESS) { fprintf(stderr, "pjmedia_aud_dev_default_param()"); return -1; } param.dir = PJMEDIA_DIR_CAPTURE; param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info); param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); param.channel_count = PJMEDIA_PIA_CCNT(&wav->info); param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info); status = pjmedia_aud_stream_create(&param, test_rec_cb, test_play_cb, wav, &strm); if (status != PJ_SUCCESS) { fprintf(stderr, "Error opening the sound stream"); return -1; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { fprintf(stderr, "Error starting the sound device"); return -1; } // Spend some time allowing the called party to pick up and recording to proceed sleep(10); // sleep 10 seconds // Clean up code omitted return 0; } Folkert van Heusden --