Recordings

SignalWire APIs support recording the audio and video of a room session. We are going to implement recording in our video-conferencing app.

Backend

First, we need to update our server to allow clients to obtain a recording. Clients cannot directly access the mp4 file of a recording, so we use our server like a proxy. Let's go back to the backend that we built in Writing a backend to proxy SignalWire Video REST API, and let's add the following endpoint:

app.get("/get_recording/:id", async (req, res) => {
  try {
    const rec = await axios.get(`${apiurl}/room_recordings/${req.params.id}`, { auth })
    res.json(rec.data)
  } catch (e) {
    console.log(e);
    return res.sendStatus(500);
  }
})

When a client performs a GET request to our endpoint, specifying the id of a recording in the path, it gets back a recording object which includes the url of the .mp4 file. In real settings, please make sure to validate your inputs (req.params.id).

We are also going to add "room.recording" to the list of moderatorPermissions, so that moderators are able to control recordings:

const moderatorPermissions = [
  "room.list_available_layouts",
  "room.recording",
  ...
]

Frontend

RecordingButton.js

On the frontend side, let's add a new file called components/RecordingButton.js. This will be a toggle button that, when pressed, can either start or stop a recording. After a recording is stopped, as soon as it is ready its associated object (the one coming from our backend, which includes the .mp4 url) is sent as a parameter of the recordingReady callback.

import React, { useState } from "react";
import ToggleButton from "react-bootstrap/ToggleButton";
import axios from "axios";

export default function RecordingButton({ room, eventLogger, recordingReady }) {
  let [recordingObj, setRecordingObj] = useState();
  return (
    <ToggleButton
      variant="outline-danger"
      type="checkbox"
      checked={recordingObj}
      onClick={async (e) => {
        if (!recordingObj) {
          const rec = await room.startRecording()
          setRecordingObj(rec)
        } else {
          const recId = recordingObj.id
          await recordingObj.stop()
          setRecordingObj(undefined)

          eventLogger(`Your recording is being processed and will be downloaded shortly.`)

          // Get the recording
          // Give the server a bit of time to process the file
          await retry(async () => {
            const res = await axios.get(`/get_recording/${recId}`)
            if (res.data && res.data.uri) {
              recordingReady(res.data)
              return true
            }
            return false
          }, 1000, 5)
        }
      }}>
      Rec
    </ToggleButton>
  );
}

async function retry(fn, timeout_ms, retries) {
  if (retries > 0 && !await fn()) {
    await new Promise(resolve => setTimeout(resolve, timeout_ms));
    retry(fn, timeout_ms, retries - 1);
  }
}

As you can observe from the code, a recording is started by calling room.startRecording(), which returns a recording object that can be used later to stop that same recording.

InCall.js

Within InCall.js we need to display the recording button. First we import it:

import RecordingButton from "../components/RecordingButton";

Then we just add it to the DOM:

<RecordingButton
  room={room}
  eventLogger={logEvent}
  recordingReady={(rec) => {
    const link = document.createElement('a');
    link.href = rec.uri;
    link.setAttribute('download', true);
    document.body.appendChild(link);
    link.click();
  }}
/>

when recordingReady is called, we get the uri of the .mp4 file and we trigger a download.

You can find the complete application on CodeSandbox, and on GitHub in the branch "extras":


Did this page help you?