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

时间:2017-09-15 15:30:00

标签: sip audio-streaming pjsip

我想使用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 ++的混合而道歉。

1 个答案:

答案 0 :(得分:1)

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

请提及/lib/modules/YOUR_KERNEL_VERSION/modules.dep

如果有,请用modprobe snd-dummy

加载

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