[新业务] 尝试使用 SkyWay 发送马赛克处理的摄像机镜头(最终版) [原型开发]
我是Meister Guild Co., Ltd.新事业部的兔子。在我们的新业务部门,我们的目标是推出新服务每天,我都会测试想法并创建原型。
前几天我写了一篇文章。
是的,您现在可以发送经过处理的原始视频!兔子很兴奋,但是……是的,我在实现另一个功能时注意到了它。
在此实施中,“缺少音频信息说
本文的定位我们还将通过 SkyWay 发送处理后的摄像头视频+音频数据。
JavaScript 历史 几个月的佩佩,兔子这将是在犹豫中开发的工作轨迹。
概要前几天,我定制了 SkyWay 的 1:1 通讯应用Rabbit 决定对摄像头图像进行处理,并通过 SkyWay 发送处理后的图像。
但是,我注意到我“丢失了音频信息”。
这一次,处理后的相机图像 + "音频数据”将被修改,以便可以通过 SkyWay 发送。
为什么音频数据消失了?脑海中浮现出一首诗。
main.js- localStream = stream; + localStream = myCvs.captureStream(30);
这里。
即使我检查操作,当localStream = stream; 时,语音被传递给通信伙伴,但是在localStream = myCvs.captureStream(30);的时候,没有声音。
了解 MediaStream 对象到目前为止,我已经看到 MediaStream 做getUserMedia()で取得できる、映像と音声が取れるやつ我用它来识别。
这一次,我只觉得理解力很弱,我决定对 MediaStream 对象进行一些研究。
参考:
下面的要点
渠道⊂卡车⊂溪流 频道是流的最小单位示例)立体声左右音频信号 轨道有多个通道示例)从网络摄像头获取的视频 流用于将多个轨道组合为一个单元・一个流由 0 个或多个轨道组成嗯。图像是什么样的?
了解捕获流一旦我以某种方式弄清楚了 MediaStream 的配置,我也会对 CaptureStream 做更多的研究。
这是我目前的理解程度。
HTMLCanvasElement.captureStream() 是一种将画布前景的实时捕获视频作为 CanvasCaptureMediaStream (en-US) 返回的方法返回对 MediaStream 对象的引用
参考:
那么什么是 CanvasCaptureMediaStream(en-US)对象?如果你检查链接
该接口继承自 MediaStreamTrack 元素生成的 MediaStream 中包含的 MediaTrack看起来像
简而言之‥‥只有视频数据! ?
带来音频数据如果我们可以使用从getUserMedia()获取的本地MediaTrack进行音频,就可以达到我们的目的。
因此,从本地 MediaStream 获取音频的 MediaTrack。使用↓来获取它。
获取音轨()返回一个对象数组,表示流的轨道集中的 MediaStreamTracks,其 MediaStreamTrack.kind 是音频
首先,我检查了有什么样的 AudioTrack。
main.jsnavigator.mediaDevices.getUserMedia({ video:{ width: CVS_WIDTH , height: CVS_HEIGHT }, audio: true }) .then(stream => { const audioTracks = stream.getAudioTracks(); console.log("audioTracks",audioTracks);在我的环境中,我能够获得这样的数据。
还要检查使用 captureStream() 创建的 MediaStream 的 audioTrack。
main.js// const audioTracks = stream.getAudioTracks(); // console.log("audioTracks",audioTracks); const audioTracks = localStream.getAudioTracks(); console.log("audioTracks",audioTracks);正如所料,它是空的!
将音频数据添加到 captureStreamstream.getAudioTracks();得到的是这是MediaStreamTrack を表すオブジェクトの配列。
在我的环境中,无论我使用耳机还是 PC 麦克风,返回数组的长度都是 1,因此我决定按原样使用其中一个。如果长度为 2 或更多,似乎有必要选择一个设备。
增加了一行。
main.jsnavigator.mediaDevices.getUserMedia({ video:{ width: CVS_WIDTH , height: CVS_HEIGHT }, audio: true }) .then(stream => { myVideo.srcObject = stream; myVideo.play(); localStream = myCvs.captureStream(30); + localStream.addTrack(stream.getAudioTracks()[0]);我尝试登录到控制台。
main.jsconst audioTracks = localStream.getAudioTracks(); console.log("audioTracks",audioTracks);添加了音频数据!
现在你也可以向对方发送语音数据了~!
整个代码(自从我上次发帖以来没有太大变化......)
索引.html 索引.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>1対1ビデオ通話</title> <style type="text/css"> .container{ width: 900px; } .main-wrapper{ height: 600px; } .my-section{ float: left; width: 50%; height: 100%; } .their-section{ float: right; width: 50%; height: 100%; } #my-video{ position:absolute; top:140px; left:10px; z-index:1; } #my-canvas{ position:absolute; top:140px; left:10px; z-index:2; } #my-setting{ position:absolute; top:420px; left:10px; z-index:3; } </style> </head> <body> <h1>1対1ビデオ通話のサンプル</h1> <div class="container"> <div class="my-section"> <p class="my-id-label">自分のPeerID: <span id="my-id"></span></p> <video id="my-video" width="360px" height="270px" autoplay muted playsinline></video> <canvas id="my-canvas"></canvas> <div id="my-setting" > <input type="button" id="mosaic-btn" value="モザイク"> <span id="mosaic-enabled">OFF</span> </div> </div> <div class="their-section"> <p> <label class="call-id-form-label" for="their-id">相手のPeerID: </label> <input id="their-id" class="call-id-form"> <button type="button" id="call-btn">発信</button> </p> <video id="their-video" width="360px" height="270px" autoplay muted playsinline></video> </div> </div> <script src="//cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script> <script src="./main.js"></script> </body> </html>main.js main.js// window.__SKYWAY_KEY__ = 'SkyWayのAPI Keyを入れてね'; let localStream; const myVideo = document.getElementById('my-video'); const theirVideo = document.getElementById('their-video'); const CVS_WIDTH = 360 const CVS_HEIGHT = 270 const myCvs = document.getElementById('my-canvas'); const myCtx = myCvs.getContext('2d'); myCvs.width = CVS_WIDTH; myCvs.height = CVS_HEIGHT; myCvs.style.width = `${CVS_WIDTH}px`; myCvs.style.height = `${CVS_HEIGHT}px`; navigator.mediaDevices.getUserMedia({ video:{ width: CVS_WIDTH , height: CVS_HEIGHT }, audio: true }) .then(stream => { myVideo.srcObject = stream; myVideo.play(); localStream = myCvs.captureStream(30); localStream.addTrack(stream.getAudioTracks()[0]); myVideo.onloadeddata = () => { setInterval(() => { if(isMosaicEnabled){ mosaicMyCvs(); }else{ copyToMyCvs(); } }, 1000 / 30); } }).catch(error => { console.error('mediaDevice.getUserMedia() error:', error); }); const MOSAIC_SIZE = 20; const mosaicMyCvs = () => { myCtx.clearRect(0, 0, CVS_WIDTH, CVS_HEIGHT); const mosaicCvs = document.createElement('canvas'); const mosaicCtx = mosaicCvs.getContext('2d'); mosaicCvs.width = CVS_WIDTH ; mosaicCvs.height = CVS_HEIGHT ; mosaicCvs.style.width = `${CVS_WIDTH}px`; mosaicCvs.style.height = `${CVS_HEIGHT}px`; mosaicCtx.drawImage(myVideo, 0, 0); const imageData = mosaicCtx.getImageData(0, 0, CVS_WIDTH, CVS_HEIGHT); //モザイクサイズ単位でループ for (let y = 0; y < myCvs.height; y = y + MOSAIC_SIZE) { for (let x = 0; x < myCvs.width; x = x + MOSAIC_SIZE) { // 該当ピクセルの色情報を取得 const cR = imageData.data[(y * myCvs.width + x) * 4]; const cG = imageData.data[(y * myCvs.width + x) * 4 + 1]; const cB = imageData.data[(y * myCvs.width + x) * 4 + 2]; // モザイクサイズの正方形を描画 myCtx.fillStyle = `rgb(${cR},${cG},${cB})`; myCtx.fillRect(x, y, x + MOSAIC_SIZE, y + MOSAIC_SIZE); } } } const copyToMyCvs = () => { myCtx.drawImage(myVideo, 0, 0, CVS_WIDTH, CVS_HEIGHT); } let isMosaicEnabled = false; document.getElementById('mosaic-btn').onclick = () => { if (isMosaicEnabled) { isMosaicEnabled = false; document.getElementById('mosaic-enabled').textContent = "OFF"; } else { isMosaicEnabled = true; document.getElementById('mosaic-enabled').textContent = "ON"; } } const peer = new Peer({ key: window.__SKYWAY_KEY__, debug: 3 }); peer.on('open', () => { document.getElementById('my-id').textContent = peer.id; }); document.getElementById('call-btn').onclick = () => { const theirID = document.getElementById('their-id').value; const mediaConnection = peer.call(theirID, localStream); setEventListener(mediaConnection); }; const setEventListener = mediaConnection => { mediaConnection.on('stream', stream => { theirVideo.srcObject = stream; theirVideo.play(); }); } peer.on('call', mediaConnection => { mediaConnection.answer(localStream); setEventListener(mediaConnection); });综上所述安全地,您现在不仅可以传送视频,还可以传送声音!接下来,我打算写一篇关于“mic ON/OFF”和“speaker ON/OFF”的文章,我也是偶然发现的。有兴趣的请看下一篇~
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308622828.html