[新业务] 尝试使用 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.js
navigator.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);

正如所料,它是空的!

将音频数据添加到 captureStream

stream.getAudioTracks();得到的是这是MediaStreamTrack を表すオブジェクトの配列。

在我的环境中,无论我使用耳机还是 PC 麦克风,返回数组的长度都是 1,因此我决定按原样使用其中一个。如果长度为 2 或更多,似乎有必要选择一个设备。

增加了一行。

main.js
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]);

尝试登录到控制台。

main.js
        const 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

44人参与, 0条评论 登录后显示评论回复

你需要登录后才能评论 登录/ 注册