StartMix
https://rtc-api.zego.im/
Overview
This interface is used to start/update Stream Mixing tasks.
For information about the client-side Stream Mixing feature, please refer to Multi-Stream Mixing. For related callbacks, please refer to Stream Mixing Started Callback and Stream Mixing Stopped Callback.

Prerequisites
Before implementing Stream Mixing, ensure that:
- You have created a project in the ZEGOCLOUD Console and applied for a valid AppId and ServerSecret. For details, please refer to Console - Project Information.
- You have enabled the "Stream Mixing" service permission in the ZEGOCLOUD Console. For details, please refer to Console - Service Configuration - Stream Mixing, or contact ZEGOCLOUD technical support to enable it.
- You have initiated publish stream and play stream in the Room through your own client. For details, please refer to Implementing Video Call.
Example Code
ZEGOCLOUD provides example code in multiple programming languages. The following example demonstrates "mixing two audio/video streams (stream1, stream2) and outputting the mixed result to one stream (stream3)":

package main
import (
"bytes"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
)
// rtc-api.zego.im indicates the product used is the cloud communication product, including Real-time Audio/Video (Express Video), Real-time Audio (Express Audio), and Low-latency Live Streaming (L3)
const DOMAIN = "https://rtc-api.zego.im"
var (
//Please modify appId to your appId, appid is uint32
//For example: 1234567890
AppId uint32 = 1234567890
//Please modify serverSecret to your serverSecret, serverSecret is string
Secret = ""
)
type MixStartRequest struct {
Appid uint64 `json:"Appid"`
UserId string `json:"UserId"`
Sequence int64 `json:"Sequence"`
MixInput []*MixInput `json:"MixInput"`
MixOutput []*MixOutput `json:"MixOutput"`
MixTaskId string `json:"TaskId"`
}
type RectInfo struct {
Top int32 `json:"Top"`
Left int32 `json:"Left"`
Bottom int32 `json:"Bottom"`
Right int32 `json:"Right"`
}
type MixInput struct {
StreamId string `json:"StreamId"`
RectInfo *RectInfo `json:"RectInfo"`
}
type MixOutput struct {
StreamId string `json:"StreamId"`
VideoBitrate int32 `json:"VideoBitrate"`
Fps int32 `json:"Fps"`
Height int32 `json:"Height"`
Width int32 `json:"Width"`
}
func main() {
nonce := make([]byte, 8)
rand.Read(nonce)
hexNonce := hex.EncodeToString(nonce)
ts := time.Now()
signature := GenerateSignature(AppId, hexNonce, Secret, ts.Unix())
value := url.Values{}
value.Add("AppId", strconv.FormatUint(AppId, 10))
value.Add("SignatureNonce", hexNonce)
value.Add("Timestamp", strconv.FormatInt(ts.Unix(), 10))
value.Add("Signature", signature)
value.Add("Action", "StartMix")
value.Add("SignatureVersion", "2.0")
urlData, err := url.Parse(DOMAIN)
if err != nil {
fmt.Println(err)
return
}
urlData.RawQuery = value.Encode()
dataJson, _ := json.Marshal(GenerateStartMixData())
req, err := http.NewRequest("POST", urlData.String(), bytes.NewBuffer(dataJson))
if err != nil {
fmt.Println(err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Close = true
client := &http.Client{}
r, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
if r.StatusCode != 200 {
fmt.Printf("status code is:%v", r.StatusCode)
}
defer r.Body.Close()
resp, err := ioutil.ReadAll(r.Body)
if err != nil {
return
}
fmt.Println(string(resp))
return
}
//The following example is to mix the audio/video stream with streamId "stream1" and the audio/video stream with streamId "stream2", and output the audio/video stream with streamId "stream3"
func GenerateStartMixData() *MixStartRequest {
inputs := make([]*MixInput, 0)
input1 := MixInput{
StreamId: "stream1",
RectInfo: &RectInfo{
Top: 70,
Left: 100,
Bottom: 160,
Right: 260,
},
}
inputs = append(inputs, &input1)
input2 := MixInput{
StreamId: "stream2",
RectInfo: &RectInfo{
Top: 200,
Left: 100,
Bottom: 290,
Right: 260,
},
}
inputs = append(inputs, &input2)
output := MixOutput{
StreamId: "stream3",
VideoBitrate: 12000,
Fps: 15,
Height: 360,
Width: 360,
}
outputs := append([]*MixOutput{}, &output)
req := &MixStartRequest{
Appid: AppId,
UserId: "123",
Sequence: 123,
MixInput: inputs,
MixOutput: outputs,
MixTaskId: "123",
}
return req
}
//Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp)
func GenerateSignature(appid uint64, nonce string, appSecret string, timestamp int64) (signature string) {
data := fmt.Sprintf("%d%s%s%d", appid, nonce, appSecret, timestamp)
h := md5.New() //Specifying the use of the MD5 algorithm in the hash algorithm
h.Write([]byte(data))
//hex.EncodeToString(h.Sum(nil)) outputs a hexadecimal string
return hex.EncodeToString(h.Sum(nil))
}# -*- coding: UTF-8 -*-
import hashlib
import secrets
import time
# Generate signature
# Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp)
from urllib.parse import urlencode
import requests
def GenerateSignature(appId, signatureNonce, serverSecret, timestamp):
str1 = str(appId) + signatureNonce + serverSecret + str(timestamp)
hash = hashlib.md5()
hash.update(str1.encode("utf8"))
signature = hash.hexdigest()
return signature
if __name__ == '__main__':
# Please modify appId to your appId, appid is a number
# For example: 1234567890
appId = 1234567890
# Please modify serverSecret to your serverSecret, serverSecret is string
serverSecret = ""
signatureNonce = secrets.token_hex(8)
timestamp = int(time.time())
sig = GenerateSignature(appId, signatureNonce, serverSecret, timestamp)
# The following example mixes the audio/video stream with StreamId "stream1" and the audio/video stream with StreamId "stream2", and outputs the audio/video stream with StreamId "stream3"
par = {
"Action": "StartMix",
"AppId": appId,
"Signature": sig,
"SignatureNonce": signatureNonce,
"SignatureVersion": "2.0",
"Timestamp": timestamp,
"IsTest": "False"
}
body = {
'TaskId': '123',
'Sequence': 123,
'UserId': '123',
'MixInput': [
{
'StreamId': 'stream1',
'RectInfo': {
"Top": 70,
"Bottom": 160,
"Left": 100,
"Right": 260,
},
},
{
'StreamId': 'stream2',
'RectInfo': {
"Top": 200,
"Bottom": 290,
"Left": 100,
"Right": 260,
},
}
],
'MixOutput': [{
'StreamId': 'stream3',
'Width': 360,
'Height': 360,
'VideoBitrate': 12000,
'Fps': 15
}]
}
url = 'https://rtc-api.zego.im/'
req = requests.post(url, params=par, json=body)
print("Url: ", req.url)
print("StatusCode", req.status_code)
print("Respone:", req.text)package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
/**
* Please first introduce com.alibaba.fastjson for easy construction of request body parameters
*/
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
public class StartMix {
//
public static void main(String[] args) {
// Get the number of users in the room
startMixStream();
}
/**
* Initiate Stream Mixing
*/
public static void startMixStream() {
// Please fill in your APP ID obtained from the console,
// For example: 1234567890L
Long APP_ID = 1234567890L;
// Please fill in your SERVER SECRET obtained from the console backend,
// For example: "abcde1234aaaaaaaabbbbbbb"
String SERVER_SECRET = ;
// Generate a random hexadecimal string (16 bits)
byte[] bytes = new byte[8];
SecureRandom sr = new SecureRandom();
sr.nextBytes(bytes);
String signatureNonce = bytesToHex(bytes);
// Set timestamp
long timestamp = System.currentTimeMillis() / 1000L;
// Generate signature Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp)
String signatue = generateSignature(APP_ID, signatureNonce, SERVER_SECRET, timestamp);
// Assemble request URL
//The following example mixes the audio/video stream with StreamId "stream1" and the audio/video stream with StreamId "stream2", and outputs the audio/video stream with StreamId "stream3"
Map<String, Object> params = new HashMap<String, Object>();
params.put("Action", "StartMix");
params.put("AppId", APP_ID);
params.put("SignatureNonce", signatureNonce);
params.put("Timestamp", timestamp);
params.put("Signature", signatue);
params.put("SignatureVersion", "2.0");
// rtc-api.zego.im indicates the product used is the cloud communication product, including Real-time Audio/Video (Express Video), Real-time Audio (Express Audio), and Low-latency Live Streaming (L3)
String url = buildAPIUrl("https://rtc-api.zego.im/", params);
JSONObject body = new JSONObject()
.fluentPut("TaskId", "123")
.fluentPut("Sequence", 123)
.fluentPut("UserId", "123");
JSONArray mixInputList = new JSONArray();
mixInputList.add(new JSONObject()
.fluentPut("StreamId", "stream1")
.fluentPut("RectInfo", new JSONObject()
.fluentPut("Top", 70)
.fluentPut("Bottom", 160)
.fluentPut("Left", 100)
.fluentPut("Right", 260)));
mixInputList.add(new JSONObject()
.fluentPut("StreamId", "stream2")
.fluentPut("RectInfo", new JSONObject()
.fluentPut("Top", 200)
.fluentPut("Bottom", 290)
.fluentPut("Left", 100)
.fluentPut("Right", 260)));
body.put("MixInput", mixInputList);
JSONArray mixOutputList = new JSONArray();
mixOutputList.add(new JSONObject()
.fluentPut("StreamId", "stream3")
.fluentPut("Width", 360)
.fluentPut("Height", 360)
.fluentPut("VideoBitrate", 12000)
.fluentPut("Fps", 15));
body.put("MixOutput", mixOutputList);
String result = sendPost(url, body.toString());
System.out.println(result);
}
/**
* Generate signature
* @param appId Application APPID
* @param signatureNonce Signature random code
* @param serverSecret Server-side API key
* @param timestamp Signature timestamp
* @return Generated signature string
*/
public static String generateSignature(long appId, String signatureNonce, String serverSecret, long timestamp){
String str = String.valueOf(appId) + signatureNonce + serverSecret + String.valueOf(timestamp);
String signature = "";
try{
//Create an object that provides the message digest algorithm, initialized as an MD5 algorithm object
MessageDigest md = MessageDigest.getInstance("MD5");
//Calculate to get byte array
byte[] bytes = md.digest(str.getBytes("utf-8"));
//Convert each byte of the array to hexadecimal and concatenate it into an MD5 string
signature = bytesToHex(bytes);
}catch (Exception e) {
e.printStackTrace();
}
return signature;
}
/**
* Convert byte array to hexadecimal
* @param bytes Byte array to be converted
* @return Converted Hex string
*/
public static String bytesToHex(byte[] bytes) {
StringBuffer md5str = new StringBuffer();
//Convert each byte of the array to hexadecimal and concatenate it into an MD5 string
int digital;
for (int i = 0; i < bytes.length; i++) {
digital = bytes[i];
if (digital < 0) {
digital += 256;
}
if (digital < 16) {
md5str.append("0");
}
md5str.append(Integer.toHexString(digital));
}
return md5str.toString();
}
/**
* Send HTTP-GET request
* @param httpurl Request path
* @return Response content
*/
public static String sendGet(String httpurl) {
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;// Return result string
try {
// Create remote url connection object
URL url = new URL(httpurl);
// Open a connection through the remote url connection object, cast to HttpURLConnection class
connection = (HttpURLConnection) url.openConnection();
// Set connection method: get
connection.setRequestMethod("GET");
// Set connection host server timeout: 15000 milliseconds
connection.setConnectTimeout(15000);
// Set read remote return data timeout: 60000 milliseconds
connection.setReadTimeout(60000);
// Send request
connection.connect();
// Get input stream through connection connection
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
// Encapsulate input stream is, specify character set
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
// Store data
StringBuffer sbf = new StringBuffer();
String temp = null;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// Close resources
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();// Close remote connection
}
return result;
}
/**
* Send HTTP-POST request
* @param param Message body parameter
* @return Response content
*/
public static String sendPost(String httpUrl, String param) {
HttpURLConnection connection = null;
InputStream is = null;
OutputStream os = null;
BufferedReader br = null;
String result = null;
try {
URL url = new URL(httpUrl);
// Open connection through remote url connection object
connection = (HttpURLConnection) url.openConnection();
// Set connection request method
connection.setRequestMethod("POST");
// Set connection host server timeout: 15000 milliseconds
connection.setConnectTimeout(15000);
// Set read host server return data timeout: 60000 milliseconds
connection.setReadTimeout(60000);
// Default value: false, when transmitting data/writing data to remote server, need to set to true
connection.setDoOutput(true);
// Default value: true, when reading data from remote service, set to true, this parameter is optional
connection.setDoInput(true);
// Set format of incoming parameters: request parameters should be in the form of name1=value1&name2=value2
connection.setRequestProperty("Content-Type", "application/json");
// Establish connection
connection.connect();
// Get an output stream object through the connection object
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(),"UTF-8");
writer.write(param);
writer.flush();
// Get an input stream through the connection object to read remotely
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
// Wrap input stream object: charset is set according to project team requirements
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuffer sbf = new StringBuffer();
String temp = null;
// Loop to read data line by line
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r");
}
result = sbf.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// Close resources
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != os) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Disconnect from remote address url
connection.disconnect();
}
return result;
}
/**
* Assemble API address string
* @param url API address
* @param params Request parameters
* @return Assembled API address string
*/
public static String buildAPIUrl(String url, Map<String, Object> params) {
if(params.isEmpty()) {
return url;
}
StringBuffer buffer = new StringBuffer(url).append("?");
for(String key : params.keySet()) {
buffer.append(key).append("=").append(params.get(key)).append("&");
}
String apiurl = buffer.toString();
if(apiurl.endsWith("&")) {
return apiurl.substring(0, apiurl.length()-1);
} else {
return apiurl;
}
}
}<?php
/**
* Simplest example code to initiate Stream Mixing
*/
//Please modify appId to your appId
//For example: 1234567890
$appId = 1234567890;
//Please modify serverSecret to your serverSecret, serverSecret is string
$serverSecret = "";
//Generate signature
//Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp)
function GenerateSignature($appId, $signatureNonce, $serverSecret, $timeStamp){
$str = $appId . $signatureNonce . $serverSecret . $timeStamp;
//Use the standard MD5 algorithm in PHP, which returns a 32-character hexadecimal number by default
return md5($str);
}
//Get random string and convert to hexadecimal value
$signatureNonce = bin2hex(random_bytes(8));
//Get millisecond-level timestamp
list($msec, $sec) = explode(' ', microtime());
$msecTime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
//Round $msecTime to get second-level timestamp
$timeStamp = round($msecTime/1000);
$sig = GenerateSignature($appId, $signatureNonce, $serverSecret, $timeStamp);
//The following example mixes the audio/video stream with StreamId "stream1" and the audio/video stream with StreamId "stream2", and outputs the audio/video stream with StreamId "stream3"
$url = "https://rtc-api.zego.im/?Action=StartMix&AppId=$appId&SignatureNonce=$signatureNonce&Timestamp=$timeStamp&Signature=$sig&SignatureVersion=2.0&IsTest=false";
$body = [
"TaskId" => "123",
"Sequence" => 123,
"UserId" => "123",
"MixInput" => [
[
"StreamId" => "stream1",
"RectInfo" => [
"Top" => 70,
"Bottom" => 160,
"Left" => 100,
"Right" => 260,
],
],
[
"StreamId" => "stream2",
"RectInfo" => [
"Top" => 200,
"Bottom" => 290,
"Left" => 100,
"Right" => 260,
],
]
],
"MixOutput" => [
[
"StreamId" => "stream3",
"Width" => 360,
"Height" => 360,
"VideoBitrate" => 12000,
"Fps" => 15
]
]
];
$post_string = json_encode($body);
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_POST, 1);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
//Set request connection timeout, unit: seconds
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json; charset=utf-8',
)
);
$response = curl_exec($ch);
curl_close($ch);
echo "Url:" . $url;
echo "\r\n";
echo "request body:" . $post_string;
echo "\r\n";
echo "response:" . $response;const crypto = require('crypto');
const request = require('request');
//Please modify appId to your appId, appid is number
var appId = ;
//Please modify serverSecret to your serverSecret, serverSecret is string
var serverSecret = "";
//Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp)
function GenerateSignature(appId, signatureNonce, serverSecret, timeStamp) {
const hash = crypto.createHash('md5'); //Specifying the use of the MD5 algorithm in the hash algorithm
var str = appId + signatureNonce + serverSecret + timeStamp;
hash.update(str);
//hash.digest('hex') indicates the output format is hexadecimal
return hash.digest('hex');
}
var signatureNonce = crypto.randomBytes(8).toString('hex');
var timeStamp = Math.round(Date.now() / 1000);
var sig = GenerateSignature(appId, signatureNonce, serverSecret, timeStamp)
//The following example mixes the audio/video stream with StreamId "stream1" and the audio/video stream with StreamId "stream2", and outputs the audio/video stream with StreamId "stream3"
var url = `https://rtc-api.zego.im/?Action=StartMix&AppId=${appId}&SignatureNonce=${signatureNonce}&Timestamp=${timeStamp}&Signature=${sig}&SignatureVersion=2.0&IsTest=false`
var body = {
'TaskId': '123',
'Sequence': 123,
'UserId': '123',
'MixInput': [
{
'StreamId': 'stream1',
'RectInfo': {
"Top": 70,
"Bottom": 160,
"Left": 100,
"Right": 260,
},
},
{
'StreamId': 'stream2',
'RectInfo': {
"Top": 200,
"Bottom": 290,
"Left": 100,
"Right": 260,
},
}
],
'MixOutput': [{
'StreamId': 'stream3',
'Width': 360,
'Height': 360,
'VideoBitrate': 12000,
'Fps': 15
}]
}
request({
url: url,
method: 'POST',
json: true,
headers: {
'content-type': 'application/json'
},
body: body
}, function (error, response, body) {
console.log(error)
console.log(response.statusCode)
if (!error && response.statusCode === 200) {
console.log(body.Data)
}
})Parameter Description
In the test environment (see IsTest common parameter description for details), for the Stream IDs of input streams and output streams:
- If the original Stream ID is entered by the developer, the "zegotest-AppId-" prefix must be added, otherwise Stream Mixing will fail (the Stream Mixing server cannot pull the input stream or pull the Stream Mixing output stream). For example, if the developer enters Stream ID "test", in the test environment with AppId "123456789", the Stream ID should be "zegotest-123456789-test".
- If obtained through SDK interfaces or server-side API interfaces, there is no need to add the "zegotest-AppId-" prefix.
For all request parameters below, parameters of type "String" only support numbers, English characters, and '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', ''', ',', '.', '<', '>', '/', ''.
The following parameter description includes all parameters for the Start Stream Mixing interface, including some advanced features (e.g., setting watermarks, background images, setting extended parameters, etc.).
If you want to quickly initiate a Stream Mixing task, please refer to the Example in the REQUEST panel on the right. Fill in the basic parameters to initiate the task.
FAQ
-
When mixing pure audio with a background image set, the background image cannot be displayed normally. How to handle this?
In this case, the customer needs to correctly set the width and height of the output layout according to their business requirements, and contact ZEGOCLOUD technical support to configure and enable black frame padding.
Request
Query Parameters
Possible values: [StartMix]
Interface prototype parameters
https://rtc-api.zego.im?Action=StartMix
💡Public parameter. Application ID, assigned by ZEGOCLOUD. Get it from the ZEGOCLOUD Admin Console.
💡Common parameter. A 16-character hexadecimal random string (hex-encoded 8-byte random number). For the generation algorithm, refer to the signature example.
💡Common parameter. Current Unix timestamp in seconds. For the generation algorithm, refer to the signature example. A maximum deviation of 10 minutes is allowed.
💡Common parameter. Signature used to verify the legitimacy of the request. Please refer to the signature mechanism to generate it.
Possible values: [2.0]
Default value: 2.0
💡Public parameter. Signature version number.
- application/json
Responses
- 200
- application/json



