創(chuàng)建PeerConnection的操作都封裝在PeerConnectionClient中,而且創(chuàng)建PeerConnection的操作必須是單線程的,而PeerConnection是由PeerConnectionFactory完成的把敢。
1、創(chuàng)建PeerConnectionFactory
我們前面說過PeerConnectionFactory一個用來生成PeerConnection的工廠類缔刹,同時負(fù)責(zé)初始化全局和底層交互愉粤。
/**
* 創(chuàng)建PeerConnectionFactory的方法
* @param context
* @param localRender 本地視頻
* @param renderEGLContext
* @param peerConnectionParameters
*/
public void createPeerConnectionFactory(
final Context context,
final VideoRenderer.Callbacks localRender,
final EglBase.Context renderEGLContext,
final PeerConnectionParameters peerConnectionParameters) {
this.peerConnectionParameters = peerConnectionParameters;
this.localRender = localRender;
videoCallEnabled = peerConnectionParameters.videoCallEnabled;
if(localRender==null)
videoCallEnabled = false;
// Reset variables to initial states.
factory = null;
preferIsac = false;
videoCapturerStopped = false;
isError = false;
mediaStream = null;
videoCapturer = null;
renderVideo = true;
localVideoTrack = null;
enableAudio = true;
localAudioTrack = null;
this.videoWidth = peerConnectionParameters.videoWidth;
this.videoHeight = peerConnectionParameters.videoHeight;
this.videoFps = peerConnectionParameters.videoFps;
statsTimer = new Timer();
//用于保存對端的IceCandidate
queuedRemoteCandidates = new ConcurrentHashMap<String,LinkedList<IceCandidate>>();
if(isRTCClosed) return;
executor.execute(new Runnable() {
@Override
public void run() {
if(isRTCClosed) return;
try {
//創(chuàng)建媒體約束暇务,創(chuàng)建PeerConnection時用
createMediaConstraintsInternal();
createPeerConnectionFactoryInternal(context, renderEGLContext);
}
catch (Exception e){
reportError("Failed to create peer connection: " + e.getMessage());
return;
}
}
});
}
主要是根據(jù)PeerConnectionParameters中的參數(shù)創(chuàng)建媒體約束
/**
* 創(chuàng)建媒體約束
*/
private void createMediaConstraintsInternal() {
// Create peer connection constraints.
pcConstraints = new MediaConstraints();
//是否允許呼叫自己
if (peerConnectionParameters.loopback) {
pcConstraints.optional.add(
new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "false"));
} else {
pcConstraints.optional.add(
new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "true"));
}
pcConstraints.optional.add(new MediaConstraints.KeyValuePair(
"RtpDataChannels", "true"));
if (videoCallEnabled) {
videoWidth = peerConnectionParameters.videoWidth;
videoHeight = peerConnectionParameters.videoHeight;
videoFps = peerConnectionParameters.videoFps;
if (videoWidth == 0 || videoHeight == 0) {
videoWidth = HD_VIDEO_WIDTH;
videoHeight = HD_VIDEO_HEIGHT;
}
if (videoFps == 0) {
videoFps = 30;
}
Logging.d(TAG, "Capturing format: " + videoWidth + "x" + videoHeight + "@" + videoFps);
}
audioConstraints = new MediaConstraints();
if (peerConnectionParameters.noAudioProcessing) {
Log.d(TAG, "Disabling audio processing");
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
AUDIO_ECHO_CANCELLATION_CONSTRAINT, "false"));
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT, "false"));
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
AUDIO_HIGH_PASS_FILTER_CONSTRAINT, "false"));
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
AUDIO_NOISE_SUPPRESSION_CONSTRAINT, "false"));
}
// Create SDP constraints.
sdpMediaConstraints = new MediaConstraints();
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
"OfferToReceiveAudio", "true"));
if (videoCallEnabled || peerConnectionParameters.loopback) {
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
"OfferToReceiveVideo", "true"));
} else {
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
"OfferToReceiveVideo", "false"));
}
}
創(chuàng)建PeerConnectionFactory實例和MediaStream實例
/**
* createPeerConnectionFactory
* @param context
* @param renderEGLContext
*/
private void createPeerConnectionFactoryInternal(Context context, final EglBase.Context renderEGLContext) {
PeerConnectionFactory.initializeInternalTracer();
if (peerConnectionParameters.tracing) {
PeerConnectionFactory.startInternalTracingCapture(
Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
+ "webrtc-trace.txt");
}
Log.d(TAG, "Create peer connection factory. Use video: " +
peerConnectionParameters.videoCallEnabled);
isError = false;
String fieldTrials = "";
if (peerConnectionParameters.videoFlexfecEnabled) {
fieldTrials += VIDEO_FLEXFEC_FIELDTRIAL;
Log.d(TAG, "Enable FlexFEC field trial.");
}
fieldTrials += VIDEO_VP8_INTEL_HW_ENCODER_FIELDTRIAL;
preferredVideoCodec = VIDEO_CODEC_VP8;
if (videoCallEnabled && peerConnectionParameters.videoCodec != null) {
switch (peerConnectionParameters.videoCodec) {
case VIDEO_CODEC_VP8:
preferredVideoCodec = VIDEO_CODEC_VP8;
break;
case VIDEO_CODEC_VP9:
preferredVideoCodec = VIDEO_CODEC_VP9;
break;
case VIDEO_CODEC_H264_BASELINE:
preferredVideoCodec = VIDEO_CODEC_H264;
break;
case VIDEO_CODEC_H264_HIGH:
// TODO(magjed): Strip High from SDP when selecting Baseline instead of using field trial.
fieldTrials += VIDEO_H264_HIGH_PROFILE_FIELDTRIAL;
preferredVideoCodec = VIDEO_CODEC_H264;
break;
default:
preferredVideoCodec = VIDEO_CODEC_VP8;
}
}
// Initialize field trials.
Log.d(TAG, "Preferred video codec: " + preferredVideoCodec);
PeerConnectionFactory.initializeFieldTrials(fieldTrials);
preferIsac = false;
if (peerConnectionParameters.audioCodec != null
&& peerConnectionParameters.audioCodec.equals(AUDIO_CODEC_ISAC)) {
preferIsac = true;
}
if (!peerConnectionParameters.useOpenSLES) {
Log.d(TAG, "Disable OpenSL ES audio even if device supports it");
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true /* enable */);
} else {
Log.d(TAG, "Allow OpenSL ES audio if device supports it");
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(false);
}
if (peerConnectionParameters.disableBuiltInAEC) {
Log.d(TAG, "Disable built-in AEC even if device supports it");
WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
} else {
Log.d(TAG, "Enable built-in AEC if device supports it");
WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(false);
}
if (peerConnectionParameters.disableBuiltInAGC) {
Log.d(TAG, "Disable built-in AGC even if device supports it");
WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(true);
} else {
Log.d(TAG, "Enable built-in AGC if device supports it");
WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(false);
}
if (peerConnectionParameters.disableBuiltInNS) {
Log.d(TAG, "Disable built-in NS even if device supports it");
WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true);
} else {
Log.d(TAG, "Enable built-in NS if device supports it");
WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(false);
}
WebRtcAudioRecord.setErrorCallback(new WebRtcAudioRecord.WebRtcAudioRecordErrorCallback() {
@Override
public void onWebRtcAudioRecordInitError(String errorMessage) {
Log.e(TAG, "onWebRtcAudioRecordInitError: " + errorMessage);
reportError(errorMessage);
}
@Override
public void onWebRtcAudioRecordStartError(
WebRtcAudioRecord.AudioRecordStartErrorCode errorCode, String errorMessage) {
Log.e(TAG, "onWebRtcAudioRecordStartError: " + errorCode + ". " + errorMessage);
reportError(errorMessage);
}
@Override
public void onWebRtcAudioRecordError(String errorMessage) {
Log.e(TAG, "onWebRtcAudioRecordError: " + errorMessage);
reportError(errorMessage);
}
});
WebRtcAudioTrack.setErrorCallback(new WebRtcAudioTrack.WebRtcAudioTrackErrorCallback() {
@Override
public void onWebRtcAudioTrackInitError(String errorMessage) {
reportError(errorMessage);
}
@Override
public void onWebRtcAudioTrackStartError(String errorMessage) {
reportError(errorMessage);
}
@Override
public void onWebRtcAudioTrackError(String errorMessage) {
reportError(errorMessage);
}
});
PeerConnectionFactory.initializeAndroidGlobals(context, peerConnectionParameters.videoCodecHwAcceleration);
if (options != null) {
Log.d(TAG, "Factory networkIgnoreMask option: " + options.networkIgnoreMask);
}
//創(chuàng)建PeerConnectionFactory
factory = new PeerConnectionFactory(options);
Log.d(TAG, "Peer connection factory created.");
//創(chuàng)建本地媒體流
mediaStream = factory.createLocalMediaStream("ARDAMS");
if (videoCallEnabled) {
//創(chuàng)建videoCapturer
videoCapturer = createVideoCapturer();
if (videoCapturer == null) {
Log.e(TAG,"Failed to open camera");
}
else {
//創(chuàng)建視頻軌道并添加到媒體流中
mediaStream.addTrack(createVideoTrack(videoCapturer));
}
}
//創(chuàng)建音頻軌道并添加到媒體流中
//如果videoCallEnabled == true的話泼掠,此時的mediaStream中有兩條軌道
mediaStream.addTrack(createAudioTrack());
if (videoCallEnabled) {
Log.d(TAG, "EGLContext: " + renderEGLContext);
//視頻硬件加速時需要用到renderEGLContext
factory.setVideoHwAccelerationOptions(renderEGLContext, renderEGLContext);
}
}
2、創(chuàng)建PeerConnection
在前面的步驟中已經(jīng)創(chuàng)建了PeerConnectionFactory的實例垦细,
/**
*
* @param peerName 對方
* @param remoteRender 遠(yuǎn)端的視頻流
* @param iceServers IceServer
* @param isCallout 呼叫還是接聽
* @param iceTransportsType
* @param events 創(chuàng)建PeerConnection的回調(diào)
*/
public void createPeerConnection(
final String peerName,
final VideoRenderer.Callbacks remoteRender,
final List<PeerConnection.IceServer> iceServers,
final boolean isCallout,
final PeerConnection.IceTransportsType iceTransportsType,
final PeerConnectionEvents events) {
Log.e(TAG,"createPeerConnection...");
this.peerName = peerName;
this.isCallout = isCallout;
//this.executor = Executors.newSingleThreadScheduledExecutor();
this.remoteRender = remoteRender;
isError = false;
this.events = events;
if (peerConnectionParameters == null) {
Log.e(TAG, "Creating peer connection without initializing factory.");
return;
}
executor.execute(new Runnable() {
@Override
public void run() {
createPeerConnectionInternal(mediaStream, iceServers, iceTransportsType);
peerConnections.put(peerName,peerConnection);
}
});
}
創(chuàng)建PeerConnection并添加本地媒體流择镇,創(chuàng)建PeerConnection需要3個參數(shù):RTCConfiguration rtcConfig, MediaConstraints constraints, Observer observer。
constraints在創(chuàng)建factory實例時已經(jīng)創(chuàng)建完成括改,rtcConfig在方法中完成腻豌,observer需要實現(xiàn)。
/**
*
* @param localmediaStream 本地視頻流
* @param iceServers IceServer
* @param iceTransportsType 轉(zhuǎn)發(fā)還是P2P
*/
private void createPeerConnectionInternal(MediaStream localmediaStream, List<PeerConnection.IceServer> iceServers, PeerConnection.IceTransportsType iceTransportsType) {
PeerConnection.RTCConfiguration rtcConfig =
new PeerConnection.RTCConfiguration(iceServers);
rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
if(PreferenceUtil.getInstance().getString("BundlePolicy","0").equals("0")){
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.BALANCED;
}else if(PreferenceUtil.getInstance().getString("BundlePolicy","0").equals("1")) {
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
}else if(PreferenceUtil.getInstance().getString("BundlePolicy","0").equals("2")){
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXCOMPAT;
}
if(PreferenceUtil.getInstance().getString("RtcpMuxPolicy","0").equals("0")){
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
}else if(PreferenceUtil.getInstance().getString("RtcpMuxPolicy","0").equals("1")){
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
}
rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
rtcConfig.iceTransportsType = iceTransportsType;
rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
Log.d(TAG, "createPeerConnection begin .");
//創(chuàng)建peerConnection實例
peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver);
Log.d(TAG, "createPeerConnection finish .");
if (dataChannelEnabled) {
DataChannel.Init init = new DataChannel.Init();
init.ordered = true;
init.negotiated = false;
init.maxRetransmits = -1;
init.maxRetransmitTimeMs = -1;
init.id = -1;
init.protocol = "";
dataChannel = peerConnection.createDataChannel("ApprtcDemo data", init);
}
Logging.enableTracing("logcat:", EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT));
Logging.enableLogToDebugOutput(Logging.Severity.LS_ERROR);
mediaStream = localmediaStream;
peerConnection.addStream(mediaStream);//將本地視頻流添加到peerConnection
Log.d(TAG, "Peer connection created.");
}
3嘱能、實現(xiàn)PeerConnection.Observer
作為PeerConnectionClient的內(nèi)部類吝梅,創(chuàng)建PeerConnection過程中的回調(diào)
private class PCObserver implements PeerConnection.Observer{
@Override
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
Log.d(TAG,"SignalingState: " + signalingState);
}
@Override
public void onIceConnectionChange(final PeerConnection.IceConnectionState iceConnectionState) {
executor.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG,"IceConnectionState: " + iceConnectionState);
if (iceConnectionState == PeerConnection.IceConnectionState.CONNECTED) {
events.onIceConnected();
} else if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
events.onIceDisconnected();
} else if (iceConnectionState == PeerConnection.IceConnectionState.FAILED) {
}
}
});
}
@Override
public void onIceConnectionReceivingChange(final boolean b) {
Log.d(TAG,"IceConnectionReceiving changed to " + b);
}
@Override
public void onIceGatheringChange(final PeerConnection.IceGatheringState iceGatheringState) {
Log.d(TAG,"IceGatheringState: " + iceGatheringState);
}
@Override
public void onIceCandidate(final IceCandidate iceCandidate) {
executor.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG,"onIceCandidate: ");
events.onIceCandidate(iceCandidate);
}
});
}
@Override
public void onIceCandidatesRemoved(final IceCandidate[] iceCandidates) {
executor.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG,"onIceCandidatesRemoved: ");
events.onIceCandidatesRemoved(iceCandidates);
}
});
}
@Override
public void onAddStream(final MediaStream mediaStream) {
executor.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG,"onAddStream " + peerConnection);
if (peerConnection == null || isError) {
return;
}
if (mediaStream.audioTracks.size() > 1 || mediaStream.videoTracks.size() > 1) {
Log.d(TAG,"Weird-looking stream: " + mediaStream);
return;
}
if(mediaStream.audioTracks.size() == 1){
remoteAudioTrack = mediaStream.audioTracks.get(0);
}
if (mediaStream.videoTracks.size() == 1) {
remoteVideoTrack = mediaStream.videoTracks.get(0);
remoteVideoTrack.setEnabled(true);
remoteVideoTrack.addRenderer(new VideoRenderer(remoteRender));
}
}
});
}
@Override
public void onRemoveStream(final MediaStream mediaStream) {
executor.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG,"onRemoveStream");
remoteAudioTrack = null;
remoteVideoTrack = null;
}
});
}
@Override
public void onDataChannel(final DataChannel dataChannel) {
Log.d(TAG,"dc rev!");
}
@Override
public void onRenegotiationNeeded() {
Log.d(TAG,"onRenegotiationNeeded!");
}
@Override
public void onAddTrack(final RtpReceiver rtpReceiver, final MediaStream[] mediaStreams) {
Log.d(TAG,"onAddTrack!");
}
}
4、總結(jié)
??至此已經(jīng)創(chuàng)建了PeerConnection對象惹骂,將音視頻數(shù)據(jù)封裝成MediaStream添加到PeerConnection中苏携。創(chuàng)建PeerConnection對象之后就可以調(diào)用PeerConnection的幾個方法了,但是還需要包裝一下对粪,以后再說右冻!