今年早些时候,我创建了一个非常有趣的项目,我称之为“大脑 到云端”,我边玩边将我的大脑数据保存到云端 使命召唤,以便我可以分析我之间的关系 认知功能和视频游戏性能。我写了一个三部分 博客文章系列并制作了一些有趣的视频来总结我的发现 那个项目。如果你想检查这些,你可以参考 这篇文章底部的链接。在我发表这篇文章几个月后 项目,我开始在 Twitch 工作,担任 Amazon Interactive Video Service (Amazon IVS) - 一个完全托管的解决方案 用于创建实时交互式视频流解决方案(查看 以了解更多信息)。我的“大脑到云端”项目的下一步很明显——我需要 我的大脑。 本系列 直播 广播我的大脑 在查看代码之前,让我们先看看最终产品。有2个 应用程序的视图:广播视图和回放视图。在 广播视图,我们可以预览直播视频,启动 广播,并连接 Muse 头带以传输大脑数据 从头带获得。在播放视图中,我们显示实时 流与 元素,并实时绘制大脑数据图表 <video> 项目概况 这个项目有5个步骤: 广播直播 捕获大脑数据 在直播中将大脑数据作为定时元数据发布 回放直播 监听定时元数据并实时在图表中呈现大脑数据 如果您更喜欢此类事物的图形描述,则如下所示: 构建项目 我在这个项目中使用了 React。为什么?还好我经验丰富 使用 Vue 和 Angular,但我可能是最后一批开发者之一 earth 尝试 React。我想是时候弄清楚一切了 炒作是关于,我知道这不会是一个困难 用它构建的项目。由于我之前缺乏经验,我不是 你称之为框架的“高级”用户,但我不得不说 我对目前所看到的很满意。我找到了过程 愉快并且没有发现自己与框架“战斗”。但 这篇博文不是关于我对 JavaScript 框架的看法,所以我会 将其保存在以后的帖子中。相反,让我们谈谈我如何广播 我的脑子! 硬件 在我最初的“大脑到云端”项目中,我使用了“老式”脑电图 称为 MindFlex 的耳机可以捕捉我的大脑读数。它工作得很好 很好,但需要我通过添加 ESP-12 来“破解”设备 微控制器,以便从设备中提取读数并发送 他们到云端。这次我接触到了一些更新的东西—— 以及我无需修改即可使用的东西。过了一会儿 研究后,我选择了 。值得庆幸的是,有一个非常棒的开源库,叫做 ,它让我可以直接在带有网络蓝牙的网络浏览器中访问大脑读数(当然,在支持的浏览器中)。 Muse S 头带 muse-js 现场直播 直到最近,使用 IVS 进行直播还需要我们使用 第三方客户端将我们的流广播为 RTMPS。但是我们最近 推出了改变游戏规则的产品: 。 顾名思义,这个 SDK 使我们能够广播我们的 直接从网络浏览器通过 WebRTC 直播。显然,这是一个 非常适合直播我的大脑,因为这意味着我可以创造 一种“一体式”解决方案,用于将我的大脑数据与我的大脑一起广播 无需依赖第三方软件或外部脚本即可进行直播。 Amazon Amazon IVS Web Broadcast SDK 将网络广播添加到 React 应用程序 我们不会查看使用 这篇文章中的 Web Broadcast SDK。相反,我们将着眼于亮点 大致了解它是如何工作的。别担心 - 我有另一篇文章 即将推出,我们将深入研究使用的“逐步”过程 Web Broadcast SDK,敬请期待。也就是说,让我们采取一个 快速了解我如何在此项目中使用 SDK。我的第一步 是使用网络广播来安装 模块。使用您最喜欢的包管理工具,运行: amazon-ivs-web-broadcast $ npm install amazon-ivs-web-broadcast 接下来,我们需要将它导入到我们的组件中。在我的 Broadcast.jsx 组件中,我添加了: import IVSBroadcastClient, { STANDARD_LANDSCAPE } from 'amazon-ivs-web-broadcast' ; 我们可以使用所需的流配置创建 IVSBroadcastClient 的实例,并从我们的 Amazon IVS 通道获取端点并将其设置为我们组件的状态。 }) }); this .setState({ broadcastClient : IVSBroadcastClient.create({ streamConfig : STANDARD_LANDSCAPE, ingestEndpoint : this .state.ingestEndpoint, 现在我们已经有了客户端实例,我们可以将相机添加到客户端。为此我们使用 . navigator.mediaDevices.getUserMedia() }, }, }, }); const streamConfig = STANDARD_LANDSCAPE; const videoStream = await navigator.mediaDevices.getUserMedia({ video : { deviceId : { exact : this .state.selectedVideoDeviceId }, width : { ideal : streamConfig.maxResolution.width, max : streamConfig.maxResolution.width, height : { ideal : streamConfig.maxResolution.height, max : streamConfig.maxResolution.height, this .state.broadcastClient.addVideoInputDevice(videoStream, 'camera1' , { index : 0 }); 将用户的麦克风添加到客户端遵循类似的模式。 }, }); const audioStream = await navigator.mediaDevices.getUserMedia({ audio : { deviceId : this .state.selectedAudioDeviceId this .state.broadcastClient.addAudioInputDevice(audioStream, 'mic1' ); 由于浏览器安全模型,我们需要获得访问用户摄像头和麦克风的权限。有关这方面的更多信息,请参阅 ,并查看我如何捕获列表 设备并在对话框中显示它们以允许用户选择 如果有多个选项可用,则广播设备。 注意: GitHub 上的项目源代码 现在我们可以在页面上添加实时预览,这样我们就可以看到我们的观众最终会在玩家端看到什么。 <canvas ref={ this .previewRef} id= 'broadcast-preview' ></canvas> 并将预览附加到 : broadcastClient this .state.broadcastClient.attachPreview( this .previewRef.current); 要开始广播,请在页面中添加一个按钮,并在 按钮调用的处理程序 在 (通过必要的 ). onClick startBroadcast() broadcastClient streamKey this .state.broadcastClient.startBroadcast( this .state.streamKey); 获取我的大脑数据 正如我上面提到的,我使用了 库,它提供了连接头带和提取原始数据的能力。然而, 不计算 EEG 数据的绝对波段功率。为此,我需要找到 : . muse-js muse-js 另一个图书馆 eeg-pipes 第一步是添加和导入库。 $ npm install @neurosity/pipes $ npm install muse-js import { zipSamples, MuseClient } from 'muse-js' ; import { powerByBand, epoch, fft } from '@neurosity/pipes' ; 接下来,我添加了一个带有点击处理程序的按钮。在处理程序中,我 连接耳机,开始监听数据,订阅 溪流。 zipSamples(client.eegReadings) .pipe( powerByBand(), ) .subscribe( } ); const client = new MuseClient(); await client.connect(); await client.start(); epoch({ duration : 1024 , interval : 250 , samplingRate : 256 }), fft({ bins : 256 }), ( data ) => { const ch0 = [data.delta[ 0 ], data.theta[ 0 ], data.alpha[ 0 ], data.beta[ 0 ], data.gamma[ 0 ]]; const ch1 = [data.delta[ 1 ], data.theta[ 1 ], data.alpha[ 1 ], data.beta[ 1 ], data.gamma[ 1 ]]; const ch2 = [data.delta[ 2 ], data.theta[ 2 ], data.alpha[ 2 ], data.beta[ 2 ], data.gamma[ 2 ]]; const ch3 = [data.delta[ 3 ], data.theta[ 3 ], data.alpha[ 3 ], data.beta[ 3 ], data.gamma[ 3 ]]; const meta = [ch0, ch1, ch2, ch3]; //publish metadata 将我的大脑数据发布为定时元数据 现在我有了一个处理程序,可以从 Muse 收集我的大脑数据 头带,是时候将该数据作为实时元数据发布 溪流。 令人敬畏的事情 是它直接嵌入到视频流中,并且始终是该流的永久部分。这意味着它甚至存在于录制版本中,这意味着即使在点播播放中我们也可以侦听和响应事件。 timed metadata Web Broadcast SDK 不支持从客户端发布定时元数据,所以我们必须使用 ( ) 通过 。为此,我创建了一个 AWS Lambda 函数。 putMetadata docs 适用于 JavaScript 的 AWS 开发工具包 }); }, }; } } } } }; } response.body = err.stack; } }; const AWS = require ( 'aws-sdk' ); const ivs = new AWS.IVS({ apiVersion : '2020-07-14' , region : 'us-east-1' exports .send = async (event, context, callback) => { // response object const response = { 'statusCode' : 200 , 'headers' : { 'Access-Control-Allow-Origin' : '*' , 'Access-Control-Allow-Methods' : 'OPTIONS,GET,PUT,POST,DELETE' , 'Content-Type' : 'application/json' 'body' : '' , 'isBase64Encoded' : false // parse payload let payload; try { payload = JSON .parse(event.body); catch (err) { response.statusCode = 500 ; response.body = JSON .stringify(err); callback( null , response); return ; // validate payload if (!payload || !payload.channelArn || !payload.metadata) { response.statusCode = 400 ; response.body = 'Must provide, channelArn and metadata' ; callback( null , response); return ; // check payload size let byteLength = Buffer.byteLength(payload.metadata, 'utf8' ); if (byteLength > 1024 ) { response.statusCode = 400 ; response.body = 'Too big. Must be less than or equal to 1K' ; callback( null , response); return ; // putmetadata input let params = { channelArn : payload.channelArn, metadata : payload.metadata try { await ivs.putMetadata(params).promise(); response.statusCode = 200 ; response.body = JSON .stringify({ 'published' : true }, '' , 2 ); callback( null , response); catch (err) { response.statusCode = 500 ; callback( null , response); return ; 为了将我的大脑数据发布为定时元数据,我创建了一个 Amazon API Gateway 来调用函数并修改 上面的方法来调用 AWS Lambda 函数。 subscribe() .pipe( powerByBand(), ) .subscribe( fetch(LAMBDA_URL, { }, }) }); } } ); zipSamples(client.eegReadings) epoch({ duration : 1024 , interval : 250 , samplingRate : 256 }), fft({ bins : 256 }), ( data ) => { const ch0 = [data.delta[ 0 ], data.theta[ 0 ], data.alpha[ 0 ], data.beta[ 0 ], data.gamma[ 0 ]]; const ch1 = [data.delta[ 1 ], data.theta[ 1 ], data.alpha[ 1 ], data.beta[ 1 ], data.gamma[ 1 ]]; const ch2 = [data.delta[ 2 ], data.theta[ 2 ], data.alpha[ 2 ], data.beta[ 2 ], data.gamma[ 2 ]]; const ch3 = [data.delta[ 3 ], data.theta[ 3 ], data.alpha[ 3 ], data.beta[ 3 ], data.gamma[ 3 ]]; const meta = [ch0, ch1, ch2, ch3]; // put metadata if broadcasting if ( this .state.isBroadcasting) { 'method' : 'POST' , 'mode' : 'no-cors' , 'headers' : { 'Content-Type' : 'application/json' , 'body' : JSON .stringify({ channelArn : this .state.channelArn, metadata : JSON .stringify(meta) 构建实时流播放并绘制我的大脑数据图表 大脑数据广播视图的直播完成后, 是时候创建显示直播流的播放体验了 并通过定时元数据实时绘制大脑数据图表。 创建直播流媒体播放器 我们可以通过 NPM 使用 IVS Web Player SDK,但由于它使用 WebAssembly, 。为了避免这种麻烦,我发现通过 标签,我将其添加到我的 在我的 React 应用程序中。 所以事情会变得棘手 <script> index.html <script src= "https://player.live-video.net/1.12.0/amazon-ivs-player.min.js" ></script> 在我的 组件,我获取了对播放器的引用和一些必要的元素。 Playback.jsx const { IVSPlayer } = window ; const { create : createMediaPlayer, isPlayerSupported, PlayerEventType, PlayerState } = IVSPlayer; const { ENDED, PLAYING, READY, BUFFERING } = PlayerState; const { TEXT_METADATA_CUE, ERROR } = PlayerEventType; 对于回放,我们使用原生 标签。 <video> <video ref={ this .videoRef} controls playsInline></video> 并初始化播放器并开始播放: this .playerRef.current = createMediaPlayer(); this .playerRef.current.attachHTMLVideoElement( this .videoRef.current); this .playerRef.current.load(STREAM_URL); this .playerRef.current.play(); 监听和响应定时元数据 现在我们正在播放直播,我们可以收听传入的大脑数据并做出响应。 this .playerRef.current.addEventListener(TEXT_METADATA_CUE, this .onPlayerMetadata); 将大脑数据设置为我们的组件状态: }); }); }; onPlayerMetadata = ( e ) => { //console.log(e); const data = JSON .parse(e.text); this .setState( state => { state.ch0.datasets[ 0 ].data = data[ 0 ]; state.ch1.datasets[ 0 ].data = data[ 1 ]; state.ch2.datasets[ 0 ].data = data[ 2 ]; state.ch3.datasets[ 0 ].data = data[ 3 ]; this .chartReferenceCh0.current.data.datasets[ 0 ].data = state.ch0.datasets[ 0 ].data; this .chartReferenceCh1.current.data.datasets[ 0 ].data = state.ch1.datasets[ 0 ].data; this .chartReferenceCh2.current.data.datasets[ 0 ].data = state.ch2.datasets[ 0 ].data; this .chartReferenceCh3.current.data.datasets[ 0 ].data = state.ch3.datasets[ 0 ].data; return ({ ch0 : state.ch0, ch1 : state.ch1, ch2 : state.ch2, ch3 : state.ch3 并使用条形图(使用 Chart.js)呈现它: options={ { }, } } } /> <Bar data={ this .state.ch0} ref={ this .chartReferenceCh0} aspectRatio : 1 , title : { display : true , text : 'Channel: ' + channelNames[ 0 ] }, responsive : true , tooltips : { enabled : false legend : { display : false 可视化很酷,当然提供了一种有趣的方式来查看我的 我在直播游戏时的大脑数据,但没有提供大量 语境。所以我认为包括一些计算是有意义的 深入了解数据的实际含义。为此,我发现 中的一些计算 其中包括一些可用于计算因素的公式 放松(alpha 除以 delta)和专注(beta 除以 θ)。 一种推导疲劳的方法 ((theta + alpha) / beta)。我将这些计算封装在一个方便、可重复使用的组件中。 muse-lsl GitHub 上的项目 我发现的另一篇很棒的博客文章强调了 </Col> </Row> <Row className= 'mb-2' > { /* Delta: 0 Theta: 1 Alpha: 2 Beta: 3 Gamma: 4 */ } <Col xs={ 12 } xxl={ 4 } className= 'align-items-center mb-2 mb-xxl-0' > < Badge className = 'fs-6 w-100' bg = 'info' > Relaxation: < span className = 'fw-bold' > < NumberFormat value = {this.props.dataset.data[0] ? ( this.props.dataset.data [ 2 ] / this.props.dataset.data [ 0 ]) : 0 } decimalScale = {2} displayType = { ' text '} /> </ span > </ Badge > < Col xs = {12} xxl = {4} className = 'align-items-center mb-2 mb-xxl-0' > < Badge className = 'fs-6 w-100' bg = 'info' > Fatigue: < span className = 'fw-bold' > < NumberFormat value = { this.props.dataset.data [ 3 ] ? ( ( this.props.dataset.data [ 1 ] + this.props.dataset.data [ 2 ]) / this.props.dataset.data [ 3 ] ) : 0 } decimalScale = {2} displayType = { ' text '} /> </ span > </ Badge > </ Col > < Col xs = {12} xxl = {4} className = 'align-items-center mb-2 mb-xxl-0' > < Badge className = 'fs-6 w-100' bg = 'info' > Focus: < span className = 'fw-bold' > < NumberFormat value = {this.props.dataset.data[1] ? ( this.props.dataset.data [ 3 ] / this.props.dataset.data [ 1 ]) : 0 } decimalScale = {2} displayType = { ' text '} /> </ span > </ Badge > </ Col > 概括 在这篇文章中,我们了解了我是如何创建一个 React 应用程序来运行的 使用 Amazon IVS 流式传输我的大脑数据。如果您想了解更多 Amazon IVS,请在 dev.to 上查看 系列。如果您有兴趣试用该应用程序或只是查看该应用程序的完整源代码,请在 上查看。随时欢迎您提出意见、问题和反馈,请在此处发表评论或在 上与我联系 Amazon Interactive Video Service 入门 GitHub Twitter 链接 Brain to the Cloud - 第一部分 - 项目介绍和架构概述 大脑到云端——第二部分——我是如何将我的大脑上传到云端的 Brain to the Cloud - 第三部分 - 检查大脑活动与视频游戏性能之间的关系 项目源代码 首发 。 于此