教程:使用媒体服务进行实时流式传输Tutorial: Stream live with Media Services

备注

尽管本教程使用了 .NET SDK 示例,但 REST APICLI 或其他受支持的 SDK 的常规步骤是相同的。Even though the tutorial uses .NET SDK examples, the general steps are the same for REST API, CLI, or other supported SDKs.

在 Azure 媒体服务中,直播活动负责处理实时传送视频流内容。In Azure Media Services, Live Events are responsible for processing live streaming content. 直播活动提供输入终结点(引入 URL),然后由你将该终结点提供给实时编码器。A Live Event provides an input endpoint (ingest URL) that you then provide to a live encoder. 直播活动从实时编码器接收实时输入流,并通过一个或多个流式处理终结点使其可用于流式处理。The Live Event receives live input streams from the live encoder and makes it available for streaming through one or more Streaming Endpoints. 直播活动还提供可用于预览的预览终结点(预览 URL),并在进一步处理和传递流之前对流进行验证。Live Events also provide a preview endpoint (preview URL) that you use to preview and validate your stream before further processing and delivery. 本教程演示如何使用 .NET Core 创建 直通 类型的直播活动。This tutorial shows how to use .NET Core to create a pass-through type of a live event.

本教程介绍如何:The tutorial shows you how to:

  • 下载本主题中所述的示例应用。Download the sample app described in the topic.
  • 检查执行实时传送视频流的代码。Examine the code that performs live streaming.
  • 使用 Azure Media Player 在 https://ampdemo.azureedge.net 观看事件。Watch the event with Azure Media Player at https://ampdemo.azureedge.net.
  • 清理资源。Clean up resources.

如果没有 Azure 试用版订阅,请在开始前创建一个试用版订阅If you don't have an Azure trail subscription, create a trial subscription before you begin.

先决条件Prerequisites

以下项目是完成本教程所需具备的条件:The following items are required to complete the tutorial:

  • 安装 Visual Studio Code 或 Visual Studio。Install Visual Studio Code or Visual Studio.
  • 创建媒体服务帐户Create a Media Services account.
    请务必以 JSON 格式复制 API 访问详细信息,或以此示例中使用的 .env 文件格式存储连接到媒体服务帐户所需的值。Make sure to copy the API Access details in JSON format or store the values needed to connect to the Media Services account in the .env file format used in this sample.
  • 遵循使用 Azure CLI 访问 Azure 媒体服务 API 中的步骤并保存凭据。Follow the steps in Access Azure Media Services API with the Azure CLI and save the credentials. 你需要使用它们来访问此示例中的 API,或将它们输入为 .env 文件格式。You'll need to use them to access the API in this sample, or enter them into the .env file format.
  • 一个用于广播事件的相机或设备(例如便携式计算机)。A camera or a device (like a laptop) that's used to broadcast an event.
  • 使用 RTMP 协议对照相机流进行编码并将其发送到媒体服务实时流服务的本地软件编码器,请参阅推荐的本地实时编码器An on-premises software encoder that encodes your camera stream and sends it to the Media Services live streaming service using the RTMP protocol, see recommended on-premises live encoders. 流必须为 RTMP 或“平滑流式处理” 格式。The stream has to be in RTMP or Smooth Streaming format.
  • 对于本示例,建议从软件编码器(如免费的 Open Broadcast Software OBS Studio)开始,以便于上手。For this sample, it is recommended to start with a software encoder like the free Open Broadcast Software OBS Studio to make it simple to get started.

提示

确保在继续操作之前查看使用媒体服务 v3 的实时传送视频流Make sure to review Live streaming with Media Services v3 before proceeding.

下载并配置示例Download and configure the sample

使用以下命令将以下包含实时流式处理 .NET 示例的 GitHub 存储库克隆到计算机:Clone the following Git Hub repository that contains the live streaming .NET sample to your machine using the following command:

git clone https://github.com/Azure-Samples/media-services-v3-dotnet.git

实时传送视频流示例位于 Live 文件夹中。The live streaming sample is located in the Live folder.

打开下载的项目中的 appsettings.jsonOpen appsettings.json in your downloaded project. 将值替换为从访问 API 获得的凭据。Replace the values with the credentials you got from accessing APIs.

请注意,还可以在项目根处使用 .env 文件格式,仅为 .NET 示例存储库中的所有项目设置一次环境变量。Note that you can also use the .env file format at the root of the project to set your environment variables only once for all projects in the .NET samples repository. 只需复制示例 .env 文件,填写你从 Azure 门户媒体服务 API 访问页面或 Azure CLI 获得的信息。Just copy the sample.env file, fill out the information that you obtain from the Azure portal Media Services API Access page, or from the Azure CLI. 将 sample.env 文件重命名为“.env”以在所有项目中使用它。Rename the sample.env file to just ".env" to use it across all projects. 已配置 .gitignore 文件以避免将此文件的内容发布到分叉存储库。The .gitignore file is already configured to avoid publishing the contents of this file to your forked repository.

重要

此示例为每个资源使用唯一的后缀。This sample uses a unique suffix for each resource. 如果取消调试操作或者中途终止应用,则帐户中会有多个直播活动。If you cancel the debugging or terminate the app without running it through, you'll end up with multiple Live Events in your account.
请务必停止正在运行的直播活动,Make sure to stop the running Live Events. 否则,将会对你“收费” !Otherwise, you'll be billed!

检查执行实时传送视频流的代码Examine the code that performs live streaming

这部分研究 LiveEventWithDVR 项目的 Program.cs 文件中定义的函数。This section examines functions defined in the Program.cs file of the LiveEventWithDVR project.

此示例为每个资源创建唯一的后缀,因此即使在没有清理的情况下运行示例多次,也不会有名称冲突。The sample creates a unique suffix for each resource so that you don't have name collisions if you run the sample multiple times without cleaning up.

开始结合使用媒体服务 API 与 .NET SDKStart using Media Services APIs with .NET SDK

若要开始将媒体服务 API 与 .NET 结合使用,需要创建 AzureMediaServicesClient 对象。To start using Media Services APIs with .NET, you need to create an AzureMediaServicesClient object. 若要创建对象,需要提供客户端所需凭据以使用 Azure AD 连接到 Azure。To create the object, you need to supply credentials needed for the client to connect to Azure using Azure AD. 在文章开头克隆的代码中,GetCredentialsAsync 函数根据本地配置文件 (appsettings.json) 中提供的凭据或通过位于存储库根处的 .env 环境变量文件创建 ServiceClientCredentials 对象。In the code you cloned at the beginning of the article, the GetCredentialsAsync function creates the ServiceClientCredentials object based on the credentials supplied in local configuration file (appsettings.json) or through the .env environment variables file located at the root of the repository.

private static async Task<IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config)
{
    var credentials = await GetCredentialsAsync(config);

    return new AzureMediaServicesClient(config.ArmEndpoint, credentials)
    {
        SubscriptionId = config.SubscriptionId,
        // Set to poll long running operations every 2 seconds. Default is 30. 
        // This helps speed up all code when creating Live Events and Live Outputs.
        LongRunningOperationRetryTimeout = 2
    };
}

创建直播活动Create a live event

本部分介绍如何创建 直通 类型的实时事件(LiveEventEncodingType 设置为 None)。This section shows how to create a pass-through type of Live Event (LiveEventEncodingType set to None). 有关实时事件的其他可用类型的详细信息,请参阅实时事件类型For more information about the other available types of Live Events, see Live Event types. 除了直通,还可以使用实时转码直播活动进行 720P 或 1080P 自适应比特率云编码。In addition to pass-through, you can use a live transcoding Live Event for 720P or 1080P adaptive bitrate cloud encoding.

可能需要在创建直播活动时指定的一些事项包括:Some things that you might want to specify when creating the live event are:

  • 直播活动的引入协议(目前支持 RTMP 和平滑流式处理协议)。The ingest protocol for the Live Event (currently, the RTMP(S) and Smooth Streaming protocols are supported).
    运行直播活动或其关联的实时输出时,无法更改协议选项。You can't change the protocol option while the Live Event or its associated Live Outputs are running. 如果需要其他协议,请为每个流式处理协议创建单独的直播活动。If you require different protocols, create separate Live Event for each streaming protocol.
  • 对引入和预览的 IP 限制。IP restrictions on the ingest and preview. 可定义允许向该直播活动引入视频的 IP 地址。You can define the IP addresses that are allowed to ingest a video to this Live Event. 允许的 IP 地址可以指定为单个 IP 地址(例如“10.0.0.1”)、使用一个 IP 地址和 CIDR 子网掩码的 IP 范围(例如“10.0.0.1/22”)或使用一个 IP 地址和点分十进制子网掩码的 IP 范围(例如“10.0.0.1(255.255.252.0)”)。Allowed IP addresses can be specified as either a single IP address (for example '10.0.0.1'), an IP range using an IP address and a CIDR subnet mask (for example, '10.0.0.1/22'), or an IP range using an IP address and a dotted decimal subnet mask (for example, '10.0.0.1(255.255.252.0)').
    如果未指定 IP 地址并且没有规则定义,则不会允许任何 IP 地址。If no IP addresses are specified and there's no rule definition, then no IP address will be allowed. 若要允许任何 IP 地址,请创建规则并设置 0.0.0.0/0。To allow any IP address, create a rule and set 0.0.0.0/0.
    IP 地址必须采用以下格式之一:具有四个数字或 CIDR 地址范围的 IpV4 地址。The IP addresses have to be in one of the following formats: IpV4 address with four numbers or CIDR address range.
  • 创建事件时,可以将其启动方式指定为自动启动。When creating the event, you can specify to autostart it.
    如果将 autostart 设置为 true,则直播活动会在创建后启动。When autostart is set to true, the Live Event will be started after creation. 这意味着,只要直播活动开始运行,就会开始计费。That means the billing starts as soon as the Live Event starts running. 必须显式对直播活动资源调用停止操作才能停止进一步计费。You must explicitly call Stop on the Live Event resource to halt further billing. 有关详细信息,请参阅直播活动状态和计费For more information, see Live Event states and billing. 此外还有备用模式,可用于以较低成本“已分配”状态启动直播活动,使其更快地移动到“正在运行”状态。There are also standby modes available to start the Live Event in a lower cost 'allocated' state that makes it faster to move to a 'Running' state. 对于需要快速向流式处理器分发通道的热池等情况,这非常有用。This is useful for situations like hotpools that need to hand out channels quickly to streamers.
  • 要使引入 URL 具有预测性且易于在基于硬件的实时编码器中维护,请将“useStaticHostname”属性设置为 true。For an ingest URL to be predictive and easier to maintain in a hardware based live encoder, set the "useStaticHostname" property to true. 有关详细信息,请参阅实时事件引入 URLFor detailed information, see Live Event ingest URLs.
Console.WriteLine($"Creating a live event named {liveEventName}");
Console.WriteLine();

// Creating the LiveEvent - the primary object for live streaming in AMS. 
// See the overview - https://docs.azure.cn/media-services/latest/live-streaming-overview

// Create the LiveEvent

// Understand the concepts of what a live event and a live output is in AMS first!
// Read the following - https://docs.azure.cn/media-services/latest/live-events-outputs-concept
// 1) Understand the billing implications for the various states
// 2) Understand the different live event types, pass-through and encoding
// 3) Understand how to use long-running async operations 
// 4) Understand the available Standby mode and how it differs from the Running Mode. 
// 5) Understand the differences between a LiveOutput and the Asset that it records to.  They are two different concepts.
//    A live output can be considered as the "tape recorder" and the Asset is the tape that is inserted into it for recording.
// 6) Understand the advanced options such as low latency support. 
//    Low Latency - https://docs.azure.cn/media-services/latest/live-event-latency

// When broadcasting to a live event, please use one of the verified on-premises live streaming encoders.
// While operating this tutorial, it is recommended to start out using OBS Studio before moving to another encoder. 

// Note: When creating a LiveEvent, you can specify allowed IP addresses in one of the following formats:                 
//      IpV4 address with 4 numbers
//      CIDR address range  

IPRange allAllowIPRange = new IPRange(
    name: "AllowAll",
    address: "0.0.0.0",
    subnetPrefixLength: 0
);

// Create the LiveEvent input IP access control object
// this will control the IP that the encoder is running on and restrict access to only that encoder IP range.
LiveEventInputAccessControl liveEventInputAccess = new LiveEventInputAccessControl
{
    Ip = new IPAccessControl(
            allow: new IPRange[]
            {
                // re-use the same range here for the sample, but in production you can lock this
                // down to the ip range for your on-premises live encoder, laptop, or device that is sending
                // the live stream
                allAllowIPRange
            }
        )

};

// Create the LiveEvent Preview IP access control object. 
// This will restrict which clients can view the preview endpoint
LiveEventPreview liveEventPreview = new LiveEventPreview
{
    AccessControl = new LiveEventPreviewAccessControl(
        ip: new IPAccessControl(
            allow: new IPRange[]
            {
                 // re-use the same range here for the sample, but in production you can lock this to the IPs of your 
                // devices that would be monitoring the live preview. 
                allAllowIPRange
            }
        )
    )
};

// To get the same ingest URL for the same LiveEvent name:
// 1. Set useStaticHostname to true so you have ingest like: 
//        rtmps://liveevent-hevc12-eventgridmediaservice-cne22.channel.media.chinacloudapi.cn:2935/live/522f9b27dd2d4b26aeb9ef8ab96c5c77           
// 2. Set the inputs:accessToken to a desired GUID string (with or without hyphen) to make it simpler to update your encoder settings

// See REST API documentation for details on each setting value
// https://docs.microsoft.com/rest/api/media/liveevents/create 

LiveEvent liveEvent = new LiveEvent(
    location: mediaService.Location,
    description: "Sample LiveEvent from .NET SDK sample",
    // Set useStaticHostname to true to make the ingest and preview URL host name the same. 
    // This can slow things down a bit. 
    useStaticHostname: true,

    // 1) Set up the input settings for the Live event...
    input: new LiveEventInput(
        streamingProtocol: LiveEventInputProtocol.RTMP,  // options are RTMP or Smooth Streaming ingest format.
                                                         // This sets a static access token for use on the ingest path. 
                                                         // Combining this with useStaticHostname:true will give you the same ingest URL on every creation.
                                                         // This is helpful when you only want to enter the URL into a single encoder one time for this Live Event name
        accessToken: "acf7b6ef-8a37-425f-b8fc-51c2d6a5a86a",  // Use this value when you want to make sure the ingest URL is static and always the same. If omitted, the service will generate a random GUID value.
        accessControl: liveEventInputAccess, // controls the IP restriction for the source encoder.
        keyFrameIntervalDuration: "PT2S" // Set this to match the ingest encoder's settings
    ),
    // 2) Set the live event to use pass-through or cloud encoding modes...
    encoding: new LiveEventEncoding(
        // Set this to Standard or Premium1080P to use the cloud live encoder.
        // Otherwise, leave as "None" to use pass-through mode
        encodingType: LiveEventEncodingType.None // also known as pass-through mode.
                                                 // OPTIONAL settings when using live cloud encoding type:
                                                 // keyFrameInterval: "PT2S", //If this value is not set for an encoding live event, the fragment duration defaults to 2 seconds. The value cannot be set for pass-through live events.
                                                 // presetName: null, // only used for custom defined presets. 
                                                 //stretchMode: "None" // can be used to determine stretch on encoder mode
    ),
    // 3) Set up the Preview endpoint for monitoring based on the settings above we already set.
    preview: liveEventPreview,
    // 4) Set up more advanced options on the live event. Low Latency is the most common one.
    streamOptions: new List<StreamOptionsFlag?>()
    {
        // Set this to Default or Low Latency
        // When using Low Latency mode, you must configure the Azure Media Player to use the 
        // quick start heuristic profile or you won't notice the change. 
        // In the AMP player client side JS options, set -  heuristicProfile: "Low Latency Heuristic Profile". 
        // To use low latency optimally, you should tune your encoder settings down to 1 second GOP size instead of 2 seconds.
        StreamOptionsFlag.LowLatency
    }
);

// Start monitoring LiveEvent events using Event Grid and Event Hub
try
{
    // Please refer README for Event Hub and storage settings.
    Console.WriteLine("Starting monitoring LiveEvent events...");
    string StorageConnectionString = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.chinacloudapi.cn",
        config.StorageAccountName, config.StorageAccountKey);

    // Create a new host to process events from an Event Hub.
    Console.WriteLine("Creating a new host to process events from an Event Hub...");
    eventProcessorHost = new EventProcessorHost(config.EventHubName,
        PartitionReceiver.DefaultConsumerGroupName, config.EventHubConnectionString,
        StorageConnectionString, config.StorageContainerName);

    // Registers the Event Processor Host and starts receiving messages.
    await eventProcessorHost.RegisterEventProcessorFactoryAsync(new MediaServicesEventProcessorFactory(liveEventName),
        EventProcessorOptions.DefaultOptions);
}
catch (Exception e)
{
    Console.WriteLine("Failed to connect to Event Hub, please refer README for Event Hub and storage settings. Skipping event monitoring...");
    Console.WriteLine(e.Message);
}

Console.WriteLine("Creating the LiveEvent, please be patient as this can take time to complete async.");
Console.WriteLine("Live Event creation is an async operation in Azure and timing can depend on resources available.");

// When autostart is set to true, the Live Event will be started after creation. 
// That means, the billing starts as soon as the Live Event starts running. 
// You must explicitly call Stop on the Live Event resource to halt further billing.
// The following operation can sometimes take awhile. Be patient.
// On optional workflow is to first call allocate() instead of create. 
// https://docs.microsoft.com/en-us/rest/api/media/liveevents/allocate 
// This allows you to allocate the resources and place the live event into a "Standby" mode until 
// you are ready to transition to "Running". This is useful when you want to pool resources in a warm "Standby" state at a reduced cost.
// The transition from Standby to "Running" is much faster than cold creation to "Running" using the autostart property.
// Returns a long running operation polling object that can be used to poll until completion.

Stopwatch watch = Stopwatch.StartNew();
liveEvent = await client.LiveEvents.CreateAsync(
    config.ResourceGroup,
    config.AccountName,
    liveEventName,
    liveEvent,
    // When autostart is set to true, you should "await" this method operation to complete. 
    // The Live Event will be started after creation. 
    // You may choose not to do this, but create the object, and then start it using the standby state to 
    // keep the resources "warm" and billing at a lower cost until you are ready to go live. 
    // That increases the speed of startup when you are ready to go live. 
    autoStart: false);
watch.Stop();
string elapsedTime = String.Format(":{0:00}.{1:00}", watch.Elapsed.Seconds, watch.Elapsed.Milliseconds / 10);
Console.WriteLine($"Create Live Event run time : {elapsedTime}");

获取引入 URLGet ingest URLs

创建直播活动后,可以获得要提供给实时编码器的引入 URL。Once the Live Event is created, you can get ingest URLs that you'll provide to the live encoder. 编码器将使用这些 URL 来输入实时流。The encoder uses these URLs to input a live stream.

// Get the RTMP ingest URL to configure in OBS Studio. 
// The endpoints is a collection of RTMP primary and secondary, and RTMPS primary and secondary URLs. 
// to get the primary secure RTMPS, it is usually going to be index 3, but you could add a loop here to confirm...
string ingestUrl = liveEvent.Input.Endpoints.First().Url;
Console.WriteLine($"The RTMP ingest URL to enter into OBS Studio is:");
Console.WriteLine($"\t{ingestUrl}");
Console.WriteLine("Make sure to enter a Stream Key into the OBS studio settings. It can be any value or you can repeat the accessToken used in the ingest URL path.");
Console.WriteLine();

获取预览 URLGet the preview URL

使用 previewEndpoint 预览来自编码器的输入并验证其是否已确实收到。Use the previewEndpoint to preview and verify that the input from the encoder is actually being received.

重要

确保视频流向预览 URL,然后再继续操作。Make sure that the video is flowing to the Preview URL before continuing.

// Use the previewEndpoint to preview and verify
// that the input from the encoder is actually being received
// The preview endpoint URL also support the addition of various format strings for HLS (format=m3u8-cmaf) and DASH (format=mpd-time-cmaf) for example.
// The default manifest is Smooth. 
string previewEndpoint = liveEvent.Preview.Endpoints.First().Url;
Console.WriteLine($"The preview url is:");
Console.WriteLine($"\t{previewEndpoint}");
Console.WriteLine();

Console.WriteLine($"Open the live preview in your browser and use the Azure Media Player to monitor the preview playback:");
Console.WriteLine($"\thttps://ampdemo.azureedge.net/?url={previewEndpoint}&heuristicprofile=lowlatency");
Console.WriteLine();

创建和管理直播活动与实时输出Create and manage Live Events and Live Outputs

将流传输到实时事件后,可以通过创建资产、实时输出和流定位符来启动流式传输事件。Once you have the stream flowing into the Live Event, you can begin the streaming event by creating an Asset, Live Output, and Streaming Locator. 这会存档流,并使观看者可通过流式处理终结点使用该流。This will archive the stream and make it available to viewers through the Streaming Endpoint.

在了解这些概念时,最好将“资产”对象视为过去你插入录像机的磁带。When learning these concepts, it is best to think of the "Asset" object as the tape that you would insert into a video tape recorder in the old days. “实时输出”是录像机。The "Live Output" is the tape recorder machine. “实时输出”只是进入机器后部的视频信号。The "Live Event" is just the video signal coming into the back of the machine.

首先通过创建“直播活动”创建信号。You first create the signal by creating the "Live Event". 在你启动直播活动并将编码器连接到输入前,信号不会流动。The signal is not flowing until you start that Live Event and connect your encoder to the input.

可以随时创建磁带。The tape can be created at any time. 这只是一个空“资产”,你会将其交给“实时输出”对象,即此类比中的录像机。It is just an empty "Asset" that you will hand to the Live Output object, the tape recorder in this analogy.

可以随时创建录像机。The tape recorder can be created at any time. 这意味着你可以在启动信号流之前或之后创建实时输出。Meaning you can create a Live Output before starting the signal flow, or after. 如果你需要加快速度,在启动信号流之前创建它有时很有帮助。If you need to speed things up, it is sometimes helpful to create it before you start the signal flow.

若要停止录像机,可以针对 LiveOutput 调用删除。To stop the tape recorder, you call delete on the LiveOutput. 这不会删除磁带“资产”上的内容。This does not delete the contents on the tape "Asset". 资产始终保留存档的视频内容,直到你针对资产本身明确调用删除。The Asset is always kept with the archived video content until you call delete explicitly on the Asset itself.

下一部分将介绍资产(“磁带”)和实时输出(“录像机”)的创建。The next section will walk through the creation of the Asset ("tape") and the Live Output ("tape recorder").

创建资产Create an Asset

创建供实时输出使用的资产。Create an Asset for the Live Output to use. 在上面的类比中,这将是我们录制实时视频信号的磁带。In the analogy above, this will be our tape that we record the live video signal onto. 观看者将能够从此虚拟磁带以实时或点播方式查看内容。Viewers will be able to see the contents live or on-demand from this virtual tape.

// Create an Asset for the LiveOutput to use. Think of this as the "tape" that will be recorded to. 
// The asset entity points to a folder/container in your Azure Storage account. 
Console.WriteLine($"Creating an asset named {assetName}");
Console.WriteLine();
Asset asset = await client.Assets.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName, assetName, new Asset());

创建实时输出Create a Live Output

实时输出在创建时启动,在删除后停止。Live Outputs start on creation and stop when deleted. 这将是事件的“录像机”。This is going to be the "tape recorder" for our event. 删除实时输出不会删除基础资产或该资产中的内容。When you delete the Live Output, you're not deleting the underlying Asset or content in the asset. 将其视为弹出磁带。Think of it as ejecting the tape. 只要你愿意,录制的资产将一直保留,当它被弹出时(这意味着当实时输出被删除时),它将立即可供点播观看。The Asset with the recording will last as long as you like, and when it is ejected (meaning, when the Live Output is deleted) it will be available for on-demand viewing immediately.

// Create the Live Output - think of this as the "tape recorder for the live event". 
// Live outputs are optional, but are required if you want to archive the event to storage,
// use the asset for on-demand playback later, or if you want to enable cloud DVR time-shifting.
// We will use the asset created above for the "tape" to record to. 
string manifestName = "output";
Console.WriteLine($"Creating a live output named {liveOutputName}");
Console.WriteLine();

watch = Stopwatch.StartNew();
// See the REST API for details on each of the settings on Live Output
// https://docs.microsoft.com/rest/api/media/liveoutputs/create
LiveOutput liveOutput = new LiveOutput(
    assetName: asset.Name,
    manifestName: manifestName, // The HLS and DASH manifest file name. This is recommended to set if you want a deterministic manifest path up front.
                                // archive window can be set from 3 minutes to 25 hours. Content that falls outside of ArchiveWindowLength
                                // is continuously discarded from storage and is non-recoverable. For a full event archive, set to the maximum, 25 hours.
    archiveWindowLength: TimeSpan.FromHours(1)
);
liveOutput = await client.LiveOutputs.CreateAsync(
    config.ResourceGroup,
    config.AccountName,
    liveEventName,
    liveOutputName,
    liveOutput);
elapsedTime = String.Format(":{0:00}.{1:00}", watch.Elapsed.Seconds, watch.Elapsed.Milliseconds / 10);
Console.WriteLine($"Create Live Output run time : {elapsedTime}");
Console.WriteLine();

创建流定位符Create a Streaming Locator

备注

创建媒体服务帐户后,一个处于“已停止” 状态的“默认” 流式处理终结点会添加到帐户。When your Media Services account is created, a default streaming endpoint is added to your account in the Stopped state. 若要开始流式传输内容并利用动态打包和动态加密,要从中流式传输内容的流式处理终结点必须处于“正在运行”状态 。To start streaming your content and take advantage of dynamic packaging and dynamic encryption, the streaming endpoint from which you want to stream content has to be in the Running state.

如果已使用流定位符发布了资产,则直播活动(长达 DVR 窗口长度)将继续可见,直到流定位符过期或被删除(以先发生为准)。When you publish the Asset using a Streaming Locator, the Live Event (up to the DVR window length) will continue to be viewable until the Streaming Locator's expiry or deletion, whichever comes first. 通过这种方式,可以使虚拟“磁带”录制可供观众进行实时和点播观看。This is how you make the virtual "tape" recording available for your viewing audience to see live and on-demand. 录制完成后(当实时输出被删除时),同一 URL 可用于观看直播活动、DVR 窗口或点播资产。The same URL can be used to watch the live event, DVR window, or the on-demand asset when the recording is complete (when the Live Output is deleted.)

Console.WriteLine($"Creating a streaming locator named {streamingLocatorName}");
Console.WriteLine();

IList<string> filters = new List<string>();
filters.Add(drvAssetFilterName);
StreamingLocator locator = await client.StreamingLocators.CreateAsync(config.ResourceGroup,
    config.AccountName,
    drvStreamingLocatorName,
    new StreamingLocator
    {
        AssetName = assetName,
        StreamingPolicyName = PredefinedStreamingPolicy.ClearStreamingOnly,
        Filters = filters   // Associate the dvr filter with StreamingLocator.
    });

// Get the default Streaming Endpoint on the account
StreamingEndpoint streamingEndpoint = await client.StreamingEndpoints.GetAsync(config.ResourceGroup, config.AccountName, streamingEndpointName);

// If it's not running, Start it. 
if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
{
    Console.WriteLine("Streaming Endpoint was Stopped, restarting now..");
    await client.StreamingEndpoints.StartAsync(config.ResourceGroup, config.AccountName, streamingEndpointName);

    // Since we started the endpoint, we should stop it in cleanup.
    stopEndpoint = true;
}

```csharp

// Get the url to stream the output
ListPathsResponse paths = await client.StreamingLocators.ListPathsAsync(resourceGroupName, accountName, locatorName);

foreach (StreamingPath path in paths.StreamingPaths)
{
    UriBuilder uriBuilder = new UriBuilder();
    uriBuilder.Scheme = "https";
    uriBuilder.Host = streamingEndpoint.HostName;

    uriBuilder.Path = path.Paths[0];
    // Get the URL from the uriBuilder: uriBuilder.ToString()
}

清理媒体服务帐户中的资源Cleaning up resources in your Media Services account

如果已完成流式处理事件,并想要清理先前设置的资源,请遵循以下过程:If you're done streaming events and want to clean up the resources provisioned earlier, follow the following procedure:

  • 停止从编码器推送流。Stop pushing the stream from the encoder.
  • 停止直播活动。Stop the Live Event. 直播活动在停止后,不会产生任何费用。Once the Live Event is stopped, it won't incur any charges. 当需要重新启动它时,它会采用相同的引入 URL,因此,无需重新配置编码器。When you need to start it again, it will have the same ingest URL so you won't need to reconfigure your encoder.
  • 除非想要继续以点播流形式提供直播活动的存档,否则可以停止流式处理终结点。You can stop your Streaming Endpoint, unless you want to continue to provide the archive of your live event as an on-demand stream. 如果直播活动处于停止状态,则不会产生任何费用。If the Live Event is in a stopped state, it won't incur any charges.
private static async Task CleanupLiveEventAndOutputAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string liveEventName, string liveOutputName)
{
    try
    {
        LiveEvent liveEvent = await client.LiveEvents.GetAsync(resourceGroup, accountName, liveEventName);

        Console.WriteLine("Deleting Live Output");
        Stopwatch watch = Stopwatch.StartNew();

        await client.LiveOutputs.DeleteAsync(resourceGroup, accountName, liveEventName, liveOutputName);

        String elapsedTime = String.Format(":{0:00}.{1:00}", watch.Elapsed.Seconds, watch.Elapsed.Milliseconds / 10);
        Console.WriteLine($"Delete Live Output run time : {elapsedTime}");

        if (liveEvent != null)
        {
            if (liveEvent.ResourceState == LiveEventResourceState.Running)
            {
                watch = Stopwatch.StartNew();
                // If the LiveEvent is running, stop it and have it remove any LiveOutputs
                await client.LiveEvents.StopAsync(resourceGroup, accountName, liveEventName, removeOutputsOnStop: false);
                elapsedTime = String.Format(":{0:00}.{1:00}", watch.Elapsed.Seconds, watch.Elapsed.Milliseconds / 10);
                Console.WriteLine($"Stop Live Event run time : {elapsedTime}");
            }

            // Delete the LiveEvent
            await client.LiveEvents.DeleteAsync(resourceGroup, accountName, liveEventName);
        }
    }
    catch (ApiErrorException e)
    {
        Console.WriteLine("CleanupLiveEventAndOutputAsync -- Hit ApiErrorException");
        Console.WriteLine($"\tCode: {e.Body.Error.Code}");
        Console.WriteLine($"\tCode: {e.Body.Error.Message}");
        Console.WriteLine();
    }
}
private static async Task CleanupLocatorandAssetAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string streamingLocatorName, string assetName)
{
    try
    {
        // Delete the Streaming Locator
        await client.StreamingLocators.DeleteAsync(resourceGroup, accountName, streamingLocatorName);

        // Delete the Archive Asset
        await client.Assets.DeleteAsync(resourceGroup, accountName, assetName);
    }
    catch (ApiErrorException e)
    {
        Console.WriteLine("CleanupLocatorandAssetAsync -- Hit ApiErrorException");
        Console.WriteLine($"\tCode: {e.Body.Error.Code}");
        Console.WriteLine($"\tCode: {e.Body.Error.Message}");
        Console.WriteLine();
    }
}

观看事件Watch the event

若要观看事件,请复制流式传输 URL(在运行“创建流定位符”中所述的代码时获得)。To watch the event, copy the streaming URL that you got when you ran code described in Create a Streaming Locator. 你可以使用所选的媒体播放器。You can use a media player of your choice. 使用 Azure Media Player 在 https://ampdemo.azureedge.net 中测试流。Azure Media Player to test your stream at https://ampdemo.azureedge.net.

直播活动在停止后会自动转换为点播内容。Live Event automatically converts events to on-demand content when stopped. 即使你停止并删除了事件,只要没有删除资产,用户也能够按需将已存档内容作为视频进行流式传输。Even after you stop and delete the event, users can stream your archived content as a video on demand for as long as you don't delete the asset. 如果资产被某个事件使用,则无法将其删除,必须先删除该事件。An asset can't be deleted if it's used by an event; the event must be deleted first.

清理资源Clean up resources

如果不再需要资源组中的任何一个资源(包括为本教程创建的媒体服务和存储帐户),请删除之前创建的资源组。If you no longer need any of the resources in your resource group, including the Media Services and storage accounts you created for this tutorial, delete the resource group you created earlier.

执行以下 CLI 命令:Execute the following CLI command:

az group delete --name amsResourceGroup

重要

让直播活动保持运行会产生费用。Leaving the Live Event running incurs billing costs. 请注意,如果项目/节目崩溃或因某种原因而关闭,可能会导致直播活动保持运行状态,从而产生费用。Be aware, if the project/program crashes or is closed out for any reason, it could leave the Live Event running in a billing state.

后续步骤Next steps

对文件进行流式处理Stream files