paint-brush
Cách tôi phát trực tiếp bộ não của mình với Amazon IVS, Muse Headband và Reactby@amazonivs
4,568
4,568

Cách tôi phát trực tiếp bộ não của mình với Amazon IVS, Muse Headband và React

Đây là lần đầu tiên chúng tôi sử dụng React để phát sóng trực tiếp dữ liệu não bộ của mình. Chúng tôi sử dụng Amazon IVS Web Broadcast SDK để phát dữ liệu não bộ trực tiếp của mình lên đám mây. Chúng tôi sẽ không xem xét từng bước để sử dụng SDK trong bài đăng này. Thay vào đó, chúng ta sẽ xem xét những điểm nổi bật để có ý tưởng chung về cách thức hoạt động của nó. Bước đầu tiên là cài đặt mô-đun am-ivcast để chạy vào công cụ phát sóng của chúng tôi: Trong mô-đun quảng bá-phát sóng, chúng tôi cần nhập nó vào gói yêu thích của mình: Trong công cụ phát sóng, chúng tôi có một bước nữa để có một bước khác vào dự án.
featured image - Cách tôi phát trực tiếp bộ não của mình với Amazon IVS, Muse Headband và React
Amazon Interactive Video Service (IVS)  HackerNoon profile picture
0-item

Đầu năm nay, tôi đã tạo một dự án thực sự thú vị mà tôi gọi là "Brain
to the Cloud" nơi tôi đã lưu dữ liệu bộ não của mình lên đám mây khi chơi
Call of Duty để tôi có thể phân tích mối quan hệ giữa
chức năng nhận thức và hiệu suất trò chơi video. Tôi đã viết lên một phần ba
loạt bài đăng trên blog và tạo một số video thú vị để tóm tắt những phát hiện của tôi về
dự án đó. Nếu bạn muốn kiểm tra chúng, bạn có thể tham khảo
các liên kết ở dưới cùng của bài viết này. Một vài tháng sau khi tôi xuất bản nó
dự án, tôi bắt đầu làm việc tại Twitch với tư cách là Người hỗ trợ phát triển chính cho
Amazon Interactive Video Service (Amazon IVS) - một giải pháp được quản lý toàn phần
để tạo các giải pháp truyền phát video trực tiếp, tương tác (hãy xem loạt bài này để tìm hiểu thêm). Bước tiếp theo trong dự án "Brain to the Cloud" của tôi rất rõ ràng - tôi cần phát trực tiếp bộ não của mình.

Phát sóng bộ não của tôi

Trước khi xem mã, hãy xem sản phẩm cuối cùng. Có 2
chế độ xem cho ứng dụng: chế độ xem phát sóng và chế độ xem phát lại. Trong
chế độ xem phát sóng, chúng tôi có thể xem trước video trực tiếp, bắt đầu
phát sóng và kết nối băng đô Muse để truyền dữ liệu não
thu được từ băng đô. Trong chế độ xem phát lại, chúng tôi hiển thị trực tiếp
phát trực tuyến với một

 <video>
yếu tố và biểu đồ dữ liệu não trong thời gian thực

Tổng quan dự án

Có 5 bước cho dự án này:

  1. Phát sóng trực tiếp
  2. Chụp dữ liệu não
  3. Xuất bản dữ liệu não dưới dạng siêu dữ liệu theo thời gian trong luồng trực tiếp
  4. Phát lại luồng trực tiếp
  5. Nghe siêu dữ liệu theo thời gian và hiển thị dữ liệu não trong biểu đồ trong thời gian thực

Nếu bạn thích mô tả đồ họa của những thứ như vậy, đây là hình thức của nó:

xây dựng dự án

Tôi đã sử dụng React cho dự án này. Tại sao? Vâng, tôi đã có rất nhiều kinh nghiệm
với Vue và Angular, nhưng có lẽ tôi là một trong những nhà phát triển cuối cùng trên
trái đất để thử React. Tôi hình dung đã đến lúc tìm ra tất cả những gì
sự cường điệu đã xảy ra, và tôi biết rằng điều này sẽ không khó
dự án để xây dựng với nó. Do thiếu kinh nghiệm trước đây, tôi không
những gì bạn gọi là người dùng "nâng cao" của khung, nhưng tôi phải nói
rằng tôi khá hài lòng với những gì tôi thấy cho đến nay. Tôi tìm thấy quá trình
thú vị và không thấy mình "chiến đấu" với khuôn khổ. Nhưng mà
bài đăng trên blog này không phải là quan điểm của tôi về các khung JavaScript, vì vậy tôi sẽ
lưu nó cho một bài viết trong tương lai. Thay vào đó, hãy nói về cách tôi phát sóng
não của tôi!

phần cứng

Trong dự án "Brain to the Cloud" ban đầu của tôi, tôi đã sử dụng điện não đồ "cổ điển"
tai nghe có tên là MindFlex để ghi lại các chỉ số não bộ của tôi. Nó hoạt động khá
tốt nhưng yêu cầu tôi "hack" thiết bị bằng cách thêm một chiếc ESP-12
vi điều khiển để lấy số đọc ra khỏi thiết bị và gửi
chúng lên đám mây. Lần này tôi với tới một cái gì đó mới hơn một chút -
và một cái gì đó mà tôi có thể sử dụng mà không cần sửa đổi. sau một chút
nghiên cứu, tôi quyết định chọn Muse S Headband . Rất may, có một thư viện mã nguồn mở thực sự tuyệt vời được gọi là muse-js cho phép tôi truy cập trực tiếp vào các bài đọc não trong trình duyệt web bằng Web Bluetooth (tất nhiên là trong các trình duyệt được hỗ trợ).

Phát sóng trực tiếp

Cho đến gần đây, phát trực tiếp với Amazon IVS yêu cầu chúng tôi sử dụng
ứng dụng khách bên thứ ba để phát các luồng của chúng tôi dưới dạng RTMPS. Nhưng chúng tôi gần đây
đã ra mắt công cụ thay đổi cuộc chơi: Amazon IVS Web Broadcast SDK .
Như tên ngụ ý, SDK này cung cấp cho chúng tôi khả năng phát sóng
phát trực tiếp qua WebRTC trực tiếp từ trình duyệt web. Rõ ràng, đây là một
hoàn toàn phù hợp để phát trực tiếp bộ não của tôi vì điều đó có nghĩa là tôi có thể tạo
một giải pháp "tất cả trong một" để phát dữ liệu não của tôi cùng với
phát trực tiếp mà không cần dựa vào phần mềm của bên thứ ba hoặc tập lệnh bên ngoài.

Thêm Web Broadcast vào ứng dụng React

Chúng tôi sẽ không xem xét từng bước cần thiết để sử dụng
Web Broadcast SDK trong bài đăng này. Thay vào đó, chúng ta sẽ xem xét những điểm nổi bật để
có được một ý tưởng chung về cách nó hoạt động. Đừng lo lắng - tôi đã có một bài viết khác
sắp ra mắt, nơi chúng tôi sẽ đào sâu vào quy trình "từng bước" để sử dụng
Web Broadcast SDK, vì vậy hãy chú ý theo dõi điều đó. Điều đó nói rằng, chúng ta hãy lấy một
hành trình nhanh để xem cách tôi sử dụng SDK trong dự án này. bước đầu tiên của tôi
là sử dụng một chương trình phát sóng trên web để cài đặt

 amazon-ivs-web-broadcast
mô-đun. Sử dụng công cụ quản lý gói yêu thích của bạn, hãy chạy:

 $ npm install amazon-ivs-web-broadcast

Tiếp theo, chúng ta cần nhập nó vào thành phần của mình. Trong thành phần Broadcast.jsx của tôi, tôi đã thêm:

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

Chúng tôi có thể tạo một phiên bản IVSBroadcastClient với cấu hình luồng mong muốn và nhập điểm cuối từ kênh Amazon IVS của chúng tôi và đặt nó vào trạng thái của thành phần.

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

Bây giờ chúng ta đã có một phiên bản của ứng dụng khách, chúng ta có thể thêm camera của mình vào ứng dụng khách. Đối với điều này, chúng tôi sử dụng

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

Việc thêm micrô của người dùng vào ứng dụng khách theo mô hình tương tự.

 const audioStream = await navigator.mediaDevices.getUserMedia({    audio : {        deviceId : this .state.selectedAudioDeviceId }, }); this .state.broadcastClient.addAudioInputDevice(audioStream, 'mic1' );
Lưu ý: Do mô hình bảo mật của trình duyệt, chúng tôi cần có quyền truy cập vào máy ảnh và micrô của người dùng. Tham khảo nguồn dự án trên GitHub để biết thêm thông tin về điều này và để xem cách tôi nắm bắt danh sách
thiết bị và trình bày chúng trong hộp thoại để cho phép người dùng chọn
thiết bị phát sóng nếu có nhiều tùy chọn.

Giờ đây, chúng tôi có thể thêm bản xem trước trực tiếp vào trang để chúng tôi có thể xem những gì người xem cuối cùng sẽ thấy ở phía người chơi.

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

Và đính kèm bản xem trước vào

 broadcastClient
:

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

Để bắt đầu truyền phát, hãy thêm một nút vào trang và trong

 onClick
trình xử lý cho cuộc gọi nút
 startBroadcast()
trên
 broadcastClient
(vượt qua cần thiết
 streamKey
).

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

Lấy dữ liệu não của tôi

Như tôi đã đề cập ở trên, tôi đã sử dụng

 muse-js
thư viện, cung cấp khả năng kết nối với băng đô và lấy dữ liệu thô. Tuy nhiên,
 muse-js
không tính toán công suất dải tuyệt đối cho dữ liệu điện não đồ. Đối với điều này, tôi cần tiếp cận với một thư viện khác :
 eeg-pipes
.

Bước đầu tiên là thêm và nhập các thư viện.

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

Tiếp theo, tôi đã thêm một nút có trình xử lý nhấp chuột. Trong trình xử lý, tôi
kết nối với tai nghe, bắt đầu nghe dữ liệu và đăng ký
dòng.

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

Xuất bản dữ liệu não của tôi dưới dạng siêu dữ liệu theo thời gian

Bây giờ tôi đã có một trình xử lý thu thập dữ liệu não bộ của tôi từ Muse
headband, đã đến lúc xuất bản dữ liệu đó dưới dạng siêu dữ liệu theo thời gian trong chương trình trực tiếp
dòng.

Điều tuyệt vời về
 timed metadata
là nó được nhúng trực tiếp vào luồng video và vẫn là một phần vĩnh viễn của luồng đó. Điều đó có nghĩa là nó tồn tại ngay cả trong các phiên bản được ghi lại, nghĩa là ngay cả khi phát lại theo yêu cầu, chúng tôi có thể nghe và phản hồi các sự kiện.

Web Broadcast SDK không hỗ trợ xuất bản siêu dữ liệu theo thời gian từ phía máy khách, vì vậy chúng tôi sẽ phải sử dụng

 putMetadata
( tài liệu ) qua AWS SDK dành cho JavaScript . Đối với điều này, tôi đã tạo một hàm 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 ; } };

Để xuất bản dữ liệu bộ não của tôi dưới dạng siêu dữ liệu theo thời gian, tôi đã tạo một Cổng API Amazon để gọi hàm và sửa đổi

 subscribe()
phương pháp trên để gọi hàm 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) }) }); } } );

Xây dựng phát lại luồng trực tiếp và lập biểu đồ dữ liệu não của tôi

Sau khi luồng trực tiếp với chế độ xem phát dữ liệu não hoàn tất, nó đã được
thời gian để tạo trải nghiệm phát lại sẽ hiển thị luồng trực tiếp
và lập biểu đồ dữ liệu não trong thời gian thực khi nó đến thông qua siêu dữ liệu được định thời gian.

Tạo trình phát trực tiếp

Chúng ta có thể sử dụng IVS Web Player SDK thông qua NPM, nhưng vì nó sử dụng WebAssembly nên mọi thứ có thể trở nên rắc rối . Để tránh sự phức tạp đó, tôi thấy việc sử dụng trình phát trên web dễ dàng hơn thông qua một

 <script>
thẻ và tôi đã thêm nó vào
 index.html
trong ứng dụng React của tôi.

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

trong tôi

 Playback.jsx
thành phần, tôi lấy một tham chiếu đến trình phát và một số thành phần cần thiết.

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

Để phát lại, chúng tôi sử dụng bản gốc

 <video>
nhãn.

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

Và để khởi tạo trình phát và bắt đầu phát lại:

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

Lắng nghe và phản hồi siêu dữ liệu theo thời gian

Bây giờ chúng tôi đang phát luồng trực tiếp, chúng tôi có thể lắng nghe và phản hồi dữ liệu não đến.

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

Đặt dữ liệu não vào trạng thái thành phần của chúng tôi:

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

Và hiển thị nó bằng biểu đồ thanh (với 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 
 } } } />

Hình ảnh trực quan rất thú vị và chắc chắn cung cấp một cách thú vị để xem
dữ liệu não bộ khi tôi đang phát trực tiếp trò chơi, nhưng không cung cấp nhiều
định nghĩa bài văn. Vì vậy, tôi nghĩ rằng sẽ hợp lý hơn nếu bao gồm một số tính toán
để cung cấp cái nhìn sâu sắc về ý nghĩa thực sự của dữ liệu. Đối với điều đó, tôi tìm thấy
một số tính toán trong

 muse-lsl
dự án trên GitHub
trong đó bao gồm một số công thức có thể được sử dụng để tính toán các yếu tố như
thư giãn (alpha chia cho delta) và nồng độ (beta chia cho
theta). Một bài đăng blog tuyệt vời khác mà tôi thấy đã nêu bật một cách để giảm mệt mỏi ((theta + alpha)/beta). Tôi gói những tính toán này trong một thành phần tiện dụng, có thể tái sử dụng.

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

Bản tóm tắt

Trong bài đăng này, chúng tôi đã xem cách tôi tạo một ứng dụng React để hoạt động
truyền dữ liệu não bộ của tôi với Amazon IVS. Nếu bạn muốn tìm hiểu thêm về
Amazon IVS, vui lòng xem loạt bài Bắt đầu với Amazon Interactive Video Service tại đây trên dev.to. Nếu bạn quan tâm đến việc dùng thử ứng dụng hoặc chỉ cần xem nguồn đầy đủ của ứng dụng, hãy xem nó trên GitHub . Nhận xét, câu hỏi và phản hồi của bạn luôn được chào đón, vì vậy hãy để lại nhận xét tại đây hoặc kết nối với tôi trên Twitter

liên kết