1.添加WebRTC库
implementation 'org.webrtc:google-webrtc:1.0.32006'
2.配置xml,添加Surface用于展示相机画面
<org.webrtc.SurfaceViewRenderer
android:id="@+id/webrtc_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<org.webrtc.SurfaceViewRenderer
android:id="@+id/webrtc_surface_remote_view"
android:layout_width="90dp"
android:layout_height="160dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="30dp"
/>
3.创建PeerConnectionFactory、PeerConnection
public void createPeerConnection() {
// 初始化 PeerConnectionFactory
PeerConnectionFactory.InitializationOptions initializationOptions = PeerConnectionFactory
.InitializationOptions.builder(Utils.getApp())
.setEnableInternalTracer(true)
.setFieldTrials("WebRTC-H264HighProfile/Enabled/")
.createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);
//创建EglBase对象
eglBaseContext = EglBase.create().getEglBaseContext();
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.disableEncryption = true;
options.disableNetworkMonitor = true;
peerConnectionFactory = PeerConnectionFactory.builder()
.setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBaseContext))
.setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglBaseContext, true, true))
.setOptions(options)
.createPeerConnectionFactory();
// 配置STUN穿透服务器 转发服务器
iceServers = new ArrayList<>();
PeerConnection.IceServer iceServer = PeerConnection.IceServer.builder(Constant.STUN).createIceServer();
iceServers.add(iceServer);
streamList = new ArrayList<>();
PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
PeerConnectionObserver connectionObserver = getObserver();
peerConnection = peerConnectionFactory.createPeerConnection(configuration, connectionObserver);
// DataChannel.Init 可配参数说明:
// ordered:是否保证顺序传输;
// maxRetransmitTimeMs:重传允许的最长时间;
// maxRetransmits:重传允许的最大次数;
DataChannel.Init init = new DataChannel.Init();
if (peerConnection != null) {
channel = peerConnection.createDataChannel(Constant.CHANNEL, init);
}
DateChannelObserver channelObserver = new DateChannelObserver();
connectionObserver.setObserver(channelObserver);
// 初始化Surface
initSurface();
// 初始化Sdp回调
initObserver();
}
-
PeerConnectionObserver 实现 PeerConnection.Observer
@NonNull
private PeerConnectionObserver getObserver() {
return new PeerConnectionObserver() {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
LogUtils.eTag(TAG, "创建iceCandidate");
// setIceCandidate(iceCandidate);
// 添加对方的ice
peerConnection.addIceCandidate(iceCandidate);
}
@Override
public void onAddStream(MediaStream mediaStream) {
super.onAddStream(mediaStream);
LogUtils.eTag(TAG, "onAddStream : " + mediaStream.toString());
List<VideoTrack> videoTracks = mediaStream.videoTracks;
if (videoTracks != null && videoTracks.size() > 0) {
VideoTrack videoTrack = videoTracks.get(0);
if (videoTrack != null) {
videoTrack.addSink(remoteSurfaceView);
}
}
List<AudioTrack> audioTracks = mediaStream.audioTracks;
if (audioTracks != null && audioTracks.size() > 0) {
AudioTrack audioTrack = audioTracks.get(0);
if (audioTrack != null) {
audioTrack.setVolume(Constant.VOLUME);
}
}
}
};
}
4. 初始化本地surface、音视频轨
public void initSurface() {
// 设置视频显示
initSurfaceView(localSurfaceView);
initSurfaceView(remoteSurfaceView);
startLocalVideoCapture(localSurfaceView);
startLocalAudioCapture();
}
private void initSurfaceView(SurfaceViewRenderer localSurfaceView) {
localSurfaceView.init(eglBaseContext, null);
localSurfaceView.setMirror(true);
localSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
localSurfaceView.setKeepScreenOn(true);
localSurfaceView.setZOrderMediaOverlay(true);
localSurfaceView.setEnableHardwareScaler(false);
}
private void startLocalVideoCapture(SurfaceViewRenderer localSurfaceView) {
VideoSource videoSource = peerConnectionFactory.createVideoSource(true);
SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(Thread.currentThread().getName()
, eglBaseContext);
VideoCapturer videoCapturer = createCameraCapturer();
videoCapturer.initialize(surfaceTextureHelper, ActivityUtils.getTopActivity(), videoSource.getCapturerObserver());
videoCapturer.startCapture(Constant.VIDEO_RESOLUTION_WIDTH, Constant.VIDEO_RESOLUTION_HEIGHT, Constant.VIDEO_FPS);
// width, height, frame per second
videoTrack = peerConnectionFactory
.createVideoTrack(Constant.VIDEO_TRACK_ID, videoSource);
videoTrack.addSink(localSurfaceView);
MediaStream localMediaStream = peerConnectionFactory
.createLocalMediaStream(Constant.LOCAL_VIDEO_STREAM);
localMediaStream.addTrack(videoTrack);
peerConnection.addTrack(videoTrack, streamList);
peerConnection.addStream(localMediaStream);
}
-
配置相机捕获
/**
* 判断使用Camera1还是Camera2
* @return VideoCapturer
*/
private VideoCapturer createCameraCapturer() {
Context context = ActivityUtils.getTopActivity();
if (Camera2Enumerator.isSupported(context)) {
return createCameraCapturer(new Camera2Enumerator(context));
} else {
return createCameraCapturer(new Camera1Enumerator(true));
}
}
private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
final String[] deviceNames = enumerator.getDeviceNames();
// 首先,尝试找到前置摄像头
LogUtils.eTag(TAG, "尝试查找前置摄像头...");
for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
LogUtils.eTag(TAG, "前置摄像头捕捉器创建成功");
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
// 没有找到前置摄像头,试试别的
LogUtils.eTag(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;
}
-
配置本地音频轨
private void startLocalAudioCapture() {
//语音
MediaConstraints audioConstraints = new MediaConstraints();
//回声消除
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));
//自动增益
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "true"));
//高音过滤
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "true"));
//噪音处理
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true"));
AudioSource audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
audioTrack = peerConnectionFactory.createAudioTrack(Constant.AUDIO_TRACK_ID, audioSource);
MediaStream localMediaStream = peerConnectionFactory.createLocalMediaStream(Constant.LOCAL_AUDIO_STREAM);
localMediaStream.addTrack(audioTrack);
audioTrack.setVolume(Constant.VOLUME);
peerConnection.addTrack(audioTrack, streamList);
peerConnection.addStream(localMediaStream);
}
5.创建本地Sdp回调,发送给对方
-
MySdpObserver 实现SdpObserver
private void initObserver() {
observer = new MySdpObserver() {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
// 将会话描述设置在本地
peerConnection.setLocalDescription(this, sessionDescription);
SessionDescription localDescription = peerConnection.getLocalDescription();
SessionDescription.Type type = localDescription.type;
LogUtils.eTag(TAG, "onCreateSuccess == " + " type == " + type);
// 将offer发送给服务器
if (type == SessionDescription.Type.OFFER) {
// 发送呼叫
offer(sessionDescription);
} else if (type == SessionDescription.Type.ANSWER) {
// 发送应答
answer(sessionDescription);
} else if (type == SessionDescription.Type.PRANSWER) {
// 发送再次应答
}
}
};
}
6.所有配置完成
在PeerConnectionObserver的onAddStream对方音视频轨成功时,画面已建立。
网友评论