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, ¶m); 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(¶m, 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 -- _______________________________________________ Visit our blog: http://blog.pjsip.org pjsip mailing list pjsip@xxxxxxxxxxxxxxx http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org