【问题标题】:How to get the audio stream from PJSIP when there is no audio hardware device没有音频硬件设备时如何从 PJSIP 获取音频流
【发布时间】:2018-02-24 20:24:16
【问题描述】:

我想使用 PJSIP 的 C API 将传入的音频录制到没有硬件声音设备的机器上的文件中。

我不确定细节,但 PJSIP 的稀疏文档表明它应该是 可以通过pjsua_set_null_snd_dev() 调用。

在下面的完整功能(Windows 偏向)示例中,调用 pjmedia_aud_dev_default_param(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, &param) 在状态中返回 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 中的音频流。它必须在结束通话时使用
  • @JimmyNJ 如果你能发布答案,我对这个问题的解决方案很感兴趣

标签: sip audio-streaming pjsip


【解决方案1】:

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

如果提到,请查看/lib/modules/YOUR_KERNEL_VERSION/modules.dep

如果你有,然后用modprobe snd-dummy加载它

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

【讨论】:

  • 不,抱歉这不起作用,我尝试了模块问题。并且无法重新编译内核。
  • 编译库时是否启用了 Alsa 支持?我在pjmedia/include/pjmedia-audiodev/config.hpjlib/include/pj/config_site_sample.h 中将#define PJMEDIA_AUDIO_DEV_HAS_ALSA 设置为1,而且我的autoconf 选项包括--enable-sound。来源:imsj.dev/blog/pjsip-on-raspberry-pi-via-alsa
  • 我也解决了,只是在这里发布了我是如何做到的。我以为你没有解决它,因为这里没有答案?!
猜你喜欢
  • 2013-02-09
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多