生成推流地址和播放地址

阿里云视频直播提供触发式推流与播放,您无需提前创建资源,只要添加经过备案的推流域名和播流域名,并完成域名解析、鉴权等操作,即可快速拼接生成推流地址和播放地址。通过阅读本文,您可以了解推流地址和播放地址的拼接规则及生成方法。

使用说明

  • 推流地址和播放地址可以创建多个,并同时执行直播活动。进行直播活动时需要注意每个直播推流域名的并发限制,详情请参见使用限制。进行直播活动前先确认当前限制是否满足您的需求。如果不满足,请联系我们

  • 推流地址和播放地址默认已开启URL鉴权(相关配置可参见URL鉴权配置),生成的推/播流地址需要附带鉴权串参数,才可以推流/播放。

直播地址

推流地址

  • 拼接规则:推流域名+AppName+StreamName+{鉴权串}

    • AppNameStreamName可以自定义。

    • {鉴权串}是根据算法(MD5)得出的一个加密字符串,推流地址中需要附带鉴权串参数,鉴权通过后才能进行推流。{鉴权串}说明可参见鉴权URL组成

    • 视频直播是通过StreamName而非AppName来判断直播流是否唯一。如果您设置了不同的AppName,还要保证StreamName不同,才能保证最终的直播流不同。如果需要同一个域名下使用不同的AppName推相同的流(StreamName),请联系我们开启App隔离功能。

  • 地址示例:

    • RTMP:rtmp://demo.aliyundoc.com/app/stream?auth_key={鉴权串}

    • ARTC:artc://demo.aliyundoc.com/app/stream?auth_key={鉴权串}

    • SRT:srt://demo.aliyundoc.com:1105?streamid=#!::h=demo.aliyundoc.com,r=/app/stream?auth_key={鉴权串},m=publish

      • SRT协议推流默认处于关闭状态,您需要对推流域名开启SRT协议(操作指引可参见SRT推流)才能使用。

      • m=publish不在鉴权串中,例如:srt://demo.aliyunlive.com:1105?streamid=#!::h=demo.aliyunlive.com,r=/1/1?auth_key=1661312655-0-0-3fded78b61b14071****aa6e6d2,m=publish

  • 生成推流地址:

    说明

    如果是纯拉流场景(比如回源拉流),您可以不用生成推流地址(只生成播放地址),但仍需关联推/播流域名

    • 您可以通过直播地址生成器生成推流地址,直播地址生成器生成的推流地址会附带加密的{鉴权串}。您也可以通过代码生成推流地址。生成地址之后可以使用自助问题排查功能对地址进行检测,验证地址,鉴权等信息是否有效。

    • 您可以参考直播推流文档使用推流地址进行推流。

播放地址

  • 拼接规则:播流域名+AppName+StreamName+{鉴权串}

    • AppNameStreamName需要与推流地址中的值保持一致。

    • {鉴权串}使用方法同推流地址

  • 地址示例:

    • RTMP:rtmp://example.aliyundoc.com/app/stream?auth_key={鉴权串}

    • FLV:http://example.aliyundoc.com/app/stream.flv?auth_key={鉴权串}

    • HLS:http://example.aliyundoc.com/app/stream.m3u8?auth_key={鉴权串}

    • ARTC(阿里云超低延时直播RTS):artc://example.aliyundoc.com/app/stream?auth_key={鉴权串}

    • SRT:推流为SRT协议时,播放协议支持RTMP、FLV、HLS、ARTC。SRT协议播放暂未对外开放,若需使用,请联系您的阿里云客户经理或联系我们进行开通。

  • 协议支持:

    播放地址

    RTMP

    FLV

    HLS

    ARTC

    说明

    直播播放地址

    支持

    支持

    支持

    支持

    • 示例:rtmp://example.aliyundoc.com/app/stream?auth_key={鉴权串}

    转码流播放地址(通用转码/自定义转码

    支持

    支持

    支持

    支持

    • 转码流地址需要在StreamName后加_转码模板ID转码模板ID需通过直播转码功能进行配置。

    • 示例:rtmp://example.aliyundoc.com/app/stream_{转码模板ID}?auth_key={鉴权串}

    转码流播放地址(多码率转码

    暂不支持

    暂不支持

    支持

    暂不支持

    • 多码率转码流地址需要在StreamName后加_转码模板组ID,地址中需增加aliyunols=on参数,转码模板组ID需通过直播转码功能进行配置。

    • 示例:http://example.aliyundoc.com/app/stream_{转码模板组ID}.m3u8?aliyunols=on&auth_key={鉴权串}

    延播播放地址

    支持

    支持

    支持

    支持

    • 延播播放地址需要在StreamName后加-alidelay。使用延播播放前需进行延播配置

    • 示例:rtmp://example.aliyundoc.com/app/stream-alidelay?auth_key={鉴权串}

    实时字幕播放地址

    支持

    支持

    支持

    暂不支持

    • 实时字幕播放地址需要在StreamName后加_字幕模板名称字幕模板名称需通过实时字幕功能进行配置。

    • 示例:rtmp://example.aliyundoc.com/app/stream_{字幕模板名称}?auth_key={鉴权串}

  • 生成播放地址:

    说明

    如果是纯推流场景(比如中心转推),您可以不用生成播放地址(只生成推流地址),但仍需关联推/播流域名

    • 播放地址生成方法同推流地址

      • 延播播放地址:使用直播地址生成器生成地址时,如果播流域名直播中心支持延播功能(可参见服务地域),生成的地址中会包含延播地址。但若对应的AppNameStreamName未进行延播配置,生成延播地址无法进行拉流。

      • 实时字幕播放地址:暂不支持使用直播地址生成器生成。

    • 您可以参考直播播放文档使用播放地址进行播放。

代码生成推/播流地址

第一步,集成SDK

pom.xml文件中引入SDK。

<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>alibabacloud-live20161101</artifactId>
  <version>2.0.3</version>
</dependency>

第二步,获取鉴权Key

在使用代码生成鉴权URL前,您需要获取鉴权Key。鉴权Key用于对URL进行加密,您可以通过控制台URL鉴权配置或调用DescribeLiveDomainConfigsAPI获取鉴权Key。

Java SDK获取鉴权Key示例代码

// This file is auto-generated, don't edit it. Thanks.
package demo;

import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.core.http.HttpClient;
import com.aliyun.core.http.HttpMethod;
import com.aliyun.core.http.ProxyOptions;
import com.aliyun.httpcomponent.httpclient.ApacheAsyncHttpClientBuilder;
import com.aliyun.sdk.service.live20161101.models.*;
import com.aliyun.sdk.service.live20161101.*;
import com.google.gson.Gson;
import darabonba.core.RequestConfiguration;
import darabonba.core.client.ClientOverrideConfiguration;
import darabonba.core.utils.CommonUtil;
import darabonba.core.TeaPair;

//import javax.net.ssl.KeyManager;
//import javax.net.ssl.X509TrustManager;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.io.*;

public class DescribeLiveDomainConfigs {
    public static void main(String[] args) throws Exception {

        // HttpClient Configuration
        /*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
                .connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
                .responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds
                .maxConnections(128) // Set the connection pool size
                .maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds
                // Configure the proxy
                .proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<YOUR-PROXY-HOSTNAME>", 9001))
                        .setCredentials("<YOUR-PROXY-USERNAME>", "<YOUR-PROXY-PASSWORD>"))
                // If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))
                .x509TrustManagers(new X509TrustManager[]{})
                .keyManagers(new KeyManager[]{})
                .ignoreSSL(false)
                .build();*/

        // Configure Credentials authentication information, including ak, secret, token
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                // Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.
                .accessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                .accessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
                //.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token
                .build());

        // Configure the Client
        AsyncClient client = AsyncClient.builder()
                .region("<Your RegionId>") // Region ID
                //.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
                .credentialsProvider(provider)
                //.serviceConfiguration(Configuration.create()) // Service-level configuration
                // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
                .overrideConfiguration(
                        ClientOverrideConfiguration.create()
                                  // Endpoint 请参考 https://api.aliyun.com/product/live
                                .setEndpointOverride("live.aliyuncs.com")
                        //.setConnectTimeout(Duration.ofSeconds(30))
                )
                .build();

        // Parameter settings for API request
        DescribeLiveDomainConfigsRequest describeLiveDomainConfigsRequest = DescribeLiveDomainConfigsRequest.builder()
                .domainName("<Your DomainName>")
                // Request-level configuration rewrite, can set Http request parameters, etc.
                // .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
                .build();

        // Asynchronously get the return value of the API request
        CompletableFuture<DescribeLiveDomainConfigsResponse> response = client.describeLiveDomainConfigs(describeLiveDomainConfigsRequest);
        // Synchronously get the return value of the API request
        DescribeLiveDomainConfigsResponse resp = response.get();
        System.out.println(new Gson().toJson(resp));
        // Asynchronous processing of return values
        /*response.thenAccept(resp -> {
            System.out.println(new Gson().toJson(resp));
        }).exceptionally(throwable -> { // Handling exceptions
            System.out.println(throwable.getMessage());
            return null;
        });*/

        // Finally, close the client
        client.close();
    }

}

获取到鉴权Key后就可以URL进行加密

说明
  • 生成推流地址时,要使用推流域名的鉴权Key

  • 生成播放地址时,要使用播流域名的鉴权Key

第三步,对URL进行加密

在此之前您可以通过鉴权URL组成了解鉴权URL结构。以下示例代码以RTMP协议为例,生成鉴权URL。

Java

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AuthDemo {
    private static String md5Sum(String src) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        md5.update(StandardCharsets.UTF_8.encode(src));
        return String.format("%032x", new BigInteger(1, md5.digest()));
    }

private static String aAuth(String uri, String key, long exp) {
    String pattern = "^(rtmp://)?([^/?]+)(/[^?]*)?(\\\\?.*)?$";
    Pattern r = Pattern.compile(pattern);
    Matcher m = r.matcher(uri);
    String scheme = "", host = "", path = "", args = "";
    if (m.find()) {
        scheme = m.group(1) == null ? "rtmp://" : m.group(1);
        host = m.group(2) == null ? "" : m.group(2);
        path = m.group(3) == null ? "/" : m.group(3);
        args = m.group(4) == null ? "" : m.group(4);
    } else {
        System.out.println("NO MATCH");
    }

    String rand = "0";  // "0" by default, other value is ok
    String uid = "0";   // "0" by default, other value is ok
    String sString = String.format("%s-%s-%s-%s-%s", path, exp, rand, uid, key);
    String hashValue = md5Sum(sString);
    String authKey = String.format("%s-%s-%s-%s", exp, rand, uid, hashValue);
    if (args.isEmpty()) {
        return String.format("%s%s%s%s?auth_key=%s", scheme, host, path, args, authKey);
    } else {
        return String.format("%s%s%s%s&auth_key=%s", scheme, host, path, args, authKey);
    }
}

public static void main(String[] args) {
    // 待加密的推/播流地址,example.aliyundoc.com为推/播流域名,liveApp为AppName,liveStream为StreamName
    // 推流和播流URL采用同样的加密方法
    // AppName或StreamName不超过256字符,支持数字、大小写字母、短划线(-)、下划线(_)、等号(=)。
    String uri = "rtmp://example.aliyundoc.com/liveApp/liveStream";  
    // 鉴权Key,推流地址使用推流域名URL鉴权Key,播流地址使用播流域名URL鉴权Key
    String key = "<input private key>";                       
    // exp值为UNIX时间戳,单位秒,最终失效时间由该值加上域名URL鉴权有效时长决定
    // 比如您在此处设置exp为:当前时间+3600秒,那最终失效时间为:当前时间+3600秒+域名URL鉴权有效时长。如果设置exp为:当前时间,那最终失效时间为:当前时间+域名URL鉴权有效时长
    long exp = System.currentTimeMillis() / 1000 + 1 * 3600;  
    String authUri = aAuth(uri, key, exp);                    
    System.out.printf("URL : %s\nAuth: %s", uri, authUri);
}
}

Python

import re
import time
import hashlib
import datetime
def md5sum(src):
    m = hashlib.md5()
    m.update(src)
    return m.hexdigest()
def a_auth(uri, key, exp):
    p = re.compile("^(rtmp://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
    if not p:
        return None
    m = p.match(uri)
    scheme, host, path, args = m.groups()
    if not scheme: scheme = "rtmp://"
    if not path: path = "/"
    if not args: args = ""
    rand = "0"      # "0" by default, other value is ok
    uid = "0"       # "0" by default, other value is ok
    sstring = "%s-%s-%s-%s-%s" %(path, exp, rand, uid, key)
    hashvalue = md5sum(sstring.encode('utf-8'))
    auth_key = "%s-%s-%s-%s" %(exp, rand, uid, hashvalue)
    if args:
        return "%s%s%s%s&auth_key=%s" %(scheme, host, path, args, auth_key)
    else:
        return "%s%s%s%s?auth_key=%s" %(scheme, host, path, args, auth_key)
def main():
    # 待加密的推/播流地址,example.aliyundoc.com为推/播流域名,liveApp为AppName,liveStream为StreamName
    # 推流和播流URL采用同样的加密方法
    # AppName或StreamName不超过256字符,支持数字、大小写字母、短划线(-)、下划线(_)、等号(=)。
    uri = "rtmp://example.aliyundoc.com/liveApp/liveStream"  
    # 鉴权Key,推流地址使用推流域名URL鉴权Key,播流地址使用播流域名URL鉴权Key
    key = "<input private key>"                         
    # exp值为UNIX时间戳,单位秒,最终失效时间由该值加上域名URL鉴权有效时长决定
    # 比如您在此处设置exp为:当前时间+3600秒,那最终失效时间为:当前时间+3600秒+域名URL鉴权有效时长。如果设置exp为:当前时间,那最终失效时间为:当前时间+域名URL鉴权有效时长
    exp = int(time.time()) + 1 * 3600                   
    authuri = a_auth(uri, key, exp)                     
    print("URL : %s\nAUTH: %s" %(uri, authuri))
if __name__ == "__main__":
    main()

Go

package main
import (
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "regexp"
    "time"
)

func md5sum(src string) string {
    h := md5.New()
    h.Write([]byte(src))
    return hex.EncodeToString(h.Sum(nil))
}

func a_auth(uri, key string, exp int64) string {
    p, err := regexp.Compile("^(rtmp://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
    if err != nil {
        fmt.Println(err)
        return ""
    }
    m := p.FindStringSubmatch(uri)
    var scheme, host, path, args string
    if len(m) == 5 {
        scheme, host, path, args = m[1], m[2], m[3], m[4]
    } else {
        scheme, host, path, args = "rtmp://", "", "/", ""
    }
    rand := "0" // "0" by default, other value is ok
    uid := "0"  // "0" by default, other value is ok
    sstring := fmt.Sprintf("%s-%d-%s-%s-%s", path, exp, rand, uid, key)
    hashvalue := md5sum(sstring)
    auth_key := fmt.Sprintf("%d-%s-%s-%s", exp, rand, uid, hashvalue)
    if len(args) != 0 {
        return fmt.Sprintf("%s%s%s%s&auth_key=%s", scheme, host, path, args, auth_key)
    } else {
        return fmt.Sprintf("%s%s%s%s?auth_key=%s", scheme, host, path, args, auth_key)
    }
}

func main() {
    // 待加密的推/播流地址,example.aliyundoc.com为推/播流域名,liveApp为AppName,liveStream为StreamName
    // 推流和播流URL采用同样的加密方法
    // AppName或StreamName不超过256字符,支持数字、大小写字母、短划线(-)、下划线(_)、等号(=)。
    uri := "rtmp://example.aliyundoc.com/liveApp/liveStream" 
    // 鉴权Key,推流地址使用推流域名URL鉴权Key,播流地址使用播流域名URL鉴权Key
    key := "<input private key>"     
    // exp值为UNIX时间戳,单位秒,最终失效时间由该值加上域名URL鉴权有效时长决定
    // 比如您在此处设置exp为:当前时间+3600秒,那最终失效时间为:当前时间+3600秒+域名URL鉴权有效时长。如果设置exp为:当前时间,那最终失效时间为:当前时间+域名URL鉴权有效时长   
    exp := time.Now().Unix() + 3600                                    
    authuri := a_auth(uri, key, exp)                                       
    fmt.Printf("URL : %s\nAUTH: %s", uri, authuri)
}

PHP

<?php
function a_auth($uri, $key, $exp) {
    preg_match("/^(rtmp:\/\/)?([^\/?]+)?(\/[^?]*)?(\\?.*)?$/", $uri, $matches);
    $scheme = $matches[1];
    $host = $matches[2];
    $path = $matches[3];
    $args = $matches[4];
    if  (empty($args)) {
        $args ="";
    }
    if  (empty($scheme)) {
        $scheme ="rtmp://";
    }
    if  (empty($path)) {
        $path ="/";
    }
    $rand = "0";
    // "0" by default, other value is ok
    $uid = "0";
    // "0" by default, other value is ok
    $sstring = sprintf("%s-%u-%s-%s-%s", $path, $exp, $rand, $uid, $key);
    $hashvalue = md5($sstring);
    $auth_key = sprintf("%u-%s-%s-%s", $exp, $rand, $uid, $hashvalue);
    if ($args) {
        return sprintf("%s%s%s%s&auth_key=%s", $scheme, $host, $path, $args, $auth_key);
    } else {
        return sprintf("%s%s%s%s?auth_key=%s", $scheme, $host, $path, $args, $auth_key);
    }
}
// 待加密的推/播流地址,example.aliyundoc.com为推/播流域名,liveApp为AppName,liveStream为StreamName
// 推流和播流URL采用同样的加密方法
// AppName或StreamName不超过256字符,支持数字、大小写字母、短划线(-)、下划线(_)、等号(=)。
$uri = "rtmp://example.aliyundoc.com/liveApp/liveStream";
// 鉴权Key,推流地址使用推流域名URL鉴权Key,播流地址使用播流域名URL鉴权Key
$key = "<input private key>";
// exp值为UNIX时间戳,单位秒,最终失效时间由该值加上域名URL鉴权有效时长决定
// 比如您在此处设置exp为:当前时间+3600秒,那最终失效时间为:当前时间+3600秒+域名URL鉴权有效时长。如果设置exp为:当前时间,那最终失效时间为:当前时间+域名URL鉴权有效时长
$exp = time() + 3600;
$authuri = a_auth($uri, $key, $exp);
echo "URL :" . $uri;
echo PHP_EOL;
echo "AUTH:" . $authuri;
?>

C#

using System;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Text;
public class Test
{
    public static void Main()
    {
        // 待加密的推/播流地址,example.aliyundoc.com为推/播流域名,liveApp为AppName,liveStream为StreamName
        // 推流和播流URL采用同样的加密方法
        // AppName或StreamName不超过256字符,支持数字、大小写字母、短划线(-)、下划线(_)、等号(=)。
        string uri= "rtmp://example.aliyundoc.com/liveApp/liveStream";  
        // 鉴权Key,推流地址使用推流域名URL鉴权Key,播流地址使用播流域名URL鉴权Key
        string key= "<input private key>";                           
        DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);
        // exp值为UNIX时间戳,单位秒,最终失效时间由该值加上域名URL鉴权有效时长决定
        // 比如您在此处设置exp为:当前时间+3600秒,那最终失效时间为:当前时间+3600秒+域名URL鉴权有效时长。如果设置exp为:当前时间,那最终失效时间为:当前时间+域名URL鉴权有效时长
        string exp  = Convert.ToInt64((DateTime.Now - dateStart).TotalSeconds+3600).ToString(); 
        string authUri = aAuth(uri, key, exp);
        Console.WriteLine (String.Format("URL :{0}",uri));
        Console.WriteLine (String.Format("AUTH :{0}",authUri));
    }
    public static string aAuth(string uri, string key, string exp)
    {
        Regex regex = new Regex("^(rtmp://)?([^/?]+)(/[^?]*)?(\\\\?.*)?$");
        Match m = regex.Match(uri);
        string scheme = "rtmp://", host = "", path = "/", args = "";
        if (m.Success)
        {
            scheme=m.Groups[1].Value;
            host=m.Groups[2].Value;
            path=m.Groups[3].Value;
            args=m.Groups[4].Value;
        }else{
            Console.WriteLine ("NO MATCH");
        }
        string rand = "0";  // "0" by default, other value is ok
        string uid = "0";   // "0" by default, other value is ok
        string u = String.Format("{0}-{1}-{2}-{3}-{4}",  path, exp, rand, uid, key);
        string hashValue  = Md5(u);
        string authKey = String.Format("{0}-{1}-{2}-{3}", exp, rand, uid, hashValue);
        if (args=="")
        {
            return String.Format("{0}{1}{2}{3}?auth_key={4}", scheme, host, path, args, authKey);
        } else
        {
            return String.Format("{0}{1}{2}{3}&auth_key={4}", scheme, host, path, args, authKey);
        }
    }
    public static string Md5(string value)
    {
        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
        byte[] bytes = Encoding.ASCII.GetBytes(value);
        byte[] encoded = md5.ComputeHash(bytes);
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<encoded.Length; ++i)
        {
            sb.Append(encoded[i].ToString("x2"));
        }
        return sb.ToString();
   }
}

相关文档

使用Java SDK,请参见Java SDK使用说明

更多鉴权URL说明,请参见推/播流地址鉴权