• 重编译 microsip 和 pjsip 支持 mp3 录音


    前言

    使用 MicroSip 源码编译后,在录音配置界面虽然有 mp3 格式选项,但是实际录音后不会生成mp3 文件夹,而选择wav格式却可以正常生成 wav 文件。

    经测试发现需要重新编译 pjsip 工程,加入 mp3 编码才可以。

    操作步骤大概如下:

    1.pjmedia 项目加入如下 pjsip2.12.1版本 third_party/mp3 文件夹中的3个文件

    pjsip2.12.1 third_party/mp3 文件夹文件

    2.在pjmedia.h 文件中包含刚引入的 mp3_port.h

    //...
    #include 
    #include 
    #include 
    //增加mp3编码 [2022-7-28 By Pafey]
    #include "../../third_party/mp3/mp3_port.h"
    //...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.修改 pjsua_lib 项目的 pjsua_aud.c 文件中的 pjsua_recorder_create 函数

    在录音格式判断那里,源码只有处理wav格式,需要多加个mp3格式进去。

    需要注意的是第七个参数 pjmedia_mp3_encoder_option param_option;

    主要是跟mp3文件的质量有关:

    vbr:指定是否应使用可变比特率。可变比特率通常会产生更好的质量,但代价可能是文件更大。

    bit_rate:目标比特率,单位为bps。如果启用了VBR,此设置指定请求的平均比特率,并将使编码器忽略质量设置。对于CBR,这指定了实际的比特率,如果该选项为零,它将被设置为采样率乘以通道数量。

    quality:编码质量,0-9,以0为最高质量。对于VBR,质量设置只在bit_rate设置为零时生效。

    关键代码如下:
        if (file_format == FMT_WAV) {
    	status = pjmedia_wav_writer_port_create(pool, path,
    						pjsua_var.media_cfg.clock_rate,
    						pjsua_var.mconf_cfg.channel_count,
    						pjsua_var.mconf_cfg.samples_per_frame,
    						pjsua_var.mconf_cfg.bits_per_sample,
    						options, 0, &port);
        }
         else if (file_format == FMT_MP3)
        {
        	//增加mp3编码 [2022-7-28 By Pafey]
    		pjmedia_mp3_encoder_option param_option;
    		param_option.vbr = PJ_TRUE;
    		param_option.bit_rate = 0;
    		param_option.quality = 6;//0-9,0质量最高,vbr启用时,仅bit_rate为0时有效
    		
    		status = pjmedia_mp3_writer_port_create(pool, path,
    			pjsua_var.media_cfg.clock_rate,
    			pjsua_var.mconf_cfg.channel_count,
    			pjsua_var.mconf_cfg.samples_per_frame,
    			pjsua_var.mconf_cfg.bits_per_sample,
    			&param_option, &port);
    
        } else {
    	PJ_UNUSED_ARG(enc_param);
    	port = NULL;
    	status = PJ_ENOTSUP;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    pjsua_recorder_create 函数完整代码:
    /*
     * Create a file recorder, and automatically connect this recorder to
     * the conference bridge.
     */
    PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
    					   unsigned enc_type,
    					   void *enc_param,
    					   pj_ssize_t max_size,
    					   unsigned options,
    					   pjsua_recorder_id *p_id)
    {
        enum Format
        {
    	FMT_UNKNOWN,
    	FMT_WAV,
    	FMT_MP3,
        };
        unsigned slot, file_id;
        char path[PJ_MAXPATH];
        pj_str_t ext;
        int file_format;
        pj_pool_t *pool = NULL;
        pjmedia_port *port;
        pj_status_t status = PJ_SUCCESS;
    
        /* Filename must present */
        PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);
    
        /* Don't support max_size at present */
        PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);
    
        /* Don't support encoding type at present */
        PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);
    
        if (filename->slen >= PJ_MAXPATH)
        	return PJ_ENAMETOOLONG;
        if (filename->slen < 4)
        	return PJ_EINVALIDOP;
    
        PJ_LOG(4,(THIS_FILE, "Creating recorder %.*s..",
    	      (int)filename->slen, filename->ptr));
        pj_log_push_indent();
    
        if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {
    	pj_log_pop_indent();
    	return PJ_ETOOMANY;
        }
    
        /* Determine the file format */
        ext.ptr = filename->ptr + filename->slen - 4;
        ext.slen = 4;
    
        if (pj_stricmp2(&ext, ".wav") == 0)
    	file_format = FMT_WAV;
        else if (pj_stricmp2(&ext, ".mp3") == 0)
    	file_format = FMT_MP3;
        else {
    	PJ_LOG(1,(THIS_FILE, "pjsua_recorder_create() error: unable to "
    			     "determine file format for %.*s",
    			     (int)filename->slen, filename->ptr));
    	pj_log_pop_indent();
    	return PJ_ENOTSUP;
        }
    
        PJSUA_LOCK();
    
        for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {
    	if (pjsua_var.recorder[file_id].port == NULL)
    	    break;
        }
    
        if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
    	/* This is unexpected */
    	pj_assert(0);
    	status = PJ_EBUG;
    	goto on_return;
        }
    
        pj_memcpy(path, filename->ptr, filename->slen);
        path[filename->slen] = '\0';
    
        pool = pjsua_pool_create(get_basename(path, (unsigned)filename->slen), 1000, 
    			     1000);
        if (!pool) {
    	status = PJ_ENOMEM;
    	goto on_return;
        }
    
        if (file_format == FMT_WAV) {
    	status = pjmedia_wav_writer_port_create(pool, path,
    						pjsua_var.media_cfg.clock_rate,
    						pjsua_var.mconf_cfg.channel_count,
    						pjsua_var.mconf_cfg.samples_per_frame,
    						pjsua_var.mconf_cfg.bits_per_sample,
    						options, 0, &port);
        }else if (file_format == FMT_MP3)
        {
    		pjmedia_mp3_encoder_option param_option;
    		param_option.vbr = PJ_TRUE;
    		param_option.bit_rate = 0;
    		param_option.quality = 6;//0-9,0质量最高,vbr启用时,仅bit_rate为0时有效
    		//增加mp3编码 [2022-7-28 By Pafey]
    		status = pjmedia_mp3_writer_port_create(pool, path,
    			pjsua_var.media_cfg.clock_rate,
    			pjsua_var.mconf_cfg.channel_count,
    			pjsua_var.mconf_cfg.samples_per_frame,
    			pjsua_var.mconf_cfg.bits_per_sample,
    			&param_option, &port);
    
        } else {
    	PJ_UNUSED_ARG(enc_param);
    	port = NULL;
    	status = PJ_ENOTSUP;
        }
    
        if (status != PJ_SUCCESS) {
    	pjsua_perror(THIS_FILE, "Unable to open file for recording", status);
    	goto on_return;
        }
    
        status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
    				   port, filename, &slot);
        if (status != PJ_SUCCESS) {
    	pjmedia_port_destroy(port);
    	goto on_return;
        }
    
        pjsua_var.recorder[file_id].port = port;
        pjsua_var.recorder[file_id].slot = slot;
        pjsua_var.recorder[file_id].pool = pool;
    
        if (p_id) *p_id = file_id;
    
        ++pjsua_var.rec_cnt;
    
        PJSUA_UNLOCK();
    
        PJ_LOG(4,(THIS_FILE, "Recorder created, id=%d, slot=%d", file_id, slot));
    
        pj_log_pop_indent();
        return PJ_SUCCESS;
    
    on_return:
        PJSUA_UNLOCK();
        if (pool) pj_pool_release(pool);
        pj_log_pop_indent();
        return status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148

    4.编译 libpjproject 项目

    生成 libpjproject-i386-Win32-vc14-Release.lib 库文件。就可以被sip软电话引入使用了。我这里用的是MicroSip,在配置界面录音选择 mp3

    在这里插入图片描述
    打个电话就可以生成mp3文件了,搞了一天终于完美收工。
    在这里插入图片描述

    问题记录

    pjsip编译完成后,引入库文件依然不能生成mp3文件,后面调试进

    pjmedia_mp3_writer_port_create -> init_blade_dll 
    
    • 1

    这个初始化需要加载 LAME_ENC.DLL
    去 microsip 安装文件夹内拷一个出来放到自己编译的microsip exe所在路径就成功了
    文末放了下载地址

    #include 
    #define DLL_NAME    PJ_T("LAME_ENC.DLL")
    
    /*
     * Load BladeEncoder DLL.
     */
    static pj_status_t init_blade_dll(void)
    {
        if (BladeDLL.refCount == 0) {
    	#define GET_PROC(type, name)  \
    	    BladeDLL.name = (type)GetProcAddress(BladeDLL.hModule, PJ_T(#name)); \
    	    if (BladeDLL.name == NULL) { \
    		PJ_LOG(1,(THIS_FILE, "Unable to find %s in %s", #name, DLL_NAME)); \
    		return PJ_RETURN_OS_ERROR(GetLastError()); \
    	    }
    
    	BE_VERSION beVersion;
    	BladeDLL.hModule = (void*)LoadLibrary(DLL_NAME);
    	if (BladeDLL.hModule == NULL) {
    	    pj_status_t status = PJ_RETURN_OS_ERROR(GetLastError());
    	    char errmsg[PJ_ERR_MSG_SIZE];
    
    	    pj_strerror(status, errmsg, sizeof(errmsg));
    	    PJ_LOG(1,(THIS_FILE, "Unable to load %s: %s", DLL_NAME, errmsg));
    	    return status;
    	}
    //...
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    编译的库文件

    库文件和编码dll下载地址

  • 相关阅读:
    选择适合您的项目管理软件:哪个更好?
    React进阶之路(二)-- 组件通信、组件进阶
    插入排序改进 将交换变成赋值语句 优点适用于近乎有序的序列
    Apache Spark 在爱奇艺的应用实践
    每天一个数据分析题(三百零五)
    Mac PF命令防火墙
    【Java核心】JDK、JRE、 JVM的联系与区别
    Java面试题之多态
    如何用chatgpt写小说
    许战海战略文库|主品牌老化:企业增长面临的关键挑战
  • 原文地址:https://blog.csdn.net/qiangzi4646/article/details/126041138