关于音频处理:网易云信的音频共享技术-体验共享专题

导读:随着古代社会生存形式变动,社交娱乐的形式也在逐步扭转。传统面对面的社交娱乐活动正在逐渐改革,越来越多的交互行为逐步转移到网络上。RTC 技术的提高也推动了网络娱乐模式的变动,单方向信息传递形式如电影、听歌、看视频为主的娱乐形式占比在降落,互动性更强的形式如互动直播、语音通话、在线 KTV 歌等却在逐渐崛起。 音频解决的必要性作为人类最重要的交换形式之一,声音的解决至关重要。一方面是因为人类对于声音极其敏感,声音的流传受人体生理构造特点的影响,因为视觉受限于光照和方位,不是时刻能够依赖的信息获取起源,很多状况下听觉成为人类对环境信息感知的最重要通道。另一方面声音脱离画面独自存在的交换形式也有独立利用的场景。 RTC 互动交换性能作为极其重要的性能,对音频通话的解决提出了以下要求: 超低延时,实时互动零距离超高的通话质量。回声、噪声等影响听感的因素需妥善处理,使通话过程无烦扰而社交娱乐的特点对音频解决又提出了新的要求。如用户心愿失去高质量的音乐、好的临场感、趣味性强的音频成果、高质量音频内容共享等方面。因而,这就要求咱们须要从不同的方面去优化音频,以达到最优成果。明天咱们分享的就是音频共享。 音频共享的概念音频共享个别是指将设施中音频声音共享给其余参与者,使单方可能听到同一种声音,如一起听歌等。 通话中的用户听到雷同的声音,在某些状况下对于用户的临场感晋升很重要。有一种间接的形式是能够从麦克风通道让对面的用户听见本端的声音,但很多时候这样的成果不会太好。采集播放环节的失真,麦克风通道针对人声的特定解决都可能会毁坏高质量音频的成果。 提供一个绕过前端解决环节并且灵便不便应答各种场景的音频共享性能就变成了事实需要。 网易云信音频共享的实现计划为了满足用户多个场景下对音频共享的需要,网易云信实现了应用灵便的音频共享计划。 这里提供了多种共享声音起源。能够应用源文件,当然也包含网络音频源。 通过内置解码器解码后混音,能够兼容常见的 Mp3,AAC 等多种格局数据文件,这是最简略常见的一种形式。 当用户对第三方软件播放的声音很喜爱时怎么办?咱们基于零碎接口提供了播放数据的抓取和解决,让用户不必苦于无奈获取数据源,使得音频共享的起源更加多样化。 这里的架构和常见的 RTC 架构仿佛有些许不同之处,不光减少了一个回声打消模块,参考信号的起源仿佛也有变动。这就是这个架构非凡的中央,上面一个回声打消模块用于根本通话,因为共享的声音同时要被本人和对方听到,麦克风采集到的声音里也可能会蕴含这部分信号,须要打消的局部不仅要包含对端的声音,还要包含本端播放的声音。 这里取用理论播出的信号作为参考输出,能够保障本端人声输出更洁净。另外一个额定的回声打消用于打消对端的人声。在应用第三方播放声音作为共享源的时候,咱们拿到的信号蕴含了播放的全部内容。这样的解决能够在共享源中打消掉对端声音,使得共享过程中仍能保障高质量的音频通话。 音频共享的利用场景上述音频共享计划是一个对立架构,能够用于游戏开黑、音频分享、线上 KTV 等场景。涵盖了娱乐办公的多个场景。 有了这个根本解决框架当前,就能够通过灵便设置外部流程,配合适当的内部逻辑实现各项性能。以下图为例: 把下面的第三方音频内容换成游戏、音乐播放器或者浏览器,就能够通过简略操作实现游戏开黑、一起听歌、会议等音频共享场景了。 如果感觉这个例子有些简略,那么以下是一个在线 KTV 独唱实现的例子。 左侧是主唱端,提供伴奏音乐,在本地的人声退出后,通过 RTC 音频流传给副唱。 右侧的演唱者的声音会通过 RTC 流传给主唱,以供两人独唱同步,同时将副唱的人声和主唱侧传过来的蕴含主唱人声的歌曲混合,造成残缺的独唱,推送给直播观众。 以上是一个在线 KTV 的场景实现。当然,在线 KTV 场景的实现波及多个方面,遇到的问题远远不止音频共享这部分。歌词的传递、各端的同步、音频端到端的延时等问题都是须要克服的阻碍,解决好这些问题能力提供更好的体验 总结网易云信的 SDK 产品提供残缺的音频共享解决方案,反对双声道全频道,能够笼罩包含游戏开黑、一起听歌,在线 KTV 等一系列场景。如有趣味能够登录网易云信官网下载 Demo 进行体验。

October 20, 2021 · 1 min · jiezi

Python37-读取-mp3-音频文件生成波形图

原文链接:何晓东 博客 测试环境为Windows 10 系统,Python3.7,转换需要提前安装pydub、ffmpeg,安装和加入环境变量配置方法自行解决,至于缺少的包直接 php install xx 搞定。主要是 mp3 转成 wav 格式的文件,因为 mp3 格式为了减小体积牺牲了音质,转成无损的 wav 格式之后,可以读取到更详细的信息。然后读取 wav 的信息,利用 matlotlib 绘图即可。 mp3towavaform.py 代码: #coding=utf8from pydub import AudioSegmentimport waveimport ioimport numpy as npimport matplotlib.pyplot as plt #专业绘图库from PIL import Imageimport pylabfrom scipy.io import wavfile# 先从本地获取 mp3 的 bytestring 作为数据样本filename = "b.mp3"fp=open(filename, 'rb')data=fp.read()fp.close()# 读取aud=io.BytesIO(data)sound=AudioSegment.from_file(aud, format='mp3')raw_data = sound._data# 写入到文件l=len(raw_data)f=wave.open(filename + ".wav",'wb')f.setnchannels(1)f.setsampwidth(2)f.setframerate(16000)f.setnframes(l)f.writeframes(raw_data)f.close()# 读取生成波形图samplerate, data = wavfile.read(filename + ".wav")times = np.arange(len(data))/float(samplerate)# print(len(data), samplerate, times)# 可以以寸为单位自定义宽高 frameon=False 为关闭边框fig = plt.figure(figsize=(20, 5), facecolor="white")# plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='off', labelbottom='on')ax = fig.add_axes([0, 0, 1, 1])ax.axis('off')plt.fill_between(times, data, linewidth = '1', color='green')plt.xticks([])plt.yticks([])plt.savefig(filename + '.png', dpi=100, transparent=False, bbox_inches='tight', edgecolor='w')#plt.show()最终就可以生成一个无边框的波形图,matplotlib 的可选参数非常多,至于生成的波形图想要什么边框,颜色,图例,坐标轴信息等,可以查阅 matplotlib 社区文档。 ...

November 4, 2019 · 1 min · jiezi

音频可视化中的信号处理方案

声明: 原创文章,未经允许不得转载。音频可视化是一个“听”起来非常“美”好的话题,其复杂程度很大程度上依赖视觉方案(一些例子),不同的视觉方案决定了你的技术方案选型,比如three.js,pixi.js等引擎。 不管你选用什么渲染方案,处理音频信号部分是相通的,本文会围绕音频信号的处理进行阐述,期望能够给大家普及一下音频相关的基础知识(由于能力所限难免疏错,欢迎指出)。 前五部分主要是一些理论性的基础概念,如果你不敢兴趣可以直接跳过。 github地址:sound-processor三个示例:demo1; demo2; demo3; 一、什么是声音?声音来源于 振动,通过声波传播,人耳中无数 毛细胞 会将振动信号转换成电信号并通过听觉神经传递给大脑,形成人主观意识上的“声音”。声波进入人耳后,因为耳蜗的特殊构造,不同部位对声音的敏感程度是不一样的: 高频声音会被耳蜗近根部位置所感知,低频声音在近端部位置被感知,因此人对不同频率声的感受是非线性的,这是后续声学计权的基础。二、声学计权声学计权常见的有频率计权和时间计权,其作用在于模拟人耳对不同频率声音的非线性感受: 对低频部分声音不敏感;最灵敏的区域在1~5K Hz之间;上限在15~20K Hz之间;人耳听觉范围如图所示: 2.1 频率计权频率计权是作用在音频信号的频谱上的,常用的有:A、B、C、D四种: 其中 A计权 是最接近人主观感受的,它会削弱低频和高频部分中人耳不敏感的部分,所以音频可视化里要选择A计权方式,详细说明可阅读wiki。2.2 时间计权现实里声音一般是连续的,人对声音的主管感受也是声音累加的结果(想象一下,第一波声波引起耳膜振动,振动还没停止,第二波声音就来了,因此实际耳膜的振动是声波在时间上累加的结果),时间计权就是就连续时间内声音的平均值。对于变化较快的信号,我们可以使用125ms的区间来求平均,对于变化缓慢的可以采用1000ms的区间。 三、声音测量声音测量最常用的物理量是声压,描述声压的大小通常用声压级(Sound Pressure Level,SPL)。人耳可听的声压范围为2×10-5Pa~20Pa,对应的声压级范围为0~120dB。 常见声音的声压声压常常用分贝来度量,这里要说明一点,分贝本身只是一种度量方式,代表测量值和参考值的对数比率: 声压级的定义: 其中P是测量幅值,P ref代表人耳能听见1000 Hz的最小声压:20 uP。四、倍频程首先,连续的信号包含了大量的数据,我们没有必要全部处理,因此我们一般会进行采样,将连续的频率划分成一个一个区间来分析,频程就代表一段频率区间,倍频程代表频率划分的一种方案。具体来说倍频程中一段区间的上限频率与下限频率之比是常数: 具体可以看这篇文章《什么是倍频程》当N等于1,就是1倍频程,简称倍频程,如果N等于2,则为1/2倍频程。频程划分好之后,将分布于频程内的频谱求均方值得到的就是倍频程功率谱: 五、webaudio对音频的处理在web端做音频可视化离不开webaudio的API,其中最重要的就是getByteFrequencyData(文档),这个方法能获取时域信号转换之后的频域信号,详细过程如下: 获取原始的时域信号;对其应用Blackman window (布莱克曼窗函数),其作用是补偿DFT造成的信号畸变和能量泄漏;快速傅里叶变换,将时域变成频域;Smooth over time,这一步是在时间维度对信号进行加权平均(webaudio只采用了2帧);按照上文的声压公式转换为dB;归一化,webaudio采用的归一化方式如下: 六、音频可视化中的信号处理方案结合上述内容,我们觉得比较合理的处理方式如下: 6.1 滤波有人会问,getByteFrequencyData内部不是已经应用了窗函数滤波吗,为什么还要再滤波? 因为webaudio内部的窗函数主要是用于补偿信号畸变和能量泄漏,其参数都是固定的。而在音频可视化的场景下,往往视觉感受要优先于数据精确性,因此我们加了一个高斯滤波来滤除突刺和平滑信号,“平滑”的程度是可以通过参数任意控制的。 6.2 计权视觉呈现应该要和人的主观听觉关联,所以计权是必要的,JavaScript的计权实现audiojs/a-weighting。另外我们也提供了额外的时间计权,内部会统计5个历史数据进行平均。 6.3 频程划分我们会根据传入的上下限频率区间和置顶的输出频带数自动进行频程划分,核心代码: // 根据起止频谱、频带数量确定倍频数: N// fu = 2^(1/N)*fl => n = 1/N = log2(fu/fl) / bandsQtylet n = Math.log2(endFrequency / startFrequency) / outBandsQty;n = Math.pow(2, n); // n = 2^(1/N) const nextBand = { lowerFrequency: Math.max(startFrequency, 0), upperFrequency: 0}; for (let i = 0; i < outBandsQty; i++) { // 频带的上频点是下频点的2^n倍 const upperFrequency = nextBand.lowerFrequency * n; nextBand.upperFrequency = Math.min(upperFrequency, endFrequency); bands.push({ lowerFrequency: nextBand.lowerFrequency, upperFrequency: nextBand.upperFrequency }); nextBand.lowerFrequency = upperFrequency;}七、sound-processorsound-processor 是一个极小(gzip < 3KB)的处理音频信号的库,作为音频可视化的底层部分,使用相对科学的方法处理原始音频信号并输出符合人类主观听觉的信号,内部的处理流程如下: ...

May 31, 2019 · 1 min · jiezi

MIRtoolbox文档摘要

时间:2019年2月版本:1.7.1(最新版本的1.7.2不知道为啥总是解压失败)IntroductionBasic OperatorsFeature ExtractorsDynamicsRhythm处理音频信号中对于节奏的估计mirfluctuation估计节奏的一种方法是基于变换的频谱图计算(FFT),通过听觉建模,然后是每个频段的频谱估计(Pampalk等,2002)。mirfluctuation就是Pampalk提出的模型。默认参数为23ms/frame,频率80Hz。默认用的是Bark bands,可以使用mirfluctuation(…, ‘Mel’)替换为Mel波段。(下图均为左图是Mel bands,右图)s = mirspectrum(rag,‘Frame’,.023,’s’,80,‘Hz’,‘Power’,‘Terhardt’,‘Mel’,‘Mask’,‘dB’)和s = mirspectrum(rag,‘Frame’,.023,’s’,80,‘Hz’,‘Power’,‘Terhardt’,‘Bark’,‘Mask’,‘dB’)获得未经FFT的结果:f = mirspectrum(s, ‘AlongBands’, ‘Max’, 10, ‘MinRes’, .01,‘Window’, 0, ‘Resonance’, ‘Fluctuation’, ‘NormalLength’)进行FFT(FFT的参数为.01Hz,使用mirfluctuation(…, ‘MinRes’, mr) 修改)。横坐标频率,纵坐标波段。横坐标默认是10Hz( mirfluctuation(…, ‘Max’, m) 修改最大值),纵坐标25-bands(Bark bands应该是20啊?为啥是25),使用Mel bands后纵坐标为40-bands。mirfluctuation(…, ‘Summary’) 获得统计信息:也可以使用mirsum(f)生成,可以应用到所有二维形式结果的统计。修改默认参数:>> mirfluctuation(rag, ‘InnerFrame’, 0.010, 30)mirbeatspectrum节奏频谱,通过计算时滞函数的自相似性,相似矩阵,参考mirsimatrix。Q:为什么横坐标只到4s?#mirevents (previously mironsets)Timbre

February 15, 2019 · 1 min · jiezi

5步告诉你QQ音乐的完美音质是怎么来的,播放器的秘密都在这里

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~本文由QQ音乐技术团队发表于云+社区专栏一、问题背景与分析不久前,团队发现其Android平台App在播放MV视频《凤凰花开的路口》时,会带有如电流声一般的杂音,这影响了用户体验。 研发同学在初步定位时,发现有如下特征:Android平台杂音问题必现;iOS、PC平台能正常播放,没有噪音。然而,各平台都是统一用HLS格式播放,即源头都是一样的。对于该问题,我们的定位思路如下:梳理视频播放流程;找到切入点排查。二、播放流程概览分析播放流程如上图(图中内容从左往右),概括其关键步骤如下:播放器初始化:创建读数据线程:read_thread;创建存放audio解码前数据的队列:audioq;创建存放audio解码后数据的队列:sampq。数据读取:①创建context;②探测协议类型:avformat_open_input;③探测媒体类型:avformat_find_stream_info;④获取音视频流:av_find_best_stream;⑤打开媒体解码器:stream_component_open;⑥读取媒体数据,获得AVPacket:av_read_frame(ic, pkt);⑦音视频数据分别送入audioq中;重复⑥、⑦步骤到数据完毕。音频解码:在audio_thread中对audioq中的数据进行decoder_decode_frame解码;解码后的帧AVFrame存放到sampq中;音频播放:aout_thread_n中,通过调用回调接口sdl_audio_callback,对sampq中的音频帧数据进行解码成PCM数据;写入PCM数据到buffer数组,并由AudioTrack播放。三、问题分解与切入在梳理出播放流程后,标记出找到有可能出错的环节,方便进行“分层定位”(图中黄色标记)播放下载文件是否有问题;数据读取是否有问题;音频解码逻辑是否有问题;AudioTrack的设置是否有问题;接下来,根据难易程度,对上述环节逐个验证。1、播放下载文件是否正常把Android平台播放的ts文件与各平台的进行比对,发现两者一样,该环节正常。2、AudioTrack设置是否正常通过日志检查AudioTrack以下配置参数:采样率位深频道以上参数设置的值与音频流的相符合,该环节正常。3、音频解码逻辑是否有问题验证解码逻辑是否有问题,可以通过对PCM数据进行分析来确认。 对aout_thread_n进行修改,将PCM数据额外输出到本地,并与正常的PCM数据进行对比。正常PCM数据频谱图:异常PCM数据频谱图:正常PCM数据波形图:异常PCM数据波形图:对比分析可得出:从频谱图中看出,异常的PCM在人耳十分敏感的频响(1000~8000Hz )区域内的音频数据严重缺失,导致“杂音问题”从波形图中看出,异常的与正常的无声区和有声区都吻合,若解封装、解码逻辑出现异常,极大几率是呈现无波动(一条直线的形式)情况。因此可以先大胆假设解码、解封装逻辑是符合预期的若解码逻辑正常,再结合之前已经验证文件下载正常。可以推测是数据读取环节出现异常。4、数据读取是否有问题通过对数据读取的各步骤增加日志后,发现在av_find_best_stream音频流选择时出现异常: ffmpeg -i 发现,该视频ts分片有2个音频流 通过强制分别读取两条音频流数据播放,发现:第一条正常播放(PCM数据正常)第二条播放杂音(PCM数据异常)Android平台选择了第二条进行播放基于此,也就验证了在第3步中的假设是正确的。由上分析,可以得出结论:Android平台选择了第二条数据有问题的流进行播放。四、问题根源:音频流选择1、选择方式分析代码,大致如下所列,av_find_best_stream函数选择音频流,该函数会根据2个主要参数进行选择:各音频流的在探测媒体类型(avformat_find_stream_info)时,额外解码出来的帧数(选择多的)各音频流的比特率(选择高的)int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags){ for (i = 0; i < nb_streams; i++) { count = st->codec_info_nb_frames; //音频流探测中解码的帧数 bitrate = avctx->bit_rate;//音频流的比特率 multiframe = FFMIN(5, count); //先比较解码帧数,再比较音频流比特率,谁大谁选 if ((best_multiframe > multiframe) || (best_multiframe == multiframe && best_bitrate > bitrate) || (best_multiframe == multiframe && best_bitrate == bitrate && best_count >= count)) continue; best_count = count; best_bitrate = bitrate; best_multiframe = multiframe; ret = real_stream_index;//最后选择的流index best_decoder = decoder; } return ret;}在该视频中,我们可以看到: codec_info_nb_framesbit_rateaudio_stream 138122625audio_stream 239126375第二条流的解码帧数和比特率要比第一条高,因此选择了第二条流播放2、对比同类方案分析了以上选择规则后,我们对各平台、框架进行了选择规则的对比:备注:ExoPlayer对多音频流的ts分片支持不完善(issue),因此测试时需要调整相关接口。但选择规则依然以上述所示(DefaultTrackSelector)iOS和PC平台采用闭源组件,因此测试时使用了“互换两条音频流顺序”的方法进行测试。互换后,两平台都播放了杂音音频流 ffmpeg -i INPUT_FILE -map 0:0 -map 0:2 -map 0:1 -c copy -y OUTPUT_FILEQuickTime同样是闭源,互换音频流后无法明显差别,通过合成第三条音频流,来验证是它是对所有音频流全播放 ffmpeg -i INPUT_FILE_1 -i INPUT_FILE_2 -map 0:0 -map 0:1 -map 0:2 -map 1:0 -c copy OUTPUT_FILE3、总结从以上数据看到,iOS和PC平台会默认选择第一条流,而在Android平台的FFmpeg和ExoPlayer会根据音频流属性来选择数值更好的一条。“默认选择第一条”方案能更容易地把音源问题暴露。“比较音频流属性”方案能更大几率地选择质量更好的流来提升用户体验。但以上2个选择方案都无法识别“内容异常”的音频流。五、问题解决方案因此,处理该问题,需要从音源上进行修复和规避,我们的建议是从源头杜绝,从终端规避:编辑重新上架正常音源;短期内增加双音频流的检测上报,帮助后台、编辑进行复查;长远看由后台开发工具,分别对存量视频进行双音频流检测和对增量视频保证只转码单音频流;参考资料https://ffmpeg.org/doxygen/2.8/https://github.com/google/Exo…https://www.jianshu.com/p/daf...https://www.jianshu.com/p/a6a...http://km.oa.com/articles/sho...https://codeday.me/bug/201707…相关阅读wamp2.0配置Zend Optimizer藏匿在邮件里的“坏小子”打造一个个人阅读追踪系统 【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识 ...

October 11, 2018 · 1 min · jiezi

小程序与WebRTC联姻能擦出怎样的火花?

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦本文由腾讯视频云终端团队发表于云+社区专栏腾讯视频云终端技术总监,rexchang(常青), 2008 年毕业加入腾讯,一直从事客户端研发相关工作,先后参与过 PC QQ、手机QQ、QQ物联 等产品项目,目前在腾讯视频云团队负责音视频终端解决方案的优化和落地工作,帮助客户在可控的研发成本投入之下,获得业内一流的音视频解决方案,目前我们的产品线包括:互动直播、点播、短视频、实时视频通话,图像处理,AI 等等。本篇文章的思维导图分开做一下介绍小程序音视频是什么?2017年腾讯视频云团队跟微信团队联合,将视频云 SDK 跟微信小程序整合在一起,并通过 <live-pusher> 和 <live-player> 两个标签的形式开放内部的功能。通过这两个标签,开发者可以实现在线直播、低延时监控、双人视频通话以及多人视频会议等功能。那么WebRTC又是什么?WebRTC(Web Real-Time Communication),是一个支持网页浏览器进行实时语音对话或视频对话的技术,是谷歌收购 GIPS 公司而获得的一项技术,在 Chrome 浏览器上无需安装插件,通过 javascript 就可以编写实时音视频通话程序。两者区别在哪里?如果您跟我一样是一个实用主义者,那我就简单从实用主义角度说一下我的结论:小程序搞定了手机,WebRTC拿下了PC。如果你对技术比较感兴趣,那我们就可以从多个技术的角度去列举两者的区别,下面是一张详细对比的表格:实现原理: 小程序音视频是将腾讯视频云的 liteavsdk 嵌入到微信内部实现的,然后通过 <live-pusher> 和 <live-player> 两个标签将 SDK 内部的音视频能力开放出来。所以小程序的标签起到了开发者 API 的作用,而内部的 SDK 则是真正用来实现音视频功能。WebRTC 由谷歌收购 GIPS 得来(这里不得不提一下,我加入腾讯时所在的第一个团队就是 QQ 团队,当时 QQ 的音视频还是购买的 GIPS 公司的产品,不过由于各种不靠谱,后来就转为自研路线了)。所以其技术被完整的保留并且加入到了 Google 的 Chrome 浏览器内核当中。而且最近苹果也已经开始在 Safari 浏览器中支持 WebRTC 的相关能力。底层协议 小程序音视频的主要协议是目前在直播领域最为常用的 RTMP 推流协议,以及 HTTP-FLV 播放协议,这两种协议都已经有多年的沉淀而且在互联网上的资料也是汗牛充栋。WebRTC的底层则是使用RTP和RTCP两种数据协议,其中RTP主要用于音视频数据传输,而RTCP则一般用于控制。移动端碎片化问题 小程序音视频由于是微信统一实现的,而且微信团队每个版本都尽量要求功能对齐,否则宁可不上,所以在碎片化问题上基本不存在。WebRTC在这里则要尴尬的多,一方面Android系统的碎片化本身让WebRTC的具体表现呈现“百花齐放”的景象,同时,iOS 目前的内嵌WebView(也就是在微信等APP里打开的各种内嵌网页)不支持WebRTC也还是个很麻烦的问题。扩展性 小程序音视频跟随微信的版本发布,有什么问题一般是当前代码流修正,然后跟随下一个版本发布,所以一般一个功能点(比如给 pusher 加一个美颜的功能)或者一个问题点(比如不支持手势放大)从确立到最终实现(或解决)仅需要一个月的时间,而且微信APP新版本的覆盖速度也确实挺快。相比之下,WebRTC则不是一个团队或者一家公司的问题了,因为它现在已经走标准路线,所以每一个新特性都是先确定标准,然后再推动浏览器厂商(包括苹果)进行跟随。这里面的故事就多了,时间也就更久了。桌面浏览器 相信您已经发现,在前面几个问题的分析上,我的观点都倾向小程序音视频。确实,在目前国内的移动领域里,谷歌和苹果都不能一家说了算,真正说了算的还是微信。但是在桌面浏览器这个部分,Chrome目前在PC浏览器市场上留到地位的存在决定了 WebRTC 的优势就很大了,开发者可以在不安装插件的情况下就可以实现自己想要的功能。相比之下,由于没有 Chrome 的原生支持,所以如果我们要在 PC 上对接小程序音视频,就需要安装浏览器插件或者通过 wxlite://start 这样的伪协议唤起本地 exe 应用程序(类似在网页上打开 QQ 聊天窗口)。并非零和博弈小程序音视频和WebRTC支架并非零和博艺,双方都有自己的优势和不足,所以本着“打不过他们,就加入他们”的思路,腾讯视频云团队在2018年春节回来后,就马不停蹄地开始了小程序音视频和WebRTC互通的相关工作。目前,需要向各位开发者汇报的是,在最新版本的微信中,小程序音视频已经可以跟WebRTC打通,目前在PC 的Chrome浏览器上就可以跟小程序进行实时音视频互通。// to-do当然,如果您想知道这个功能是怎么实现的,可以继续看下去:充分了解WebRTC就像结婚一样,既然你决定要选择另一个人作为人生下半辈子的伴侣,那你肯定会先深入地了解一下TA这个人,比如性格,脾气,爱好等各个方面。同样,我们要想很好的将小程序音视频和WebRTC打通,那也必须要多了解一下WebRTC,这里我就说一下我对 WebRTC 这个“人” 在性格上的一些理解。首先,她虽然长得不太好看,但很有内涵。说WebRTC长得不好看,只是我的一种比喻,我的意思是想说WebRTC的学习成本不低,虽然Google做了很多浅显易懂的PPT来教你怎么 Getting Start,但真要完整的学进去,还是需要静下心来,慢慢地把她当成自己认可的目标去学下去。但是如果你是第一次恋爱(也就是第一次接触实时音视频),你会发现学习WebRTC的过程,本身就是了解一个实时音视频技术细节的过程。其次,她非常喜欢迁就别人,各种架构方案她都能支持到。说WebRTC喜欢迁就比人,也是一种比喻,WebRTC所支持的后台架构非常多(比如 Mixer, Mesh,Router),而且谷歌认为这些后台实现都比较简单,所以既没有开放后台相关的源码,也没有提供统一的后台解决方案。这种开放式的设计思路非常好,但副作用就是实现成本高。在真刀真枪的项目落地时,小规模的公司或者开发者就很容易被这种技术门槛挡在门外。尤其是想要将 WebRTC 真正应用到企业级解决方案中,面对录制和存档的刚性需求,就需要花费大量时间进行定制开发。方案的确立了解到 WebRTC 的这些特点后,我们的互通方案也就比较清晰了:首先,小程序音视频的特点是接口简单,快速上手,这是小程序的优势;而这一点恰恰是WebRTC的劣势,所以我们没有必要在小程序端为WebRTC暴露十几个接口类,而是继续采用小程序音视频的 <live-pusher> 和 <live-player> 标签来解决问题。其次,WebRTC 的后台没有官方实现,那就意味着这里有很大的发挥空间,腾讯视频云就可以实现一套WebRTC后台并将其同小程序音视频所使用RTMP后台进行打通。简单来说,腾讯视频云要在小程序音视频和WebRTC之间充当红娘(更确切的说,应该是翻译员)的角色。但是看过《新闻联播》里国家领导人之间谈话镜头的人都知道,这种翻译是会影响交流速度的。小程序音视频和WebRTC之间互通,中间引入一个翻译员,是不是通讯延时也就增加了?其实不会,因为小程序音视频和WebRTC的视频编码标准在常规应用场景中是一致的,都是H.264标准,这是音频格式不同而已。这就意味着,翻译员要做的事情很少,两边基本都能挺对对方在说什么,所以延时不会增加太多。成功的握手下图所展示的就是腾讯视频云在小程序音视频和WebRTC互通问题上所采取的方案:(1)首先,微信端的小程序通过腾讯视频云SDK将音视频流推送到腾讯云 RTMP 服务器。(2)其次,腾讯云 RTMP 服务器的会对音视频数据进行初步的转化处理,然后透传给腾讯视频云的实时音视频后台集群。(3)再次,实时音视频后台会再次将数据交给一个叫做 WebRTC-Proxy 的模块,就在这里, WebRTC-Proxy 要将来自小程序音视频的音视频数据翻译成 WebRTC 理解的“语言”。(4)最后,在PC上的Chrome浏览器,就可以通过浏览器内置的WebRTC模块跟 WebRTC-Proxy 通讯,进而看到小程序端的视频影像。(5)上面的四个过程倒过来,就可以实现双向视频通话;而将腾讯视频云作为星型结构的中心节点,多个端(不管是小程序还是Chrome浏览器)都接入进来,那就可以形成多人音视频解决方案。打通房间逻辑仅仅完成了音视频数据在小程序和WebRTC之间的握手还远远不够,因为在一次成功的音视频通话背后,不仅仅是把一端的音视频数据传递到另一端这么简单,还有状态的同步和成员间的状态协同。比如多人视频通话中,涉及到呼叫和接通的流程,其中一方如果挂断了,其他人要收到挂断的通知。同时,如果有新的参与者加入,那么其他人也要收到相应的通知。WebRTC 中有很多组件,比如 RTCPeerConnection 就在处理上诉林林种种的逻辑。但是 WebRTC 的接口中引入的新名词非常多,对于初学者来说还是有一定的入门门槛,为了简化这里的逻辑,我们引入一个叫做“房间”的概念。所谓房间(Room),就是把同时参与视频通话的各方圈在一起的一个东西。比如双人通话中,通话中的两个人 A 和 B 就可以认为在一个房间中。再比如在多人通话中,通话中的五个人(A B C D E)也可以认为是在一个房间里。有了房间的概念,那我们就可以对刚才说的状态协同用两个简单的动作描述一下:如果有一个人加入了视频通话,那么就可以理解为他/她已经进房(EnterRoom)了;如果有一个退出了视频通话,那么就可以理解为他/她已经离开房间(LeaveRoom)了。而房间的门板上始终写着:“目前在房间里有哪几个人”。有了房间的概念,我们就可以将小程序的两个简单的 <live-pusher> 和 <live-player> 标签,同 WebRTC 那一套复杂的 API 进行功能上的对齐,我们甚至不需要修改我们在第一版中定义的接口,就可以达成这个目标:(1)<live-pusher> 的 url 接口不再传递 rtmp:// 协议的推流地址,而是传递 room:// 协议的推流地址。room:// 协议的使用方式可以参考我们的原理版文档 DOC。 (2)<live-pusher> 标签在 start 成功之后,就相当于成功进入一个 room,之后,您可以通过 onPushEvent (PUSH_EVT_ROOM_USERLIST = 1020) 事件,收到房间里还有那些人的信息。在视频通话期间,房间内各个成员的进进出出,也都会通过这个事件通知给您的小程序代码。(3)ROOM_USERLIST 里每一项都是一个二元组(如果是 1v1 的视频通话,ROOM_USERLIST 里只会有一个人): userid 和 playurl。 userid 代表是哪个用户, playurl 则是这个用户远程画面的播放地址。您要做的只是使用 <live-player> 标签播放这些远程画面的图像和声音而已。(4)在 WebRTC 这一端,您可以参考我们的 webrtc API,这套 API 相对于 WebRTC 原生的 API,更适合初学者使用。如何快速接入?如果您希望一天内就打通 webrtc 和 小程序音视频 的互通,那么我推荐您不要从零开始,因为那会耗费您太多时间去踩坑和 bugfix,推荐您直接使用我们封装好的 [](https://cloud.tencent.com/doc… ,这套方案既可以帮助您完成快速接入,又能满足一定的定制需求。另外,不要忘记在微信=>发现=>小程序=>腾讯云视频云,体验一下腾讯云官方 Demo 中的 WebRTC 互通效果哦。标签说明<webrtc-room> 标签是基于 <live-pusher> 和 <live-player> 实现的用于 WebRTC 互通的自定义组件。如果您希望直接使用 <live-pusher> 和 <live-player> 标签完成对接,或者想要了解 <webrtc-room> 的内部原理,可以参考 DOC。版本要求微信 6.6.6 版本开始支持。效果演示PC 端 用 Chrome 浏览器打开 体验页面 可以体验桌面版 WebRTC 的效果。微信端 发现=>小程序=>搜索“腾讯视频云”,点击 WebRTC 功能卡,就可以体验跟桌面版 Chrome 互通的效果了。对接资料对接资料说明下载链接小程序源码包含<webrtc-room>的组件源码以及demo源码DOWNLOADPC端源码基于WebrtcAPI实现的Chrome版WebRTC接入源码(其中 component/WebRTCRoom.js 实现了一个简单的房间管理功能,component/mainwindow.js包含了对 WebRTC API 的使用代码)webrtc(Chrome).zip后台源码实现了一个简单的房间列表功能,同时包含<webrtc-room>几个所需参数的生成代码webrtc_server_list.zip标签详解属性定义属性类型值说明templateString'1v3’必要,标识组件使用的界面模版(用户如果需要自定义界面,请看 界面定制)sdkAppIDString‘’必要,开通 IM 服务所获取到的 AppIDuserIDString’‘必要,用户 IDuserSigString‘’必要,身份签名,相当于登录密码的作用roomIDNumber‘’必要,房间号privateMapKeyString‘’必要,房间权限 key,相当于进入指定房间 roomID 的钥匙beautyNumber05可选,默认 5, 美颜级别 0~5mutedBooleantrue, false可选,默认 false,是否静音debugBooleantrue, false可选,默认 false,是否打印推流 debug 信息bindRoomEventfunction 必要,监听 <webrtc-room> 组件返回的事件enableIMBooleantrue, false可选,默认falsebindIMEventfunction 当IM开启时必要,监听 IM 返回的事件操作接口<webrtc-room> 组件包含如下操作接口,您需要先通过 selectComponent 获取 <webrtc-room> 标签的引用,之后就可以进行相应的操作了。函数名说明start()启动pause()暂停resume()恢复stop()停止switchCamera()切换摄像头var webrtcroom = this.selectComponent("#webrtcroomid")webrtcroom.pause();事件通知<webrtc-room> 标签通过 onRoomEvent 返回内部事件,通过 onIMEvent 返回 IM 消息事件,事件参数格式如下"detail": { “tag”: “事件tag标识,具有唯一性”, “code”: “事件代码”, “detail”: “对应事件的详细参数”}示例代码// Page.wxml 文件<webrtc-room id=“webrtcroom” roomID="{{roomID}}" userID="{{userID}}" userSig="{{userSig}}" sdkAppID="{{sdkAppID}}" privateMapKey="{{privateMapKey}}" template=“1v3” beauty="{{beauty}}" muted="{{muted}}" debug="{{debug}}" bindRoomEvent=“onRoomEvent” enableIM="{{enableIM}}" bindIMEvent=“onIMEvent”></webrtc-room>// Page.js 文件Page({ data: { //… roomID: ‘’, userID: ‘’, userSig: ‘’, sdkAppID: ‘’, beauty: 3, muted: false, debug: false, enableIM: false }, onRoomEvent: function(e){ switch(e.detail.tag){ case ’error’: { //发生错误 var code = e.detail.code; var detail = e.detail.detail; break; } } }, onIMEvent: function(e){ switch(e.detail.tag){ case ‘big_group_msg_notify’: //收到群组消息 console.debug( e.detail.detail ) break; case ’login_event’: //登录事件通知 console.debug( e.detail.detail ) break; case ‘connection_event’: //连接状态事件 console.debug( e.detail.detail ) break; case ‘join_group_event’: //进群事件通知 console.debug( e.detail.detail ) break; } }, onLoad: function (options) { self.setData({ userID: self.data.userID, userSig: self.data.userSig, sdkAppID: self.data.sdkAppID, roomID: self.data.roomID, privateMapKey: res.data.privateMapKey }, function() { var webrtcroomCom = this.selectComponent(’#webrtcroom’); if (webrtcroomCom) { webrtcroomCom.start(); } }) }, })使用指引请确认已经参照 Demo部署 开通了相关服务和并正确的完成了配置。step1: 下载自定义组件源码<webrtc-room> 并非微信小程序原生提供的标签,而是一个自定义组件,所以您需要额外的代码来支持这个标签。点击 小程序源码 下载源码包,您可以在 wxlite 文件夹下获取到所需文件。step2: 在工程中引入组件在 page 目录下的 json 配置文件内引用组件,这一步是必须的,因为 <webrtc-room> 并非原生标签。 “usingComponents”: { “webrtc-room”: “/pages/webrtc-room/webrtc-room” }在 page 目录下的 wxml 文件中使用标签<webrtc-room id=“webrtcroomid” roomID="{{roomID}}" userID="{{userID}}" userSig="{{userSig}}" sdkAppID="{{sdkAppID}}" privateMapKey="{{privateMapKey}}" template=“1v3” beauty="{{beauty}}" muted="{{muted}}" debug="{{debug}}" bindRoomEvent=“onRoomEvent” enableIM="{{enableIM}}" bindIMEvent=“onIMEvent”> </webrtc-room>step3: 获取 key 信息按照如下表格获取关键的 key 信息,这是使用腾讯云互通直播服务所必须的几个信息:KEY示例作用获取方案sdkAppID1400087915用于计费和业务区分step1 中获取userIDxiaoming用户名可以由您的服务器指定,或者使用小程序的openiduserSig加密字符串相当于 userid 对应的登录密码由您的服务器签发(PHP / JAVA)roomID12345房间号可以由您的服务器指定privateMapKey加密字符串进房票据:相当于是进入 roomid 的钥匙由您的服务器签发(PHP / JAVA)下载 sign_src.zip 可以获得服务端签发 userSig 和 privateMapKey 的计算代码(生成 userSig 和 privateMapKey 的签名算法是 ECDSA-SHA256)。step4: 进入房间self.setData({ userID: userID, userSig: userSig, sdkAppID: sdkAppID, roomID: roomID, privateMapKey: privateMapKey}, function() { var webrtcroomCom = this.selectComponent(’#webrtcroomid’); if (webrtcroomCom) { webrtcroomCom.start(); }})<h2 id=“CustomUI”> 界面定制 </h2>创建界面模版//第一步:新建 /pages/templates/mytemplate 文件夹,并创建 mytemplate.wxml 和 mytemplate.wxss 文件//第二步:编写 mytemplate.wxml 和 mytemplate.wxss 文件//mytemplate.wxml<template name=‘mytemplate’> <view class=‘videoview’> <view class=“pusher-box”> <live-pusher id=“rtcpusher” autopush mode=“RTC” url="{{pushURL}}" aspect="{{aspect}}" min-bitrate="{{minBitrate}}" max-bitrate="{{maxBitrate}}" audio-quality=“high” beauty="{{beauty}}" muted="{{muted}}" waiting-image=“https://mc.qcloudimg.com/static/img/ daeed8616ac5df256c0591c22a65c4d3/pause_publish.jpg” background-mute="{{true}}" debug="{{debug}}" bindstatechange=“onPush” binderror=“onError”> <cover-image class=‘character’ src="/pages/Resources/mask.png"></cover-image> <cover-view class=‘character’ style=‘padding: 0 5px;’>我</cover-view> </live-pusher> </view> <view class=“player-box” wx:for="{{members}}" wx:key=“userID”> <view class=‘poster’> <cover-image class=‘set’ src=“https://miniprogram-1252463788.file.myqcloud.com/roomset_{{index + 2}}.png”> </cover-image> </view> <live-player id="{{item.userID}}" autoplay mode=“RTC” wx:if="{{item.accelerateURL}}" object-fit=“fillCrop” min-cache=“0.1” max-cache=“0.3” src="{{item.accelerateURL}}" debug="{{debug}}" background-mute="{{true}}" bindstatechange=“onPlay”> <cover-view class=‘loading’ wx:if="{{item.loading}}"> <cover-image src="/pages/Resources/loading_image0.png"></cover-image> </cover-view> <cover-image class=‘character’ src="/pages/Resources/mask.png"></cover-image> <cover-view class=‘character’ style=‘padding: 0 5px;’>{{item.userName}}</cover-view> </live-player> </view> </view></template>//mytemplate.wxss.videoview { background-repeat:no-repeat; background-size: cover; width: 100%; height: 100%;}webrtc-room 组件引入模版//为 <webrtc-room> 组件中的 webrtcroom.wxml 文件添加自定义模版<import src=’/pages/templates/mytemplate/mytemplate.wxml’/><view class=‘conponent-box’> <view styles=“width:100%;height=100%;” wx:if="{{template==‘1v3’}}"> <template is=‘mytemplate’ data="{{pushURL, aspect, minBitrate, maxBitrate, beauty, muted, debug, members}}"/> </view></view>//为 <webrtc-room> 组件中的 webrtcroom.wxss 文件添加自定义样式@import “../templates/mytemplate/mytemplate.wxss”;Chrome端对接了解腾讯云官网的 WebrtcAPI ,可以对接 Chrome 端的 H5 视频通话,因为不是本文档的重点,此处不做赘述。实时音视频产品开通想要尝试这些接入,首先要开通腾讯云实时音视频,快来接入吧~问答怎样部署小程序?相关阅读教你1天搭建自己的“微视”教你从0到1搭建小程序音视频教你快速搭建一场发布会直播方案 【每日课程推荐】新加坡南洋理工大学博士,带你深度学习NLP技术 ...

September 1, 2018 · 3 min · jiezi

再也不用担心网吧开黑队友听不清了!降噪解决方案了解一下?

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~本文由腾讯游戏云发表于云+社区专栏经常逛游戏论坛的朋友会深有感触,很多玩家经常会在论坛里吐槽在网吧开黑的体验很差,噪音太多。在游戏语音开黑的过程中,如果其中一个队友身处网吧,则其他人的耳机总是难免会被各种嘈杂的噪音所充斥,这是十分糟糕的体验,甚至会影响整个团队的发挥,那么在这样的场景下,降噪就成为了提升游戏体验的基本操作。网吧场景下的降噪难度往往大于普通噪音环境下的降噪难度,其源于网吧的噪音环境和普通的噪声环境差别很大,网吧的噪音来源比较广泛,包括有众多人的聊天、呼喊声,大幅度的鼠标键盘敲击声,桌椅挪动人员走动等等,有些网吧还像理发店那样广播背景音乐及一些语音广播。并且网吧座位离得较近,几乎每个人左右都有很近的邻居,这些近距离的声音互相干扰更加恼人。消除这些复杂的噪音却不是一件简单的事情,网吧环境下的噪声几乎都是非平稳的,所以传统噪声消除方法难以很好的应用在网吧场景下。腾讯云游戏多媒体引擎GME(Gaming Multimedia Engine,以下简称 GME)针对网吧场景提出了一套降噪技术解决方案,能在复杂的环境下将噪音对语音的影响降到最低。如何在复杂的网吧环境下实现降噪?在网吧嘈杂环境下的降噪诉求是:队友不讲话时,听不到任何其他声音,当队友讲话的时候,希望听到的是队友清晰的声音,当队友话毕其他声音随即静默。以上要解决的问题可以抽象成嘈杂环境里单一主讲人的通话处理。针对可容忍的体验诉求,需要一个排除主讲人以外声音的语音活性检测算法(VAD)。而这个VAD算法和常规意义的语音检测有所不同,因为它不但要排除掉非语音,还要排除掉主讲人以外的语音,否则队友附近的人的话音甚至环境较远处的嘈杂语音仍会被发送给耳机这头的你。针对这样的情况,GME朝着满足诉求的方向,给出了这个“VAD”算法,流程如下:在判断声音性质时,一个要进行的过程是,计算语音的相关性,相关性测度定义如下:E()=N−1∑n=0[s(n)−s(n−)]2 其中 为增益因子,N 为分析帧长。令∂E()∂=0 ,求得:=N−1∑n=0s(n)−s(n−)N−1∑n=0s2(n−) 从而,有E()=N−1∑n=0s2(n)−[N−1∑n=0s(n)s(n−)]2N−1∑n=0s2(n−) 相对误差能量为R[E()]=E()N−1∑n=0s2(n)=1−2()其中()=N−1∑n=0s(n)s(n−)√N−1∑n=0s2(n)N−1∑n=0s2(n−) 为了得到这个结果,还需要做一些预处理:1,去均值:分析窗口内非零均值或非常低的低频噪声出现时,() 会在所有 上都很大,这对依赖() 进行清浊分类时的安静段语音尤其麻烦。解决办法是去掉均值:s′(n)=s(n)−1NN−1∑n=0s(n)2,低通滤波:为减小高频共振峰和高频噪声的影响,要进行一个800赫兹的低通滤波,去掉大部分共振峰影响,并可以再基音频率最高500赫兹时仍能保留其一次,二次谐波,其技术指标要求为:1T=8000Hz ,c2=800Hz,r2=1200Hz,1−1=−0.25dB,2=−50dB据此,由双线性变换法设计一个五阶的滤波器,幅频响应如图:3,数值滤波:上述低通滤波可以较为有效的去掉第三个第四个共振峰的影响,但前两个共振峰的影响仍然存在,浊音语音周期性会模糊,为了去掉这一影响,进行数值滤波。数值滤波能正确显现信号的趋势,如上升沿:y(n)=12K+1K∑i=−Kx(n+i)但这个是个非因果的数字系统,为了因果性改写如下:y(n)=1NN∑i=0x(n−i)注意,这个过程引入了算法延时。在一些参数编码原理的语音编码器中,会用LPC过程的残差来估计基音周期,就是因为残差经过“白化”排出了共振峰影响。因为lpc分析主要产出并非为计算基音,而其又涉及加重叠窗又需要求解尤利沃克方程或burg迭代所以本文并未使用。我们最终关心的是,周期性水平的度量,我们定义如下Zperiod=max1+max2+max33+max当这个周期性水平满足条件后,还要看周期是否满足语音信号基音周期范围:语音信号的基音频率范围是60Hz到500Hz;对于8k采样,用采样周期表示的区间为[80,147],[40,79],[20,39],同时满足周期性和周期范围,我们认为该声音性质具备语音特性。其他环节,诸如底噪包络跟踪,主讲声强跟踪这里不再详述。在此方案下,我们将大部分嘈杂的声音从时间段上全部排除掉了,如图:上侧:原始声音,下方:排除掉嘈杂后的声音通过效果图可以明显看到,噪声被大大的抑制了,但没有影响玩家正常的语音对话,网吧嘈杂环境的诉求得到了满足。通过自研技术,GME已经能在复杂的网吧环境下也准确的检测到特定的人声并有效的去掉环境音或其他玩家带来的噪声,给玩家带来了极致的开黑体验,让好友之间的语音互动不再有噪点,目前游戏多媒体引擎GME 已正式登陆腾讯云,为广大游戏厂商开发者提供服务,详细信息可点击这里浏览。问答策略游戏服务器概念是什么?相关阅读3行代码,为QQ轻游戏加上语音互动能力实时语音趣味变声,大叔变声“妙音娘子”Get一下内行看门道:看似“佛系”的《QQ炫舞手游》,背后的音频技术一点都不简单 云学院 · 课程推荐 | 腾讯专项技术测试组长,结合8年经验为你细说冷热分离法则

August 30, 2018 · 1 min · jiezi