Jack Jiang

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

          本文由陸業(yè)聰分享,原題“一文掌握直播技術(shù):實(shí)時(shí)音視頻采集、編碼、傳輸與播放”,本文進(jìn)行了排版和內(nèi)容優(yōu)化。

          1、引言

          從游戲、教育、電商到娛樂,直播技術(shù)的應(yīng)用場景無處不在。隨著移動(dòng)端的網(wǎng)速越來越快,直播技術(shù)的普及和發(fā)展將更加迅速。

          本文詳細(xì)介紹了Android端直播技術(shù)的全貌,涵蓋了從實(shí)時(shí)音視頻采集、編碼、傳輸?shù)浇獯a與播放的各個(gè)環(huán)節(jié)。文章還探討了直播中音視頻同步、編解碼器選擇、傳輸協(xié)議以及直播延遲優(yōu)化等關(guān)鍵問題。希望本文能為你提供有關(guān)Andriod端直播技術(shù)的深入理解和實(shí)踐指導(dǎo)。

           

          2、系列文章

          本文是系列文章中的第 11 篇,本系列總目錄如下:

          3、知識(shí)準(zhǔn)備

          音視頻技術(shù)的門檻一直以來都相對較高,如果你對音視頻相關(guān)技術(shù)的理論知識(shí)了解不多,建議務(wù)必優(yōu)先閱讀這幾篇零基礎(chǔ)音視頻入門文章:

          1. 零基礎(chǔ),史上最通俗視頻編碼技術(shù)入門
          2. 愛奇藝技術(shù)分享:輕松詼諧,講解視頻編解碼技術(shù)的過去、現(xiàn)在和將來
          3. 零基礎(chǔ)入門:實(shí)時(shí)音視頻技術(shù)基礎(chǔ)知識(shí)全面盤點(diǎn)
          4. 快速掌握11個(gè)視頻技術(shù)相關(guān)的基礎(chǔ)概念

          另外兩篇入門提納式的文章也可以一并閱讀:

          1. 寫給小白的實(shí)時(shí)音視頻技術(shù)入門提綱
          2. 福利貼:最全實(shí)時(shí)音視頻開發(fā)要用到的開源工程匯總

          以上資料學(xué)習(xí)完成后,再回頭來閱讀本篇效果會(huì)更好一點(diǎn)。

          4、實(shí)時(shí)音視頻采集

          4.1音視頻采集設(shè)備與API

          在 Android 設(shè)備中,音視頻的采集主要依賴于攝像頭和麥克風(fēng)這兩個(gè)硬件設(shè)備。攝像頭負(fù)責(zé)圖像的采集,麥克風(fēng)則負(fù)責(zé)音頻的采集。

          為了調(diào)用這兩個(gè)設(shè)備,Android 提供了 Camera API 和 AudioRecord API。通過這兩個(gè) API,我們可以方便地控制設(shè)備,獲取音視頻數(shù)據(jù)。

          以下是具體實(shí)踐步驟。

          1)使用 Camera 或 Camera2 API 來調(diào)用攝像頭:

          // Camera API

          Camera camera = Camera.open();

          Camera.Parameters parameters = camera.getParameters();

          parameters.setPreviewSize(width, height);

          camera.setParameters(parameters);

          camera.setPreviewCallback(previewCallback);

          camera.startPreview();

           

          // Camera2 API

          CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

          String cameraId = cameraManager.getCameraIdList()[0];

          CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);

          StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

          Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);

          // 選擇合適的預(yù)覽尺寸

          cameraManager.openCamera(cameraId, stateCallback, null);

          2)使用 AudioRecord API 來調(diào)用麥克風(fēng):

          int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

          AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, bufferSize);

          audioRecord.startRecording();

          4.2音視頻采集參數(shù)設(shè)置

          音視頻采集的質(zhì)量和流暢度,很大程度上取決于采集參數(shù)的設(shè)置。這些參數(shù)包括分辨率、幀率和碼率等。

          具體是:

          • 1)分辨率:決定了圖像的清晰度。高分辨率可以得到更清晰的圖像,但也會(huì)增加數(shù)據(jù)量,可能導(dǎo)致網(wǎng)絡(luò)傳輸壓力增大;
          • 2)幀率:決定了視頻的流暢度。高幀率可以得到更流暢的視頻,但同樣會(huì)增加數(shù)據(jù)量;
          • 3)碼率:決定了音視頻數(shù)據(jù)的壓縮程度。高碼率可以得到更高質(zhì)量的音視頻,但也會(huì)增加數(shù)據(jù)量。

          在設(shè)置音視頻采集參數(shù)時(shí),需要根據(jù)網(wǎng)絡(luò)狀況和設(shè)備性能,做出合適的折衷。

          以下是具體實(shí)踐步驟。

          1)設(shè)置攝像頭的分辨率和幀率:

          Camera.Parameters parameters = camera.getParameters();

          parameters.setPreviewSize(width, height);

          parameters.setPreviewFrameRate(frameRate);

          camera.setParameters(parameters);

          2)設(shè)置 AudioRecord 的采樣率、聲道數(shù)和音頻格式:

          int sampleRate = 44100;

          int channelConfig = AudioFormat.CHANNEL_IN_MONO;

          int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

          4.3音視頻同步與時(shí)間戳處理

          在直播中,音視頻同步是一個(gè)重要的問題。

          為了實(shí)現(xiàn)同步,我們需要為每幀音視頻數(shù)據(jù)添加時(shí)間戳。時(shí)間戳記錄了數(shù)據(jù)的采集時(shí)間,可以用來調(diào)整播放順序,保證音視頻的同步。在解碼和播放時(shí),播放器會(huì)根據(jù)時(shí)間戳,正確地排列和播放音視頻數(shù)據(jù)。

          為了處理視頻幀數(shù)據(jù)和時(shí)間戳,我們需要將采集到的音視頻幀數(shù)據(jù)和對應(yīng)的時(shí)間戳封裝成一個(gè)數(shù)據(jù)結(jié)構(gòu),然后將這個(gè)結(jié)構(gòu)傳遞給編碼器和傳輸模塊。

          以下是一個(gè)簡單的處理方法。

          1)首先,定義一個(gè)數(shù)據(jù)結(jié)構(gòu)來保存音視頻幀數(shù)據(jù)和時(shí)間戳:

          public class FrameData {

              public byte[] data;

              public long timestamp;

           

              public FrameData(byte[] data, long timestamp) {

                  this.data = data;

                  this.timestamp = timestamp;

              }

          }

          2)在攝像頭的預(yù)覽回調(diào)中添加時(shí)間戳:

          camera.setPreviewCallback(new Camera.PreviewCallback() {

              @Override

              public void onPreviewFrame(byte[] data, Camera camera) {

                  long timestamp = System.nanoTime();

                  // 處理視頻幀數(shù)據(jù)和時(shí)間戳

                  FrameData frameData = new FrameData(data, timestamp);

                  // 將 frameData 傳遞給編碼器和傳輸模塊

              }

          });

          3)在 AudioRecord 的錄音循環(huán)中添加時(shí)間戳:

          while (isRecording) {

              long timestamp = System.nanoTime();

              int bytesRead = audioRecord.read(buffer, 0, bufferSize);

              // 處理音頻幀數(shù)據(jù)和時(shí)間戳

              FrameData frameData = new FrameData(Arrays.copyOf(buffer, bytesRead), timestamp);

              // 將 frameData 傳遞給編碼器和傳輸模塊

          }

          4)在編碼器和傳輸模塊中,根據(jù)FrameData對象的時(shí)間戳來處理音視頻幀數(shù)據(jù)。

          例如,在編碼時(shí),將時(shí)間戳作為編碼后的音視頻數(shù)據(jù)的顯示時(shí)間;在傳輸時(shí),根據(jù)時(shí)間戳來調(diào)整發(fā)送順序和發(fā)送速度。

          這樣,在解碼和播放時(shí),播放器可以根據(jù)時(shí)間戳正確地排列和播放音視頻數(shù)據(jù),實(shí)現(xiàn)同步。

          5、音頻編碼

          5.1音頻編碼格式對比

          常見的音頻編碼格式有 AAC 和 Opus 等。AAC 具有較高的編碼效率,而 Opus 則在實(shí)時(shí)通信中表現(xiàn)更優(yōu)。

          具體是:

          • 1)AAC編碼格式:適用于非實(shí)時(shí)通信領(lǐng)域,如音樂、廣播、視頻等,具有較高的編碼效率和廣泛的設(shè)備兼容性,但在實(shí)時(shí)通信中的延遲優(yōu)化較弱;
          • 2)Opus編碼格式:適用于實(shí)時(shí)通信領(lǐng)域,如VoIP、在線會(huì)議、游戲語音等,具有高音質(zhì)、低延遲和強(qiáng)網(wǎng)絡(luò)適應(yīng)性,但設(shè)備兼容性相對不如AAC。

          5.2在Android中實(shí)現(xiàn)音頻編碼

          在 Android 中實(shí)現(xiàn)音頻編碼,可以使用 Android 提供的 MediaCodec 類。MediaCodec 支持多種音頻編碼格式,如 AAC 和 Opus 等。要選擇合適的編碼格式,可以參考以下步驟。

          1)創(chuàng)建一個(gè) MediaCodec 編碼器實(shí)例:

          MediaCodec audioEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);

          2)配置編碼器參數(shù):

          MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);

          audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);

          audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);

          audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

          3)開始編碼:

          audioEncoder.start();

          6、視頻編碼

          6.1視頻編碼格式對比

          常見的視頻編碼格式有 H.264、H.265 和 VP8 等。H.264 是當(dāng)前最常用的編碼格式,而 H.265 和 VP8 則在特定場景下有更好的性能。

          具體是:

          • 1)H.264編碼格式:適用于視頻會(huì)議、網(wǎng)絡(luò)直播、視頻分享等場景,具有較高的壓縮效率和廣泛的設(shè)備兼容性,但壓縮效率相比H.265較低;
          • 2)H.265編碼格式:適用于4K、8K超高清視頻、虛擬現(xiàn)實(shí)等需要高分辨率和高畫質(zhì)的場景,具有極高的壓縮效率,但編解碼復(fù)雜度高,需要更強(qiáng)的計(jì)算能力,且設(shè)備兼容性相對不如H.264;
          • 3)VP8編碼格式:適用于網(wǎng)絡(luò)視頻通話、在線視頻服務(wù)等對開源和免費(fèi)有要求的場景,延遲低,適合實(shí)時(shí)通信,但壓縮效率和視頻質(zhì)量不如H.264和H.265,且設(shè)備兼容性較差。

          6.2 在Android中實(shí)現(xiàn)視頻編碼

          在 Android 中實(shí)現(xiàn)視頻編碼,同樣可以使用 MediaCodec 類。要選擇合適的編碼格式,可以參考以下步驟。

          1)創(chuàng)建一個(gè) MediaCodec 編碼器實(shí)例:

          MediaCodec videoEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

          2)配置編碼器參數(shù):

          MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);

          videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);

          videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

          videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);

          videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

          3)開始編碼:

          videoEncoder.start();

          7、硬件編碼與軟件編碼的選擇與優(yōu)缺點(diǎn)

          硬件編碼利用 GPU 進(jìn)行編碼,性能更高,但兼容性較差;軟件編碼則兼容性更好,但性能較低。在實(shí)際應(yīng)用中,需要根據(jù)設(shè)備性能和需求進(jìn)行選擇。

          在 Android 中,MediaCodec 類會(huì)根據(jù)設(shè)備性能和需求自動(dòng)選擇硬件編碼器或軟件編碼器。要強(qiáng)制使用硬件編碼器或軟件編碼器,可以在創(chuàng)建 MediaCodec 實(shí)例時(shí),指定編碼器名稱。

          例如,要使用硬件 H.264 編碼器,可以使用以下代碼:

          MediaCodec videoEncoder = MediaCodec.createByCodecName("OMX.google.h264.encoder");

          8、傳輸協(xié)議

          9、音視頻解碼與播放

          9.1音視頻解碼器的選擇與性能優(yōu)化

          解碼器的選擇會(huì)影響播放質(zhì)量和性能。通常,硬件解碼器性能更高,但兼容性較差;軟件解碼器兼容性較好,但性能較低。在實(shí)際應(yīng)用中,需要根據(jù)設(shè)備性能和需求進(jìn)行選擇。

          在 Android 中,解碼器的選擇可以通過 MediaCodec 類來實(shí)現(xiàn)。MediaCodec 支持硬件解碼和軟件解碼,通常情況下,它會(huì)根據(jù)設(shè)備性能和需求自動(dòng)選擇解碼器。

          9.2音視頻渲染與同步策略

          在渲染音視頻時(shí),需要保證音視頻同步。可以通過校準(zhǔn)時(shí)間戳或者調(diào)整播放速度等方法實(shí)現(xiàn)同步。

          在 Android 中,音視頻的渲染可以通過 SurfaceView 或 TextureView 來實(shí)現(xiàn)。為了保證音視頻同步,可以在渲染每幀數(shù)據(jù)時(shí),根據(jù)時(shí)間戳來調(diào)整渲染速度。

          以下是具體實(shí)踐步驟。

          1)創(chuàng)建一個(gè) SurfaceView 或 TextureView:

          SurfaceView surfaceView = new SurfaceView(context);

          // 或

          TextureView textureView = new TextureView(context);

          2)在解碼每幀數(shù)據(jù)時(shí),根據(jù)時(shí)間戳來調(diào)整渲染速度:

          long presentationTimeUs = bufferInfo.presentationTimeUs;

          long delayUs = presentationTimeUs - System.nanoTime() / 1000;

          if (delayUs > 0) {

              Thread.sleep(delayUs / 1000);

          }

          decoder.releaseOutputBuffer(outputBufferIndex, true);

          9.3播放器的緩沖與自適應(yīng)碼率調(diào)整

          為了應(yīng)對網(wǎng)絡(luò)波動(dòng),播放器需要設(shè)置合適的緩沖策略。自適應(yīng)碼率調(diào)整則可以根據(jù)網(wǎng)絡(luò)狀況動(dòng)態(tài)調(diào)整視頻質(zhì)量,以保證流暢度。

          在 Android 中,播放器的緩沖策略可以通過 MediaPlayer 或 ExoPlayer 的 API 來設(shè)置。自適應(yīng)碼率調(diào)整則可以通過 ExoPlayer 的 TrackSelection API 來實(shí)現(xiàn)。

          以下是具體實(shí)踐步驟。

          1)設(shè)置播放器的緩沖策略:

          MediaPlayer mediaPlayer = new MediaPlayer();

          mediaPlayer.setBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {

              @Override

              public void onBufferingUpdate(MediaPlayer mp, int percent) {

                  // 更新緩沖進(jìn)度

              }

          });

          // 或

          ExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context).build();

          exoPlayer.setBufferedPositionUpdateListener(new ExoPlayer.BufferedPositionUpdateListener() {

              @Override

              public void onBufferedPositionUpdate(long bufferedPosition) {

                  // 更新緩沖進(jìn)度

              }

          });

          2)設(shè)置自適應(yīng)碼率調(diào)整:

          TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory();

          DefaultTrackSelector trackSelector = new DefaultTrackSelector(context, trackSelectionFactory);

          ExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();

          10、直播架構(gòu)概述

          10.1直播架構(gòu)圖

          以下是直播架構(gòu)圖:

          解釋一下:

          • 1)推流端:需要實(shí)現(xiàn)音視頻采集、編碼、傳輸?shù)裙δ堋jP(guān)鍵組件包括采集模塊、編碼器、傳輸模塊等;
          • 2)服務(wù)器端:負(fù)責(zé)接收、轉(zhuǎn)發(fā)和存儲(chǔ)音視頻數(shù)據(jù)。關(guān)鍵組件包括負(fù)載均衡、轉(zhuǎn)碼、錄制等功能模塊;
          • 3)拉流端:需要實(shí)現(xiàn)音視頻解碼、渲染和播放等功能。關(guān)鍵組件包括解碼器、渲染模塊、播放器等。

          10.2直播延遲與優(yōu)化策略

          直播延遲會(huì)影響用戶體驗(yàn)。通過優(yōu)化采集、編碼、傳輸、解碼等環(huán)節(jié),可以降低延遲,提高實(shí)時(shí)性。

          直播延遲優(yōu)化策略有:

          • 1)優(yōu)化采集模塊:提高采集效率,減少數(shù)據(jù)處理時(shí)間;
          • 2)優(yōu)化編碼器:選擇性能更高的編碼器,減少編碼時(shí)間;
          • 3)優(yōu)化傳輸模塊:優(yōu)化網(wǎng)絡(luò)傳輸策略,如使用更快的傳輸協(xié)議、提高網(wǎng)絡(luò)帶寬等;
          • 4)優(yōu)化解碼器:選擇性能更高的解碼器,減少解碼時(shí)間;
          • 5)優(yōu)化渲染模塊和播放器:提高渲染效率,減少播放延遲。

          11、本文小結(jié)

          本文介紹了直播技術(shù)的全貌,涉及實(shí)時(shí)音視頻采集到播放的各個(gè)環(huán)節(jié)。

          以下是一個(gè)簡化的直播流程圖:

          直播流程包括以下幾個(gè)關(guān)鍵環(huán)節(jié):

          • 1)實(shí)時(shí)音視頻采集:通過攝像頭和麥克風(fēng)采集音視頻數(shù)據(jù),并進(jìn)行參數(shù)設(shè)置和同步處理;
          • 2)音視頻編碼:將采集到的音視頻數(shù)據(jù)進(jìn)行編碼,以便進(jìn)行傳輸。選擇合適的編碼器和編碼格式,如AAC、Opus、H.264、H.265和VP8等;
          • 3)傳輸協(xié)議:選擇合適的傳輸協(xié)議,如RTMP、HLS和WebRTC等,以保證音視頻數(shù)據(jù)的實(shí)時(shí)傳輸;
          • 4)服務(wù)器處理:服務(wù)器接收、轉(zhuǎn)發(fā)和存儲(chǔ)音視頻數(shù)據(jù),進(jìn)行負(fù)載均衡、轉(zhuǎn)碼和錄制等處理;
          • 5)音視頻解碼與播放:將接收到的音視頻數(shù)據(jù)進(jìn)行解碼、渲染和播放,實(shí)現(xiàn)音視頻同步和延遲優(yōu)化。

          在實(shí)際應(yīng)用中,需要根據(jù)需求和場景選擇合適的技術(shù)和策略,以實(shí)現(xiàn)高質(zhì)量、低延遲的直播體驗(yàn)。

          12、參考資料

          [1] 詳解音頻編解碼的原理、演進(jìn)和應(yīng)用選型

          [2] 零基礎(chǔ),史上最通俗視頻編碼技術(shù)入門

          [3] 理解實(shí)時(shí)音視頻聊天中的延時(shí)問題一篇就夠

          [4] 淺談開發(fā)實(shí)時(shí)視頻直播平臺(tái)的技術(shù)要點(diǎn)

          [5] 福利貼:最全實(shí)時(shí)音視頻開發(fā)要用到的開源工程匯總

          [6] 愛奇藝技術(shù)分享:輕松詼諧,講解視頻編解碼技術(shù)的過去、現(xiàn)在和將來

          [7] 零基礎(chǔ)入門:實(shí)時(shí)音視頻技術(shù)基礎(chǔ)知識(shí)全面盤點(diǎn)

          [8] 實(shí)時(shí)音視頻面視必備:快速掌握11個(gè)視頻技術(shù)相關(guān)的基礎(chǔ)概念

          [9] 理論聯(lián)系實(shí)際:實(shí)現(xiàn)一個(gè)簡單地基于html]5的實(shí)時(shí)視頻直播

          [10] 實(shí)時(shí)視頻直播客戶端技術(shù)盤點(diǎn):Native、html]5、WebRTC、微信小程序

          [11] Android直播入門實(shí)踐:動(dòng)手搭建一套簡單的直播系統(tǒng)

          [12] 視頻直播技術(shù)干貨:一文讀懂主流視頻直播系統(tǒng)的推拉流架構(gòu)、傳輸協(xié)議等

          [13] 零基礎(chǔ)入門:基于開源WebRTC,從0到1實(shí)現(xiàn)實(shí)時(shí)音視頻聊天功能

          [14] 實(shí)時(shí)音視頻入門學(xué)習(xí):開源工程WebRTC的技術(shù)原理和使用淺析

          [15] 實(shí)時(shí)音視頻開發(fā)理論必備:如何省流量?視頻高度壓縮背后的預(yù)測技術(shù)

          [16] 萬字長文詳解QQ Linux端實(shí)時(shí)音視頻背后的跨平臺(tái)實(shí)踐


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



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


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


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 镇巴县| 武强县| 始兴县| 莫力| 城固县| 历史| 台北县| 五台县| 鸡泽县| 南汇区| 阳高县| 阿勒泰市| 潮州市| 麦盖提县| 平安县| 琼海市| 德兴市| 喀喇| 沁阳市| 平定县| 河池市| 临猗县| 七台河市| 绥德县| 都安| 建始县| 金阳县| 台东市| 金山区| 溧阳市| 三门峡市| 玛曲县| 贞丰县| 会泽县| 西安市| 荥经县| 峨眉山市| 芦山县| 绥芬河市| 五峰| 云龙县|