Учебник. Потоковая трансляция в реальном времени с помощью Служб мультимедиа Azure и .NET 5.0

В Службах мультимедиа Azure за обработку содержимого потоковой трансляции отвечают компоненты трансляций. Трансляция предоставляет входную конечную точку (URL-адрес приема), которую вы можете передать динамическому кодировщику. Трансляция получает от динамического кодировщика входные потоки и затем предоставляет их для потоковой передачи через одну конечную точку потоковой передачи или несколько. Кроме того, трансляции предоставляют конечную точку предварительного просмотра (URL-адрес предварительного просмотра), которая позволяет просмотреть и проверить поток перед его обработкой и доставкой.

В этом руководстве описано, как с помощью .NET 5.0 создавать трансляции сквозного типа. В этом руководстве рассмотрены следующие задачи:

Примечание

Несмотря на то, что в этом учебнике используются примеры для пакета SDK для .NET, общие шаги одинаковы для REST API, CLI, или других поддерживаемых пакетов SDK.

Предварительные требования

Для работы с этим учебником требуется следующее.

Для программ потоковой передачи в реальном времени также понадобится следующее.

  • Веб-камера или другое устройство (например, ноутбук) для трансляции события.

  • Локальный программный кодировщик для кодирования потока с камеры и отправки его в Службы мультимедиа Azure по протоколу RTMP (Real-Time Messaging Protocol). Дополнительные сведения см. в статье о рекомендуемых локальных динамических кодировщиках. Поток должен иметь формат RTMP или Smooth Streaming.

    В контексте данного примере предполагается, что вы будете использовать Open Broadcaster Software (OBS) Studio для широковещательной передачи RTMP на конечную точку приема. Установка OBS Studio.

Совет

Прежде чем продолжить работу, ознакомьтесь со статьей Потоковая трансляция в Службах мультимедиа Azure версии 3.

Скачивание и настройка примера

Клонируйте на свой компьютер репозиторий GitHub, содержащий пример для потоковой передачи данных .NET, с помощью следующей команды.

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

Пример потоковой трансляции находится в папке Live.

Откройте файл appsettings.json в скачанном проекте. Замените значения учетными данными, которые вы получили, выполнив действия из статьи Доступ к API Служб мультимедиа Azure с помощью Azure CLI.

Примечание

Можно также использовать ENV-файл в корне проекта, чтобы единоразово задать переменные среды для всех проектов в репозитории примеров .NET. Просто скопируйте файл sample.env и затем добавьте в него сведения, полученные на странице Доступ к API Служб мультимедиа на портале Azure или посредством Azure CLI. Переименуйте файл sample.env в .env, чтобы использовать его во всех проектах.

Файл с расширением .gitignore уже настроен, чтобы не публиковать содержимое этого файла в разветвленном репозитории.

Важно!

Этот пример использует уникальный суффикс для каждого ресурса. Если вы отмените отладку или завершите приложение раньше, чем оно закончит свою работу, в вашей учетной записи сохранятся несколько трансляций.

Не забудьте остановить все запущенные трансляции. В противном случае за них будет начислена плата!

Проверка кода, который выполняет потоковую трансляцию

В этом разделе рассматриваются функции, определенные в файлах Authentication.cs (в папке Common_Utils) и Program.cs проекта LiveEventWithDVR.

Этот пример создает уникальный суффикс для каждого ресурса, чтобы избежать конфликтов имен при многократном запуске примера без очистки.

Начало использования интерфейсов API Служб мультимедиа с пакетом SDK для .NET

Authentication.cs создает объект AzureMediaServicesClient с использованием учетных данных, указанных в локальных файлах конфигурации (appsettings.json или .env).

Объект AzureMediaServicesClient позволяет начать использовать API Служб мультимедиа с .NET. Чтобы создать объект, введите учетные данные, необходимые клиенту для подключения к Azure с помощью Azure Active Directory, которые реализованы в GetCredentialsAsync. Другой вариант — использовать интерактивную проверку подлинности, реализованную в GetCredentialsInteractiveAuthAsync.

public static async Task<IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config, bool interactive = false)
{
    ServiceClientCredentials credentials;
    if (interactive)
        credentials = await GetCredentialsInteractiveAuthAsync(config);
    else
        credentials = await GetCredentialsAsync(config);

    return new AzureMediaServicesClient(config.ArmEndpoint, credentials)
    {
        SubscriptionId = config.SubscriptionId,
    };
}

В коде, клонированном в начале этой статьи, функция GetCredentialsAsync создает объект ServiceClientCredentials на основе учетных данных, предоставленных в локальном файле конфигурации (appsettings.json) или ENV-файле переменных среды в корне репозитория.

private static async Task<ServiceClientCredentials> GetCredentialsAsync(ConfigWrapper config)
{
    // Use ConfidentialClientApplicationBuilder.AcquireTokenForClient to get a token using a service principal with symmetric key

    var scopes = new[] { config.ArmAadAudience + "/.default" };

    var app = ConfidentialClientApplicationBuilder.Create(config.AadClientId)
        .WithClientSecret(config.AadSecret)
        .WithAuthority(AzureCloudInstance.AzurePublic, config.AadTenantId)
        .Build();

    var authResult = await app.AcquireTokenForClient(scopes)
                                             .ExecuteAsync()
                                             .ConfigureAwait(false);

    return new TokenCredentials(authResult.AccessToken, TokenType);
}

В случае интерактивной проверки подлинности функция GetCredentialsInteractiveAuthAsync создает объект ServiceClientCredentials на основе интерактивной проверки подлинности и параметров подключения, переданных в локальном файле конфигурации (appsettings.json) или в ENV-файле переменных среды в корне репозитория. В этом случае не требуется указывать AADCLIENTID и AADSECRET в файле конфигурации или переменных среды.

private static async Task<ServiceClientCredentials> GetCredentialsInteractiveAuthAsync(ConfigWrapper config)
{
    var scopes = new[] { config.ArmAadAudience + "/user_impersonation" };

    // client application of Az Cli
    string ClientApplicationId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";

    AuthenticationResult result = null;

    IPublicClientApplication app = PublicClientApplicationBuilder.Create(ClientApplicationId)
        .WithAuthority(AzureCloudInstance.AzurePublic, config.AadTenantId)
        .WithRedirectUri("http://localhost")
        .Build();

    var accounts = await app.GetAccountsAsync();

    try
    {
        result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
    }
    catch (MsalUiRequiredException)
    {
        try
        {
            result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
        }
        catch (MsalException maslException)
        {
            Console.Error.WriteLine($"ERROR: MSAL interactive authentication exception with code '{maslException.ErrorCode}' and message '{maslException.Message}'.");
        }
    }
    catch (MsalException maslException)
    {
        Console.Error.WriteLine($"ERROR: MSAL silent authentication exception with code '{maslException.ErrorCode}' and message '{maslException.Message}'.");
    }

    return new TokenCredentials(result.AccessToken, TokenType);
}

Создание события прямой трансляции

В этом разделе объясняется, как создавать трансляции сквозного типа (для LiveEventEncodingType задано значение None). Сведения о доступных типах см. в разделе Типы трансляций. В дополнение к сквозной передаче можно использовать трансляцию с перекодированием для облачного кодирования в формате 720p или 1080p с адаптивной скоростью.

При создании трансляции может потребоваться указать следующие параметры.

  • Протокол приема для трансляции. В настоящее время поддерживаются протоколы RTMP, RTMPS и Smooth Streaming. Нельзя изменить протокол во время трансляции или если запущены трансляции или выходные потоки, связанные с этой трансляцией. Если требуются другие протоколы, создайте отдельную трансляцию для каждого протокола потоковой передачи.

  • Ограничения по IP-адресам для приема и предварительного просмотра Можно задать IP-адреса, с которых разрешено принимать видео в поток трансляции. Разрешенные IP-адреса можно указать следующим образом:

    • отдельный IP-адрес (например, 10.0.0.1);
    • диапазон IP-адресов, заданный с помощью IP-адреса и маски подсети бесклассовой междоменной маршрутизации (CIDR) (например, 10.0.0.1/22);
    • диапазон IP-адресов, заданный с помощью IP-адреса и маски подсети в десятичной записи (например, 10.0.0.1(255.255.252.0)).

    Если IP-адреса не указаны и правила не заданы, ни один IP-адрес не разрешен. Чтобы разрешить все IP-адреса, создайте правило и задайте адрес 0.0.0.0/0. IP-адреса должны быть представлены в одном из следующих форматов: IPv4-адрес с четырьмя цифрами или диапазон адресов CIDR.

  • Автозапуск события при его создании Если для автозапуска задано значение true, трансляция будет запущена после ее создания. Это означает, что плата начисляется сразу же после запуска события трансляции. Чтобы остановить начисление оплаты, нужно явно вызвать функцию Stop для ресурса трансляции. Дополнительные сведения см. в статье Состояния трансляции и выставление счетов.

    Существуют режимы ожидания для запуска трансляции в менее затратном состоянии "Выделено", что ускоряет переход в рабочее состояние. Это полезно для таких ситуаций, как горячие пулы, когда нужно быстро раздавать каналы стримерам.

  • Статическое имя узла и уникальный идентификатор GUID Чтобы принимающий URL-адрес был предсказуемым и удобным в обслуживании в аппаратном динамическом кодировщике, задайте для свойства useStaticHostname значение true. Подробные сведения см. в разделе URL-адреса приема трансляции.

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.microsoft.com/azure/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.microsoft.com/azure/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, and live transcription/captioning support. 
//    Live Transcription - https://docs.microsoft.com/en-us/azure/media-services/latest/live-transcription
//    Low Latency - https://docs.microsoft.com/en-us/azure/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(
    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()
{
    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()
{
    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-usw22.channel.media.azure.net: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(
    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 (720P) or Premium1080P to use the cloud live encoder.
        // See https://go.microsoft.com/fwlink/?linkid=2095101 for more information
        // Otherwise, set to PassthroughBasic or PassthroughStandard to use the two different pass-through modes. 
        encodingType: LiveEventEncodingType.PassthroughStandard // Choose the type of live event - standard or basic pass-through, or the encoding types for 720P or 1080P
                                                                // 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
    }
//,
// 5) Optionally enable live transcriptions if desired. This is only supported on PassthroughStandard, and the transcoding live event types. It is not supported on Basic pass-through type.
// WARNING : This is extra cost ($$$), so please check pricing before enabling.
/*transcriptions:new List<LiveEventTranscription>(){
    new LiveEventTranscription(
        // The value should be in BCP-47 format (e.g: 'en-US'). See https://go.microsoft.com/fwlink/?linkid=2133742
        language: "en-us",
        outputTranscriptionTrack : new LiveEventOutputTranscriptionTrack(
            trackName: "English" // set the name you want to appear in the output manifest
        )
    )
}*/
);

// Start monitoring LiveEvent events using Event Grid and Event Hub
try
{
    // Please refer README for Event Hub and storage settings.
    // A storage account is required to process the Event Hub events from the Event Grid subscription in this sample.

    // Create a new host to process events from an Event Hub.
    Console.WriteLine("Creating a new client to process events from an Event Hub...");
    var credential = new DefaultAzureCredential();
    var storageConnectionString = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
       config.StorageAccountName, config.StorageAccountKey);
    var blobContainerName = config.StorageContainerName;
    var eventHubsConnectionString = config.EventHubConnectionString;
    var eventHubName = config.EventHubName;
    var consumerGroup = config.EventHubConsumerGroup;

    storageClient = new BlobContainerClient(
        storageConnectionString,
        blobContainerName);

    processorClient = new EventProcessorClient(
        storageClient,
        consumerGroup,
        eventHubsConnectionString,
        eventHubName);

    mediaEventProcessor = new MediaServicesEventProcessor(null, null, liveEventName);
    processorClient.ProcessEventAsync += mediaEventProcessor.ProcessEventsAsync;
    processorClient.ProcessErrorAsync += mediaEventProcessor.ProcessErrorAsync;

    await processorClient.StartProcessingAsync();
}
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}");

Получение URL-адресов приема

После создания трансляции можно получить URL-адреса приема, которые необходимо передать динамическому кодировщику. Он использует эти адреса для передачи динамического потока на вход.

// 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();

Получение URL-адреса предварительного просмотра

Используйте previewEndpoint для предварительного просмотра и проверки получения входных данных от кодировщика.

Важно!

Прежде чем продолжить, убедитесь, что видео правильно передается на URL-адрес предварительного просмотра.

// 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();

Создание трансляций и выходных данных и управление ими

Настроив передачу потока данных в трансляцию, можно запустить событие потоковой передачи, создав ресурс, выходные данные трансляции и указатель потоковой передачи. В результате вы сможете запустить архивирование потока и предложить его зрителям через конечную точку потоковой передачи.

При изучении этих понятий удобно рассматривать объект "ресурс" как кассету, которую раньше вставляли в видеомагнитофон. Выходные данные потоковой трансляции — это сам видеомагнитофон. Трансляция — это просто видеосигнал, поступающий на заднюю панель магнитофона.

Сначала вы создаете сигнал, создав трансляцию. Сигнал не передастся, пока вы не запустите эту трансляцию и не подключите кодировщик к входным данным.

"Кассету" можно создать в любой момент. Это просто пустой ресурс, который можно передать в объект выходных данных потоковой трансляции — магнитофон в нашей аналогии.

"Магнитофон" также можно создать в любое время. Выходные данные потоковой трансляции можно создать перед запуском потока сигналов или после него. Если необходимо ускорить процесс, иногда бывает удобно создать выходные данные перед запуском потока сигналов.

Чтобы остановить "магнитофон", необходимо вызвать delete для LiveOutput. Это действие не удаляет содержимое "кассеты" (ресурс). Ресурс всегда хранится с архивным видеосодержимым до тех пор, пока не будет явно вызвана функция delete для самого ресурса.

В следующем разделе будет рассмотрено создание ресурса и выходных данных потоковой трансляции.

Создание ресурса

Создайте ресурс, который будет использоваться в выходных данных потоковой трансляции. В нашей аналогии это будет "кассета", на которую записывается потоковый видеосигнал. Зрители смогут просматривать содержимое этой виртуальной кассеты в реальном времени или по запросу.

// 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 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(
    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();

Создание указателя потоковой передачи

Примечание

При создании учетной записи Служб мультимедиа в нее добавляется конечная точка потоковой передачи по умолчанию в остановленном состоянии. Чтобы начать потоковую передачу содержимого и воспользоваться функциями динамической упаковки и динамического шифрования, конечная точка потоковой передачи, из которой необходимо выполнять потоковую передачу содержимого, должна находиться в запущенном состоянии.

В случае публикации ресурса с указателем потоковой передачи трансляция (до продолжительности окна DVR) будет доступна для просмотра, пока не истечет срок действия указателя потоковой передачи или он не будет удален, в зависимости от того, что произойдет первым. Так вы сделаете запись на виртуальной "кассете", доступной для зрителей в реальном времени и по запросу. По завершении записи (при удалении выходных данных потоковой трансляции) один и тот же URL-адрес можно будет использовать для просмотра трансляции, окна DVR или ресурса по запросу.

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

IList<string> filters = new List<string>
{
    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;
}

// 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()
}

Очистка ресурсов в учетной записи Служб мультимедиа

После завершения потоковой передачи вы можете удалить выделенные ранее ресурсы с помощью следующей процедуры.

  1. Остановите трансляцию потока из кодировщика.
  2. Остановите динамическое событие. Плата за остановленное событие потоковой трансляции не взимается. Если вам понадобится снова запустить его, вы можете воспользоваться тем же URL-адресом приема (перенастраивать кодировщик не потребуется).
  3. Остановите конечную точку потоковой передачи, если больше не собираетесь предоставлять доступ к архиву мероприятия в качестве потоковой передачи по требованию. Если трансляция находится в остановленном состоянии, плата не взимается.
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 (ErrorResponseException e)
    {
        Console.WriteLine("CleanupLiveEventAndOutputAsync -- Hit ErrorResponseException");
        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 (ErrorResponseException e)
    {
        Console.WriteLine("CleanupLocatorandAssetAsync -- Hit ErrorResponseException");
        Console.WriteLine($"\tCode: {e.Body.Error.Code}");
        Console.WriteLine($"\tCode: {e.Body.Error.Message}");
        Console.WriteLine();
    }
}

Просмотр события

Нажмите клавиши Ctrl+F5, чтобы выполнить код. Будут выведены URL-адреса потоковой передачи, которые можно использовать для просмотра трансляции. Скопируйте полученный URL-адрес потоковой передачи, чтобы создать указатель потоковой передачи. Вы можете использовать проигрыватель мультимедиа по своему усмотрению. Проигрыватель мультимедиа Azure позволяет протестировать поток на демонстрационном сайте Проигрывателя мультимедиа.

После остановки трансляция автоматически преобразует события в содержимое по требованию. Даже после остановки и удаления события пользователи смогут запрашивать потоковую трансляцию архивированного видеосодержимого, пока не удален соответствующий ресурс. Ресурс невозможно удалить, пока он используется каким-либо событием: сначала нужно удалить это событие.

Очистка оставшихся ресурсов

Если вам больше не нужны какие-либо ресурсы в группе ресурсов, включая Службы мультимедиа и учетные записи хранения, созданные для этого руководства, удалите группу ресурсов, созданную ранее.

Выполните приведенную ниже команду интерфейса командной строки.

az group delete --name amsResourceGroup

Важно!

Если ставить трансляцию выполняться, за это будет взиматься плата. Имейте в виду, что если проект или программа перестает отвечать или закрывается по какой-либо причине, то трансляция может остаться в запущенном состоянии, за которое будет взиматься плата.