paint-brush
Como eu transmito meu cérebro ao vivo com o Amazon IVS, uma bandana Muse e Reactpor@amazonivs
4,596 leituras
4,596 leituras

Como eu transmito meu cérebro ao vivo com o Amazon IVS, uma bandana Muse e React

Muito longo; Para ler

Esta é a primeira vez que usamos o React para transmitir ao vivo nossos dados cerebrais. Usamos o Amazon IVS Web Broadcast SDK para transmitir nossos dados cerebrais ao vivo para a nuvem. Não veremos cada etapa para utilizar o SDK nesta postagem. Em vez disso, veremos os destaques para ter uma ideia geral de como funciona. A primeira etapa é instalar o módulo am-ivcast para rodar em nossa ferramenta de transmissão: No módulo broadcast-broadcast, precisamos importá-lo para nosso pacote favorito: Na ferramenta de transmissão, temos outra etapa para obter outra etapa no projeto.
featured image - Como eu transmito meu cérebro ao vivo com o Amazon IVS, uma bandana Muse e React
Amazon Interactive Video Service (IVS)  HackerNoon profile picture
0-item

No início deste ano, criei um projeto muito divertido que chamei de "Brain
to the Cloud" onde salvei meus dados cerebrais na nuvem enquanto jogava
Call of Duty para que eu pudesse analisar a relação entre meu
função cognitiva e desempenho de videogame. Eu escrevi um em três partes
série de postagens no blog e criei alguns vídeos divertidos para resumir minhas descobertas sobre
aquele projeto. Se você gostaria de verificá-los, você pode consultar o
links no final deste post. Alguns meses depois de publicar aquele
projeto, comecei a trabalhar no Twitch como o principal Developer Advocate para
Amazon Interactive Video Service (Amazon IVS) - uma solução totalmente gerenciada
para criar soluções de transmissão de vídeo ao vivo e interativas (confira esta série para saber mais). A próxima etapa do meu projeto "Brain to the Cloud" era óbvia - eu precisava transmitir meu cérebro ao vivo .

Transmitindo meu cérebro

Antes de olharmos para o código, vamos ver o produto final. Há 2
visualizações para o aplicativo: uma visualização de transmissão e uma visualização de reprodução. Dentro
a exibição de transmissão, podemos visualizar o vídeo ao vivo, iniciar o
transmissão e conecte a faixa de cabeça Muse para transmitir os dados do cérebro
obtido da faixa de cabeça. Na exibição de reprodução, exibimos o ao vivo
transmitir com um

 <video>
elemento e mapear os dados do cérebro em tempo real

Visão Geral do Projeto

Existem 5 passos para este projeto:

  1. Transmitir transmissão ao vivo
  2. Capturar dados cerebrais
  3. Publique dados cerebrais como metadados cronometrados na transmissão ao vivo
  4. Reproduzir transmissão ao vivo
  5. Ouça metadados cronometrados e renderize dados cerebrais em um gráfico em tempo real

Se você preferir representações gráficas de tais coisas, veja como fica:

Construindo o Projeto

Eu usei o React para este projeto. Por quê? Bem, eu tenho muita experiência
com Vue e Angular, mas provavelmente sou um dos últimos desenvolvedores
earth para tentar React. Achei que era hora de descobrir o que tudo
o hype era sobre, e eu sabia que isso não seria uma tarefa difícil
projeto para construir com ele. Devido à minha falta de experiência anterior, não estou
o que você chamaria de usuário "avançado" da estrutura, mas devo dizer
que estou muito feliz com o que vejo até agora. achei o processo
agradável e não me vi "brigando" com o framework. Mas
esta postagem do blog não é sobre minha opinião sobre frameworks JavaScript, então vou
guarde isso para um post futuro. Em vez disso, vamos falar sobre como eu transmito
meu cérebro!

O hardware

No meu projeto original "Brain to the Cloud", usei um EEG "vintage"
um fone de ouvido chamado MindFlex para capturar minhas leituras cerebrais. Funcionou bastante
bem, mas exigiu que eu "hackeasse" o dispositivo adicionando um ESP-12
microcontrolador para extrair as leituras do dispositivo e enviar
eles para a nuvem. Desta vez, procurei algo um pouco mais novo -
e algo que eu poderia usar sem modificações. Depois de um pouco de
pesquisa, optei pela bandana Muse S. Felizmente, existe uma biblioteca de código aberto realmente incrível chamada muse-js que me permite acessar as leituras do cérebro diretamente em um navegador da Web com Web Bluetooth (em navegadores compatíveis, é claro).

A transmissão ao vivo

Até recentemente, a transmissão ao vivo com o Amazon IVS exigia que usássemos um
cliente de terceiros para transmitir nossos fluxos como RTMPS. Mas nós recentemente
lançou um divisor de águas: o Amazon IVS Web Broadcast SDK .
Como o nome indica, este SDK nos dá a capacidade de transmitir nosso
transmissão ao vivo via WebRTC diretamente de um navegador da web. Claramente, este foi um
ajuste perfeito para transmitir ao vivo meu cérebro, pois significa que posso criar
uma solução "tudo-em-um" para transmitir meus dados cerebrais junto com meu
transmissão ao vivo sem depender de software de terceiros ou scripts externos.

Adicionando transmissão da Web ao aplicativo React

Não vamos examinar cada etapa necessária para utilizar o
Web Broadcast SDK nesta postagem. Em vez disso, veremos os destaques para
ter uma ideia geral de como funciona. Não se preocupe - tenho outro post
em breve, onde vamos nos aprofundar no processo "passo a passo" para usar
o Web Broadcast SDK, portanto, fique atento a isso. Dito isso, vamos dar uma
jornada rápida para ver como usei o SDK neste projeto. meu primeiro passo
era usar uma transmissão na web para instalar o

 amazon-ivs-web-broadcast
módulo. Usando sua ferramenta de gerenciamento de pacotes favorita, execute:

 $ npm install amazon-ivs-web-broadcast

Em seguida, precisamos importá-lo para o nosso componente. No meu componente Broadcast.jsx, adicionei:

 import IVSBroadcastClient, { STANDARD_LANDSCAPE } from 'amazon-ivs-web-broadcast' ;

Podemos criar uma instância do IVSBroadcastClient com a configuração de fluxo desejada e ingerir o endpoint de nosso canal Amazon IVS e configurá-lo no estado de nosso componente.

 this .setState({  broadcastClient : IVSBroadcastClient.create({    streamConfig : STANDARD_LANDSCAPE,    ingestEndpoint : this .state.ingestEndpoint, }) });

Agora que temos uma instância do cliente, podemos adicionar nossa câmera ao cliente. Para isso usamos

 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 });

Adicionar o microfone do usuário ao cliente segue um padrão semelhante.

 const audioStream = await navigator.mediaDevices.getUserMedia({    audio : {        deviceId : this .state.selectedAudioDeviceId }, }); this .state.broadcastClient.addAudioInputDevice(audioStream, 'mic1' );
Observação: devido ao modelo de segurança do navegador, precisamos obter permissões para acessar a câmera e o microfone do usuário. Consulte a fonte do projeto no GitHub para obter mais informações sobre isso e para ver como capturei uma lista de
dispositivos e os apresentou em uma caixa de diálogo para permitir que o usuário escolha o
dispositivo de transmissão se várias opções estiverem disponíveis.

Agora podemos adicionar uma visualização ao vivo à página para que possamos ver o que nossos visualizadores verão no final das contas do jogador.

 <canvas ref={ this .previewRef} id= 'broadcast-preview' ></canvas>

E anexe a visualização ao

 broadcastClient
:

 this .state.broadcastClient.attachPreview( this .previewRef.current);

Para iniciar a transmissão, adicione um botão à página e, no

 onClick
manipulador para a chamada do botão
 startBroadcast()
no
 broadcastClient
(passando o necessário
 streamKey
).

 this .state.broadcastClient.startBroadcast( this .state.streamKey);

Obtendo meus dados cerebrais

Como mencionei acima, usei o

 muse-js
biblioteca, que fornece a capacidade de conectar-se à faixa de cabeça e extrair os dados brutos. No entanto,
 muse-js
não calcula as potências absolutas da banda para os dados EEG. Para isso, precisei buscar outra biblioteca :
 eeg-pipes
.

A primeira etapa é adicionar e importar as bibliotecas.

 $ npm install muse-js $ npm install @neurosity/pipes
 import { zipSamples, MuseClient } from 'muse-js' ; import { powerByBand, epoch, fft } from '@neurosity/pipes' ;

Em seguida, adicionei um botão com um manipulador de cliques. No manipulador, eu
conecte-se ao fone de ouvido, comece a ouvir os dados e assine o
fluxo.

 const client = new MuseClient(); await client.connect(); await client.start(); zipSamples(client.eegReadings) .pipe( epoch({ duration : 1024 , interval : 250 , samplingRate : 256 }), fft({ bins : 256 }), powerByBand(), ) .subscribe(    ( 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
 } );

Publicando meus dados cerebrais como metadados cronometrados

Agora que tenho um manipulador que coleta meus dados cerebrais do Muse
headband, é hora de publicar esses dados como metadados cronometrados no live
fluxo.

A coisa incrível sobre
 timed metadata
é que ele está diretamente incorporado no fluxo de vídeo e permanece uma parte permanente desse fluxo. Isso significa que existe mesmo em versões gravadas, o que significa que, mesmo na reprodução sob demanda, podemos ouvir e responder aos eventos.

O Web Broadcast SDK não oferece suporte à publicação de metadados cronometrados do lado do cliente, portanto, teremos que usar

 putMetadata
( docs ) por meio do AWS SDK para JavaScript . Para isso, criei uma função AWS Lambda.

 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 ; response.body = err.stack; callback( null , response);    return ; } };

Para publicar meus dados cerebrais como metadados cronometrados, criei um Amazon API Gateway para invocar a função e modificar o

 subscribe()
método acima para chamar a função AWS Lambda.

 zipSamples(client.eegReadings) .pipe( epoch({ duration : 1024 , interval : 250 , samplingRate : 256 }), fft({ bins : 256 }), powerByBand(), ) .subscribe(    ( 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) { fetch(LAMBDA_URL, {          'method' : 'POST' ,          'mode' : 'no-cors' ,          'headers' : {            'Content-Type' : 'application/json' , },          'body' : JSON .stringify({            channelArn : this .state.channelArn,            metadata : JSON .stringify(meta) }) }); } } );

Construindo a reprodução da transmissão ao vivo e mapeando meus dados cerebrais

Depois que a transmissão ao vivo com exibição de transmissão de dados cerebrais foi concluída, foi
tempo para criar uma experiência de reprodução que exibiria a transmissão ao vivo
e mapeie os dados do cérebro em tempo real conforme eles chegam por meio de metadados cronometrados.

Criando o reprodutor de transmissão ao vivo

Podemos usar o IVS Web Player SDK via NPM, mas como ele usa WebAssembly, as coisas podem ficar complicadas . Para evitar essa complicação, acho mais fácil usar o web player por meio de um

 <script>
tag e eu adicionei isso ao meu
 index.html
no meu aplicativo React.

 <script src= "https://player.live-video.net/1.12.0/amazon-ivs-player.min.js" ></script>

No meu

 Playback.jsx
componente, pego uma referência ao jogador e alguns elementos necessários.

 const { IVSPlayer } = window ; const { create : createMediaPlayer, isPlayerSupported, PlayerEventType, PlayerState } = IVSPlayer; const { ENDED, PLAYING, READY, BUFFERING } = PlayerState; const { TEXT_METADATA_CUE, ERROR } = PlayerEventType;

Para reprodução, usamos o nativo

 <video>
marcação.

 <video ref={ this .videoRef} controls playsInline></video>

E para inicializar o player e iniciar a reprodução:

 this .playerRef.current = createMediaPlayer(); this .playerRef.current.attachHTMLVideoElement( this .videoRef.current); this .playerRef.current.load(STREAM_URL); this .playerRef.current.play();

Ouvindo e respondendo a metadados cronometrados

Agora que estamos reproduzindo a transmissão ao vivo, podemos ouvir e responder aos dados cerebrais recebidos.

 this .playerRef.current.addEventListener(TEXT_METADATA_CUE, this .onPlayerMetadata);

Defina os dados do cérebro em nosso estado de componente:

 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 }); }); };

E renderize-o com um gráfico de barras (com Chart.js):

 <Bar data={ this .state.ch0} ref={ this .chartReferenceCh0} options={ {      aspectRatio : 1 ,      title : {        display : true ,        text : 'Channel: ' + channelNames[ 0 ] },        responsive : true ,        tooltips : {          enabled : false 
 },        legend : {          display : false 
 } } } />

A visualização é legal e certamente fornece uma maneira divertida de ver meu
dados cerebrais enquanto estou transmitindo um jogo ao vivo, mas não fornece uma tonelada de
contexto. Achei que faria sentido incluir alguns cálculos
para dar uma visão sobre o que os dados realmente significam. Para isso, encontrei
alguns cálculos no

 muse-lsl
projeto no GitHub
que incluiu algumas fórmulas que podem ser usadas para calcular fatores como
relaxamento (alfa dividido por delta) e concentração (beta dividido por
teta). Outra ótima postagem de blog que encontrei destacou uma maneira de derivar a fadiga ((teta + alfa) / beta). Envolvi esses cálculos em um componente útil e reutilizável.

 <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>  < 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 >
</Row>

Resumo

Neste post, vimos como criei um aplicativo React para viver
transmitir meus dados cerebrais com o Amazon IVS. Se você gostaria de saber mais sobre
Amazon IVS, confira a série Getting Started with Amazon Interactive Video Service aqui no dev.to. Se você estiver interessado em experimentar o aplicativo ou apenas verificar o código-fonte completo do aplicativo, verifique-o no GitHub . Seus comentários, perguntas e feedback são sempre bem-vindos, então deixe um comentário aqui ou conecte-se comigo no Twitter

links