npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

drachtio-fsmrf

v4.0.0

Published

freeswitch-based media resource function for drachtio

Downloads

714

Readme

drachtio-fsmrf Build Status

drachtio logo

Welcome to the Drachtio Media Resource framework, a partner module to drachtio-srf for building high-performance SIP server applications in pure javascript.

drachtio-fsmrf implements common media server functions on top of Freeswitch and enables rich media applications involving IVR, conferencing and other features to be built in pure javascript without requiring in-depth knowledge of freeswitch configuration.

Note, drachtio-fsmrf applications require a freeswitch media server, configured as per this ansible role.

API documentation for drachtio-fsmrf can be found here.

Data Model

This module exports a single class, Mrf (aka Media Resource Framework).

Invoking the constructor creates an instance of the Mrf class that can then be used to connect to Mediaservers; once connected to a Mediaserver you can create and manipulate instances of Endpoints and Conferences.

That's it -- those are all the classes you need to work with. You can connect calls to a Mediaserver, producing an Endpoint. You can then perform operations like play, say, bridge, park etc on the Endpoint (which equates to a Freeswitch channel). You can create Conferences, join Endpoints into Conferences, and perform operations on the Conference. And you can call any of the myriad freeswitch applications or api methods via the Endpoint and Conference classes.

Let's dive in.

Getting Started

First, create an instance of both the drachtio signaling resource framework and the media resource framework, as per below.

const Srf = require('drachtio-srf');
const Mrf = require('drachtio-fsmrf');

const srf = new Srf() ;
srf.connect(host: '127.0.0.1');

srf.on('connect', (err, hostport) => {
  console.log(`successfully connected to drachtio listening on ${hostport}`);
});

const mrf = new Mrf(srf) ;

At that point, the mrf instance can be used to connect to and produce instances of MediaServers

mrf.connect({address: '127.0.0.1', port: 8021, secret: 'ClueCon'})
  .then((mediaserver) => {
    console.log('successfully connected to mediaserver');
  })
  .catch ((err) => {
    console.error(`error connecting to mediaserver: ${err}`);
  });

In the example above, we see the mrf#connect method returns a Promise that resolves with an instance of the media server. As with all public methods, a callback variant is available as well:

// we're connecting to the Freeswitch event socket
mrf.connect({address: '127.0.0.1', port: 8021, secret: 'ClueCon'}, (err, mediaserver) => {
    if (err) {
      return console.log(`error connecting to mediaserver: ${err}`);
    }
    console.log(`connected to mediaserver listening on ${JSON.stringify(ms.sip)}`);
    /*
      {
        "ipv4": {
          "udp": {
            "address":"172.28.0.11:5060"
          },
          "dtls": {
            "address":"172.28.0.11:5081"
          }
        },
        "ipv6":{
          "udp":{},
          "dtls":{}
        }
      }
    */
  }
});

Having a media server instance, we can now create instances of Endpoints and Conferences and invoke operations on those objects.

Performing Media Operations

We can create an Endpoint when we have an incoming call, by connecting it to a Mediaserver.

srf.invite((req, res) => {
  mediaserver.connectCaller(req, res)
    .then(({endpoint, dialog}) => {
      console.log('successfully connected call to media server');

In the example above, we use MediaServer#connectCaller() to connect a call to a Mediaserver, producing both an Endpoint (represening the channel on Freeswitch) and a Dialog (representing the UAS dialog).

Again, note that a callback version is also available:

srf.invite((req, res) => {   
  mediaserver.connectCaller(req, res, (err, {endpoint, dialog} => {
    if (err) return console.log(`Error connecting ${err}`);
    console.log('successfully connected call to media server');
  });

We can also create an Endpoint outside of any inbound call by calling MediaServer#createEndpoint(). This will give us an initially inactive Endpoint that we can later modify to stream to a remote destination:

mediaserver.createEndpoint()
  .then((endpoint) => {

    // some time later...
    endpoint.modify(remoteSdp);

  });

Once we have an Endpoint, we can do things like play a prompt and collect dtmf:

endpoint.playCollect({file: myFile, min: 1, max: 4})
  .then((obj) => {
    console.log(`collected digits: ${obj.digits}`);
  });

Conferences work similarly - we create them and then can join Endpoints to them.

mediaserver.createConference('my_conf', {maxMembers: 50})
  .then((conference) => {
    return endpoint.join(conference)
  })
  .then(() => {
    console.log('endpoint joined to conference')
  });

When an Endpoint is joined to a Conference, we have an additional set of operations we can invoke on the Endpoint -- things like mute/unmute, turn on or off automatic gain control, playing a file directly to the participant on that Endpoint, etc. These actions are performed by methods that all begin with conf:

endpoint.join(conference, (err) => {
  if (err) return console.log(`Error ${err}`);

  endpoint.confMute();
  endpoint.confPlay(myFile);
}

Execute any Freeswitch application or api

As shown above, some methods have been added to the Endpoint and Conference class to provide syntactic sugar over freeswitch aplications and apis. However, any Freeswitch application or api can also be called directly.

Endpoint#execute executes a Freeswitch application and returns in either the callback or the Prompise the contents of the associated CHANNEL_EXECUTE_COMPLETE event that Freeswitch returns. The event structure is defined here:

// generate dtmf from an Endpoint
endpoint.execute('send_dtmf', `${digits}@125`, (err, evt) => {
  if (err) return console.error(err);

  console.log(`last dtmf duration was ${evt.getHeader('variable_last_dtmf_duration')}`);
})

Endpoint#api executes a Freeswitch api call and returns in either the callback or the Promise the response that Freeswitch returns to the command.

endpoint.api('uuid_dump', endpoint.uuid)
  .then((response) => {
    console.log(`${JSON.stringify(response)}`);
    //
    //    {
    //  "headers": [{
    //    "name": "Content-Type",
    //    "value": "api/response"
    //  }, {
    //    "name": "Content-Length",
    //    "value": 8475
    //  }],
    //  "hPtr": null,
    //  "body": "Event-Name: CHANNEL_DATA\n..

  });

Note that Content-Type api/response returned by api requests return a body consisting of plain text separated by newlines. To parse this body into a plain javascript object with named properties, use the Mrf#utils#parseBodyText method, as per below:

endpoint.api('uuid_dump', endpoint.uuid)
  .then((evt) => {
    const vars = Mrf.utils.parseBodyText(evt.getBody());
    console.log(`${JSON.stringify(vars)}`);
    //   {
    //    "Event-Name": "CHANNEL_DATA",
    //    "Core-UUID": "de006bc8-f892-11e7-a989-3b397b4b8083",
    //     ...
    //   }
  });

Tests

To run tests you will need Docker and docker-compose installed on your host, as the test suite runs in a docker network created by docker-compose-testbed.yaml. The first time you run the tests, it will take a while since docker images will be downloaded to your host.

$ npm test

  starting docker network..

    docker network started, giving extra time for freeswitch to initialize...

  Mrf#connect using Promise

    ✔ mrf.localAddresses is an array
    ✔ socket connected
    ✔ mediaserver.srf is an Srf

    ...etc...

License

MIT