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 pour en savoir plus). La prochaine étape de mon projet "Brain to the Cloud" était évidente : j'avais besoin de mon cerveau en direct. cette série diffuser 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 élément et tracer les données cérébrales en temps réel <video> Aperçu du projet Il y a 5 étapes dans ce projet : Diffuser en direct Capturer les données cérébrales Publier des données cérébrales sous forme de métadonnées chronométrées dans le flux en direct Lecture en direct É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 Heureusement, il existe une bibliothèque open source vraiment géniale appelée 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). bandeau Muse S. muse-js La diffusion en direct Jusqu'à récemment, la diffusion en direct avec 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 . 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. Amazon SDK Amazon IVS Web Broadcast 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 module. À l'aide de votre outil de gestion de packages préféré, exécutez : amazon-ivs-web-broadcast $ 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' ); 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 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. Remarque : source du projet sur GitHub 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 gestionnaire pour l'appel du bouton sur le (passer le nécessaire ). onClick startBroadcast() broadcastClient 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 bibliothèque, qui offre la possibilité de se connecter au bandeau et d'extraire les données brutes. Cependant, ne calcule pas les puissances de bande absolues pour les données EEG. Pour cela, j'avais besoin d'atteindre une : . muse-js muse-js autre bibliothèque eeg-pipes La première étape consiste à ajouter et importer les bibliothèques. $ npm install @neurosity/pipes $ npm install muse-js 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. 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 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 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. 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 ( ) via le . Pour cela, j'ai créé une fonction AWS Lambda. putMetadata docs kit AWS SDK pour JavaScript }); }, }; } } } } }; } 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 ; 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 méthode ci-dessus pour appeler la fonction 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) 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, . Pour éviter cette astuce, je trouve plus simple d'utiliser le player web via un tag et je l'ai ajouté à mon dans mon application React. les choses peuvent devenir délicates <script> index.html <script src= "https://player.live-video.net/1.12.0/amazon-ivs-player.min.js" ></script> Dans mon composant, je saisis une référence au joueur et quelques éléments nécessaires. Playback.jsx 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 étiquette. <video> <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): 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 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 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 un moyen de dériver la fatigue ((thêta + alpha) / bêta). J'ai enveloppé ces calculs dans un composant pratique et réutilisable. muse-lsl projet sur GitHub excellent article de blog que j'ai trouvé a mis en évidence </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 > 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 ici sur dev.to. Si vous souhaitez essayer l'application ou simplement consulter la source complète de l'application, consultez-la sur . Vos commentaires, questions et commentaires sont toujours les bienvenus, alors laissez un commentaire ici ou connectez-vous avec moi sur Premiers pas avec Amazon Interactive Video Service GitHub Twitter Liens Brain to the Cloud - Partie I - Présentation du projet et aperçu de l'architecture Brain to the Cloud - Partie II - Comment j'ai téléchargé mon cerveau sur le cloud Brain to the Cloud - Partie III - Examen de la relation entre l'activité cérébrale et les performances des jeux vidéo Code source du projet Première publication . ici