user15380685
user15380685

Reputation: 61

Convert Kinesis Vides Stream /GetMedia result into audio file - node.js

Currently, I'm trying to convert a response of Kinesis Vides Stream GetMedia API to an audio file but have had no success in this. According to AWS documentation, - result of GetMedia request? it's recommended to use Kinesis Video Stream Parser Library. But I'd like to use js/ts implementation. Is it possible to convert this stream to an audio file using just js/ts?

Thank you for your help.

Upvotes: 2

Views: 1179

Answers (1)

chiekemi
chiekemi

Reputation: 41

I finally figured out how to convert a Kinesis Video Stream into an audio file. This is how I did it with Node.js and the AWS SDK for JavaScript:

>> mkdir aws-sdk-js && cd aws-sdk-js
>> npm init

After choosing the default npm options, open the newly generated package.json file and add the following:

 "dependencies": {
    "aws-sdk": "^2.1587.0”,
    “ebml”: “^3.0.0”
 }

Now install the SDK modules:

>> npm install
>> touch index.js

Then in the index.js file, copy and paste the following:

const AWS = require("aws-sdk");
const ebml = require("ebml");
const fs = require("fs");

const region = "<your AWS region>";
const streamInfo = {
    streamName: "<your Kinesis Video stream name>",
    startFragmentNumber: "<your Kinesis Video fragment number>",
    //streamArn: "<your Kinesis Video stream ARN>"
};

// Suppress AWS SDK version warning message
process.env["AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE"] = 1;


async function main() {        
    //const streamName = streamInfo.streamArn.split("stream/")[1].split("/")[0];
    const streamName = streamInfo.streamName;
    const fragmentNumber =  streamInfo.startFragmentNumber;
    
    const raw = await getMedia(streamName, fragmentNumber);

    const wav = Converter.createWav(raw, 8000);

    fs.writeFile("audio.wav", Buffer.from(wav.buffer), (err) => {
        if (err) throw err;
        console.log("Audio file saved.");
    });
    
    return {};
};


async function getMedia(streamName, fragmentNumber) {
    // Obtain the Kinesis Video endpoint URL
    const kinesisvideo = new AWS.KinesisVideo({region: region});
    var params = {
        APIName: "GET_MEDIA",
        StreamName: streamName
    };
    const end = await kinesisvideo.getDataEndpoint(params).promise();

    // Get the raw audio data
    const kinesisvideomedia = new AWS.KinesisVideoMedia({endpoint: end.DataEndpoint, region:region});
    var params = {
        StartSelector: { 
            StartSelectorType: "FRAGMENT_NUMBER",
            AfterFragmentNumber: fragmentNumber,
        },
        StreamName: streamName
    };
    const data = await kinesisvideomedia.getMedia(params).promise();
    const decoder = new ebml.Decoder();
    let chunks = [];
    decoder.on("data", chunk => {
        if(chunk[1].name == "SimpleBlock"){
            chunks.push(chunk[1].data);
        }
    });
    decoder.write(data["Payload"]);
    
    // Join the audio chunks
    const margin = 4;
    var sumLength = 0;
    chunks.forEach( chunk => {
        sumLength += chunk.byteLength - margin;
    })
    var sample = new Uint8Array(sumLength);
    var pos = 0;
    chunks.forEach(chunk => {
        let tmp = new Uint8Array(chunk.byteLength - margin);
        for(var e = 0; e < chunk.byteLength -  margin; e++){
            tmp[e] = chunk[e + margin];
        }
        sample.set(tmp, pos);
        pos += chunk.byteLength - margin;

    })
    return sample.buffer;
}


class Converter {
    // Convert the raw audio into wav format
    static createWav(samples, sampleRate) {
        const len = samples.byteLength;
        const view = new DataView(new ArrayBuffer(44 + len));
        this._writeString(view, 0, "RIFF");
        view.setUint32(4, 32 + len, true);
        this._writeString(view, 8, "WAVE");
        this._writeString(view, 12, "fmt ");
        view.setUint32(16, 16, true);
        view.setUint16(20, 1, true);
        view.setUint16(22, 1, true);
        view.setUint32(24, sampleRate, true); 
        view.setUint32(28, sampleRate * 2, true);
        view.setUint16(32, 2, true);
        view.setUint16(34, 16, true);
        this._writeString(view, 36, "data");
        view.setUint32(40, len, true);
        let offset = 44;
        const srcView = new DataView(samples);
        for (var i = 0; i < len; i+=4, offset+=4) {
            view.setInt32(offset, srcView.getUint32(i));
        }
        return view;
    }
    
    static _writeString(view, offset, string) {
        for (var i = 0; i < string.length; i++) {
          view.setUint8(offset + i, string.charCodeAt(i));
        }
    }
}

main();

Run the script:

>> node index.js

and this should output the audio.wav file!

Upvotes: 1

Related Questions