How to Upload Audio Stream to Amazon S3
author By Trilok Nagvenkar
Blog

How to Upload Audio Stream to Amazon S3

author By Trilok Nagvenkar Jan 10, 2019
aws-s3-audio-streaming
Submit guest post

Through this blog, I’ll take you through the tutorial on uploading AWS live audio streaming to AWS S3 audio streaming using AWS SDK.

We will use certain services of AWS which includes Amazon Cognito Identity Pools (federated identities), and S3 ofcourse.

AWS Configurations

Assuming you have logged into the AWS console, let us get started by creating a S3 Bucket, where all the audio files will be stored. To create the bucket, navigate to AWS S3 -> Create bucket

Once the bucket is created, our next step is to create a Federated Identity which provides the necessary permission for a file upload from browser to S3 bucket.

To create the Federated Identity please navigate to the Cognito service - > Manage identity pools > Create new identity.

Give the Identity pool name and check the Enable access to unauthenticated identities Or Authentication Providers.

The next screen is all about setting the necessary permission for the Federated Identity via IAM roles. Here, we will create a new IAM role with specific permission defined via custom policy as policy mentioned below:

 

{

 

"Version": "2012-10-17",

 

"Statement": [

 

{

 

"Effect": "Allow",

 

"Action": [

 

"s3:PutObject",

 

"s3:GetObject",

 

"s3:ListMultipartUploadParts",

 

"s3:ListBucketMultipartUploads"

 

],

 

"Resource": "arn:aws:s3:::S3_BUCKET_NAME/*"

 

}

 

]

 

}

view rawpolicy.json hosted with ❤ by GitHub

Post creation, it will provide the Identity Pool Id. That ID is required to communicate with AWS services.

Front-end App

Now we will create a small front-end app to record and upload audio stream to S3.

HTML:

 

<button type="button" class="btn kc record" id="record_q1" disabled="disabled" onclick="AudioStream.startRecording(this.id)">Record</button>

 

<button type="button" class="btn kc stop" id="stop_q1" disabled="disabled" onclick="AudioStream.stopRecording(this.id)">Stop</button>

view rawindex.html hosted with ❤ by GitHub

 

JS:

We will create a AudioStream class which will have functions used in above HTML events and also the one used to upload the audio stream to s3.

Initialization:

1- audioStreamInitialize function is used to request the microphone permission, and on receiving the data, it will create a multi-part upload.

  audioStreamInitialize() {
  /*
  Feature detecting is a simple check for the existence of "navigator.mediaDevices.getUserMedia"
   
  To use the microphone. we need to request permission.
  The parameter to getUserMedia() is an object specifying the details and requirements for each type of media you want to access.
  To use microphone it shud be {audio: true}
   
  */
  navigator.mediaDevices.getUserMedia(self.audioConstraints)
  .then(function(stream) {
  /*
  Creates a new MediaRecorder object, given a MediaStream to record.
  */
  self.recorder = new MediaRecorder(stream);
   
  /*
  Called to handle the dataavailable event, which is periodically triggered each time timeslice milliseconds of media have been recorded
  (or when the entire media has been recorded, if timeslice wasn't specified).
  The event, of type BlobEvent, contains the recorded media in its data property.
  You can then collect and act upon that recorded media data using this event handler.
  */
  self.recorder.addEventListener('dataavailable', function(e) {
  var normalArr = [];
  /*
  Here we push the stream data to an array for future use.
  */
  self.recordedChunks.push(e.data);
  normalArr.push(e.data);
   
  /*
  here we create a blob from the stream data that we have received.
  */
  var blob = new Blob(normalArr, {
  type: 'audio/webm'
  });
   
  /*
  if the length of recordedChunks is 1 then it means its the 1st part of our data.
  So we createMultipartUpload which will return an upload id.
  Upload id is used to upload the other parts of the stream
   
  else.
  It Uploads a part in a multipart upload.
  */
  if (self.recordedChunks.length == 1) {
  self.startMultiUpload(blob, self.filename)
  } else {
  /*
  self.incr is basically a part number.
  Part number of part being uploaded. This is a positive integer between 1 and 10,000.
  */
  self.incr = self.incr + 1
  self.continueMultiUpload(blob, self.incr, self.uploadId, self.filename, self.bucketName);
  }
  })
  });
  }
   
   

view rawaudioStreamInitialize.js hosted with ❤ by GitHub

 

Start and Stop Recording:

1- startRecording function will be triggered when we start a new recording.

  /*
  The MediaRecorder method start(), which is part of the MediaStream Recording API,
  begins recording media into one or more Blob objects.
  You can record the entire duration of the media into a single Blob (or until you call requestData()),
  or you can specify the number of milliseconds to record at a time.
  Then, each time that amount of media has been recorded, an event will be delivered to let you act upon the recorded media,
  while a new Blob is created to record the next slice of the media
  */
  startRecording(id) {
  var self = this;
   
  /*
  1800000 is the number of milliseconds to record into each Blob.
  If this parameter isn't included, the entire media duration is recorded into a single Blob unless the requestData()
  method is called to obtain the Blob and trigger the creation of a new Blob into which the media continues to be recorded.
  */
  /*
  PLEASE NOTE YOU CAN CHANGE THIS PARAM OF 1800000 but the size should be greater then or equal to 5MB.
  As for multipart upload the minimum breakdown of the file should be 5MB
  */
  this.recorder.start(1800000);
   
  }
view rawstartRecording.js hosted with ❤ by GitHub

 

2- stopRecording

  /*
  When the stop() method is invoked, the UA queues a task that runs the following steps:
  1 - If MediaRecorder.state is "inactive", raise a DOM InvalidState error and terminate these steps.
  If the MediaRecorder.state is not "inactive", continue on to the next step.
  2 - Set the MediaRecorder.state to "inactive" and stop capturing media.
  3 - Raise a dataavailable event containing the Blob of data that has been gathered.
  4 - Raise a stop event.
  */
  stopRecording(id) {
  var self = this;
  self.recorder.stop();
  }
view rawstopRecording.js hosted with ❤ by GitHub

 

Uploading to AWS S3 Audio Streaming:

1- startMultiUpload

  /*
  Initiates a multipart upload and returns an upload ID.
  Upload id is used to upload the other parts of the stream
  */
  startMultiUpload(blob, filename) {
  var self = this;
  var audioBlob = blob;
  var params = {
  Bucket: self.bucketName,
  Key: filename,
  ContentType: 'audio/webm',
  ACL: 'private',
  };
  self.s3.createMultipartUpload(params, function(err, data) {
  if (err) {
  console.log(err, err.stack); // an error occurred
  } else {
  self.uploadId = data.UploadId
  self.incr = 1;
  self.continueMultiUpload(audioBlob, self.incr, self.uploadId, self.filename, self.bucketName);
  }
  });
  }
view rawstartMultiUpload.js hosted with ❤ by GitHub

 

2- continueMultiUpload

  /*
  Uploads a part in a multipart upload.
  The following code uploads part of a multipart upload.
  it specifies a file name for the part data. The Upload ID is same that is returned by the initiate multipart upload.
  */
  continueMultiUpload(audioBlob, PartNumber, uploadId, key, bucketName) {
  var self = this;
  var params = {
  Body: audioBlob,
  Bucket: bucketName,
  Key: key,
  PartNumber: PartNumber,
  UploadId: uploadId
  };
  console.log(params);
  self.s3.uploadPart(params, function(err, data) {
  if (err) {
  console.log(err, err.stack)
  } // an error occurred
  else {
  /*
  Once the part of data is uploaded we get an Entity tag for the uploaded object(ETag).
  which is used later when we complete our multipart upload.
  */
  self.etag.push(data.ETag);
  if (self.booleanStop == true) {
  self.completeMultiUpload();
  }
  }
  });
  }

 

3- completeMultiUpload

 

/*

 

Completes a multipart upload by assembling previously uploaded parts.

 

*/

 

completeMultiUpload() {

 

var self = this;

 

var outputTag = [];

 

/*

 

here we are constructing the Etag data in the required format.

 

*/

 

self.etag.forEach((data, index) => {

 

const obj = {

 

ETag: data,

 

PartNumber: ++index

 

};

 

outputTag.push(obj);

 

});

   
 

var params = {

 

Bucket: self.bucketName, // required

 

Key: self.filename, // required

 

UploadId: self.uploadId, // required

 

MultipartUpload: {

 

Parts: outputTag

 

}

 

};

   
 

self.s3.completeMultipartUpload(params, function(err, data) {

 

if (err) {

 

console.log(err, err.stack)

 

} // an error occurred

 

else {

 

// initialize variable back to normal

 

self.etag = [], self.recordedChunks = [];

 

self.uploadId = "";

 

self.booleanStop = false;

 

self.disableAllButton();

 

self.removeLoader();

  alert("we have successfully saved the questionaire..");
  }
  });
  }

 

This is how you can upload the live streaming audio to S3 bucket via front-end using AWS SDK. 

To find a working example, refer to my blog on uploading audio stream to AWS S3.

 

Discussion

Write to us

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms Of Service apply. By submitting this form, you agree to our Privacy Policy.

See how our uniquely collaborative work style, can help you redesign your business.

Contact us