本文由融云技术团队原创投稿,作者是融云WebRTC高级工程师苏道,转载请注明出处。

1、引言

在一个典型的IM应用里,使用实时音视频聊天功能时,视频首帧的显示,是一项很重要的用户体验指标。

本文主要通过对WebRTC接收端的音视频处理过程分析,来了解和优化视频首帧的显示时间,并进行了总结和分享。

(本文同步发布于:http://www.52im.net/thread-3169-1-1.html

2、什么是WebRTC?

对于没接触过实时音视频技术的人来说,总是看到别人在提WebRTC,那WebRTC是什么?我们有必要简单介绍一下。

说到 WebRTC,我们不得不提到 Gobal IP Solutions,简称 GIPS。这是一家 1990 年成立于瑞典斯德哥尔摩的 VoIP 软件开发商,提供了可以说是世界上最好的语音引擎。相关介绍详见《访谈WebRTC标准之父:WebRTC的过去、现在和未来》。

Skype、腾讯 QQ、WebEx、Vidyo 等都使用了它的音频处理引擎,包含了受专利保护的回声消除算法,适应网络抖动和丢包的低延迟算法,以及先进的音频编解码器。

Google 在 Gtalk 中也使用了 GIPS 的授权。Google 在 2011 年以6820万美元收购了 GIPS,并将其源代码开源,加上在 2010 年收购的 On2 获取到的 VPx 系列视频编解码器(详见《即时通讯音视频开发(十七):视频编码H.264、VP8的前世今生》),WebRTC 开源项目应运而生,即 GIPS 音视频引擎 + 替换掉 H.264 的 VPx 视频编解码器。

在此之后,Google 又将在 Gtalk 中用于 P2P 打洞的开源项目 libjingle 融合进了 WebRTC。目前 WebRTC 提供了包括 Web、iOS、Android、Mac、Windows、Linux 在内的所有平台支持。

(以上介绍,引用自《了不起的WebRTC:生态日趋完善,或将实时音视频技术白菜化》)

虽然WebRTC的目标是实现跨平台的Web端实时音视频通讯,但因为核心层代码的Native、高品质和内聚性,开发者很容易进行除Web平台外的移殖和应用。目前为止,WebRTC几乎是是业界能免费得到的唯一高品质实时音视频通讯技术。

3、流程介绍

一个典型的实时音视频处理流程大概是这样:

  • 1)发送端采集音视频数据,通过编码器生成帧数据;
  • 2)这数据被打包成 RTP 包,通过 ICE 通道发送到接收端;
  • 3)接收端接收 RTP 包,取出 RTP payload,完成组帧的操作;
  • 4)之后音视频解码器解码帧数据,生成视频图像或音频 PCM 数据。

如下图所示:

本文所涉及的参数调整,谈论的部分位于上图中的第 4 步。

因为是接收端,所以会收到对方的 Offer 请求。先设置 SetRemoteDescription 再 SetLocalDescription。

如下图蓝色部分: 

4、参数调整

4.1 视频参数调整

当收到 Signal 线程 SetRemoteDescription 后,会在 Worker 线程中创建 VideoReceiveStream 对象。具体流程为 SetRemoteDescription -> VideoChannel::SetRemoteContent_w 创建 WebRtcVideoReceiveStream。

WebRtcVideoReceiveStream 包含了一个 VideoReceiveStream 类型 stream_ 对象, 通过 webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream 创建。

创建后立即启动 VideoReceiveStream 工作,即调用 Start() 方法。

此时 VideoReceiveStream 包含一个 RtpVideoStreamReceiver 对象准备开始处理 video RTP 包。

接收方创建 createAnswer 后通过 setLocalDescription 设置 local descritpion。

对应会在 Worker 线程中 setLocalContent_w 方法中根据 SDP 设置 channel 的接收参数,最终会调用到 WebRtcVideoReceiveStream::SetRecvParameters。

WebRtcVideoReceiveStream::SetRecvParameters 实现如下:

void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetRecvParameters(

const ChangedRecvParameters& params) {

bool video_needs_recreation = false;

bool flexfec_needs_recreation = false;

if(params.codec_settings) {

ConfigureCodecs(*params.codec_settings);

video_needs_recreation = true;

}

if(params.rtp_header_extensions) {

config_.rtp.extensions = *params.rtp_header_extensions;

flexfec_config_.rtp_header_extensions = *params.rtp_header_extensions;

video_needs_recreation = true;

flexfec_needs_recreation = true;

}

if(params.flexfec_payload_type) {

ConfigureFlexfecCodec(*params.flexfec_payload_type);

flexfec_needs_recreation = true;

}

if(flexfec_needs_recreation) {

RTC_LOG(LS_INFO) << "MaybeRecreateWebRtcFlexfecStream (recv) because of "

"SetRecvParameters";

MaybeRecreateWebRtcFlexfecStream();

}

if(video_needs_recreation) {

RTC_LOG(LS_INFO)

<< "RecreateWebRtcVideoStream (recv) because of SetRecvParameters";

RecreateWebRtcVideoStream();

}

}

根据上面 SetRecvParameters 代码,如果 codec_settings 不为空、rtp_header_extensions 不为空、flexfec_payload_type 不为空都会重启 VideoReceiveStream。

video_needs_recreation 表示是否要重启 VideoReceiveStream。

重启过程为:把先前创建的释放掉,然后重建新的 VideoReceiveStream。

以 codec_settings 为例:初始 video codec 支持 H264 和 VP8。若对端只支持 H264,协商后的 codec 仅支持 H264。SetRecvParameters 中的 codec_settings 为 H264 不空。其实前后 VideoReceiveStream 的都有 H264 codec,没有必要重建 VideoReceiveStream。可以通过配置本地支持的 video codec 初始列表和 rtp extensions,从而生成的 local SDP 和 remote SDP 中影响接收参数部分调整一致,并且判断 codec_settings 是否相等。 如果不相等再 video_needs_recreation 为 true。

这样设置就会使 SetRecvParameters 避免触发重启 VideoReceiveStream 逻辑。

在 debug 模式下,修改后,验证没有 “RecreateWebRtcVideoStream (recv) because of SetRecvParameters” 的打印, 即可证明没有 VideoReceiveStream 重启。

4.2 音频参数调整

和上面的视频调整类似,音频也会有因为 rtp extensions 不一致导致重新创建 AudioReceiveStream,也是释放先前的 AudioReceiveStream,再重新创建 AudioReceiveStream。

参考代码:

bool WebRtcVoiceMediaChannel::SetRecvParameters(

const AudioRecvParameters& params) {

TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::SetRecvParameters");

RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());

RTC_LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetRecvParameters: "

<< params.ToString();

// TODO(pthatcher): Refactor this to be more clean now that we have

// all the information at once.

if(!SetRecvCodecs(params.codecs)) {

return false;

}

if(!ValidateRtpExtensions(params.extensions)) {

return false;

}

std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(

params.extensions, webrtc::RtpExtension::IsSupportedForAudio, false);

if(recv_rtp_extensions_ != filtered_extensions) {

recv_rtp_extensions_.swap(filtered_extensions);

for(auto& it : recv_streams_) {

it.second->SetRtpExtensionsAndRecreateStream(recv_rtp_extensions_);

}

}

return true;

}

AudioReceiveStream 的构造方法会启动音频设备,即调用 AudioDeviceModule 的 StartPlayout。

AudioReceiveStream 的析构方法会停止音频设备,即调用 AudioDeviceModule 的 StopPlayout。

因此重启 AudioReceiveStream 会触发多次 StartPlayout/StopPlayout。

经测试,这些不必要的操作会导致进入视频会议的房间时,播放的音频有一小段间断的情况。

解决方法:同样是通过配置本地支持的 audio codec 初始列表和 rtp extensions,从而生成的 local SDP 和 remote SDP 中影响接收参数部分调整一致,避免 AudioReceiveStream 重启逻辑。

另外 audio codec 多为 WebRTC 内部实现,去掉一些不用的 Audio Codec,可以减小 WebRTC 对应的库文件。

4.3 音视频相互影响

WebRTC 内部有三个非常重要的线程:

  • 1)woker 线程;
  • 2)signal 线程;
  • 3)network 线程。

调用 PeerConnection 的 API 的调用会由 signal 线程进入到 worker 线程。

worker 线程内完成媒体数据的处理,network 线程处理网络相关的事务,channel.h 文件中有说明,以 _w 结尾的方法为 worker 线程的方法,signal 线程的到 worker 线程的调用是同步操作。

如下面代码中的 InvokerOnWorker 是同步操作,setLocalContent_w 和 setRemoteContent_w 是 worker 线程中的方法。

bool BaseChannel::SetLocalContent(const MediaContentDescription* content,

SdpType type,

std::string* error_desc) {

TRACE_EVENT0("webrtc", "BaseChannel::SetLocalContent");

returnI nvokeOnWorker<bool>(

RTC_FROM_HERE,

Bind(&BaseChannel::SetLocalContent_w, this, content, type, error_desc));

}

bool BaseChannel::SetRemoteContent(const MediaContentDescription* content,

SdpType type,

std::string* error_desc) {

TRACE_EVENT0("webrtc", "BaseChannel::SetRemoteContent");

return InvokeOnWorker<bool>(

RTC_FROM_HERE,

Bind(&BaseChannel::SetRemoteContent_w, this, content, type, error_desc));

}

setLocalDescription 和 setRemoteDescription 中的 SDP 信息都会通过 PeerConnection 的 PushdownMediaDescription 方法依次下发给 audio/video RtpTransceiver 设置 SDP 信息。

举例:执行 audio 的 SetRemoteContent_w 执行很长(比如音频 AudioDeviceModule 的 InitPlayout 执行耗时), 会影响后面的 video SetRemoteContent_w 的设置时间。

PushdownMediaDescription 代码:

RTCError PeerConnection::PushdownMediaDescription(

SdpType type,

cricket::ContentSource source) {

const SessionDescriptionInterface* sdesc =

(source == cricket::CS_LOCAL ? local_description()

: remote_description());

RTC_DCHECK(sdesc);

// Push down the new SDP media section for each audio/video transceiver.

for(const auto& transceiver : transceivers_) {

const ContentInfo* content_info =

FindMediaSectionForTransceiver(transceiver, sdesc);

cricket::ChannelInterface* channel = transceiver->internal()->channel();

if(!channel || !content_info || content_info->rejected) {

continue;

}

const MediaContentDescription* content_desc =

content_info->media_description();

if(!content_desc) {

continue;

}

std::string error;

bool success = (source == cricket::CS_LOCAL)

? channel->SetLocalContent(content_desc, type, &error)

: channel->SetRemoteContent(content_desc, type, &error);

if(!success) {

LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error);

}

}

...

}

5、其他影响首帧显示的问题

5.1 Android图像宽高16字节对齐

AndroidVideoDecoder 是 WebRTC Android 平台上的视频硬解类。AndroidVideoDecoder 利用 MediaCodec API 完成对硬件解码器的调用。

MediaCodec 有已下解码相关的 API:

  • 1)dequeueInputBuffer:若大于 0,则是返回填充编码数据的缓冲区的索引,该操作为同步操作;
  • 2)getInputBuffer:填充编码数据的 ByteBuffer 数组,结合 dequeueInputBuffer 返回值,可获取一个可填充编码数据的 ByteBuffer;
  • 3)queueInputBuffer:应用将编码数据拷贝到 ByteBuffer 后,通过该方法告知 MediaCodec 已经填写的编码数据的缓冲区索引;
  • 4)dequeueOutputBuffer:若大于 0,则是返回填充解码数据的缓冲区的索引,该操作为同步操作;
  • 5)getOutputBuffer:填充解码数据的 ByteBuffer 数组,结合 dequeueOutputBuffer 返回值,可获取一个可填充解码数据的 ByteBuffer;
  • 6)releaseOutputBuffer:告诉编码器数据处理完成,释放 ByteBuffer 数据。

在实践当中发现,发送端发送的视频宽高需要 16 字节对齐,因为在某些 Android 手机上解码器需要 16 字节对齐。

大致的原理就是:Android 上视频解码先是把待解码的数据通过 queueInputBuffer 给到 MediaCodec。然后通过 dequeueOutputBuffer 反复查看是否有解完的视频帧。若非 16 字节对齐,dequeueOutputBuffer 会有一次MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED。而不是一上来就能成功解码一帧。

经测试发现:帧宽高非 16 字节对齐会比 16 字节对齐的慢 100 ms 左右。

5.2 服务器需转发关键帧请求

iOS 移动设备上,WebRTC App应用进入后台后,视频解码由 VTDecompressionSessionDecodeFrame 返回 kVTInvalidSessionErr,表示解码session 无效。从而会触发观看端的关键帧请求给服务器。

这里要求服务器必须转发接收端发来的关键帧请求给发送端。若服务器没有转发关键帧给发送端,接收端就会长时间没有可以渲染的图像,从而出现黑屏问题。

这种情况下只能等待发送端自己生成关键帧,发送个接收端,从而使黑屏的接收端恢复正常。

5.3 WebRTC内部的一些丢弃数据逻辑举例

Webrtc从接受报数据到、给到解码器之间的过程中也会有很多验证数据的正确性。

举例1:

PacketBuffer 中记录着当前缓存的最小的序号 first_seq_num_(这个值也是会被更新的)。 当 PacketBuffer 中 InsertPacket 时候,如果即将要插入的 packet 的序号 seq_num 小于 first_seq_num,这个 packet 会被丢弃掉。如果因此持续丢弃 packet,就会有视频不显示或卡顿的情况。

举例2:

正常情况下 FrameBuffer 中帧的 picture id,时间戳都是一直正增长的。

如果 FrameBuffer 收到 picture_id 比最后解码帧的 picture id 小时,分两种情况:

  • 1)时间戳比最后解码帧的时间戳大,且是关键帧,就会保存下来。
  • 2)除情况 1 之外的帧都会丢弃掉。

代码如下: 

auto last_decoded_frame = decoded_frames_history_.GetLastDecodedFrameId();

auto last_decoded_frame_timestamp =

decoded_frames_history_.GetLastDecodedFrameTimestamp();

if(last_decoded_frame && id <= *last_decoded_frame) {

if(AheadOf(frame->Timestamp(), *last_decoded_frame_timestamp) &&

frame->is_keyframe()) {

// If this frame has a newer timestamp but an earlier picture id then we

// assume there has been a jump in the picture id due to some encoder

// reconfiguration or some other reason. Even though this is not according

// to spec we can still continue to decode from this frame if it is a

// keyframe.

RTC_LOG(LS_WARNING)

<< "A jump in picture id was detected, clearing buffer.";

ClearFramesAndHistory();

last_continuous_picture_id = -1;

} else{

RTC_LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) ("

<< id.picture_id << ":"

<< static_cast<int>(id.spatial_layer)

<< ") inserted after frame ("

<< last_decoded_frame->picture_id << ":"

<< static_cast<int>(last_decoded_frame->spatial_layer)

<< ") was handed off for decoding, dropping frame.";

return last_continuous_picture_id;

}

}

因此为了能让收到了流顺利播放,发送端和中转的服务端需要确保视频帧的 picture_id, 时间戳正确性。

WebRTC 还有其他很多丢帧逻辑,若网络正常且有持续有接收数据,但是视频卡顿或黑屏无显示,多为流本身的问题。

6、本文小结

本文通过分析 WebRTC 音视频接收端的处理逻辑,列举了一些可以优化首帧显示的点,比如通过调整 local SDP 和 remote SDP 中与影响接收端处理的相关部分,从而避免 Audio/Video ReceiveStream 的重启。

另外列举了 Android 解码器对视频宽高的要求、服务端对关键帧请求处理、以及 WebRTC 代码内部的一些丢帧逻辑等多个方面对视频显示的影响。 这些点都提高了融云 SDK 视频首帧的显示时间,改善了用户体验。

因个人水平有限,文章内容或许存在一定的局限性,欢迎回复进行讨论。

附录1:融云分享的其它文章

融云技术分享:融云安卓端IM产品的网络链路保活技术实践

IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略

融云技术分享:基于WebRTC的实时音视频首帧显示时间优化实践》(* 本文)

即时通讯云融云CTO的创业经验分享:技术创业,你真的准备好了?

附录2:更多实时音视频相关技术文章

[1] 开源实时音视频技术WebRTC的文章:
开源实时音视频技术WebRTC的现状
简述开源实时音视频技术WebRTC的优缺点
访谈WebRTC标准之父:WebRTC的过去、现在和未来
良心分享:WebRTC 零基础开发者教程(中文)[附件下载]
WebRTC实时音视频技术的整体架构介绍
新手入门:到底什么是WebRTC服务器,以及它是如何联接通话的?
WebRTC实时音视频技术基础:基本架构和协议栈
浅谈开发实时视频直播平台的技术要点
[观点] WebRTC应该选择H.264视频编码的四大理由
基于开源WebRTC开发实时音视频靠谱吗?第3方SDK有哪些?
开源实时音视频技术WebRTC中RTP/RTCP数据传输协议的应用
简述实时音视频聊天中端到端加密(E2EE)的工作原理
实时通信RTC技术栈之:视频编解码
开源实时音视频技术WebRTC在Windows下的简明编译教程
网页端实时音视频技术WebRTC:看起来很美,但离生产应用还有多少坑要填?
了不起的WebRTC:生态日趋完善,或将实时音视频技术白菜化
腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践
融云技术分享:基于WebRTC的实时音视频首帧显示时间优化实践
>> 更多同类文章 ……

[2] 实时音视频开发的其它精华资料:
即时通讯音视频开发(一):视频编解码之理论概述
即时通讯音视频开发(二):视频编解码之数字视频介绍
即时通讯音视频开发(三):视频编解码之编码基础
即时通讯音视频开发(四):视频编解码之预测技术介绍
即时通讯音视频开发(五):认识主流视频编码技术H.264
即时通讯音视频开发(六):如何开始音频编解码技术的学习
即时通讯音视频开发(七):音频基础及编码原理入门
即时通讯音视频开发(八):常见的实时语音通讯编码标准
即时通讯音视频开发(九):实时语音通讯的回音及回音消除概述
即时通讯音视频开发(十):实时语音通讯的回音消除技术详解
即时通讯音视频开发(十一):实时语音通讯丢包补偿技术详解
即时通讯音视频开发(十二):多人实时音视频聊天架构探讨
即时通讯音视频开发(十三):实时视频编码H.264的特点与优势
即时通讯音视频开发(十四):实时音视频数据传输协议介绍
即时通讯音视频开发(十五):聊聊P2P与实时音视频的应用情况
即时通讯音视频开发(十六):移动端实时音视频开发的几个建议
即时通讯音视频开发(十七):视频编码H.264、VP8的前世今生
即时通讯音视频开发(十八):详解音频编解码的原理、演进和应用选型
即时通讯音视频开发(十九):零基础,史上最通俗视频编码技术入门
实时语音聊天中的音频处理与编码压缩技术简述
网易视频云技术分享:音频处理与压缩技术快速入门
学习RFC3550:RTP/RTCP实时传输协议基础知识
基于RTMP数据传输协议的实时流媒体技术研究(论文全文)
声网架构师谈实时音视频云的实现难点(视频采访)
浅谈开发实时视频直播平台的技术要点
还在靠“喂喂喂”测试实时语音通话质量?本文教你科学的评测方法!
实现延迟低于500毫秒的1080P实时音视频直播的实践分享
移动端实时视频直播技术实践:如何做到实时秒开、流畅不卡
如何用最简单的方法测试你的实时音视频方案
技术揭秘:支持百万级粉丝互动的Facebook实时视频直播
简述实时音视频聊天中端到端加密(E2EE)的工作原理
移动端实时音视频直播技术详解(一):开篇
移动端实时音视频直播技术详解(二):采集
移动端实时音视频直播技术详解(三):处理
移动端实时音视频直播技术详解(四):编码和封装
移动端实时音视频直播技术详解(五):推流和传输
移动端实时音视频直播技术详解(六):延迟优化
理论联系实际:实现一个简单地基于HTML5的实时视频直播
IM实时音视频聊天时的回声消除技术详解
浅谈实时音视频直播中直接影响用户体验的几项关键技术指标
如何优化传输机制来实现实时音视频的超低延迟?
首次披露:快手是如何做到百万观众同场看直播仍能秒开且不卡顿的?
Android直播入门实践:动手搭建一套简单的直播系统
网易云信实时视频直播在TCP数据传输层的一些优化思路
实时音视频聊天技术分享:面向不可靠网络的抗丢包编解码器
P2P技术如何将实时视频直播带宽降低75%?
专访微信视频技术负责人:微信实时视频聊天技术的演进
腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天
微信团队分享:微信每日亿次实时音视频聊天背后的技术解密
近期大热的实时直播答题系统的实现思路与技术难点分享
福利贴:最全实时音视频开发要用到的开源工程汇总
七牛云技术分享:使用QUIC协议实现实时视频直播0卡顿!
实时音视频聊天中超低延迟架构的思考与技术实践
理解实时音视频聊天中的延时问题一篇就够
实时视频直播客户端技术盘点:Native、HTML5、WebRTC、微信小程序
写给小白的实时音视频技术入门提纲
微信多媒体团队访谈:音视频开发的学习、微信的音视频技术和挑战等
腾讯技术分享:微信小程序音视频技术背后的故事
微信多媒体团队梁俊斌访谈:聊一聊我所了解的音视频技术
新浪微博技术分享:微博短视频服务的优化实践之路
实时音频的混音在视频直播应用中的技术原理和实践总结
以网游服务端的网络接入层设计为例,理解实时通信的技术挑战
腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践
新浪微博技术分享:微博实时直播答题的百万高并发架构实践
技术干货:实时视频直播首屏耗时400ms内的优化实践
爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来
零基础入门:实时音视频技术基础知识全面盘点
>> 更多同类文章 ……

本文已同步发布于“即时通讯技术圈”公众号:

本文在公众号上的链接是:点此进入,原文链接是:http://www.52im.net/thread-3169-1-1.html

融云技术分享:基于WebRTC的实时音视频首帧显示时间优化实践的更多相关文章

  1. 融云技术分享:解密融云IM产品的聊天消息ID生成策略

    本文来自融云技术团队原创分享,原文发布于“融云全球互联网通信云”公众号,原题<如何实现分布式场景下唯一 ID 生成?>,即时通讯网收录时有部分改动. 1.引言 对于IM应用来说,消息ID( ...

  2. 融云技术分享:融云安卓端IM产品的网络链路保活技术实践

    本文来自融云技术团队原创分享,原文发布于“ 融云全球互联网通信云”公众号,原题<IM 即时通讯之链路保活>,即时通讯网收录时有部分改动. 1.引言 众所周知,IM 即时通讯是一项对即时性要 ...

  3. 腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

    1.概述 本文来自腾讯视频云终端技术总监rexchang(常青)技术分享,内容分别介绍了微信小程序视音视频和WebRTC的技术特征.差异等,并针对两者的技术差异分享和总结了微信小程序视音视频和WebR ...

  4. 腾讯技术分享:微信小程序音视频技术背后的故事

    1.引言 微信小程序自2017年1月9日正式对外公布以来,越来越受到关注和重视,小程序上的各种技术体验也越来越丰富.而音视频作为高速移动网络时代下增长最快的应用形式之一,在微信小程序中也当然不能错过. ...

  5. iOS WebRTC 点对点实时音视频流程介绍

    前言 公司某个项目需要接入音视频即时通讯, 功能类似微信的拨打视频通话,语音通话的场景.那么对于音视频通讯会用到什么技术呢?没错,它就是 WebRTC . 什么是WebRTC WebRTC,名称源自网 ...

  6. 融云携新版实时音视频亮相 LiveVideoStack 2019

    4 月 19 日,LiveVideoStack 2019 音视频大会在上海隆重开幕,全球多媒体创新专家.音视频技术工程师.产品负责人.高端行业用户等共襄盛会,聚焦音频.视频.图像.AI 等技术的最新探 ...

  7. 了不起的WebRTC:生态日趋完善,或将实时音视频技术白菜化

    本文原文由声网WebRTC技术专家毛玉杰分享. 1.前言 有人说 2017 年是 WebRTC 的转折之年,2018 年将是 WebRTC 的爆发之年,这并非没有根据.就在去年(2017年),WebR ...

  8. 实时音视频互动系列(下):基于 WebRTC 技术的实战解析

    在 WebRTC 项目中,又拍云团队做到了覆盖系统全局,保证项目进程流畅.这牵涉到主要三大块技术点: 网络端.服务端的开发和传输算法 WebRTC 协议中牵扯到服务端的应用协议和信令服务 客户端iOS ...

  9. 恒天云技术分享系列5 – 虚拟化平台性能对比(KVM & VMware)

    恒天云技术分享系列:http://www.hengtianyun.com/download-show-id-14.html 概述 本性能测试报告将详细陈述各虚拟化平台基准性能测试的主要结论和详细结果. ...

  10. 恒天云技术分享系列4 – OpenStack网络攻击与防御

    恒天云技术分享系列:http://www.hengtianyun.com/download-show-id-13.html 云主机的网络结构本质上和传统的网络结构一致,区别大概有两点. 1.软网络管理 ...

随机推荐

  1. 修复 K8s SSL/TLS 漏洞(CVE-2016-2183)指南

    前言 测试服务器配置 主机名 IP CPU 内存 系统盘 数据盘 用途 zdeops-master 192.168.9.9 2 4 40 200 Ansible 运维控制节点 ks-k8s-maste ...

  2. jmeter测试rpc接口-使用dubbo框架调用

    1.下载用于测试dubbo的spring boot项目 参考文章: http://t.zoukankan.com/111testing-p-11297038.html https://zhuanlan ...

  3. 在Ubuntu小设备上使用VSCode+SSH开发部署nicegui的Web应用,并设置系统开机自动启动应用

    在一些小的设备上跑Ubuntu系统,需要快速的开发和调整项目的时候,往往使用SSH进行远程的开发测试,这样可以避免传统的打包更新处理,能够快速的在实际环境上测试具体的内容.另外由于系统设备往往需要重启 ...

  4. NOIP2024模拟12:孤帆远影

    NOIP2024模拟12:孤帆远影 听了机房同学的讨论,于是T1死磕冒泡和逆序对做法.最后只得了40pts. 思想对了,但不是自己的做法. 还是要坚持自己想,坚持自己可以想出来,不要被任何人带偏. T ...

  5. 什么是AOP面向切面编程?怎么简单理解?

    本文原文地址:什么是AOP面向切面编程?怎么简单理解? 什么是AOP面向切面编程 面向切面编程(AOP)通过将横切关注点(cross-cutting concerns)分离出来,提供了一种增强代码模块 ...

  6. Context的典型使用场景

    获取应用文件路径 基类Context提供了获取应用文件路径的能力,ApplicationContext.AbilityStageContext.UIAbilityContext和ExtensionCo ...

  7. 记录CentOS 部署 express 项目

    第一步.安装 node.js1.在服务器上 /opt 下创建 node 文件夹,并进入该文件夹mkdir /opt/nodecd /opt/node 2.下载 node.js3.下载的 node.js ...

  8. Gitlab运维操作

    部署 安装Postfix以发送通知邮件 yum install postfix 将postfix服务设置成开机自启动 systemctl enable postfix 启动postfix system ...

  9. 万字长文带你深入Redis底层数据结构

    Redis数据库的数据结构 Redis 的键值对中的 key 就是字符串对象,而 value 就是指Redis的数据类型,可以是String,也可以是List.Hash.Set. Zset 的数据类型 ...

  10. .NET使用Umbraco CMS快速构建一个属于自己的内容管理系统

    前言 今天大姚给大家分享一个.NET开源.免费(MIT License).功能强大.灵活易用的内容管理系统:Umbraco CMS.本文将介绍在.NET中如何使用Umbraco CMS快速构建一个属于 ...