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.
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 Il y a 5 étapes dans ce projet :
Si vous préférez les représentations graphiques de telles choses, voici à quoi cela ressemble :
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!
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).
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);
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
} );
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 deest 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.timed metadata
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) }) }); } } );
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();
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 <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>
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
Première publicationici .