webrtc在进行媒体协商时使用sdp来描述媒体信息,其中媒体数据的传输方向包括以下取值:
- a=sendonl表示仅发送数据
- a=recvonly表示仅接受
- a=sendrecv表示既可以发送数据也可以接收数据
- a=inavtive表示不进行传输
但我们在使用webrtc构建应用时怎么确定我们生成offer的媒体数据方向呢?
通过对如下代码建立的最简单的webrtc双向音视频应用的验证,我们发现offer中媒体数据方向的取值由addTrack和createOffer的参数来决定的。
let localStream;
let pc1;
let pc2;
const mediaOptions = {
audio:true,
video:true
};
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
async function start() {
try {
const stream = await navigator.mediaDevices.getUserMedia(mediaOptions);
localVideo.srcObject = stream;
localStream = stream;
} catch (e) {
alert(`getUserMedia() error: ${e.name}`);
}
}
async function call() {
const configuration = {};
pc1 = new RTCPeerConnection(configuration);
pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
pc2 = new RTCPeerConnection(configuration);
pc2.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
pc2.addEventListener('track', gotRemoteStream);
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
try {
const offer = await pc1.createOffer(offerOptions);
await onCreateOfferSuccess(offer);
} catch (e) {
alert(`createOffer() error: ${e.name}`);
}
}
async function onIceCandidate(pc, event) {
try {
await (getOtherPc(pc).addIceCandidate(event.candidate));
} catch (e) {
alert(`addIceCandidate() error: ${e.name}`);
}
}
function gotRemoteStream(e) {
if (remoteVideo.srcObject !== e.streams[0]) {
remoteVideo.srcObject = e.streams[0];
}
}
async function onCreateOfferSuccess(desc) {
try {
await pc1.setLocalDescription(desc);
} catch (e) {
alert(`setLocalDescription() error: ${e.name}`);
}
try {
await pc2.setRemoteDescription(desc);
} catch (e) {
alert(`setRemoteDescription() error: ${e.name}`);
}
try {
const answer = await pc2.createAnswer();
await onCreateAnswerSuccess(answer);
} catch (e) {
alert(`createAnswer() error: ${e.name}`);
}
}
async function onCreateAnswerSuccess(desc) {
try {
await pc2.setLocalDescription(desc);
} catch (e) {
alert(`setLocalDescription() error: ${e.name}`);
}
try {
await pc1.setRemoteDescription(desc);
} catch (e) {
alert(`setRemoteDescription() error: ${e.name}`);
}
}
start();
call();
addTrack的参数表示了我们可以提供的媒体数据,代表了数据发送方向,在上面demo中track实际上由mediaOptions中的audio和video参数决定,当参数取值为true时表示我们可以提供对应类型的媒体数据用于发送。
createOffer的参数表示了我们希望接受的媒体数据,代表了数据接收方向,在上面demo中实际上由offerOptions的offerToReceiveAudio和offerToReceiveVideo决定,当参数取值为true时表示我们希望接受对应类型的媒体数据。
因此我们可以水到渠成的得到下面由audio、video、offerToReceiveAudio、offerToReceiveVideo这4个参数到媒体数据发送方向的表格。
发送参数 | 接受参数 | 媒体数据方向 |
audio true | offerToReceiveAudio true | 音频 a=sendrecv |
audio true | offerToReceiveAudio false | 音频 a=sendonly |
audio false | offerToReceiveAudio true | 音频 a=recvonly |
audio false | offerToReceiveAudio false | 没有音频媒体信息描述 |
video true | offerToReceiveVideo true | 视频 a=sendrecv |
video true | offerToReceiveVideo false | 视频 a=sendonly |
video false | offerToReceiveVideo true | 视频 a=recvonly |
video false | offerToReceiveVideo false | 没有视频媒体信息描述 |