infobip / infobip-media-stream-showcase

Simple application that showcases how to parse incoming media streamed from Infobip.
0 stars 0 forks source link

Media streaming is not enabled for this account #1

Open JordanDalton opened 8 months ago

JordanDalton commented 8 months ago

How do I enable media streaming on my account?

When I attempt to start streaming media I encounter: Media streaming is not enabled for this account.

{
    "conferenceId": null,
    "callId": "<redacted>",
    "timestamp": "2023-11-19T17:37:12.931Z",
    "applicationId": "<redacted>",
    "bulkId": null,
    "entityId": null,
    "dialogId": null,
    "properties": {
        "reason": "Media streaming is not enabled for this account."
    },
    "type": "MEDIA_STREAM_FAILED"
}
hhadzem commented 8 months ago

Hi @JordanDalton, as I can see from logs, your account now has media streaming enabled and the issue seems to be resolved. Can we close this ticket?

JordanDalton commented 8 months ago

I do have the access now but when Infobip is sending the messages to the websocket server they appear to be encoded in a value other than utf-8 so I can't decode the message to see what the content of the message is.

hhadzem commented 8 months ago

Are we talking about initial message or following messages? Only the first initial message will be of string type; all the remaining are byte arrays. Additionally, initial message should be utf-8 encoded.

JordanDalton commented 8 months ago

The initial message is a string (unparsed json), for example:

{
        "callId":  "21a2bb42-bd77-4ebc-bc03-7b47046d5d99",
        "sampleRate": 8000,
        "packetizationTime":  20
}

Each message afterward that comes in is a Buffer.

Here's my server::

import WebSocket from 'ws';
import https from 'https';
import fs from 'fs';

const serverOptions = {
  key: fs.readFileSync('./private-key.pem'),
  cert: fs.readFileSync('./certificate.pem')
};

const server = https.createServer(serverOptions);

const port = 3000;

const wss = new WebSocket.Server({ server });

wss.on("connection", (ws, req) => {
  console.log("New client connected");

  const authHeader = req.headers['authorization'];

  console.log('authheader: ', authHeader);

  // Sending a welcome message to the client
  ws.send('Welcome, you are connected!');

  ws.on('message', function (data) {

    if(typeof data === 'string') {

      console.log('initial data', data);
      console.log('initial data.toString', data.toString());

    } else {

      console.log('---data', data); // <Buffer 08 00 08 01 18 ff 38 00 c8 00 c8 ff f8 ff 68 ff a8 fe 28 00 a8 ff b8 fe 38 00 d8 ff 08 00 98 00 28 00 68 ff 98 00 28 00 e8 ff 48 ff e8 00 a8 ff 48 00 ... 270 more bytes>
      console.log('---typeof', data.constructor.name) // Buffer

      const buf1 = Buffer.from(data);

      console.log('buf1', buf1.toString());
      console.log('buf1.ascii->', buf1.toString('ascii'));
      console.log('buf1.utf-8->', buf1.toString('utf-8'));
      console.log('buf1.base64->', buf1.toString('base64'));
      console.log('buf1.latin1->', buf1.toString('latin1'));
      console.log('buf1.hex->', buf1.toString('hex'));

    }

  });

  ws.on("close", () => {
    console.log("The client has disconnected");
  });

  ws.onerror = function (error) {
    console.error("WebSocket connection error:", error.message);
  };
});

server.listen(port, () => {
  console.log(`The WebSocket server is running on port ${port}`);
});

When I answer a call and start the media stream the following are logged in the server.

The WebSocket server is running on port 3000
New client connected
authheader:  Basic am9yZGFuOnBhc3N3b3Jk
initial data {
        "callId":       "59c37284-7ba9-4c9e-8464-bb5a2e17b158",
        "sampleRate":   8000,
        "packetizationTime":    20
}
initial data.toString {
        "callId":       "59c37284-7ba9-4c9e-8464-bb5a2e17b158",
        "sampleRate":   8000,
        "packetizationTime":    20
}
---data <Buffer f8 ff f8 ff f8 ff f8 ff 08 00 f8 ff f8 ff 08 00 08 00 f8 ff 08 00 08 00 f8 ff e8 ff 08 00 f8 ff f8 ff 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 ... 270 more bytes>
---typeof Buffer
buf1 ��������������������������������������������������������
buf1.ascii-> xxxxhxxxxxxxxxxxxxxxxxxxxxxhxxxhxxxh
buf1.utf-8-> ��������������������������������������������������������
buf1.base64-> +P/4//j/+P8IAPj/+P8IAAgA+P8IAAgA+P/o/wgA+P/4/wgACAAIAAgACAAIAAgACAAIAAgA6P8IAAgA+P8IAAgA+P/o/wgA+P/o/wgACAD4/wgA+P8IAAgA+P/4/wgA+P/o/wgACADo/wgACAD4/+j/CAAIAOj/CAD4//j/CAAIAAgACADo/wgACAAIAAgACAAIAPj/CAAIAPj/CAAIAPj/+P8IAPj/CAAIAPj/CAAIAPj/6P8IAAgA+P/4/wgA+P/4/wgA+P/4/wgA+P/4/wgA+P/4//j/CAD4//j/CAD4//j/CAAIAOj/CAD4//j/CAD4/wgACADo/wgACADo/wgACADo/wgACAD4//j/CAAIAPj/CAAIAAgA+P8IAAgA+P8IAAgA6P8IAAgA6P/4/wgA+P/4/wgA+P8IAAgA6P8=
buf1.latin1-> øÿøÿøÿøÿèøÿøøÿøøøÿøøÿøÿøøÿøøÿøøÿøøÿøÿøøÿøøÿèøÿøøèÿøøÿøèÿ
buf1.hex-> f8fff8fff8fff8ff0800f8fff8ff08000800f8ff08000800f8ffe8ff0800f8fff8ff0800080008000800080008000800080008000800e8ff08000800f8ff08000800f8ffe8ff0800f8ffe8ff08000800f8ff0800f8ff08000800f8fff8ff0800f8ffe8ff08000800e8ff08000800f8ffe8ff08000800e8ff0800f8fff8ff0800080008000800e8ff080008000800080008000800f8ff08000800f8ff08000800f8fff8ff0800f8ff08000800f8ff08000800f8ffe8ff08000800f8fff8ff0800f8fff8ff0800f8fff8ff0800f8fff8ff0800f8fff8fff8ff0800f8fff8ff0800f8fff8ff08000800e8ff0800f8fff8ff0800f8ff08000800e8ff08000800e8ff08000800e8ff08000800f8fff8ff08000800f8ff080008000800f8ff08000800f8ff08000800e8ff08000800e8fff8ff0800f8fff8ff0800f8ff08000800e8ff
---data <Buffer e8 ff 18 00 f8 ff f8 ff 08 00 08 00 f8 ff 08 00 08 00 f8 ff f8 ff 08 00 08 00 e8 ff 08 00 08 00 e8 ff 08 00 08 00 f8 ff f8 ff 08 00 f8 ff e8 ff 08 00 ... 270 more bytes>
---typeof Buffer
buf1 ����������������������������������������������������������������
buf1.ascii-> hxxxxxxxxxxxxxxxxxxxxxxxxxxxxhxxxxxxxxx
buf1.utf-8-> ����������������������������������������������������������������
buf1.base64-> 6P8YAPj/+P8IAAgA+P8IAAgA+P/4/wgACADo/wgACADo/wgACAD4//j/CAD4/+j/CAAIAPj/+P8IAAgA+P8IAAgACAD4/wgACAD4/wgACAD4//j/CAAIAPj/CAAIAPj/+P8IAPj/+P8IAAgA+P8IAAgA+P8IAAgACAAIAPj/CAD4/+j/CAAIAOj/CAAIAPj/CAAIAAgA+P8IAPj/+P8IAAgA+P8IAPj/+P/4//j/CAAIAPj/CAAIAPj/+P8IAPj/+P8IAPj/CAD4/wgACAD4//j/CAAIAOj/CAAIAPj/+P8IAAgA+P8IAAgA+P/4/wgA+P/4/+j/CAD4/+j/CAAIAPj/CAAIAPj/+P8IAAgA+P/4/wgA+P/4//j/CAD4//j/CAAIAAgACAD4//j/CAAIAPj/CAAIAPj/CAAIAPj/CAA=
buf1.latin1-> èÿøÿøÿøÿøøÿøøÿøÿøøøøøÿøøÿøÿøÿøÿøøÿøøøÿøÿøÿøøÿøÿèøÿøÿøÿøøÿøÿøøÿøÿ
buf1.hex-> e8ff1800f8fff8ff08000800f8ff08000800f8fff8ff08000800e8ff08000800e8ff08000800f8fff8ff0800f8ffe8ff08000800f8fff8ff08000800f8ff080008000800f8ff08000800f8ff08000800f8fff8ff08000800f8ff08000800f8fff8ff0800f8fff8ff08000800f8ff08000800f8ff0800080008000800f8ff0800f8ffe8ff08000800e8ff08000800f8ff080008000800f8ff0800f8fff8ff08000800f8ff0800f8fff8fff8fff8ff08000800f8ff08000800f8fff8ff0800f8fff8ff0800f8ff0800f8ff08000800f8fff8ff08000800e8ff08000800f8fff8ff08000800f8ff08000800f8fff8ff0800f8fff8ffe8ff0800f8ffe8ff08000800f8ff08000800f8fff8ff08000800f8fff8ff0800f8fff8fff8ff0800f8fff8ff0800080008000800f8fff8ff08000800f8ff08000800f8ff08000800f8ff0800
hhadzem commented 8 months ago

The buffer contains raw audio data (type int) , it is not text, therefore you cannot parse it the way you tried. Check this method for proper parsing of the audio data.

JordanDalton commented 8 months ago

So I've been able to capture the voice of the person that receives the call. When the call connects I instruct the api to say something.

Though I hear it come across on the phone I can't seem to access that channel of information. How many channels are there?

import WebSocket from 'ws';
import https from 'https';
import fs from 'fs';
import wav from 'wav';

const serverOptions = {
  key: fs.readFileSync('./private-key.pem'),
  cert: fs.readFileSync('./certificate.pem')
};

const server = https.createServer(serverOptions);

const port = 3000;

const wss = new WebSocket.Server({ server });

// Create an array to store audio buffers
const audioBuffers = [];

// Create a WAV file writer
const wavWriter = new wav.FileWriter('output.wav', {
  channels: 1,
  sampleRate: 8000,
  bitDepth: 16,
});

wss.on('connection', (ws, req) => {
  console.log('New client connected');

  ws.on('message', (message) => {

    if (message instanceof Buffer) {
      const audioData = new Int8Array(message);
      const buffer = Buffer.from(audioData.buffer);

      // Save the buffer to the array
      audioBuffers.push(buffer);

      // Write the buffer directly to the WAV file
      wavWriter.write(buffer);
    }
  });

  ws.on('close', () => {
    console.log('The client has disconnected');

    // End the WAV file writer
    wavWriter.end(() => {
      console.log('WAV file conversion completed');

      // Concatenate all buffers into one buffer
      const concatenatedBuffer = Buffer.concat(audioBuffers);

      // Save the concatenated buffer to a file
      fs.writeFileSync('output.raw', concatenatedBuffer);

      console.log('Audio data saved to output.raw');
    });
  });

  ws.onerror = function (error) {
    console.error('WebSocket connection error:', error.message);
  };
});

server.listen(port, () => {
  console.log(`The WebSocket server is running on port ${port}`);
});
hhadzem commented 8 months ago

Each of the legs of the call (leg A going from the caller to Infobip platform, and leg B going from Infobip platform to the receiver) have their own callId. Make sure you start media streaming on a correct leg. Every stream is of type mono, so single channel only.

hhadzem commented 8 months ago

To try to put this in different words: media streaming is always done on the audio going from endpoint to the Infobip platform.

JordanDalton commented 8 months ago

I'm using the system to place an outbound call to my personal number. It seems this constitutes only 1 call leg. When Info sends status updates to my webhook it is only providing details about 1 call leg.

Outbound -> I can't capture audio. Receiving Party -> I am able to capture audio.

hhadzem commented 8 months ago

You can only capture the audio going from the endpoint to the Infobip platform which, in your case, is audio coming from your personal number. It is not possible to record the audio going to your personal number using Calls API. What is your use-case to try suggesting some workaround if possible?

JordanDalton commented 8 months ago

If you like we can move this conversation to email. Is your email your email hadzem.hadzic@infobip.com?

hhadzem commented 7 months ago

@JordanDalton Sure, you can contact me on that e-mail if needed.