In this quickstart, you will learn how to use the Agora Recording SDK to enable recording.

The Agora Recording SDK for Linux (Recording SDK) supports:

  • Recording communication and live broadcast content.
  • Recording the voice and video of all users in a channel.
  • Recording the voice and video of all users in multiple channels simultaneously.
  • Recording the voice of all users in a channel or in multiple channels simultaneously.
  • Recording an encrypted channel if the application has integrated Agora built-in encryption.

The Agora Recording SDK for Linux is integrated on your Linux server instead of your application:

../_images/recording_linux_en.png

To record the content of a channel, a ‘special audience’ joins the channel, gets the content and stores the content on a Linux server. You must:

  • Implement the recording SDK on your Linux server.
  • Use the same App ID in the Agora Recording SDK and in other Agora SDKs implementing voice or video communication. For detailed information about App ID, see Getting an App ID.
  • Specify the channel to record.

The Recording SDK must use the same channel profile as the Agora Native/Web SDK, otherwise issues may occur.

Prerequisites

See Setting up Your Environment for the prerequisites.

Quickstart

Import the C++ Libraries

Import the C++ libraries for the variable definitions and streaming.

Library Description
<csignal> Signal handling library.
<cstdint> Defines a set of integral type aliases.
<iostream> Defines the standard input/output stream objects.
<sstream> Provides the string stream classes.
<string> Defines the string types, character traits, and a set of converting functions.
<vector> Defines the vector container class.
<algorithm> Defines a collection of functions designed to be used on ranges of elements.
#include <csignal>
#include <cstdint>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>

Import the Agora SDK libraries.

Library Description
IAgoraLinuxSdkCommon.h Defines Agora variable types and classes.
IAgoraRecordingEngine.h Defines the Agora recording engine class.
base/atomic.h Defines the Agora namespace.
base/log.h Agora logging library.
base/opt_parser.h Agora communication helper library.
agorasdk/AgoraSdk.h Agora Recording SDK.
#include "IAgoraLinuxSdkCommon.h"
#include "IAgoraRecordingEngine.h"

#include "base/atomic.h"
#include "base/log.h"
#include "base/opt_parser.h"
#include "agorasdk/AgoraSdk.h"

Add the Namespaces and Global Variables

Define the standard and Agora classes, and specify the namespaces to use in the code.

using std::string;
using std::cout;
using std::cerr;
using std::endl;

using agora::base::opt_parser;
using agora::linuxsdk::VideoFrame;
using agora::linuxsdk::AudioFrame;

Define the Global Variables to Determine the Service Status.

Variable Description
g_bSignalStop Used to define if a signal has stopped
g_bSignalStartService Used to define if a service has started
g_bSignalStopService Used to define if a service has stopped
atomic_bool_t g_bSignalStop;
atomic_bool_t g_bSignalStartService;
atomic_bool_t g_bSignalStopService;

Create the Start and Stop Service Methods

The start_service and stop_service methods update the global g_bSignalStartService and g_bSignalStopService variables.

void start_service(int signo) {
    (void)signo;
    g_bSignalStartService = true;
}

void stop_service(int signo) {
    (void)signo;
    g_bSignalStopService = true;
}

Define the Variables

Define the Agora variables for the Agora SDK engine.

Variable Description
uid User ID
appId App ID
channelKey Channel key
name Channel name
channelProfile Channel profile
uint32_t uid = 0;
string appId;
string channelKey;
string name;
uint32_t channelProfile = 0;

Define the Variables for the Recording Configurations.

Define the decryption mode decryptionMode and the decryption key secret.

string decryptionMode;
string secret;

Define the resolution for the video mix and the idle time limit (seconds).

string mixResolution("360,640,15,500");

int idleLimitSec=5*60;//300s

Define the paths for the application.

Variable Description
applitePath Path to store AgoraCoreService
recordFileRootDir Directory to save the recording files
cfgFilePath Path to the configuration file
proxyServer IP and port for the proxy server
string applitePath;
string appliteLogPath;
string recordFileRootDir = "";
string cfgFilePath = "";
string proxyServer;

Define the variables for the low and high UDP ports.

int lowUdpPort = 0;//40000;
int highUdpPort = 0;//40004;

Define the audio, video, and mixing variables for the settings in the Agora parser.

bool isAudioOnly=0;
bool isVideoOnly=0;
bool isMixingEnabled=0;
bool mixedVideoAudio=0;

Define the audio, video, and stream format types.

uint32_t getAudioFrame = agora::linuxsdk::AUDIO_FORMAT_DEFAULT_TYPE;
uint32_t getVideoFrame = agora::linuxsdk::VIDEO_FORMAT_DEFAULT_TYPE;
uint32_t streamType = agora::linuxsdk::REMOTE_VIDEO_STREAM_HIGH;

Define the video snapshot interval captureInterval (seconds). Set the trigger mode to automatic.

int captureInterval = 5;
int triggerMode = agora::linuxsdk::AUTOMATICALLY_MODE;

Define the following video parameters: Width, height, frame rate, and bitrate.

int width = 0;
int height = 0;
int fps = 0;
int kbps = 0;

Define the signal variables, which track the communication signal of the recording.

g_bSignalStop = false;
g_bSignalStartService = false;
g_bSignalStopService = false;

Set the Signal Event Handlers

Use the signal method to set the signal event handlers.

Event Description
SIGQUIT The signal is terminated.
SIGABRT The signal is aborted.
SIGINT The signal is interrupted.
SIGPIPE Broken pipe signal. Passing SIG_IGN as a handler ignores the broken pipe signal.
signal(SIGQUIT, signal_handler);
signal(SIGABRT, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGPIPE, SIG_IGN);

Define the parser object, using the parser.add_long_opt method to parse the following rtcEngine parameters:

  • app ID
  • user ID
  • channel name
  • application path
  • channel key
  • channel profile
opt_parser parser;

parser.add_long_opt("appId", &appId, "App Id/must", agora::base::opt_parser::require_argu);
parser.add_long_opt("uid", &uid, "User Id default is 0/must", agora::base::opt_parser::require_argu);

parser.add_long_opt("channel", &name, "Channel Id/must", agora::base::opt_parser::require_argu);
parser.add_long_opt("appliteDir", &applitePath, "directory of app lite 'AgoraCoreService', Must pointer to 'Agora_Recording_SDK_for_Linux_FULL/bin/' folder/must",
        agora::base::opt_parser::require_argu);

parser.add_long_opt("channelKey", &channelKey, "channelKey/option");
parser.add_long_opt("channelProfile", &channelProfile, "channel_profile:(0:COMMUNICATION),(1:broadcast) default is 0/option");

Parse the audio, video, and mix settings.

parser.add_long_opt("isAudioOnly", &isAudioOnly, "Default 0:A/V, 1:AudioOnly (0:1)/option");
parser.add_long_opt("isVideoOnly", &isVideoOnly, "Default 0:A/V, 1:VideoOnly (0:1)/option");
parser.add_long_opt("isMixingEnabled", &isMixingEnabled, "Mixing Enable? (0:1)/option");
parser.add_long_opt("mixResolution", &mixResolution, "change default resolution for vdieo mix mode/option");
parser.add_long_opt("mixedVideoAudio", &mixedVideoAudio, "mixVideoAudio:(0:seperated Audio,Video) (1:mixed Audio & Video), default is 0 /option");

Parse the decryption mode decryptionMode and decryption key secret.

parser.add_long_opt("decryptionMode", &decryptionMode, "decryption Mode, default is NULL/option");
parser.add_long_opt("secret", &secret, "input secret when enable decryptionMode/option");

Parse the idle time limit and root directory for the recording files.

parser.add_long_opt("idle", &idleLimitSec, "Default 300s, should be above 3s/option");
parser.add_long_opt("recordFileRootDir", &recordFileRootDir, "recording file root dir/option");

Parse the low and high UDP ports.

parser.add_long_opt("lowUdpPort", &lowUdpPort, "default is random value/option");
parser.add_long_opt("highUdpPort", &highUdpPort, "default is random value/option");

Parse the audio and video frame settings.

parser.add_long_opt("getAudioFrame", &getAudioFrame, "default 0 (0:save as file, 1:aac frame, 2:pcm frame, 3:mixed pcm frame) (Can't combine with isMixingEnabled) /option");
parser.add_long_opt("getVideoFrame", &getVideoFrame, "default 0 (0:save as file, 1:h.264, 2:yuv, 3:jpg buffer, 4:jpg file, 5:jpg file and video file) (Can't combine with isMixingEnabled) /option");

Parse the video snapshot interval, configuration file path, and proxy server.

parser.add_long_opt("captureInterval", &captureInterval, "default 5 (Video snapshot interval (second))");
parser.add_long_opt("cfgFilePath", &cfgFilePath, "config file path / option");
parser.add_long_opt("proxyServer", &proxyServer, "proxyServer:format ip:port, eg,\"127.0.0.1:1080\"/option");

Parse the video stream type and trigger mode.

parser.add_long_opt("streamType", &streamType, "remote video stream type(0:STREAM_HIGH,1:STREAM_LOW), default is 0/option");
parser.add_long_opt("triggerMode", &triggerMode, "triggerMode:(0: automatically mode, 1: manually mode) default is 0/option");

Ensure that the parser settings, appID, and channel name are all valid. If any of these are invalid, terminate the application.

if (!parser.parse_opts(argc, argv) || appId.empty() || name.empty()) {
  std::ostringstream sout;
  parser.print_usage(argv[0], sout);
  cout<<sout.str()<<endl;
  return -1;
}

If the trigger mode is set to manual, add additional signal event listeners to start and stop the service.

if(triggerMode == agora::linuxsdk::MANUALLY_MODE) {
    signal(SIGUSR1, start_service);
    signal(SIGUSR2, stop_service);
}

Check if the recording file directory and configuration file path are empty. If the directories are not empty, log an error using the LOG method and terminate the application.

if(!recordFileRootDir.empty() && !cfgFilePath.empty()){
  LOG(ERROR,"Client can't set both recordFileRootDir and cfgFilePath");
  return -1;
}

Set the default path for recording files to ..

if(recordFileRootDir.empty() && cfgFilePath.empty())
    recordFileRootDir = ".";

If the directories are empty and mixing is enabled, set the video parameters. If the parameters are invalid, terminate the application.

//Once recording video under video mixing model, client needs to config width, height, fps and kbps
if(isMixingEnabled && !isAudioOnly) {
   if(4 != sscanf(mixResolution.c_str(), "%d,%d,%d,%d", &width,
                &height, &fps, &kbps)) {
      LOG(ERROR, "Illegal resolution: %s", mixResolution.c_str());
      return -1;
   }
}

Add a log to track the users that join the channel.

LOG(INFO, "uid %" PRIu32 " from vendor %s is joining channel %s",
        uid, appId.c_str(), name.c_str());

Define the Agora SDK recorder and configuration.

agora::AgoraSdk recorder;
agora::recording::RecordingConfig config;

Apply the idle time limit and channel profile.

config.idleLimitSec = idleLimitSec;
config.channelProfile = static_cast<agora::linuxsdk::CHANNEL_PROFILE_TYPE>(channelProfile);

Apply the video, audio, and mix settings.

config.isVideoOnly = isVideoOnly;
config.isAudioOnly = isAudioOnly;
config.isMixingEnabled = isMixingEnabled;
config.mixResolution = (isMixingEnabled && !isAudioOnly)? const_cast<char*>(mixResolution.c_str()):NULL;
config.mixedVideoAudio = mixedVideoAudio;

Apply the application directory, recording file directory, and configuration file path.

config.appliteDir = const_cast<char*>(applitePath.c_str());
config.recordFileRootDir = const_cast<char*>(recordFileRootDir.c_str());
config.cfgFilePath = const_cast<char*>(cfgFilePath.c_str());

Apply the decryption mode decryptionMode, decryption key secret, and proxy server.

config.secret = secret.empty()? NULL:const_cast<char*>(secret.c_str());
config.decryptionMode = decryptionMode.empty()? NULL:const_cast<char*>(decryptionMode.c_str());
config.proxyServer = proxyServer.empty()? NULL:const_cast<char*>(proxyServer.c_str());

Apply the low and high UDP ports and the video capture interval.

config.lowUdpPort = lowUdpPort;
config.highUdpPort = highUdpPort;
config.captureInterval = captureInterval;

Apply the audio, video, and stream format types and the trigger mode.

config.decodeAudio = static_cast<agora::linuxsdk::AUDIO_FORMAT_TYPE>(getAudioFrame);
config.decodeVideo = static_cast<agora::linuxsdk::VIDEO_FORMAT_TYPE>(getVideoFrame);
config.streamType = static_cast<agora::linuxsdk::REMOTE_VIDEO_STREAM_TYPE>(streamType);
config.triggerMode = static_cast<agora::linuxsdk::TRIGGER_MODE_TYPE>(triggerMode);

Set the mix mode for the recorder.

recorder.updateMixModeSetting(width, height, isMixingEnabled ? !isAudioOnly:false);

Create a recording engine instance and join the video channel. If it fails, terminate the application.

if (!recorder.createChannel(appId, channelKey, name, uid, config)) {
  cerr << "Failed to create agora channel: " << name << endl;
  return -1;
}

Update the recording storage directory.

cout << "Recording directory is " << recorder.getRecorderProperties()->storageDir << endl;
recorder.updateStorageDir(recorder.getRecorderProperties()->storageDir);

While the recorder is running, update the signal service using recorder.startService and recorder.stopService.

while (!recorder.stopped() && !g_bSignalStop) {
    if(g_bSignalStartService) {
        recorder.startService();
        g_bSignalStartService = false;
    }

    if(g_bSignalStopService) {
        recorder.stopService();
        g_bSignalStopService = false;
    }

    sleep(1);
}

Once the signal stops, leave the channel using recorder.leaveChannel and release the recorder object.

if (g_bSignalStop) {
  recorder.leaveChannel();
  recorder.release();
}

cerr << "Stopped \n";
return 0;

Start recording

Now you can start recording.

Reference

Refer to Recording Voice and Video for more functions of the Agora Recording SDK.

For details of the APIs in the Agora Recording SDK, please refer to Recording API.