Talk to us
Talk to us
menu

How to Add Cloud Recording into Your App

How to Add Cloud Recording into Your App

Cloud recording allows apps to save voice calls, video calls, and live streams so users can revisit important conversations at any time. This improves user experience by helping them catch missed details or review key moments. While the idea sounds simple, implementing recording features involves complex systems for capturing media, securely storing files, and providing easy access. Building this from scratch can be time-consuming and costly. In this article, we’ll show you how to integrate cloud recording into your app using ZEGOCLOUD’s Cloud Recording API, including how to start recordings, save files automatically, and help users easily access their content.

How to Add Cloud Recording into Your App

Now let’s add cloud recording to your app using ZEGOCLOUD. The ZEGOCLOUD’s Cloud Recording API lets you add recording to existing video call and streaming apps with just a few server requests. If you already have a video or voice call app working, recording integration takes minutes instead of months.

We’ll use server-side API calls to start recordings during important calls, monitor their progress automatically, and give users access to saved files through your chosen cloud storage. No additional SDK integration required.

Prerequisites

Before you start building, make sure you have these things ready:

  • Working video or voice calling app using ZEGOCLOUD SDK.
  • Cloud recording service enabled.
  • Third-party cloud storage account (AWS S3, Alibaba Cloud, etc.)
  • Server-side environment to make API calls.
  • AppID and ServerSecret from ZEGOCLOUD Console.

1. Set Up API Authentication

Cloud recording uses server-side APIs that require proper authentication. Set up signature generation to secure your API calls.

const crypto = require('crypto');

class ZegoCloudAPI {
    constructor(appId, serverSecret) {
        this.appId = appId;
        this.serverSecret = serverSecret;
        this.baseURL = 'https://cloudrecord-api.zego.im/';
    }

    generateSignature(timestamp, nonce) {
        // Signature = md5(AppId + SignatureNonce + ServerSecret + Timestamp)
        const data = `${this.appId}${nonce}${this.serverSecret}${timestamp}`;
        return crypto.createHash('md5').update(data).digest('hex');
    }

    generateNonce() {
        return crypto.randomBytes(8).toString('hex');
    }

    buildCommonParams() {
        const timestamp = Math.floor(Date.now() / 1000);
        const nonce = this.generateNonce();
        const signature = this.generateSignature(timestamp, nonce);

        return {
            AppId: this.appId,
            Timestamp: timestamp,
            SignatureNonce: nonce,
            Signature: signature,
            SignatureVersion: '2.0'
        };
    }
}

2. Configure Cloud Storage

Set up your preferred cloud storage provider where recording files will be saved automatically. Choose your storage provider based on geographic location, pricing, and integration preferences.

const createStorageConfig = (provider, credentials) => {
    const storageConfigs = {
        // AWS S3 Configuration
        s3: {
            Vendor: 1,
            Region: credentials.region,
            Bucket: credentials.bucket,
            AccessKeyId: credentials.accessKeyId,
            AccessKeySecret: credentials.accessKeySecret
        },

        // Alibaba Cloud OSS Configuration  
        alibabaOSS: {
            Vendor: 2,
            Region: credentials.region,
            Bucket: credentials.bucket,
            AccessKeyId: credentials.accessKeyId,
            AccessKeySecret: credentials.accessKeySecret
        },

        // Tencent Cloud COS Configuration
        tencentCOS: {
            Vendor: 3,
            Region: credentials.region,
            Bucket: credentials.bucket,
            AccessKeyId: credentials.accessKeyId,
            AccessKeySecret: credentials.accessKeySecret
        }
    };

    return storageConfigs[provider];
};

3. Start Recording Sessions

Begin recording by calling the StartRecord API when users start important conversations or meetings.

const startRecording = async (roomId, recordingOptions = {}) => {
    const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
    const commonParams = api.buildCommonParams();

    // Build recording configuration
    const recordingConfig = {
        RoomId: roomId,
        RecordInputParams: {
            RecordMode: recordingOptions.mode || 2, // 1=single-stream, 2=mixed-stream
            StreamType: 3, // 1=audio only, 2=video only, 3=audio+video
            MaxIdleTime: 300, // Stop after 5 minutes of no streams
            MaxRecordTime: 86400, // Maximum 24 hours
            MixConfig: {
                MixMode: 2, // 1=custom, 2=grid, 3=horizontal, 4=vertical, 5=floating
                MixOutputStreamId: `mix_${roomId}`,
                MixOutputVideoConfig: {
                    Width: 1280,
                    Height: 720,
                    Fps: 15,
                    Bitrate: 1130000
                }
            }
        },
        RecordOutputParams: {
            OutputFileFormat: 'mp4',
            OutputFolder: `recordings/${new Date().getFullYear()}/`
        },
        StorageParams: createStorageConfig('s3', {
            region: 'us-west-2',
            bucket: 'your-recordings-bucket',
            accessKeyId: process.env.AWS_ACCESS_KEY,
            accessKeySecret: process.env.AWS_SECRET_KEY
        })
    };

    // Make API request
    const queryParams = new URLSearchParams({
        Action: 'StartRecord',
        ...commonParams
    });

    try {
        const response = await fetch(`${api.baseURL}?${queryParams}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(recordingConfig)
        });

        const result = await response.json();

        if (result.Code === 0) {
            console.log('Recording started:', result.Data.TaskId);
            return result.Data.TaskId;
        } else {
            throw new Error(`Recording failed: ${result.Message}`);
        }
    } catch (error) {
        console.error('Start recording error:', error);
        throw error;
    }
};

This function creates a recording task and returns a TaskId that you’ll use for all subsequent operations.

4. Monitor Recording Status

Check recording progress and file upload status using the DescribeRecordStatus API. This monitoring system tracks recording progress and notifies your app when files become available.

const checkRecordingStatus = async (taskId) => {
    const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
    const commonParams = api.buildCommonParams();

    const queryParams = new URLSearchParams({
        Action: 'DescribeRecordStatus',
        ...commonParams
    });

    try {
        const response = await fetch(`${api.baseURL}?${queryParams}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ TaskId: taskId })
        });

        const result = await response.json();

        if (result.Code === 0) {
            return {
                status: result.Data.Status, // 1=initialized, 2=ongoing, 3=completed, 4=failed, 5=paused
                startTime: result.Data.RecordBeginTimestamp,
                endTime: result.Data.RecordEndTimestamp,
                files: result.Data.RecordFiles || []
            };
        } else {
            throw new Error(`Status check failed: ${result.Message}`);
        }
    } catch (error) {
        console.error('Status check error:', error);
        throw error;
    }
};

const monitorRecording = async (taskId, onStatusChange) => {
    const checkInterval = setInterval(async () => {
        try {
            const status = await checkRecordingStatus(taskId);

            if (onStatusChange) {
                onStatusChange(status);
            }

            // Stop monitoring when recording completes or fails
            if (status.status === 3 || status.status === 4) {
                clearInterval(checkInterval);
                console.log('Recording monitoring ended:', status);
            }
        } catch (error) {
            console.error('Monitoring error:', error);
        }
    }, 10000); // Check every 10 seconds

    return checkInterval;
};

5. Update Recording Layout

Change how participants appear in mixed recordings during active sessions.

const updateRecordingLayout = async (taskId, layoutType, customLayout = null) => {
    const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
    const commonParams = api.buildCommonParams();

    const layoutConfig = {
        TaskId: taskId,
        MixMode: layoutType // 1=custom, 2=grid, 3=horizontal, 4=vertical, 5=floating
    };

    // Add custom layout configuration if provided
    if (layoutType === 1 && customLayout) {
        layoutConfig.MixInputList = customLayout.map(region => ({
            StreamId: region.streamId || '',
            ViewType: 1, // 1=audio/video, 2=whiteboard
            Top: region.top,
            Left: region.left,
            Bottom: region.bottom,
            Right: region.right,
            Layer: region.layer || 1
        }));
    }

    const queryParams = new URLSearchParams({
        Action: 'UpdateLayout',
        ...commonParams
    });

    try {
        const response = await fetch(`${api.baseURL}?${queryParams}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(layoutConfig)
        });

        const result = await response.json();

        if (result.Code === 0) {
            console.log('Layout updated successfully');
            return true;
        } else {
            throw new Error(`Layout update failed: ${result.Message}`);
        }
    } catch (error) {
        console.error('Layout update error:', error);
        throw error;
    }
};

// Example usage: Switch to grid layout
await updateRecordingLayout(taskId, 2);

// Example usage: Custom layout with two participants
await updateRecordingLayout(taskId, 1, [
    { streamId: 'user_123', top: 0, left: 0, bottom: 720, right: 640, layer: 1 },
    { streamId: 'user_456', top: 0, left: 640, bottom: 360, right: 1280, layer: 1 }
]);

Layout updates happen immediately without interrupting the recording process.

6. Stop Recording Sessions

End recording sessions when calls finish or reach natural stopping points.

const stopRecording = async (taskId) => {
    const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
    const commonParams = api.buildCommonParams();

    const queryParams = new URLSearchParams({
        Action: 'StopRecord',
        ...commonParams
    });

    try {
        const response = await fetch(`${api.baseURL}?${queryParams}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ TaskId: taskId })
        });

        const result = await response.json();

        if (result.Code === 0) {
            console.log('Recording stopped successfully');
            // Continue monitoring for file upload completion
            return monitorRecording(taskId, (status) => {
                if (status.status === 3) {
                    console.log('Recording files ready:', status.files);
                }
            });
        } else {
            throw new Error(`Stop recording failed: ${result.Message}`);
        }
    } catch (error) {
        console.error('Stop recording error:', error);
        throw error;
    }
};

Always stop recordings explicitly to prevent unnecessary charges and ensure proper file processing.

7. Handle Recording Events

Set up webhook endpoints to receive real-time notifications about recording status changes.

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const verifyWebhookSignature = (secret, timestamp, nonce, signature) => {
    const data = [secret, timestamp, nonce].sort().join('');
    const computed = crypto.createHash('sha1').update(data).digest('hex');
    return computed === signature;
};

app.post('/webhooks/recording', (req, res) => {
    const { 
        signature, 
        timestamp, 
        nonce, 
        app_id, 
        task_id, 
        room_id, 
        event_type, 
        detail 
    } = req.body;

    // Verify webhook authenticity
    if (!verifyWebhookSignature(CALLBACK_SECRET, timestamp, nonce, signature)) {
        return res.status(401).send('Invalid signature');
    }

    console.log(`Recording event: ${event_type} for task ${task_id}`);

    switch (event_type) {
        case 1: // Recording file upload completed
            console.log('Files uploaded:', detail.file_info);
            notifyUsersRecordingReady(task_id, detail.file_info);
            break;

        case 2: // Recording ended abnormally
            console.error('Recording failed:', detail);
            handleRecordingFailure(task_id, detail);
            break;

        case 5: // Recording completed normally
            console.log('Recording completed successfully');
            break;

        case 7: // File upload in progress
            console.log('Uploading files...');
            break;

        default:
            console.log('Unknown event type:', event_type);
    }

    res.status(200).send('OK');
});

const notifyUsersRecordingReady = (taskId, fileList) => {
    // Implement user notification logic
    fileList.forEach(file => {
        console.log(`File ready: ${file.file_id}, Size: ${file.file_size} bytes`);
    });
};

8. Query Recording History

Retrieve lists of past and ongoing recording sessions for management and user access.

const getRecordingHistory = async (options = {}) => {
    const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
    const commonParams = api.buildCommonParams();

    const queryParams = new URLSearchParams({
        Action: 'DescribeTasks',
        ...commonParams
    });

    const requestBody = {
        Status: options.status || 3, // 2=ongoing, 3=completed
        StartTime: options.startTime || (Date.now() - 3 * 24 * 60 * 60 * 1000), // Last 3 days
        EndTime: options.endTime || Date.now(),
        PageOffset: options.page || 1,
        PageSize: options.pageSize || 50
    };

    if (options.roomId) {
        requestBody.RoomId = options.roomId;
    }

    try {
        const response = await fetch(`${api.baseURL}?${queryParams}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(requestBody)
        });

        const result = await response.json();

        if (result.Code === 0) {
            return {
                total: result.Data.TotalCount,
                page: result.Data.PageOffset,
                pageSize: result.Data.PageSize,
                recordings: result.Data.TaskList
            };
        } else {
            throw new Error(`Query failed: ${result.Message}`);
        }
    } catch (error) {
        console.error('History query error:', error);
        throw error;
    }
};

// Example: Get completed recordings from last week
const lastWeek = await getRecordingHistory({
    status: 3,
    startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
    endTime: Date.now()
});

Conclusion

Your app now has reliable cloud recording powered by ZEGOCLOUD’s server APIs. Users can capture important conversations automatically without worrying about device storage or missing crucial details during calls.

ZEGOCLOUD’s infrastructure handles all the complex recording tasks – video processing, file compression, and cloud storage uploads happen seamlessly in the background. Your app just makes simple API calls to start and stop recordings.

This recording foundation supports many use cases. Customer support teams can review service calls. Remote teams can catch up on missed meetings. Educational platforms can provide lecture replays. The recording files work with any video player, making them easy to share and access.

FAQ

Q1: What is cloud recording in the context of real-time apps?

This refers to the ability to automatically record audio, video, or screen content from a live session (like a video call or livestream) and store it on a cloud server. It allows for on-demand playback, compliance, or content reuse without requiring users to save files locally.

Q2: How does cloud recording work in a video/audio SDK?

Most SDKs offer cloud recording by routing media streams through a cloud server, where they are captured and saved. Developers usually trigger recording sessions via API calls and can configure formats, layouts, storage locations, and duration.

Q3: Does cloud recording affect app performance or latency?

No, it typically runs on the server side and does not impact user-side performance. The recording server handles all processing, so users can continue using the app without noticing any delay or quality drop.

Let’s Build APP Together

Start building with real-time video, voice & chat SDK for apps today!

Talk to us

Take your apps to the next level with our voice, video and chat APIs

Free Trial
  • 10,000 minutes for free
  • 4,000+ corporate clients
  • 3 Billion daily call minutes

Stay updated with us by signing up for our newsletter!

Don't miss out on important news and updates from ZEGOCLOUD!

* You may unsubscribe at any time using the unsubscribe link in the digest email. See our privacy policy for more information.