Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 497, comments - 13, trackbacks - 0, articles - 1

          本文由融云技術團隊原創(chuàng)投稿,作者是融云WebRTC高級工程師蘇道,轉載請注明出處。

          1、引言

          在一個典型的IM應用里,使用實時音視頻聊天功能時,視頻首幀的顯示,是一項很重要的用戶體驗指標。

          本文主要通過對WebRTC接收端的音視頻處理過程分析,來了解和優(yōu)化視頻首幀的顯示時間,并進行了總結和分享。

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

          2、什么是WebRTC?

          對于沒接觸過實時音視頻技術的人來說,總是看到別人在提WebRTC,那WebRTC是什么?我們有必要簡單介紹一下。

          說到 WebRTC,我們不得不提到 Gobal IP Solutions,簡稱 GIPS。這是一家 1990 年成立于瑞典斯德哥爾摩的 VoIP 軟件開發(fā)商,提供了可以說是世界上最好的語音引擎。相關介紹詳見《訪談WebRTC標準之父:WebRTC的過去、現(xiàn)在和未來》。

          Skype、騰訊 QQ、WebEx、Vidyo 等都使用了它的音頻處理引擎,包含了受專利保護的回聲消除算法,適應網絡抖動和丟包的低延遲算法,以及先進的音頻編解碼器。

          Google 在 Gtalk 中也使用了 GIPS 的授權。Google 在 2011 年以6820萬美元收購了 GIPS,并將其源代碼開源,加上在 2010 年收購的 On2 獲取到的 VPx 系列視頻編解碼器(詳見《即時通訊音視頻開發(fā)(十七):視頻編碼H.264、VP8的前世今生),WebRTC 開源項目應運而生,即 GIPS 音視頻引擎 + 替換掉 H.264 的 VPx 視頻編解碼器。

          在此之后,Google 又將在 Gtalk 中用于 P2P 打洞的開源項目 libjingle 融合進了 WebRTC。目前 WebRTC 提供了包括 Web、iOS、Android、Mac、Windows、Linux 在內的所有平臺支持。

          以上介紹,引用自《了不起的WebRTC:生態(tài)日趨完善,或將實時音視頻技術白菜化

          雖然WebRTC的目標是實現(xiàn)跨平臺的Web端實時音視頻通訊,但因為核心層代碼的Native、高品質和內聚性,開發(fā)者很容易進行除Web平臺外的移殖和應用。目前為止,WebRTC幾乎是是業(yè)界能免費得到的唯一高品質實時音視頻通訊技術。

          3、流程介紹

          一個典型的實時音視頻處理流程大概是這樣:

          • 1)發(fā)送端采集音視頻數(shù)據(jù),通過編碼器生成幀數(shù)據(jù);
          • 2)這數(shù)據(jù)被打包成 RTP 包,通過 ICE 通道發(fā)送到接收端;
          • 3)接收端接收 RTP 包,取出 RTP payload,完成組幀的操作;
          • 4)之后音視頻解碼器解碼幀數(shù)據(jù),生成視頻圖像或音頻 PCM 數(shù)據(jù)。

          如下圖所示:

          本文所涉及的參數(shù)調整,談論的部分位于上圖中的第 4 步。

          因為是接收端,所以會收到對方的 Offer 請求。先設置 SetRemoteDescription 再 SetLocalDescription。

          如下圖藍色部分: 

          4、參數(shù)調整

          4.1 視頻參數(shù)調整

          當收到 Signal 線程 SetRemoteDescription 后,會在 Worker 線程中創(chuàng)建 VideoReceiveStream 對象。具體流程為 SetRemoteDescription -> VideoChannel::SetRemoteContent_w 創(chuàng)建 WebRtcVideoReceiveStream。

          WebRtcVideoReceiveStream 包含了一個 VideoReceiveStream 類型 stream_ 對象, 通過 webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream 創(chuàng)建。

          創(chuàng)建后立即啟動 VideoReceiveStream 工作,即調用 Start() 方法。

          此時 VideoReceiveStream 包含一個 RtpVideoStreamReceiver 對象準備開始處理 video RTP 包。

          接收方創(chuàng)建 createAnswer 后通過 setLocalDescription 設置 local descritpion。 

          對應會在 Worker 線程中 setLocalContent_w 方法中根據(jù) SDP 設置 channel 的接收參數(shù),最終會調用到 WebRtcVideoReceiveStream::SetRecvParameters。

          WebRtcVideoReceiveStream::SetRecvParameters 實現(xiàn)如下:

          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();

            }

          }

          根據(jù)上面 SetRecvParameters 代碼,如果 codec_settings 不為空、rtp_header_extensions 不為空、flexfec_payload_type 不為空都會重啟 VideoReceiveStream。

          video_needs_recreation 表示是否要重啟 VideoReceiveStream。

          重啟過程為:把先前創(chuàng)建的釋放掉,然后重建新的 VideoReceiveStream。

          以 codec_settings 為例:初始 video codec 支持 H264 和 VP8。若對端只支持 H264,協(xié)商后的 codec 僅支持 H264。SetRecvParameters 中的 codec_settings 為 H264 不空。其實前后 VideoReceiveStream 的都有 H264 codec,沒有必要重建 VideoReceiveStream。可以通過配置本地支持的 video codec 初始列表和 rtp extensions,從而生成的 local SDP 和 remote SDP 中影響接收參數(shù)部分調整一致,并且判斷 codec_settings 是否相等。 如果不相等再 video_needs_recreation 為 true。

          這樣設置就會使 SetRecvParameters 避免觸發(fā)重啟 VideoReceiveStream 邏輯。 

          在 debug 模式下,修改后,驗證沒有 “RecreateWebRtcVideoStream (recv) because of SetRecvParameters” 的打印, 即可證明沒有 VideoReceiveStream 重啟。

          4.2 音頻參數(shù)調整

          和上面的視頻調整類似,音頻也會有因為 rtp extensions 不一致導致重新創(chuàng)建 AudioReceiveStream,也是釋放先前的 AudioReceiveStream,再重新創(chuàng)建 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 會觸發(fā)多次 StartPlayout/StopPlayout。

          經測試,這些不必要的操作會導致進入視頻會議的房間時,播放的音頻有一小段間斷的情況。

          解決方法:同樣是通過配置本地支持的 audio codec 初始列表和 rtp extensions,從而生成的 local SDP 和 remote SDP 中影響接收參數(shù)部分調整一致,避免 AudioReceiveStream 重啟邏輯。

          另外 audio codec 多為 WebRTC 內部實現(xiàn),去掉一些不用的 Audio Codec,可以減小 WebRTC 對應的庫文件。

          4.3 音視頻相互影響

          WebRTC 內部有三個非常重要的線程:

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

          調用 PeerConnection 的 API 的調用會由 signal 線程進入到 worker 線程。

          worker 線程內完成媒體數(shù)據(jù)的處理,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 方法依次下發(fā)給 audio/video RtpTransceiver 設置 SDP 信息。

          舉例:執(zhí)行 audio 的 SetRemoteContent_w 執(zhí)行很長(比如音頻 AudioDeviceModule 的 InitPlayout 執(zhí)行耗時), 會影響后面的 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字節(jié)對齊

          AndroidVideoDecoder 是 WebRTC Android 平臺上的視頻硬解類。AndroidVideoDecoder 利用 MediaCodec API 完成對硬件解碼器的調用。

          MediaCodec 有已下解碼相關的 API:

          • 1)dequeueInputBuffer:若大于 0,則是返回填充編碼數(shù)據(jù)的緩沖區(qū)的索引,該操作為同步操作;
          • 2)getInputBuffer:填充編碼數(shù)據(jù)的 ByteBuffer 數(shù)組,結合 dequeueInputBuffer 返回值,可獲取一個可填充編碼數(shù)據(jù)的 ByteBuffer;
          • 3)queueInputBuffer:應用將編碼數(shù)據(jù)拷貝到 ByteBuffer 后,通過該方法告知 MediaCodec 已經填寫的編碼數(shù)據(jù)的緩沖區(qū)索引;
          • 4)dequeueOutputBuffer:若大于 0,則是返回填充解碼數(shù)據(jù)的緩沖區(qū)的索引,該操作為同步操作;
          • 5)getOutputBuffer:填充解碼數(shù)據(jù)的 ByteBuffer 數(shù)組,結合 dequeueOutputBuffer 返回值,可獲取一個可填充解碼數(shù)據(jù)的 ByteBuffer;
          • 6)releaseOutputBuffer:告訴編碼器數(shù)據(jù)處理完成,釋放 ByteBuffer 數(shù)據(jù)。

          在實踐當中發(fā)現(xiàn),發(fā)送端發(fā)送的視頻寬高需要 16 字節(jié)對齊,因為在某些 Android 手機上解碼器需要 16 字節(jié)對齊。

          大致的原理就是:Android 上視頻解碼先是把待解碼的數(shù)據(jù)通過 queueInputBuffer 給到 MediaCodec。然后通過 dequeueOutputBuffer 反復查看是否有解完的視頻幀。若非 16 字節(jié)對齊,dequeueOutputBuffer 會有一次MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED。而不是一上來就能成功解碼一幀。

          經測試發(fā)現(xiàn):幀寬高非 16 字節(jié)對齊會比 16 字節(jié)對齊的慢 100 ms 左右。

          5.2 服務器需轉發(fā)關鍵幀請求

          iOS 移動設備上,WebRTC App應用進入后臺后,視頻解碼由 VTDecompressionSessionDecodeFrame 返回 kVTInvalidSessionErr,表示解碼session 無效。從而會觸發(fā)觀看端的關鍵幀請求給服務器。

          這里要求服務器必須轉發(fā)接收端發(fā)來的關鍵幀請求給發(fā)送端。若服務器沒有轉發(fā)關鍵幀給發(fā)送端,接收端就會長時間沒有可以渲染的圖像,從而出現(xiàn)黑屏問題。

          這種情況下只能等待發(fā)送端自己生成關鍵幀,發(fā)送個接收端,從而使黑屏的接收端恢復正常。

          5.3 WebRTC內部的一些丟棄數(shù)據(jù)邏輯舉例

          Webrtc從接受報數(shù)據(jù)到、給到解碼器之間的過程中也會有很多驗證數(shù)據(jù)的正確性。

          舉例1:

          PacketBuffer 中記錄著當前緩存的最小的序號 first_seq_num_(這個值也是會被更新的)。 當 PacketBuffer 中 InsertPacket 時候,如果即將要插入的 packet 的序號 seq_num 小于 first_seq_num,這個 packet 會被丟棄掉。如果因此持續(xù)丟棄 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;

             }

           }

          因此為了能讓收到了流順利播放,發(fā)送端和中轉的服務端需要確保視頻幀的 picture_id, 時間戳正確性。

          WebRTC 還有其他很多丟幀邏輯,若網絡正常且有持續(xù)有接收數(shù)據(jù),但是視頻卡頓或黑屏無顯示,多為流本身的問題。

          6、本文小結

          本文通過分析 WebRTC 音視頻接收端的處理邏輯,列舉了一些可以優(yōu)化首幀顯示的點,比如通過調整 local SDP 和 remote SDP 中與影響接收端處理的相關部分,從而避免 Audio/Video ReceiveStream 的重啟。

          另外列舉了 Android 解碼器對視頻寬高的要求、服務端對關鍵幀請求處理、以及 WebRTC 代碼內部的一些丟幀邏輯等多個方面對視頻顯示的影響。 這些點都提高了融云 SDK 視頻首幀的顯示時間,改善了用戶體驗。

          因個人水平有限,文章內容或許存在一定的局限性,歡迎回復進行討論。

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

          融云技術分享:融云安卓端IM產品的網絡鏈路保活技術實踐

          IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略

          融云技術分享:基于WebRTC的實時音視頻首幀顯示時間優(yōu)化實踐》(* 本文)

          即時通訊云融云CTO的創(chuàng)業(yè)經驗分享:技術創(chuàng)業(yè),你真的準備好了?

          附錄2:更多實時音視頻相關技術文章

          [1] 開源實時音視頻技術WebRTC的文章:
          開源實時音視頻技術WebRTC的現(xiàn)狀
          簡述開源實時音視頻技術WebRTC的優(yōu)缺點
          訪談WebRTC標準之父:WebRTC的過去、現(xiàn)在和未來
          良心分享:WebRTC 零基礎開發(fā)者教程(中文)[附件下載]
          WebRTC實時音視頻技術的整體架構介紹
          新手入門:到底什么是WebRTC服務器,以及它是如何聯(lián)接通話的?
          WebRTC實時音視頻技術基礎:基本架構和協(xié)議棧
          淺談開發(fā)實時視頻直播平臺的技術要點
          [觀點] WebRTC應該選擇H.264視頻編碼的四大理由
          基于開源WebRTC開發(fā)實時音視頻靠譜嗎?第3方SDK有哪些?
          開源實時音視頻技術WebRTC中RTP/RTCP數(shù)據(jù)傳輸協(xié)議的應用
          簡述實時音視頻聊天中端到端加密(E2EE)的工作原理
          實時通信RTC技術棧之:視頻編解碼
          開源實時音視頻技術WebRTC在Windows下的簡明編譯教程
          網頁端實時音視頻技術WebRTC:看起來很美,但離生產應用還有多少坑要填?
          了不起的WebRTC:生態(tài)日趨完善,或將實時音視頻技術白菜化
          騰訊技術分享:微信小程序音視頻與WebRTC互通的技術思路和實踐
          融云技術分享:基于WebRTC的實時音視頻首幀顯示時間優(yōu)化實踐
          >> 更多同類文章 ……

          [2] 實時音視頻開發(fā)的其它精華資料:
          即時通訊音視頻開發(fā)(一):視頻編解碼之理論概述
          即時通訊音視頻開發(fā)(二):視頻編解碼之數(shù)字視頻介紹
          即時通訊音視頻開發(fā)(三):視頻編解碼之編碼基礎
          即時通訊音視頻開發(fā)(四):視頻編解碼之預測技術介紹
          即時通訊音視頻開發(fā)(五):認識主流視頻編碼技術H.264
          即時通訊音視頻開發(fā)(六):如何開始音頻編解碼技術的學習
          即時通訊音視頻開發(fā)(七):音頻基礎及編碼原理入門
          即時通訊音視頻開發(fā)(八):常見的實時語音通訊編碼標準
          即時通訊音視頻開發(fā)(九):實時語音通訊的回音及回音消除概述
          即時通訊音視頻開發(fā)(十):實時語音通訊的回音消除技術詳解
          即時通訊音視頻開發(fā)(十一):實時語音通訊丟包補償技術詳解
          即時通訊音視頻開發(fā)(十二):多人實時音視頻聊天架構探討
          即時通訊音視頻開發(fā)(十三):實時視頻編碼H.264的特點與優(yōu)勢
          即時通訊音視頻開發(fā)(十四):實時音視頻數(shù)據(jù)傳輸協(xié)議介紹
          即時通訊音視頻開發(fā)(十五):聊聊P2P與實時音視頻的應用情況
          即時通訊音視頻開發(fā)(十六):移動端實時音視頻開發(fā)的幾個建議
          即時通訊音視頻開發(fā)(十七):視頻編碼H.264、VP8的前世今生
          即時通訊音視頻開發(fā)(十八):詳解音頻編解碼的原理、演進和應用選型
          即時通訊音視頻開發(fā)(十九):零基礎,史上最通俗視頻編碼技術入門
          實時語音聊天中的音頻處理與編碼壓縮技術簡述
          網易視頻云技術分享:音頻處理與壓縮技術快速入門
          學習RFC3550:RTP/RTCP實時傳輸協(xié)議基礎知識
          基于RTMP數(shù)據(jù)傳輸協(xié)議的實時流媒體技術研究(論文全文)
          聲網架構師談實時音視頻云的實現(xiàn)難點(視頻采訪)
          淺談開發(fā)實時視頻直播平臺的技術要點
          還在靠“喂喂喂”測試實時語音通話質量?本文教你科學的評測方法!
          實現(xiàn)延遲低于500毫秒的1080P實時音視頻直播的實踐分享
          移動端實時視頻直播技術實踐:如何做到實時秒開、流暢不卡
          如何用最簡單的方法測試你的實時音視頻方案
          技術揭秘:支持百萬級粉絲互動的Facebook實時視頻直播
          簡述實時音視頻聊天中端到端加密(E2EE)的工作原理
          移動端實時音視頻直播技術詳解(一):開篇
          移動端實時音視頻直播技術詳解(二):采集
          移動端實時音視頻直播技術詳解(三):處理
          移動端實時音視頻直播技術詳解(四):編碼和封裝
          移動端實時音視頻直播技術詳解(五):推流和傳輸
          移動端實時音視頻直播技術詳解(六):延遲優(yōu)化
          理論聯(lián)系實際:實現(xiàn)一個簡單地基于HTML5的實時視頻直播
          IM實時音視頻聊天時的回聲消除技術詳解
          淺談實時音視頻直播中直接影響用戶體驗的幾項關鍵技術指標
          如何優(yōu)化傳輸機制來實現(xiàn)實時音視頻的超低延遲?
          首次披露:快手是如何做到百萬觀眾同場看直播仍能秒開且不卡頓的?
          Android直播入門實踐:動手搭建一套簡單的直播系統(tǒng)
          網易云信實時視頻直播在TCP數(shù)據(jù)傳輸層的一些優(yōu)化思路
          實時音視頻聊天技術分享:面向不可靠網絡的抗丟包編解碼器
          P2P技術如何將實時視頻直播帶寬降低75%?
          專訪微信視頻技術負責人:微信實時視頻聊天技術的演進
          騰訊音視頻實驗室:使用AI黑科技實現(xiàn)超低碼率的高清實時視頻聊天
          微信團隊分享:微信每日億次實時音視頻聊天背后的技術解密
          近期大熱的實時直播答題系統(tǒng)的實現(xiàn)思路與技術難點分享
          福利貼:最全實時音視頻開發(fā)要用到的開源工程匯總
          七牛云技術分享:使用QUIC協(xié)議實現(xiàn)實時視頻直播0卡頓!
          實時音視頻聊天中超低延遲架構的思考與技術實踐
          理解實時音視頻聊天中的延時問題一篇就夠
          實時視頻直播客戶端技術盤點:Native、HTML5、WebRTC、微信小程序
          寫給小白的實時音視頻技術入門提綱
          微信多媒體團隊訪談:音視頻開發(fā)的學習、微信的音視頻技術和挑戰(zhàn)等
          騰訊技術分享:微信小程序音視頻技術背后的故事
          微信多媒體團隊梁俊斌訪談:聊一聊我所了解的音視頻技術
          新浪微博技術分享:微博短視頻服務的優(yōu)化實踐之路
          實時音頻的混音在視頻直播應用中的技術原理和實踐總結
          以網游服務端的網絡接入層設計為例,理解實時通信的技術挑戰(zhàn)
          騰訊技術分享:微信小程序音視頻與WebRTC互通的技術思路和實踐
          新浪微博技術分享:微博實時直播答題的百萬高并發(fā)架構實踐
          技術干貨:實時視頻直播首屏耗時400ms內的優(yōu)化實踐
          愛奇藝技術分享:輕松詼諧,講解視頻編解碼技術的過去、現(xiàn)在和將來
          零基礎入門:實時音視頻技術基礎知識全面盤點
          >> 更多同類文章 ……

          本文已同步發(fā)布于“即時通訊技術圈”公眾號:

          本文在公眾號上的鏈接是:點此進入,原文鏈接是:http://www.52im.net/thread-3169-1-1.html



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發(fā)交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發(fā)表評論。


          網站導航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 定安县| 海城市| 延吉市| 广德县| 邯郸市| 舒兰市| 咸宁市| 荔浦县| 通渭县| 红原县| 大新县| 锡林浩特市| 铜山县| 吉安市| 灵丘县| 丘北县| 霍山县| 乌兰县| 曲沃县| 漾濞| 长汀县| 大理市| 齐齐哈尔市| 四子王旗| 临沭县| 贵州省| 敖汉旗| 大田县| 沾益县| 屏山县| 罗甸县| 科尔| 青海省| 延安市| 古浪县| 芦山县| 孟津县| 宜章县| 津市市| 灯塔市| 黄龙县|