paint-brush
Comment je diffuse en direct mon cerveau avec Amazon IVS, un bandeau Muse et Reactpar@amazonivs
4,671 lectures
4,671 lectures

Comment je diffuse en direct mon cerveau avec Amazon IVS, un bandeau Muse et React

Trop long; Pour lire

C'est la première fois que nous utilisons React pour diffuser en direct nos données cérébrales. Nous utilisons le SDK Amazon IVS Web Broadcast pour diffuser nos données cérébrales en direct sur le cloud. Nous n'allons pas examiner chaque étape pour utiliser le SDK dans cet article. Au lieu de cela, nous examinerons les faits saillants pour avoir une idée générale de la façon dont cela fonctionne. La première étape consiste à installer le module am-ivcast pour l'exécuter dans notre outil de diffusion : dans le module broadcast-broadcast, nous devons l'importer dans notre package préféré : dans l'outil de diffusion, nous avons une autre étape pour obtenir une autre étape dans le projet.
featured image - Comment je diffuse en direct mon cerveau avec Amazon IVS, un bandeau Muse et React
Amazon Interactive Video Service (IVS)  HackerNoon profile picture
0-item

Plus tôt cette année, j'ai créé un projet vraiment amusant que j'ai appelé "Brain
vers le cloud" où j'ai enregistré mes données cérébrales dans le cloud tout en jouant
Call of Duty afin que je puisse analyser la relation entre mon
fonctions cognitives et performances des jeux vidéo. J'ai écrit un trois parties
série d'articles de blog et créé des vidéos amusantes pour résumer mes découvertes sur
ce projet. Si vous souhaitez les consulter, vous pouvez consulter le
liens en bas de cet article. Quelques mois après avoir publié ça
projet, j'ai commencé à travailler chez Twitch en tant que développeur principal pour
Amazon Interactive Video Service (Amazon IVS) - une solution entièrement gérée
pour créer des solutions de streaming vidéo interactives en direct (consultez cette série pour en savoir plus). La prochaine étape de mon projet "Brain to the Cloud" était évidente : j'avais besoin de diffuser mon cerveau en direct.

Diffuser mon cerveau

Avant de regarder le code, voyons le produit final. Il ya deux
vues pour l'application : une vue de diffusion et une vue de lecture. Dans
la vue de diffusion, nous pouvons prévisualiser la vidéo en direct, démarrer le
diffusez et connectez le bandeau Muse pour diffuser les données cérébrales
obtenu à partir du bandeau. Dans la vue de lecture, nous affichons le live
diffuser avec un

 <video>
élément et tracer les données cérébrales en temps réel

Aperçu du projet

Il y a 5 étapes dans ce projet :

  1. Diffuser en direct
  2. Capturer les données cérébrales
  3. Publier des données cérébrales sous forme de métadonnées chronométrées dans le flux en direct
  4. Lecture en direct
  5. Écoutez les métadonnées chronométrées et affichez les données cérébrales dans un graphique en temps réel

Si vous préférez les représentations graphiques de telles choses, voici à quoi cela ressemble :

Construire le projet

J'ai utilisé React pour ce projet. Pourquoi? Eh bien, j'ai beaucoup d'expérience
avec Vue et Angular, mais je suis probablement l'un des derniers développeurs sur
terre pour essayer React. J'ai pensé qu'il était temps de comprendre ce que tout
le battage médiatique était sur, et je savais que ce ne serait pas difficile
projet de construire avec lui. En raison de mon manque d'expérience préalable, je ne suis pas
ce que vous appelleriez un utilisateur "avancé" du framework, mais je dois dire
que je suis assez content de ce que je vois jusqu'à présent. j'ai trouvé la procédure
agréable et ne me suis pas retrouvé "à me battre" avec le cadre. Mais
ce billet de blog ne concerne pas mon opinion sur les frameworks JavaScript, donc je vais
gardez ça pour un prochain article. Au lieu de cela, parlons de la façon dont je diffuse
mon cerveau!

Le matériel

Dans mon projet original "Brain to the Cloud", j'ai utilisé un EEG "vintage"
casque appelé MindFlex pour capturer mes lectures cérébrales. Cela a fonctionné assez
bien mais m'a obligé à "hacker" l'appareil en rajoutant un ESP-12
microcontrôleur afin de retirer les lectures de l'appareil et d'envoyer
vers le cloud. Cette fois, j'ai opté pour quelque chose de légèrement plus récent -
et quelque chose que je pourrais utiliser sans modifications. Après un peu de
recherches, j'ai opté pour le bandeau Muse S. Heureusement, il existe une bibliothèque open source vraiment géniale appelée muse-js qui me permet d'accéder aux lectures du cerveau directement dans un navigateur Web avec Web Bluetooth (dans les navigateurs pris en charge, bien sûr).

La diffusion en direct

Jusqu'à récemment, la diffusion en direct avec Amazon IVS nous obligeait à utiliser un
client tiers pour diffuser nos flux en RTMPS. Mais nous avons récemment
a lancé un jeu qui change la donne : le SDK Amazon IVS Web Broadcast .
Comme son nom l'indique, ce SDK nous permet de diffuser nos
diffusion en direct via WebRTC directement depuis un navigateur Web. De toute évidence, il s'agissait d'un
parfait pour diffuser en direct mon cerveau car cela signifie que je peux créer
une solution "tout-en-un" pour diffuser mes données cérébrales avec mes
diffuser en direct sans recourir à des logiciels tiers ou à des scripts externes.

Ajout de la diffusion Web à l'application React

Nous n'allons pas examiner chaque étape nécessaire pour utiliser le
SDK de diffusion Web dans cet article. Au lieu de cela, nous examinerons les faits saillants pour
avoir une idée générale de son fonctionnement. Ne vous inquiétez pas - j'ai un autre message
bientôt où nous approfondirons le processus "étape par étape" d'utilisation
le SDK de diffusion Web, alors restez à l'écoute pour cela. Cela dit, prenons un
voyage rapide pour voir comment j'ai utilisé le SDK dans ce projet. Ma première étape
était d'utiliser une diffusion Web pour installer le

 amazon-ivs-web-broadcast
module. À l'aide de votre outil de gestion de packages préféré, exécutez :

 $ npm install amazon-ivs-web-broadcast

Ensuite, nous devons l'importer dans notre composant. Dans mon composant Broadcast.jsx, j'ai ajouté :

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

Nous pouvons créer une instance de IVSBroadcastClient avec la configuration de flux souhaitée et ingérer le point de terminaison de notre canal Amazon IVS et le définir dans l'état de notre composant.

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

Maintenant que nous avons une instance du client, nous pouvons ajouter notre caméra au client. Pour cela nous utilisons

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

L'ajout du microphone de l'utilisateur au client suit un schéma similaire.

 const audioStream = await navigator.mediaDevices.getUserMedia({    audio : {        deviceId : this .state.selectedAudioDeviceId }, }); this .state.broadcastClient.addAudioInputDevice(audioStream, 'mic1' );
Remarque : En raison du modèle de sécurité du navigateur, nous devons obtenir des autorisations pour accéder à la caméra et au microphone de l'utilisateur. Reportez-vous à la source du projet sur GitHub pour plus d'informations à ce sujet et pour voir comment j'ai capturé une liste de
périphériques et les a présentés dans une boîte de dialogue pour permettre à l'utilisateur de choisir le
périphérique de diffusion si plusieurs options sont disponibles.

Nous pouvons maintenant ajouter un aperçu en direct à la page afin que nous puissions voir ce que nos téléspectateurs verront finalement du côté des joueurs.

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

Et joignez l'aperçu au

 broadcastClient
:

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

Pour démarrer la diffusion, ajoutez un bouton à la page, et dans le

 onClick
gestionnaire pour l'appel du bouton
 startBroadcast()
sur le
 broadcastClient
(passer le nécessaire
 streamKey
).

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

Obtenir mes données cérébrales

Comme je l'ai mentionné plus haut, j'ai utilisé le

 muse-js
bibliothèque, qui offre la possibilité de se connecter au bandeau et d'extraire les données brutes. Cependant,
 muse-js
ne calcule pas les puissances de bande absolues pour les données EEG. Pour cela, j'avais besoin d'atteindre une autre bibliothèque :
 eeg-pipes
.

La première étape consiste à ajouter et importer les bibliothèques.

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

Ensuite, j'ai ajouté un bouton avec un gestionnaire de clic. Dans le gestionnaire, je
connectez-vous au casque, commencez à écouter les données et abonnez-vous au
flux.

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

Publier mes données cérébrales en tant que métadonnées chronométrées

Maintenant que j'ai un gestionnaire qui collecte mes données cérébrales auprès de la Muse
bandeau, il est temps de publier ces données sous forme de métadonnées chronométrées dans le live
flux.

La chose géniale à propos de
 timed metadata
est qu'il est directement intégré dans le flux vidéo et reste une partie permanente de ce flux. Cela signifie qu'il existe même dans les versions enregistrées, ce qui signifie que même en lecture à la demande, nous pouvons écouter et répondre aux événements.

Le SDK Web Broadcast ne prend pas en charge la publication de métadonnées chronométrées du côté client, nous devrons donc utiliser

 putMetadata
( docs ) via le kit AWS SDK pour JavaScript . Pour cela, j'ai créé une fonction 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 ; } };

Pour publier mes données cérébrales sous forme de métadonnées chronométrées, j'ai créé une passerelle d'API Amazon pour appeler la fonction et modifier le

 subscribe()
méthode ci-dessus pour appeler la fonction 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) }) }); } } );

Construire la lecture du flux en direct et tracer mes données cérébrales

Une fois le flux en direct avec la vue de diffusion des données cérébrales terminé, il était
le temps de créer une expérience de lecture qui afficherait le flux en direct
et tracez les données cérébrales en temps réel au fur et à mesure qu'elles arrivent via des métadonnées chronométrées.

Création du lecteur de diffusion en direct

Nous pouvons utiliser le SDK IVS Web Player via NPM, mais comme il utilise WebAssembly, les choses peuvent devenir délicates . Pour éviter cette astuce, je trouve plus simple d'utiliser le player web via un

 <script>
tag et je l'ai ajouté à mon
 index.html
dans mon application React.

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

Dans mon

 Playback.jsx
composant, je saisis une référence au joueur et quelques éléments nécessaires.

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

Pour la lecture, nous utilisons le natif

 <video>
étiquette.

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

Et pour initialiser le lecteur et lancer la lecture :

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

Écouter et répondre aux métadonnées chronométrées

Maintenant que nous jouons le flux en direct, nous pouvons écouter et répondre aux données cérébrales entrantes.

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

Définissez les données cérébrales dans notre état de composant :

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

Et rendez-le avec un graphique à barres (avec 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 visualisation est cool et offre certainement une façon amusante de voir mon
données cérébrales pendant que je diffuse en direct un jeu, mais ne fournit pas une tonne de
le contexte. J'ai donc pensé qu'il serait logique d'inclure quelques calculs
pour donner un aperçu de ce que les données signifient réellement. Pour cela, j'ai trouvé
certains calculs dans

 muse-lsl
projet sur GitHub
qui comprenait des formules qui peuvent être utilisées pour calculer des facteurs tels que
relaxation (alpha divisé par delta) et concentration (bêta divisé par
thêta). Un autre excellent article de blog que j'ai trouvé a mis en évidence un moyen de dériver la fatigue ((thêta + alpha) / bêta). J'ai enveloppé ces calculs dans un composant pratique et réutilisable.

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

Sommaire

Dans cet article, nous avons vu comment j'ai créé une application React pour vivre
diffuser mes données cérébrales avec Amazon IVS. Si vous souhaitez en savoir plus sur
Amazon IVS, veuillez consulter la série Premiers pas avec Amazon Interactive Video Service ici sur dev.to. Si vous souhaitez essayer l'application ou simplement consulter la source complète de l'application, consultez-la sur GitHub . Vos commentaires, questions et commentaires sont toujours les bienvenus, alors laissez un commentaire ici ou connectez-vous avec moi sur Twitter

Liens