OSS 直播功能深度分享

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 场景描述 目前有很多直播爱好者使用的是 OSS + RTMP 做的推流直播,其中不乏一些企业级别应用,由于 OSS 作为推流的接收略微有一些复杂,故单独开篇讲一下。其实不建议使用 OSS+RTMP 做直播推流,因为功能性相比专业的阿里云直播产品来说,OSS 的推流适合监管备份等特定场景 ,客户端对直播推流的延迟要求不是很敏感。

场景描述

目前有很多直播爱好者使用的是 OSS + RTMP 做的推流直播,其中不乏一些企业级别应用,由于 OSS 作为推流的接收略微有一些复杂,故单独开篇讲一下。其实不建议使用 OSS+RTMP 做直播推流,因为功能性相比专业的阿里云直播产品来说,OSS 的推流适合监管备份等特定场景 ,客户端对直播推流的延迟要求不是很敏感。如果对直播的拉流推流延迟有高敏感的场景,建议大家使用阿里云视频直播服务,可以做到上下行链路加速,且支持多样化的直播功能适配;

使用基础

简单的直播知识

您需要了解甚至掌握简单的直播知识技巧才能熟练的使用 OSS 直播,不仅是针对 OSS 还涉及到客户端的推流等相关问题,所以需要明白直播的相关基础

【音视频头介绍】

OSS 的直播功能是建立在 RTMP 直播传输协议的基础上,所以需要指导一些基础的 RMTP 知识:RTMP 的音视频流的封装形式和 FLV 格式相似, 流媒体服务器向客户端发送包含 H264 和 AAC 的 RTMP 直播流,需要首先发送这两个 header,没有这些信息播放端是无法解码音视频流的,其中音频 tag 格式如下

1)AVC sequence header
2)AAC sequence header
image
从上面推论出 AAC sequence header 内容的前 2 个字节是 0xAF 0x00,我们来看一个示例:
image

3)ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。

4)ADTS:Audio Data Transport Stream 音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于 mp3 数据流格式。

【RTMP 内容介绍】

以下是我对 RTMP 总结的一张完整描述图

image

对象存储推流架构

看下官网对于 OSS 推流的过程定义:

1)只能使用RTMP推流的方式,不支持拉流。
2)必须包含视频流,且视频流格式为H264。
3)音频流是可选的,并且只支持AAC格式,其他格式的音频流会被丢弃。
4)转储只支持HLS协议。
5)一个LiveChannel同时只能有一个客户端向其推流。

RTMP 的推流格式:

  • demo :rtmp://your-bucket.oss-cn-hangzhou.aliyuncs.com/live/test-channel
  • live 等同于 RTMP 的 APP 挂载点
  • test-channel 等同于 RTMP 的 stream name

RTMP URL 推流签名:

  • demo:rtmp://${bucket}.${host}/live/${channel}?OSSAccessKeyId=xxx&Expires=yyy&Signature=zzz&${params}
  • 推流前 LiveChannel有两种Status:enabled和disabled,用户可以使用本接口在两种Status之间进行切换。处于disabled状态时,OSS会禁止用户向该LiveChannel进行推流操作;如果有用户正在向该LiveChannel推流,那么推流的客户端会被强制断开(可能会有10s左右的延迟)

对象存储推流的流程汇总图如下

生成推流 URL API
设置推流状态API

image

使用 JAVA SDK 生成推流地址

我们现在用 java 的 SDK 演示一下如上的推理过程,在跑 SDK 之前,需要先搭建好一套本地的 eclipse 环境,如下是我用的 eclipse,如果有没搭建请网上搜索一下 eclipse 的搭建方式(之前需要安装 JDK 且配置环境变量)

环境要求

  • Eclipse 版本:Version: Neon.3 Release (4.6.3)
  • JDK 版本:jdk1.8.0_144
  • OSS:公开读(为了验证推流功能是否正常,我们用公开读的方式快速测试)
  • 我们采用主函数入口的方式,实例化其他类进行调用,这样方便类的拆分,不用都集合在主函数中。
  • 主函数 domain,实例化 OSSClient 对象传入到 RtmpTest 类中测试。
  • 所有的 jar 都会通过官方的 maven 解决的依赖关系,https://help.aliyun.com/document_detail/32009.html
package javasdk;

import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

import com.aliyun.oss.OSSClient;

public class domain {
    public static void main( String[] args ) throws ParseException, FileNotFoundException
    {
        
        System.out.println( "Hello World!" );
        String accessid = "AK";
        String secretkey = "SK";
        String objectpath = "C://Users//hanli.zyb//Desktop//running.png";
        String bucket = "bucket";
        String object = "running";
        String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";

        
        // OSS + rtmp 推流
        String bucketName = "ali-hangzhou";
        RtmpTest pushoss = new RtmpTest();
        OSSClient ossClient = new OSSClient(endpoint, AK, SK);
        pushoss.testCreateLiveChannel(bucketName,ossClient);

    }
}

package javasdk;

import java.text.ParseException;
import java.util.Date;
import java.util.List;
import junit.framework.Assert;

import org.junit.Ignore;
import org.junit.Test;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSErrorCode;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.DateUtil;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateLiveChannelRequest;
import com.aliyun.oss.model.CreateLiveChannelResult;
import com.aliyun.oss.model.ListLiveChannelsRequest;
import com.aliyun.oss.model.LiveChannel;
import com.aliyun.oss.model.LiveChannelInfo;
import com.aliyun.oss.model.LiveChannelListing;
import com.aliyun.oss.model.LiveChannelStat;
import com.aliyun.oss.model.LiveChannelStatus;
import com.aliyun.oss.model.LiveChannelTarget;
import com.aliyun.oss.model.LiveRecord;
import com.aliyun.oss.model.PushflowStatus;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;

public class RtmpTest  {
    String bucketName = "bucket";
    final String liveChannel = "stream name";
    @Test
    public void testCreateLiveChannelDefault(String bucketname,OSSClient ossClient) {

        try {
            CreateLiveChannelRequest createLiveChannelRequest = new CreateLiveChannelRequest(
                    bucketName, liveChannel);
            CreateLiveChannelResult createLiveChannelResult = ossClient.createLiveChannel(createLiveChannelRequest);
            LiveChannelInfo liveChannelInfo = ossClient.getLiveChannelInfo(bucketName, liveChannel);

            
            ossClient.deleteLiveChannel(bucketName, liveChannel);
        } catch (Exception e) {
            Assert.fail(e.getMessage());
        }
    }
    
    @Test
    public void testCreateLiveChannel(String bucketname,OSSClient ossClient) {
        final String liveChannel = "normal-create-live-channel";
        final String liveChannelDesc = "my test live channel";

        try {
            LiveChannelTarget target = new LiveChannelTarget("HLS", 100, 99, "myplaylist.m3u8");
            CreateLiveChannelRequest createLiveChannelRequest = new CreateLiveChannelRequest(
                    bucketName, liveChannel, liveChannelDesc, LiveChannelStatus.Enabled, target);
            
            CreateLiveChannelResult createLiveChannelResult = ossClient.createLiveChannel(createLiveChannelRequest);
            System.out.println(createLiveChannelResult.getPublishUrls());
            /*Assert.assertEquals(createLiveChannelResult.getPublishUrls().size(), 1);
            Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).startsWith("rtmp://"));
            Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).endsWith("live/" + liveChannel));
            Assert.assertEquals(createLiveChannelResult.getPlayUrls().size(), 1);
            Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).startsWith("http://"));
            Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).endsWith(liveChannel + "/myplaylist.m3u8"));*/
            
           /* LiveChannelInfo liveChannelInfo = ossClient.getLiveChannelInfo(bucketName, liveChannel);
            Assert.assertEquals(liveChannelInfo.getDescription(), liveChannelDesc);
            Assert.assertEquals(liveChannelInfo.getStatus(), LiveChannelStatus.Disabled);
            Assert.assertEquals(liveChannelInfo.getTarget().getType(), "HLS");
            Assert.assertEquals(liveChannelInfo.getTarget().getFragDuration(), 100);
            Assert.assertEquals(liveChannelInfo.getTarget().getFragCount(), 99);
            Assert.assertEquals(liveChannelInfo.getTarget().getPlaylistName(), "myplaylist.m3u8");*/
            
            
           // ossClient.deleteLiveChannel(bucketName, liveChannel);
        } catch (Exception e) {
            Assert.fail(e.getMessage());
        }
    }
}

其中我们最关注的是 testCreateLiveChannel 类,创建了推流地址,其中参数 LiveChannelStatus.enable 是要让推流变为可用状态。
running 主函数,打印出推流地址。
rtmp://hangzhou.oss-cn-hangzhou.aliyuncs.com/live/normal-create-live-channel

public void testCreateLiveChannel(String bucketname,OSSClient ossClient) {
        final String liveChannel = "normal-create-live-channel";
        final String liveChannelDesc = "my test live channel";

        try {
            LiveChannelTarget target = new LiveChannelTarget("HLS", 100, 99, "myplaylist.m3u8");
            CreateLiveChannelRequest createLiveChannelRequest = new CreateLiveChannelRequest(
                    bucketName, liveChannel, liveChannelDesc, LiveChannelStatus.Enabled, target);
            
            CreateLiveChannelResult createLiveChannelResult = ossClient.createLiveChannel(createLiveChannelRequest);
            System.out.println(createLiveChannelResult.getPublishUrls());
            /*Assert.assertEquals(createLiveChannelResult.getPublishUrls().size(), 1);
            Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).startsWith("rtmp://"));
            Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).endsWith("live/" + liveChannel));
            Assert.assertEquals(createLiveChannelResult.getPlayUrls().size(), 1);
            Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).startsWith("http://"));
            Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).endsWith(liveChannel + "/myplaylist.m3u8"));*/
            
            
           // ossClient.deleteLiveChannel(bucketName, liveChannel);
        } catch (Exception e) {
            Assert.fail(e.getMessage());
        }
    }​
相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
编解码 Java 视频直播
OSS 直播功能深度分享
目前有很多直播爱好者使用的是 OSS + RTMP 做的推流直播,其中不乏一些企业级别应用,由于 OSS 作为推流的接收略微有一些复杂,故单独开篇讲一下。
OSS 直播功能深度分享
|
5月前
|
机器学习/深度学习 人工智能 专有云
人工智能平台PAI使用问题之怎么将DLC的数据写入到另一个阿里云主账号的OSS中
阿里云人工智能平台PAI是一个功能强大、易于使用的AI开发平台,旨在降低AI开发门槛,加速创新,助力企业和开发者高效构建、部署和管理人工智能应用。其中包含了一系列相互协同的产品与服务,共同构成一个完整的人工智能开发与应用生态系统。以下是对PAI产品使用合集的概述,涵盖数据处理、模型开发、训练加速、模型部署及管理等多个环节。
|
1月前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
4月前
|
存储 机器学习/深度学习 弹性计算
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
|
5月前
|
消息中间件 分布式计算 DataWorks
DataWorks产品使用合集之如何使用Python和阿里云SDK读取OSS中的文件
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
5月前
|
存储 运维 安全
阿里云OSS的优势
【7月更文挑战第19天】阿里云OSS的优势
216 2
|
5月前
|
存储 API 开发工具
阿里云OSS
【7月更文挑战第19天】阿里云OSS
198 1
|
5月前
|
存储 弹性计算 对象存储
预留空间是什么?阿里云OSS对象存储预留空间说明
阿里云OSS预留空间是预付费存储产品,提供折扣价以锁定特定容量,适用于抵扣有地域属性的Bucket标准存储费用及ECS快照费。通过购买预留空间,如500GB通用预留+100GB标准-本地冗余存储包,用户可优化成本。
221 4
|
5月前
|
人工智能 对象存储
【阿里云AI助理】自家产品提供错误答案。阿里云OSS 资源包类型: 下行流量 地域: 中国内地通用 下行流量包规格: 300 GB 套餐: 下行流量包(中国内地) ,包1年。那么这个是每月300GB,1年是3600GB的流量;还是1年只有300GB的流量?
自家产品提供错误答案。阿里云OSS 资源包类型: 下行流量 地域: 中国内地通用 下行流量包规格: 300 GB 套餐: 下行流量包(中国内地) ,包1年。那么这个是每月300GB,1年是3600GB的流量;还是1年只有300GB的流量?
133 1
|
6月前
|
SQL 分布式计算 DataWorks
DataWorks产品使用合集之如何将CSV文件从阿里云OSS同步到ODPS表,并且使用列作为表分区
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
DataWorks产品使用合集之如何将CSV文件从阿里云OSS同步到ODPS表,并且使用列作为表分区