当没有音频硬件设备时,如何从PJSIP获取音频流

5
我希望使用PJSIP的C API将传入的音频记录到没有硬件声音设备的机器上。PJSIP的文档很稀少,但建议使用pjsua_set_null_snd_dev()调用进行操作。
在下面的完全运行(基于Windows)的示例中,调用pjmedia_aud_dev_default_param(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV,¶m)返回状态中包含PJMEDIA_AUD_INVALID_DEV。
当没有硬件音频设备存在时,此代码在Linux(Ubuntu 14)和Windows 10上生成相同的错误。 如果已安装硬件音频设备驱动程序,则相同的代码在两个操作系统上都可以正常工作。
我已启用了PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO来编译PJSIP库。在Linux上,模块snd-dummy的存在并没有帮助解决这个问题。
在调用pjsua_set_null_snd_dev()之后,如何访问SIP呼叫的音频数据流?
#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>

    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 = 4;

            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]", 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
            status = pjsua_set_null_snd_dev();
            if (status != PJ_SUCCESS)
            {
                fprintf(stderr, "pjsua_set_null_snd_dev error");
                return -1;
            }

    // 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");
            {
                pjsua_acc_config cfg;
                pjsua_acc_config_default(&cfg);
                cfg.id = pj_str("sip:6001@10.0.0.21");
                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("6001");
                cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
                cfg.cred_info[0].data = pj_str("teddy");

                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(2000); // sleep 2 seconds

    // Call extension 9 on my Asterisk server at 10.0.0.21:5060
            pj_str_t sip_target(pj_str("sip:9@10.0.0.21"));
            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;
            }

            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(10000); // sleep 10 seconds

    // Clean up code omitted
    return 0;
    }

对于纯粹的心灵因上面混合使用C和C++而道歉。


pjsua_set_null_snd_dev() 用于在 pjsip 中断开音频流。在结束通话时,必须使用它。 - Nandhakumar Kittusamy
@JimmyNJ,如果您能发布一个答案,我对解决这个问题很感兴趣。 - kiliantics
1个回答

1

通过加载Alsa模块snd-dummy解决了这个问题。

如果在/lib/modules/YOUR_KERNEL_VERSION/modules.dep中提到了它,请查看。

如果有,请使用modprobe snd-dummy进行加载。

否则,重新编译内核以将其包含为模块,或按照上面链接中的安装说明操作。


不好意思,这个不起作用,我已经尝试了模块问题。而且内核重新编译也不可能。 - JimmyNJ
你在编译库时启用了Alsa支持吗?我在 pjmedia/include/pjmedia-audiodev/config.hpjlib/include/pj/config_site_sample.h 中设置了 #define PJMEDIA_AUDIO_DEV_HAS_ALSA 为1,同时我的autoconf选项包括 --enable-sound。来源:https://imsj.dev/blog/pjsip-on-raspberry-pi-via-alsa/ - Inhinias
我也解决了,并在这里发布了我是如何做到的。我以为你没有解决它,因为这里没有答案?! - Inhinias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接