A principios de este año, creé un proyecto muy divertido que llamé "Brain
a la nube", donde guardé los datos de mi cerebro en la nube mientras jugaba
Call of Duty para poder analizar la relación entre mi
Función cognitiva y rendimiento en videojuegos. Escribí una de tres partes
serie de publicaciones de blog y creé algunos videos divertidos para resumir mis hallazgos sobre
ese proyecto Si desea comprobarlos, puede consultar el
enlaces en la parte inferior de esta publicación. Unos meses después de que publiqué eso
proyecto, comencé a trabajar en Twitch como desarrollador defensor principal para
Amazon Interactive Video Service (Amazon IVS): una solución completamente administrada
para crear soluciones de transmisión de video interactivas en vivo (consulte esta serie para obtener más información). El siguiente paso de mi proyecto "Brain to the Cloud" era obvio: necesitaba transmitir en vivo mi cerebro.
Antes de mirar el código, veamos el producto final. Hay 2
vistas para la aplicación: una vista de transmisión y una vista de reproducción. En
la vista de transmisión, podemos obtener una vista previa del video en vivo, iniciar el
transmitir y conectar la diadema Muse para transmitir los datos del cerebro
obtenido de la diadema. En la vista de reproducción, mostramos el vivo
corriente con un
<video>
elemento, y trazar los datos del cerebro en tiempo real Hay 5 pasos para este proyecto:
Si prefiere representaciones gráficas de tales cosas, así es como se ve:
Usé React para este proyecto. ¿Por qué? Bueno, tengo mucha experiencia.
con Vue y Angular, pero probablemente sea uno de los últimos desarrolladores en
tierra para probar React. Supuse que ya era hora de descubrir qué era todo
el bombo estaba a punto, y yo sabía que esto no sería difícil
proyecto a construir con él. Debido a mi falta de experiencia previa, no estoy
lo que llamarías un usuario "avanzado" del marco, pero tengo que decir
que estoy bastante contento con lo que veo hasta ahora. encontré el proceso
agradable y no me encontré "luchando" con el marco. Pero
esta publicación de blog no se trata de mi opinión sobre los marcos de JavaScript, así que
guárdalo para una publicación futura. En su lugar, hablemos de cómo transmito
¡mi cerebro!
En mi proyecto original "Brain to the Cloud", usé un EEG "antiguo"
auricular llamado MindFlex para capturar las lecturas de mi cerebro. funcionó bastante
bueno, pero me requirió "hackear" el dispositivo agregando un ESP-12
microcontrolador para extraer las lecturas del dispositivo y enviarlas
ellos a la nube. Esta vez busqué algo un poco más nuevo:
y algo que podría usar sin modificaciones. Después de un poco de
investigación, me decidí por la diadema Muse S. Afortunadamente, hay una biblioteca de código abierto realmente increíble llamada muse-js que me permite acceder a las lecturas cerebrales directamente en un navegador web con Web Bluetooth (en navegadores compatibles, por supuesto).
Hasta hace poco, la transmisión en vivo con Amazon IVS requería que usáramos un
cliente de terceros para transmitir nuestras transmisiones como RTMPS. Pero recientemente
lanzó un cambio de juego: el SDK de transmisión web de Amazon IVS .
Como su nombre lo indica, este SDK nos brinda la capacidad de transmitir nuestro
transmisión en vivo a través de WebRTC directamente desde un navegador web. Claramente, esto fue un
ajuste perfecto para la transmisión en vivo de mi cerebro, ya que significa que puedo crear
una solución "todo en uno" para transmitir los datos de mi cerebro junto con mi
transmisión en vivo sin depender de software de terceros o scripts externos.
Agregar transmisión web a la aplicación React
No vamos a ver cada uno de los pasos necesarios para utilizar el
Web Broadcast SDK en esta publicación. En su lugar, veremos los aspectos más destacados para
obtener una idea general de cómo funciona. No te preocupes, tengo otra publicación.
próximamente donde profundizaremos en el proceso "paso a paso" para usar
el SDK de Web Broadcast, así que estad atentos. Dicho esto, tomemos un
recorrido rápido para ver cómo utilicé el SDK en este proyecto. mi primer paso
fue usar una transmisión web para instalar el
amazon-ivs-web-broadcast
módulo. Con su herramienta de administración de paquetes favorita, ejecute: $ npm install amazon-ivs-web-broadcast
A continuación, debemos importarlo a nuestro componente. En mi componente Broadcast.jsx, agregué:
import IVSBroadcastClient, { STANDARD_LANDSCAPE } from 'amazon-ivs-web-broadcast' ;
Podemos crear una instancia de IVSBroadcastClient con la configuración de flujo deseada e ingerir el punto final de nuestro canal Amazon IVS y establecerlo en el estado de nuestro componente.
this .setState({ broadcastClient : IVSBroadcastClient.create({ streamConfig : STANDARD_LANDSCAPE, ingestEndpoint : this .state.ingestEndpoint, }) });
Ahora que tenemos una instancia del cliente, podemos agregar nuestra cámara al cliente. Para esto 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 });
Agregar el micrófono del usuario al cliente sigue un patrón similar.
const audioStream = await navigator.mediaDevices.getUserMedia({ audio : { deviceId : this .state.selectedAudioDeviceId }, }); this .state.broadcastClient.addAudioInputDevice(audioStream, 'mic1' );
Nota: debido al modelo de seguridad del navegador, necesitamos obtener permisos para acceder a la cámara y al micrófono del usuario. Consulte la fuente del proyecto en GitHub para obtener más información sobre esto y para ver cómo capturé una lista de
dispositivos y los presentó en un cuadro de diálogo para permitir al usuario elegir el
dispositivo de transmisión si hay varias opciones disponibles.
Ahora podemos agregar una vista previa en vivo a la página para que podamos ver lo que nuestros espectadores finalmente verán en el lado del jugador.
<canvas ref={ this .previewRef} id= 'broadcast-preview' ></canvas>
Y adjunte la vista previa a la
broadcastClient
: this .state.broadcastClient.attachPreview( this .previewRef.current);
Para iniciar la transmisión, agregue un botón a la página, y en el
onClick
controlador para la llamada de botón startBroadcast()
sobre el broadcastClient
(pasando lo necesario streamKey
). this .state.broadcastClient.startBroadcast( this .state.streamKey);
Como mencioné anteriormente, usé el
muse-js
biblioteca, que brinda la capacidad de conectarse a la banda para la cabeza y extraer los datos sin procesar. Sin embargo, muse-js
no calcula las potencias de banda absolutas para los datos de EEG. Para esto, necesitaba buscar otra biblioteca : eeg-pipes
.El primer paso es agregar e importar las bibliotecas.
$ npm install muse-js $ npm install @neurosity/pipes
import { zipSamples, MuseClient } from 'muse-js' ; import { powerByBand, epoch, fft } from '@neurosity/pipes' ;
A continuación, agregué un botón con un controlador de clic. En el controlador, yo
conéctese a los auriculares, comience a escuchar datos y suscríbase al
corriente.
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
} );
Ahora que tengo un controlador que recopila datos de mi cerebro de Muse
banda para la cabeza, es hora de publicar esos datos como metadatos cronometrados en vivo
corriente.
Lo maravilloso dees que está directamente incrustado en la transmisión de video y sigue siendo una parte permanente de esa transmisión. Eso significa que existe incluso en versiones grabadas, lo que significa que incluso en la reproducción bajo demanda podemos escuchar y responder a los eventos.timed metadata
El SDK de Web Broadcast no admite la publicación de metadatos cronometrados desde el lado del cliente, por lo que tendremos que usar
putMetadata
( docs ) a través de AWS SDK para JavaScript . Para esto, creé una función 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 mis datos cerebrales como metadatos cronometrados, creé un Amazon API Gateway para invocar la función y modificar el
subscribe()
método anterior para llamar a la función 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) }) }); } } );
Una vez que se completó la transmisión en vivo con la vista de transmisión de datos cerebrales, se
tiempo para crear una experiencia de reproducción que muestre la transmisión en vivo
y registre los datos cerebrales en tiempo real tal como llegaron a través de metadatos cronometrados.
Crear el reproductor de transmisión en vivo
Podemos usar el IVS Web Player SDK a través de NPM, pero dado que usa WebAssembly, las cosas pueden complicarse . Para evitar ese engaño, me resulta más fácil usar el reproductor web a través de un
<script>
etiqueta y lo agregué a mi index.html
en mi aplicación React. <script src= "https://player.live-video.net/1.12.0/amazon-ivs-player.min.js" ></script>
En mi
Playback.jsx
componente, agarro una referencia al jugador y algunos elementos necesarios. const { IVSPlayer } = window ; const { create : createMediaPlayer, isPlayerSupported, PlayerEventType, PlayerState } = IVSPlayer; const { ENDED, PLAYING, READY, BUFFERING } = PlayerState; const { TEXT_METADATA_CUE, ERROR } = PlayerEventType;
Para la reproducción, usamos el nativo
<video>
etiqueta. <video ref={ this .videoRef} controls playsInline></video>
Y para inicializar el reproductor y comenzar la reproducción:
this .playerRef.current = createMediaPlayer(); this .playerRef.current.attachHTMLVideoElement( this .videoRef.current); this .playerRef.current.load(STREAM_URL); this .playerRef.current.play();
Ahora que estamos reproduciendo la transmisión en vivo, podemos escuchar y responder a los datos cerebrales entrantes.
this .playerRef.current.addEventListener(TEXT_METADATA_CUE, this .onPlayerMetadata);
Establezca los datos del cerebro en nuestro 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 }); }); };
Y renderícelo con un gráfico de barras (con 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
} } } />
La visualización es genial y ciertamente proporciona una forma divertida de ver mi
datos cerebrales mientras estoy transmitiendo en vivo un juego, pero no proporciona una tonelada de
contexto. Así que pensé que tendría sentido incluir algunos cálculos
para dar una idea de lo que los datos realmente significan. Por eso encontré
algunos cálculos en el
muse-lsl
proyecto en GitHub <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>
En esta publicación, vimos cómo creé una aplicación React para vivir
transmitir mis datos cerebrales con Amazon IVS. Si desea obtener más información sobre
Amazon IVS, consulte la serie Getting Started with Amazon Interactive Video Service aquí en dev.to. Si está interesado en probar la aplicación o simplemente consultar el código fuente completo de la aplicación, compruébelo en GitHub . Sus comentarios, preguntas y comentarios siempre son bienvenidos, así que deje un comentario aquí o conéctese conmigo en Twitter
Publicado por primera vezaquí .