2023年6月

对产品的认知的变化,以前觉得,只要作出的产品功能都实现了,这个产品就可以提供了客户服务了,就可以换来真金白银,想法就是too young too simple,缺少的,是在产品的在用户体验,性能、可运营、可维护、可拓展升级等方面的考虑。

比方最近几年做了几个音视频实时通信相关的产品,有那么一点的经验,在对讲、监控、实时VOIP、直播、点播等等业务上做了些应用,对SIP协议、RTP/RTCP协议、音视频编解码、WebRTC、RTSP/RTMP/GB28181协议等等,包括Janus、Freeswitch、SRS等等服务器框架也有了一些积累和应用,产品也慢慢走向成熟,但公司最终还是放弃了这块,原因当然有跟公司主营方向不同的原因,但更重要的是,我们这三五个人,做了近2年的产品,给公司只是服务了不到10个项目,投入产出比太低,实际应用少,产品稳定周期长,售后问题也不少,放弃那是必然,可惜了的,是这些积累的经验,价值在哪里?

推出的音视频服务的产品,其可用性、易用性和用户体验如何,对于复杂网络场景下的处理如何,这是相当考验技术的一个行业,投入的资源和技术积累的程度,核心算法的设计、开发能力,都是尤其重要的,所以通过互联网开源项目堆出的产品,只能处在demo环节,离真正的产品化还有好远好远的距离;

这类产品做出demo容易,做出稳定可用的产品很难!

各方面的技能要求,比方通信网络技术:

TCP和UDP的选择;搞不定丢包就选TCP,比方POC语音对讲相对小数据量的应用,就适合选用TCP;比方实时性要求相对高的推送,当然也选TCP;如果选了UDP作为传输层协议,那就要考虑好丢包检测、丢包重传,丢包恢复或者相关的技术应用,否则这个产品就是有天然缺陷的,除非你能保证网络100%的可靠,很明显是不存在这样的网络的;

客户端技术:

处理编码、解码、抖动、丢包、时间同步等等复杂的场景和业务,基础的框架比方类似WebRTC的框架越熟练越好;

是否有编解码算法优化和算法定制能力?

服务端技术:

网络通信技术至关重要,其次才是协议,才是业务;

是否有网络路由、负载、运营监控、扩容能力?

你能做什么?自我认知最重要的,你的能力边界在那里?

最近也做了几个不相关的小应用,顺利的通过了验收,拿到了票子,有点类似一手交钱一手交货的感觉,你的货对方愿意付钱!

这类应用,有一个特点,就好比砖瓦匠干的或一样,计件也好,记工时也罢,计算机编程就是一门技术,你要根据客户的需求,将描述性的语言转换为程序可以理解的逻辑,这个是你当前能挣的钱,仅此而已。对这种活的认知就是,不应该仅此而已,你能干更重要的事,挣更多的钱,这种事在哪里,不知道,但不积跬步无以至千里!

对开源软件的认知,开源软件一定有不完善的地方,功能的也好,性能的也罢,多少是和产品的要求存在差异的,只有具备了对开源软件全盘定制,优化的能力,才敢说基于这款开源软件输出的产品可以提供个用户使用。

对职业态度的认知,记得第一个项目,被客户评价遵守契约精神,把客户需求的各种场景做了一个单元测试验收表格,连同软件和验收excel文件都发给了客户,得到如此的评价,甚至超过了客户付的RMB,颇感意外,确实是对专业态度的肯定!

主站的逻辑:

决定标签测距的时间槽位信息;
决定多个站测距过程中回复A包的时序;
从站的逻辑:

根据从站的内部序号,决定测距过程中回复A包的时序;

方案:

一、开机上电同步主站tick,并收集基站列表:

1. 开机上电后,即发上线通知, 只有主站回复自己的tick;

2. 如果超时20ms 没有收到主站回复的SYNC,则决定自己就是主站;

3. 收到主站回复的SYNC ,以及包括主站的tick,则同步tick,计算主站到从站的距离,如果超过400m(避免超过区域的主站交叉覆盖的情况发生),则设置自己为主,否则设置为从站;


二、定时周期同步tick:

每个站都是2s定期广播发送tick同步包,根据tick时间戳大小决定谁是主站;

如果超过3次没有收到任何同步tick的数据包,则自动决定自己为主站,能解决比方主站下电了的情况,将备站自动升级为主站!

修改选主策略:选主策略调整为根据ID号大小决定谁是主,这样,每次选主都能稳定的选出固定的主站,而不是来回切换主站!

缺点是:主站一直是主站,可能在相同区域的超过测距要求的基站就得不到工作的机会!

1、编译

支持webrtc-aec

./configure --host=arm-openwrt-linux-muslgnueabi --prefix=$PWD/install --disable-libwebrtc --disable-libyuv --disable-v4l2 --disable-opencore-amrnb --disable-speex-codec --disable-speex-aec --with-openh264=/home/lyz/work/broadcast_app/app/thirds_libs_src/pjproject-2.12.1/third_party/openh264-2.3.1 --enable-libwebrtc-aec3 --with-opus=/home/lyz/work/broadcast_app/app/thirds_libs_src/pjproject-2.12.1/third_party/opus/

支持外部webrtc

./configure --host=arm-openwrt-linux-muslgnueabi --prefix=$PWD/install --enable-libwebrtc --disable-libyuv --disable-v4l2 --disable-opencore-amrnb --disable-speex-codec --disable-speex-aec --with-openh264=/home/lyz/work/broadcast_app/app/thirds_libs_src/pjproject-2.12.1/third_party/openh264-2.3.1 --disable-libwebrtc-aec3 --with-opus=/home/lyz/work/broadcast_app/app/thirds_libs_src/pjproject-2.12.1/third_party/opus/

增加两个方法处理播放和录制;
代码

PJ_DEF(pj_status_t) webrtc_aec_cancel_echo_playback(void *state,

                     pj_int16_t *play_frm ){
webrtc_ec *echo = (webrtc_ec*) state;
unsigned i, j, frm_idx = 0;
int status;
const sample * buf_ptr;
                     
for(i = echo->samples_per_frame / echo->subframe_len; i > 0; i--) {

if PJMEDIA_WEBRTC_AEC_USE_MOBILE

        buf_ptr = &play_frm[frm_idx];

else

        for (j = 0; j < echo->subframe_len; j++) {
            echo->tmp_buf2[j] = play_frm[frm_idx+j];
        }
        buf_ptr = echo->tmp_buf2;

endif

    
    /* Feed farend buffer */
    status = WebRtcAec_BufferFarend(echo->AEC_inst, buf_ptr,
                                        echo->subframe_len);
       frm_idx+= echo->subframe_len;
}
return PJ_SUCCESS;

}
PJ_DEF(pj_status_t) webrtc_aec_cancel_echo_capture(void *state,

                     pj_int16_t *rec_frm,
                     unsigned options ){
webrtc_ec *echo = (webrtc_ec*) state;
int status;
unsigned i, j, frm_idx = 0;
const sample * buf_ptr;
sample * out_buf_ptr;
             
for(i = echo->samples_per_frame / echo->subframe_len; i > 0; i--) {

if PJMEDIA_WEBRTC_AEC_USE_MOBILE

        buf_ptr = &rec_frm[frm_idx];

else

        for (j = 0; j < echo->subframe_len; j++) {
                echo->tmp_buf[j] = rec_frm[frm_idx+j];
            echo->tmp_buf2[j] = NULL;
        }
        buf_ptr = echo->tmp_buf2;

endif

    buf_ptr = echo->tmp_buf;
    out_buf_ptr = echo->tmp_buf2;

if PJMEDIA_WEBRTC_AEC_USE_MOBILE

    status = WebRtcAecm_Process(echo->AEC_inst, &rec_frm[frm_idx],
                                (echo->NS_inst? buf_ptr: NULL),
                                out_buf_ptr, echo->subframe_len,
                                echo->tail);

else

    status = WebRtcAec_Process(echo->AEC_inst, &buf_ptr,
                               echo->channel_count, &out_buf_ptr,
                               echo->subframe_len, (int16_t)echo->tail, 0);

endif

    if (status != 0) {
        print_webrtc_aec_error("Process echo", echo->AEC_inst);
        return PJ_EUNKNOWN;
    }
     for (j = 0; j < echo->subframe_len; j++) {
        rec_frm[frm_idx++] = (pj_int16_t)out_buf_ptr[j];
    }
}
return PJ_SUCCESS;

}

/*

  • WebRTC AEC prototypes
    */

if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0

static struct ec_operations webrtc_aec_op =
{

"WebRTC AEC",
&webrtc_aec_create,
&webrtc_aec_destroy,
&webrtc_aec_reset,
&webrtc_aec_cancel_echo,
&webrtc_aec_cancel_echo_playback,
&webrtc_aec_cancel_echo_capture,
&webrtc_aec_get_stat

};

endif

修改头文件:

"pjlib/include/pj/config_site.h"

支持外部webrtc

define PJMEDIA_HAS_WEBRTC_AEC 1

define PJMEDIA_WEBRTC_AEC_USE_MOBILE 1

复制
支持webrtc_aec3

ifdef PJMEDIA_HAS_WEBRTC_AEC3

undef PJMEDIA_HAS_WEBRTC_AEC3

define PJMEDIA_HAS_WEBRTC_AEC3 1

endif

复制
修改default.conf文件配置pjsua_app的启动配置文件支持回声消除:

webrtc echo

--ec-opt=3

webrtc-aec3 echo

--ec-opt=4

--stereo

--ec-tail=75