成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

WebRTC入門教程(三) | Android 端如何使用 WebRTC

番茄西紅柿 / 3112人閱讀

摘要:下面我們就看一下具體如何申請權(quán)限靜態(tài)權(quán)限申請在項目中的中增加以下代碼動態(tài)權(quán)限申請隨著的發(fā)展,對安全性要求越來越高。其定義如下通過上面的代碼我們就將顯示視頻的定義好了。當(dāng)發(fā)送消息,并收到服務(wù)端的后,其狀態(tài)變?yōu)椤?/p>

作者:李超,如遇到相關(guān)問題,可以點擊這里與作者直接交流。

前言

在學(xué)習(xí) WebRTC 的過程中,學(xué)習(xí)的一個基本步驟是先通過 JS 學(xué)習(xí) WebRTC的整體流程,在熟悉了整體流程之后,再學(xué)習(xí)其它端如何使用 WebRTC 進行互聯(lián)互通。

我們已經(jīng)在前面分享了信令服務(wù)器的搭建和 STUN/TURN服務(wù)器的搭建:

rtcdeveloper.com/t/topic/133…

rtcdeveloper.com/t/topic/137…

本文將講解 Android 端是如何使用WebRTC的,至于 P2P 穿越、STUN/TURN/ICE、RTP/RTCP協(xié)議、DTLS等內(nèi)容不做講解。

對這方面有興趣的同學(xué)可以多帶帶再聯(lián)系我。

申請權(quán)限

我們要使用 WebRTC 進行音視頻互動時需要申請訪問硬件的權(quán)限,至少要申請以下三種權(quán)限:

Camera 權(quán)限

Record Audio 權(quán)限

Intenet 權(quán)限

在Android中,申請權(quán)限分為靜態(tài)權(quán)限申請和動態(tài)權(quán)限申請,這對于做 Android 開發(fā)的同學(xué)來說已經(jīng)是習(xí)以為常的事情了。下面我們就看一下具體如何申請權(quán)限:

靜態(tài)權(quán)限申請

在 Android 項目中的 AndroidManifest.xml 中增加以下代碼:

...








...

動態(tài)權(quán)限申請

隨著 Android 的發(fā)展,對安全性要求越來越高。除了申請靜態(tài)權(quán)限之外,還需要動態(tài)申請權(quán)限。代碼如下:

void requestPermissions(String[] permissions, intrequestCode);

實際上,對于權(quán)限這塊的處理真正做細(xì)了要寫不少代碼,好在 Android 官方給我們又提供了一個非常好用的庫 EasyPermissions , 有了這個庫我們可以少寫不少代碼。使用 EasyPermissions 非常簡單,在MainActivity中添加代碼如下:

...

protected void onCreate ( Bundle savedInstanceState ) {
	...
	
	String[] perms = {
				Manifest.permission.CAMERA,
				Manifest.permission.RECORD_AUDIO
	};
	
	if (!EasyPermissions.hasPermissions(this, perms)) {
	    EasyPermissions.requestPermissions(this, 
	    								   "Need permissions for camera & microphone", 
	    									0, 
	    									perms);
	}
}

@Override
public void onRequestPermissionsResult(int requestCode,
									   String[] permissions, 
									   int[] grantResults) {
									   
    super.onRequestPermissionsResult(requestCode, 
							         permissions, 
							         grantResults);
							         
    EasyPermissions.onRequestPermissionsResult(requestCode,
     										   permissions, 
     										   grantResults, 
     										   this);
}

...

通過添加以上代碼,就將權(quán)限申請好了,是不是非常簡單?權(quán)限申請好了,我們開始做第二步,看在 Android 下如何引入 WebRTC 庫。

引入庫

在我們這個例子中要引入兩個比較重要的庫,第一個當(dāng)然就是 WebRTC 庫了,第二個是 socket.io 庫,用它來與信令服務(wù)器互聯(lián)。

首先我們看一下如何引入 WebRTC 庫(我這里使用的是最新 Android Studio 3.3.2)。在 Module 級別的 build.gradle 文件中增加以下代碼:

...
dependencies {
    ...
    implementation org.webrtc:google-webrtc:1.0.+
    ...
}

是不是非常簡單?

接下來要引入 socket.io 庫,用它來與我們之前用 Nodejs 搭建的信令服務(wù)器進行對接。再加上前面用到的EasyPermissions庫,所以真正的代碼應(yīng)寫成下面的樣子:

...
dependencies {
    ...
    implementation io.socket:socket.io-client:1.0.0
    implementation org.webrtc:google-webrtc:1.0.+
    implementation pub.devrel:easypermissions:1.1.3
}

通過上面的方式我們就將需要引入的庫全部引入進來了。下面就可以開始真的 WebRTC 之旅了。

萬物的開始

我們都知道萬物有個起源,我們在開發(fā) WebRTC 程序時也不例外,WebRTC程序的起源就是PeerConnectionFactory。這也是與使用 JS 開發(fā) WebRTC 程序最大的不同點之一,因為在 JS 中不需要使用 PeerConnectionFactory 來創(chuàng)建 PeerConnection 對象。

而在 Android/iOS 開發(fā)中,我們使用的 WebRTC 中的大部分對象基本上都是通過 PeerConnectionFactory 創(chuàng)建出來的。下面這張圖就清楚的表達(dá)了 PeerConnectionFactory 在 WebRTC 中的地位。

通過該圖我們可以知道,WebRTC中的核心對象 PeerConnection、LocalMediaStream、LocalVideoTrack、LocalAudioTrack都是通過 WebRTC 創(chuàng)建出來的。

PeerConnectionFactory的初始化與構(gòu)造

在 WebRTC 中使用了大量的設(shè)計模式,對于 PeerConnectionFactory 也是如此。它本身就是工廠模式,而這個構(gòu)造 PeerConnection 等核心對象的工廠又是通過 builder 模式構(gòu)建出來的。

下面我們就來看看如何構(gòu)造 PeerConectionFactory。在我們構(gòu)造 PeerConnectionFactory 之前,首先要對其進行初始化,其代碼如下:

PeerConnectionFactory.initialize(...);

初始化之后,就可以通過 builder 模式來構(gòu)造 PeerConnecitonFactory 對象了。

...

PeerConnectionFactory.Builder builder = 		
				PeerConnectionFactory.builder()
                	.setVideoEncoderFactory(encoderFactory)
                	.setVideoDecoderFactory(decoderFactory);
                
 ...

 return builder.createPeerConnectionFactory();

通過上面的代碼,大家也就能夠理解為什么 WebRTC 要使用 buider 模式來構(gòu)造 PeerConnectionFactory 了吧?主要是方便調(diào)整建造 PeerConnectionFactory的組件,如編碼器、解碼器等。

從另外一個角度我們也可以了解到,要更換WebRTC引警的編解碼器該從哪里設(shè)置了哈!

音視頻數(shù)據(jù)源

有了PeerConnectionFactory對象,我們就可以創(chuàng)建數(shù)據(jù)源了。實際上,數(shù)據(jù)源是 WebRTC 對音視頻數(shù)據(jù)的一種抽象,表式數(shù)據(jù)可以從這里獲取。

使用過 JS WebRTC API的同學(xué)都非常清楚,在 JS中 VideoTrack 和 AudioTrack 就是數(shù)據(jù)源。而在 Android 開發(fā)中我們可以知道 Video/AudioTrack 就是 Video/AudioSouce的封裝,可以認(rèn)為他們是等同的。

創(chuàng)建數(shù)據(jù)源的方式如下:

...
VideoSource videoSource = 
					mPeerConnectionFactory.createVideoSource(false);
mVideoTrack = mPeerConnectionFactory.createVideoTrack(
													VIDEO_TRACK_ID, 
													videoSource);
													
...

AudioSource audioSource = 
					mPeerConnectionFactory.createAudioSource(new MediaConstraints());
mAudioTrack = mPeerConnectionFactory.createAudioTrack(
													AUDIO_TRACK_ID, 
													audioSource);
        
...													
        

數(shù)據(jù)源只是對數(shù)據(jù)的一種抽象,它是從哪里獲取的數(shù)據(jù)呢?對于音頻來說,在創(chuàng)建 AudioSource時,就開始從音頻設(shè)備捕獲數(shù)據(jù)了。對于視頻來說我們可以指定采集視頻數(shù)據(jù)的設(shè)備,然后使用觀察者模式從指定設(shè)備中獲取數(shù)據(jù)。

接下來我們就來看一下如何指定視頻設(shè)備。

視頻采集

在 Android 系統(tǒng)下有兩種 Camera,一種稱為 Camera1, 是一種比較老的采集視頻數(shù)據(jù)的方式,別一種稱為 Camera2, 是一種新的采集視頻的方法。它們之間的最大區(qū)別是 Camera1使用同步方式調(diào)用API,Camera2使用異步方式,所以Camera2更高效。

我們看一下 WebRTC 是如何指定具體的 Camera 的:

private VideoCapturer createVideoCapturer() {
        if (Camera2Enumerator.isSupported(this)) {
            return createCameraCapturer(new Camera2Enumerator(this));
        } else {
            return createCameraCapturer(new Camera1Enumerator(true));
        }
}


private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // First, try to find front facing camera
        Log.d(TAG, "Looking for front facing cameras.");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating front facing camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // Front facing camera not found, try something else
        Log.d(TAG, "Looking for other cameras.");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating other camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        
        return null;
}
    

上面代碼的邏輯也比較簡單:

首先看 Android 設(shè)備是否支持 Camera2.

如果支持就使用 Camera2, 如果不支持就使用 Camera1.

在獲到到具體的設(shè)備后,再看其是否有前置攝像頭,如果有就使用

如果沒有有效的前置攝像頭,則選一個非前置攝像頭。

通過上面的方法就可以拿到使用的攝像頭了,然后將攝像頭與視頻源連接起來,這樣從攝像頭獲取的數(shù)據(jù)就源源不斷的送到 VideoTrack 里了。

下面我們來看看 VideoCapture 是如何與 VideoSource 關(guān)聯(lián)到一起的:

...

mSurfaceTextureHelper = 
			SurfaceTextureHelper.create("CaptureThread",
										mRootEglBase.getEglBaseContext());

mVideoCapturer.initialize(mSurfaceTextureHelper,
 						  getApplicationContext(), 
 						  videoSource.getCapturerObserver());

...

mVideoTrack.setEnabled(true);
...

上面的代碼中,在初始化 VideoCaptuer 的時候,可以過觀察者模式將 VideoCapture 與 VideoSource 聯(lián)接到了一起。因為 VideoTrack 是 VideoSouce 的一層封裝,所以此時我們開啟 VideoTrack 后就可以拿到視頻數(shù)據(jù)了。

當(dāng)然,最后還要調(diào)用一下 VideoCaptuer 對象的 startCapture 方法真正的打開攝像頭,這樣 Camera 才會真正的開始工作哈,代碼如下:

@Override
protected void onResume() {
    super.onResume();
    mVideoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, 
    							VIDEO_RESOLUTION_HEIGHT, 
    							VIDEO_FPS);
}

拿到了視頻數(shù)據(jù)后,我們?nèi)绾螌⑺故境鰜砟兀?/p> 渲染視頻

在 Android 下 WebRTC 使用OpenGL ES 進行視頻渲染,用于展示視頻的控件是 WebRTC 對 Android 系統(tǒng)控件 SurfaceView 的封裝。

WebRTC 封裝后的 SurfaceView 類為 org.webrtc.SurfaceViewRenderer。在界面定義中應(yīng)該定義兩個SurfaceViewRenderer,一個用于顯示本地視頻,另一個用于顯示遠(yuǎn)端視頻。

其定義如下:

...

"@+id/LocalSurfaceView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

"@+id/RemoteSurfaceView"
    android:layout_width="120dp"
    android:layout_height="160dp"
    android:layout_gravity="top|end"
    android:layout_margin="16dp"/>

...

通過上面的代碼我們就將顯示視頻的 View 定義好了。光定義好這兩個View 還不夠,還要對它做進一步的設(shè)置:

...

mLocalSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
mLocalSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
mLocalSurfaceView.setMirror(true);
mLocalSurfaceView.setEnableHardwareScaler(false /* enabled */);

...

其含義是:

使用 OpenGL ES 的上下文初始化 View。

設(shè)置圖像的拉伸比例。

設(shè)置圖像顯示時反轉(zhuǎn),不然視頻顯示的內(nèi)容與實際內(nèi)容正好相反。

是否打開便件進行拉伸。

通過上面的設(shè)置,我們的 view 就設(shè)置好了,對于遠(yuǎn)端的 Veiw 與本地 View 的設(shè)置是一樣的,我這里就不再贅述了。

接下來將從攝像頭采集的數(shù)據(jù)設(shè)置到該view里就可以顯示了。設(shè)置非常的簡單,代碼如下:

...
mVideoTrack.addSink(mLocalSurfaceView);
...

對于遠(yuǎn)端來說與本地視頻的渲染顯示是類似的,只不過數(shù)據(jù)源是從網(wǎng)絡(luò)獲取的。

通過以上講解,大家應(yīng)該對 WebRTC 如何采集數(shù)據(jù)、如何渲染數(shù)據(jù)有了基本的認(rèn)識。下面我們再看來下遠(yuǎn)端的數(shù)據(jù)是如何來的。

創(chuàng)建 PeerConnection

要想從遠(yuǎn)端獲取數(shù)據(jù),我們就必須創(chuàng)建 PeerConnection 對象。該對象的用處就是與遠(yuǎn)端建立聯(lián)接,并最終為雙方通訊提供網(wǎng)絡(luò)通道。

我們來看下如何創(chuàng)建 PeerConnecion 對象。

...
PeerConnection.RTCConfiguration rtcConfig = 
				new PeerConnection.RTCConfiguration(iceServers);
...
PeerConnection connection =
                mPeerConnectionFactory.createPeerConnection(rtcConfig,
                                                            mPeerConnectionObserver);
        
...
connection.addTrack(mVideoTrack, mediaStreamLabels);
connection.addTrack(mAudioTrack, mediaStreamLabels);
...

PeerConnection 對象的創(chuàng)建還是要使我們之前講過的 PeerConnectionFactory 來創(chuàng)建。WebRTC 在建立連接時使用 ICE 架構(gòu),一些參數(shù)需要在創(chuàng)建 PeerConnection 時設(shè)置進去。

另外,當(dāng) PeerConnection 對象創(chuàng)建好后,我們應(yīng)該將本地的音視頻軌添加進去,這樣 WebRTC 才能幫我們生成包含相應(yīng)媒體信息的 SDP,以便于后面做媒體能力協(xié)商使用。

通過上面的方式,我們就將 PeerConnection 對象創(chuàng)建好了。與 JS 中的 PeerConnection 對象一樣,當(dāng)其創(chuàng)建好之后,可以監(jiān)聽一些我們感興趣有事件了,如收到 Candidate 事件時,我們要與對方進行交換。

PeerConnection 事件的監(jiān)聽與 JS 還是有一點差別的。在 JS 中,監(jiān)聽 PeerConnection的相關(guān)事件非常直接,直接實現(xiàn)peerconnection.onXXX就好了。而 Android 中的方式與 JS 略有區(qū)別,它是通過觀察者模式來監(jiān)聽事件的。大家這點一定要注意!

雙方都創(chuàng)建好 PeerConnecton 對象后,就會進行媒體協(xié)商,協(xié)商完成后,數(shù)據(jù)在底層就開始傳輸了。

信令驅(qū)動

在整個 WebRTC 雙方交互的過程中,其業(yè)務(wù)邏輯的核心是信令, 所有的模塊都是通過信令串聯(lián)起來的。

以 PeerConnection 對象的創(chuàng)建為例,該在什么時候創(chuàng)建 PeerConnection 對象呢?最好的時機當(dāng)然是在用戶加入房間之后了 。

下面我們就來看一下,對于兩人通訊的情況,信令該如何設(shè)計。在我們這個例子中,可以將信令分成兩大類。第一類為客戶端命令;第二類為服務(wù)端命令;

客戶端命令有:

join: 用戶加入房間

leave: 用戶離開房間

message: 端到端命令(offer、answer、candidate)

服務(wù)端命令:

joined: 用戶已加入

leaved: 用戶已離開

other_joined:其它用戶已加入

bye: 其它用戶已離開

full: 房間已滿

通過以上幾條信令就可以實現(xiàn)一對一實時互動的要求,是不是非常的簡單?

在本例子中我們?nèi)匀皇峭ㄟ^socket.io與之前搭建的信令服備器互聯(lián)的。由于 socket.io 是跨平臺的,所以無論是在 js 中,還是在 Android 中,我們都可以使用其客戶端與服務(wù)器相聯(lián),非常的方便。

下面再來看一下,收到不同信令后,客戶端的狀態(tài)變化:

客戶端一開始的時候處于 Init/Leave 狀態(tài)。當(dāng)發(fā)送 join 消息,并收到服務(wù)端的 joined 后,其狀態(tài)變?yōu)?joined。

此時,如果第二個用戶加入到房間,則客戶端的狀態(tài)變?yōu)榱?joined_conn, 也就是說此時雙方可以進行實時互動了。

如果此時,該用戶離開,則其狀態(tài)就變成了 初始化狀態(tài)。其它 case 大家可以根據(jù)上面的圖自行理解了。

小結(jié)

本文首先介紹了在 Android 中使用 WebRTC 要需申請的權(quán)限,以及如何引入 WebRTC 庫。然后從如何采集音視頻數(shù)據(jù)、如何渲染、如何與對方建立連接等幾個方面向大家詳細(xì)介紹了如何在 Android 系統(tǒng)下開發(fā)一套 1對1的直播系統(tǒng)。

本文介紹的知識與我之前所寫的通過 《Nodejs 搭建 WebRTC 信令服務(wù)器》完整的構(gòu)成了一套 1對1直播系統(tǒng)。希望通過本文的學(xué)習(xí),同學(xué)們可以快速的撐握 WebRTC 的使用,并根據(jù)自己的需要構(gòu)建自己的直播系統(tǒng)。

謝謝!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7054.html

相關(guān)文章

  • 使用WebRTC搭建前視頻聊天室——入門

    摘要:在處于使用了設(shè)備的私有網(wǎng)絡(luò)中的主機之間需要建立連接時需要使用穿越技術(shù)。目前已經(jīng)有很多穿越技術(shù),但沒有一項是完美的,因為的行為是非標(biāo)準(zhǔn)化的。 什么是WebRTC? 眾所周知,瀏覽器本身不支持相互之間直接建立信道進行通信,都是通過服務(wù)器進行中轉(zhuǎn)。比如現(xiàn)在有兩個客戶端,甲和乙,他們倆想要通信,首先需要甲和服務(wù)器、乙和服務(wù)器之間建立信道。甲給乙發(fā)送消息時,甲先將消息發(fā)送到服務(wù)器上,服務(wù)器對甲...

    Carl 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<