💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 概述 天气燥热,出门不易。遂研究一下owt-server,从owt-client-javascript的流程开始吧。 ## 获取一个会议室 owt-client-javascript的时候有一个初始化房间的过程 ``` ///owt-client-javascript/src/samples/conference/samplertcservice.js (function initSampleRoom () { icsREST.API.getRooms(pageOption, function(rooms){ ... // 获取房间信息,如果有房间,直接跳出,如果没有房间则创建一个房间 if (sampleRoom !== undefined) { break; } } // 创建房间的方法 var tryCreate = function(room, callback) {...} if (!sampleRoom) { room = { name: 'sampleRoom' }; //创建房间 tryCreate(room, function(Id) { sampleRoom = Id; console.log('sampleRoom Id:', sampleRoom); }); } }) ``` ## 获取token 从owt-client-javascript的rest-sample.js开始发送获取token请求。 ``` ///owt-client-javascript/src/samples/conference/public/scripts/rest-sample.js var createToken = function (room, user, role, callback, host) { ... send('POST', '/tokens/', body, callback, host); }; ``` 请求到本地服务 ``` ///owt-client-javascript/src/samples/conference/samplertcservice.js app.post('/tokens', function(req, res) { ... icsREST.API.createToken(room, user, role, preference, ...}); } ``` 本地服务开始请求到owt-server的接口创建token,并且返回token ``` ///owt-server/source/management_api/resource/v1/index.js router.post('/rooms/:room/tokens', tokensResource.create); ``` ## 加入会议并且获取房间中的流 加入会议: ``` ///owt-client-javascript/src/samples/conference/public/scripts/index.js conference.join(token).then(resp => {} ``` 通过返回的token解析出host, 连接上房间,并且把房间中的流信息加入到map里 ``` ///owt-client-javascript/src/samples/conference/public/scripts/index.js signaling.connect(host, isSecured, loginInfo).then((resp) => { //修改连接状态 signalingState = SignalingState.CONNECTED; room = resp.room; if (room.streams !== undefined) { for (const st of room.streams) { //判断流的状态 if (st.type === 'mixed') { st.viewport = st.info.label; } //把房间的流信息加入到列表里 remoteStreams.set(st.id, createRemoteStream(st)); } } ... } ``` 设置好房间的流后,开始获取本地流并且开始推流。 ``` ///owt-client-javascript/src/samples/conference/public/scripts/index.js ... let mediaStream; Owt.Base.MediaStreamFactory.createMediaStream(new Owt.Base.StreamConstraints( audioConstraints, videoConstraints)).then(stream => { let publishOption; if (simulcast) { publishOption = {video:[ {rid: 'q', active: true/*, scaleResolutionDownBy: 4.0*/}, {rid: 'h', active: true/*, scaleResolutionDownBy: 2.0*/}, {rid: 'f', active: true} ]}; } // 获取本地流 mediaStream = stream; localStream = new Owt.Base.LocalStream( mediaStream, new Owt.Base.StreamSourceInfo( 'mic', 'camera')); $('.local video').get(0).srcObject = stream; // 推流 conference.publish(localStream, publishOption).then(publication => { publicationGlobal = publication; mixStream(myRoom, publication.id, 'common') publication.addEventListener('error', (err) => { console.log('Publication error: ' + err.error.message); }); }); }, err => { console.error('Failed to create MediaStream, ' + err); }); ``` ## 拉流 开始拉流 ``` ///owt-client-javascript/src/samples/conference/public/scripts/index.js for (const stream of streams) { if(!subscribeForward){ if (stream.source.audio === 'mixed' || stream.source.video === 'mixed') { //开始拉流 subscribeAndRenderVideo(stream); } } else if (stream.source.audio !== 'mixed') { //开始拉流 subscribeAndRenderVideo(stream); } } ``` 拉流所在的函数 ``` function subscribeAndRenderVideo(stream){ let subscirptionLocal=null; function subscribeDifferentResolution(stream, resolution){ subscirptionLocal && subscirptionLocal.stop(); subscirptionLocal = null; const videoOptions = {}; videoOptions.resolution = resolution; conference.subscribe(stream, { audio: true, video: videoOptions }).then(( subscription) => { subscirptionLocal = subscription; $(`#${stream.id}`).get(0).srcObject = stream.mediaStream; }); } // 创建不同分辨率的按钮和拉对应分辨率的流 let $p = createResolutionButtons(stream, subscribeDifferentResolution); //拉默认流 conference.subscribe(stream) .then((subscription)=>{ subscirptionLocal = subscription; let $video = $(`<video controls autoplay id=${stream.id} style="display:block" >this browser does not supported video tag</video>`); $video.get(0).srcObject = stream.mediaStream; $p.append($video); }, (err)=>{ console.log('subscribe failed', err); }); //流结束的事件 stream.addEventListener('ended', () => { removeUi(stream.id); $(`#${stream.id}resolutions`).remove(); }); // 更新流的事件 stream.addEventListener('updated', () => { // Update resolution buttons $p.children('button').remove(); createResolutionButtons(stream, subscribeDifferentResolution); }); } ``` 开始webrtc的交互流程createPeerConnection ,CreateOffer,setLocalDescription ``` ///owt-client-javascript/src/sdk/conference/client.js this._createPeerConnection(); const offerOptions = {}; if (typeof this._pc.addTransceiver === 'function') { // |direction| seems not working on Safari. if (mediaOptions.audio) { this._pc.addTransceiver('audio', {direction: 'recvonly'}); } if (mediaOptions.video) { this._pc.addTransceiver('video', {direction: 'recvonly'}); } } else { offerOptions.offerToReceiveAudio = !!options.audio; offerOptions.offerToReceiveVideo = !!options.video; } this._pc.createOffer(offerOptions).then((desc) => { if (options) { desc.sdp = this._setRtpReceiverOptions(desc.sdp, options); } this._pc.setLocalDescription(desc).then(() => { this._signaling.sendSignalingMessage('soac', { id: this ._internalId, signaling: desc, }); }, function(errorMessage) { Logger.error('Set local description failed. Message: ' + JSON.stringify(errorMessage)); }); }, function(error) { Logger.error('Create offer failed. Error info: ' + JSON.stringify( error)); }).catch((e)=>{ Logger.error('Failed to create offer or set SDP. Message: ' + e.message); this._unsubscribe(); this._rejectPromise(e); this._fireEndedEventOnPublicationOrSubscription(); }); } ``` 设置setRemoteDescription,_createAndSendAnswer ``` ////owt-client-javascript/src/sdk/conference/client.js // 信令回调 signaling.addEventListener('data', (event) => { onSignalingMessage(event.message.notification, event.message.data); }); // _onOffer(sdp) { Logger.debug('About to set remote description. Signaling state: ' + this._pc.signalingState); sdp.sdp = this._setRtpSenderOptions(sdp.sdp, this._config); // Firefox only has one codec in answer, which does not truly reflect its // decoding capability. So we set codec preference to remote offer, and let // Firefox choose its preferred codec. // Reference: https://bugzilla.mozilla.org/show_bug.cgi?id=814227. if (Utils.isFirefox()) { sdp.sdp = this._setCodecOrder(sdp.sdp); } const sessionDescription = new RTCSessionDescription(sdp); this._pc.setRemoteDescription(sessionDescription).then(() => { this._createAndSendAnswer(); }, (error) => { Logger.debug('Set remote description failed. Message: ' + error.message); this._stop(error, true); }); } ```