Come eseguire lo streaming live con Servizi multimediali di Azure per creare flussi a più bitrate con .NET

Nota

Per completare l'esercitazione, è necessario un account Azure. Per informazioni dettagliate, vedere la pagina relativa alla versione di valutazione gratuita di Azure.

Overview

Questa esercitazione illustra i passaggi per creare un canale che riceve un flusso live a velocità in bit singola e lo codifica in un flusso a più velocità in bit.

Per altre informazioni concettuali sui canali correlati abilitati per la codifica live, vedere Uso di canali abilitati per l'esecuzione della codifica live con Servizi multimediali di Azure.

Scenario comune di streaming live

I seguenti passaggi descrivono le attività relative alla creazione di applicazioni comuni di streaming live.

Nota

Attualmente, la durata massima consigliata per un evento live è 8 ore. Se è necessario eseguire un canale per lunghi periodi di tempo, contattare amslived in Microsoft.com.

  1. Connettere una videocamera a un computer. Avviare e configurare un codificatore live locale che può restituire un flusso a velocità in bit singola in uno dei protocolli seguenti: RTMP, Smooth Streaming o RTP (MPEG-TS). Per altre informazioni, vedere l'argomento relativo a codificatori live e supporto RTMP di Servizi multimediali di Azure.

    Questa operazione può essere eseguita anche dopo la creazione del canale.

  2. Creare e avviare un canale.

  3. Recuperare l'URL di inserimento del canale.

    L'URL di inserimento viene usato dal codificatore live per inviare il flusso al canale.

  4. Recuperare l'URL di anteprima del canale.

    Usare questo URL per verificare che il canale riceva correttamente il flusso live.

  5. Creare un asset.

  6. Se si desidera che l'asset sia crittografato in modo dinamico durante la riproduzione, seguire questa procedura:
  7. Creare una chiave simmetrica.
  8. Configurare i criteri di autorizzazione della chiave simmetrica.
  9. Configurare i criteri di distribuzione degli asset (usati per la creazione dinamica dei pacchetti e la crittografia dinamica).
  10. Creare un programma e specificare di usare l'asset creato.
  11. Pubblicare l'asset associato al programma creando un localizzatore OnDemand.

    Nota

    Quando l'account AMS viene creato, un endpoint di streaming predefinito viene aggiunto all'account con stato Arrestato. L'endpoint di streaming da cui si vuole trasmettere il contenuto deve essere nello stato In esecuzione.

  12. Avviare il programma quando si è pronti a iniziare lo streaming e l'archiviazione.

  13. Facoltativamente, il codificatore live può ricevere il segnale per l'avvio di un annuncio. L'annuncio viene inserito nel flusso di output.
  14. Arrestare il programma ogni volta che si vuole interrompere lo streaming e l'archiviazione dell'evento.
  15. Eliminare il programma e, facoltativamente, eliminare l'asset.

Contenuto dell'esercitazione

Questo argomento illustra come eseguire diverse operazioni su canali e programmi tramite Media Services .NET SDK. Poiché molte operazioni hanno un'esecuzione prolungata, vengono usate API .NET che gestiscono questo tipo di operazioni.

Questo argomento illustra come eseguire le operazioni seguenti:

  1. Creare e avviare un canale. Vengono usate API con esecuzione prolungata.
  2. Ottenere l'endpoint di inserimento (input) del canale. L'endpoint deve essere fornito al codificatore che invia un flusso live a velocità in bit singola.
  3. Ottenere l'endpoint di anteprima. Questo endpoint viene usato per visualizzare il flusso in anteprima.
  4. Creare un asset che verrà usato per archiviare il contenuto. È necessario configurare anche i criteri di distribuzione degli asset, come illustrato in questo esempio.
  5. Creare un programma e specificare l'uso dell'asset creato in precedenza. Avviare il programma. Vengono usate API con esecuzione prolungata.
  6. Creare un localizzatore per l'asset in modo che il contenuto venga pubblicato e possa essere trasmesso in streaming ai client.
  7. Mostrare e nascondere slate. Avviare e arrestare annunci. Vengono usate API con esecuzione prolungata.
  8. Pulire il canale e tutte le risorse associate.

Prerequisiti

Per completare l'esercitazione è necessario quanto segue.

  • Un account Azure. Se non si dispone di un account, è possibile creare un account di valutazione gratuita in pochi minuti. Per informazioni dettagliate, vedere la pagina relativa alla versione di valutazione gratuita di Azure. sono inclusi crediti da usare per provare i servizi di Azure a pagamento. Una volta esauriti i crediti, è possibile mantenere l'account e usare le funzionalità e i servizi di Azure gratuiti, ad esempio la funzionalità App Web nel servizio app di Azure.
  • Account di Servizi multimediali. Per creare un account di Servizi multimediali, vedere l'argomento relativo alla creazione di un account.
  • Visual Studio 2010 SP1 (Professional, Premium, Ultimate, o Express) o versioni successive.
  • È necessario usare Media Services .NET SDK versione 3.2.0.0 o successiva.
  • Una webcam e un codificatore in grado di inviare un flusso live a velocità in bit singola.

Considerazioni

  • Attualmente, la durata massima consigliata per un evento live è 8 ore. Se è necessario eseguire un canale per lunghi periodi di tempo, contattare amslived in Microsoft.com.
  • È previsto un limite di 1.000.000 di criteri per i diversi criteri AMS (ad esempio per i criteri Locator o ContentKeyAuthorizationPolicy). Usare lo stesso ID criterio se si usano sempre gli stessi giorni/autorizzazioni di accesso, come nel cado di criteri per i localizzatori che devono rimanere attivi per molto tempo (criteri di non caricamento). Per altre informazioni, vedere questo argomento.

Scaricare un esempio

È possibile scaricare l'esempio descritto in questo argomento qui.

Configurare lo sviluppo con Media Services SDK per .NET

Configurare l'ambiente di sviluppo e popolare il file app.config con le informazioni di connessione, come descritto in Sviluppo di applicazioni di Servizi multimediali con .NET.

Esempio di codice

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using Microsoft.WindowsAzure.MediaServices.Client;
using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption;

namespace EncodeLiveStreamWithAmsClear
{
    class Program
    {
    private const string ChannelName = "channel001";
    private const string AssetlName = "asset001";
    private const string ProgramlName = "program001";

    // Read values from the App.config file.
    private static readonly string _AADTenantDomain =
    ConfigurationManager.AppSettings["AADTenantDomain"];
    private static readonly string _RESTAPIEndpoint =
    ConfigurationManager.AppSettings["MediaServiceRESTAPIEndpoint"];

    private static CloudMediaContext _context = null;

    static void Main(string[] args)
    {
        var tokenCredentials = new AzureAdTokenCredentials(_AADTenantDomain, AzureEnvironments.AzureCloudEnvironment);
        var tokenProvider = new AzureAdTokenProvider(tokenCredentials);

        _context = new CloudMediaContext(new Uri(_RESTAPIEndpoint), tokenProvider);

        IChannel channel = CreateAndStartChannel();

        // The channel's input endpoint:
        string ingestUrl = channel.Input.Endpoints.FirstOrDefault().Url.ToString();

        Console.WriteLine("Intest URL: {0}", ingestUrl);


        // Use the previewEndpoint to preview and verify 
        // that the input from the encoder is actually reaching the Channel. 
        string previewEndpoint = channel.Preview.Endpoints.FirstOrDefault().Url.ToString();

        Console.WriteLine("Preview URL: {0}", previewEndpoint);

        // When Live Encoding is enabled, you can now get a preview of the live feed as it reaches the Channel. 
        // This can be a valuable tool to check whether your live feed is actually reaching the Channel. 
        // The thumbnail is exposed via the same end-point as the Channel Preview URL.
        string thumbnailUri = new UriBuilder
        {
        Scheme = Uri.UriSchemeHttps,
        Host = channel.Preview.Endpoints.FirstOrDefault().Url.Host,
        Path = "thumbnails/input.jpg"
        }.Uri.ToString();

        Console.WriteLine("Thumbain URL: {0}", thumbnailUri);

        // Once you previewed your stream and verified that it is flowing into your Channel, 
        // you can create an event by creating an Asset, Program, and Streaming Locator. 
        IAsset asset = CreateAndConfigureAsset();

        IProgram program = CreateAndStartProgram(channel, asset);

        ILocator locator = CreateLocatorForAsset(program.Asset, program.ArchiveWindowLength);

        // You can use slates and ads only if the channel type is Standard.  
        StartStopAdsSlates(channel);

        // Once you are done streaming, clean up your resources.
        Cleanup(channel);
    }

    public static IChannel CreateAndStartChannel()
    {
        var channelInput = CreateChannelInput();
        var channePreview = CreateChannelPreview();
        var channelEncoding = CreateChannelEncoding();

        ChannelCreationOptions options = new ChannelCreationOptions
        {
        EncodingType = ChannelEncodingType.Standard,
        Name = ChannelName,
        Input = channelInput,
        Preview = channePreview,
        Encoding = channelEncoding
        };

        Log("Creating channel");
        IOperation channelCreateOperation = _context.Channels.SendCreateOperation(options);
        string channelId = TrackOperation(channelCreateOperation, "Channel create");

        IChannel channel = _context.Channels.Where(c => c.Id == channelId).FirstOrDefault();

        Log("Starting channel");
        var channelStartOperation = channel.SendStartOperation();
        TrackOperation(channelStartOperation, "Channel start");

        return channel;
    }

    /// <summary>
    /// Create channel input, used in channel creation options. 
    /// </summary>
    /// <returns></returns>
    private static ChannelInput CreateChannelInput()
    {
        return new ChannelInput
        {
        StreamingProtocol = StreamingProtocol.RTPMPEG2TS,
        AccessControl = new ChannelAccessControl
        {
            IPAllowList = new List<IPRange>
            {
                new IPRange
                {
                Name = "TestChannelInput001",
                Address = IPAddress.Parse("0.0.0.0"),
                SubnetPrefixLength = 0
                }
            }
        }
        };
    }

    /// <summary>
    /// Create channel preview, used in channel creation options. 
    /// </summary>
    /// <returns></returns>
    private static ChannelPreview CreateChannelPreview()
    {
        return new ChannelPreview
        {
        AccessControl = new ChannelAccessControl
        {
            IPAllowList = new List<IPRange>
            {
                new IPRange
                {
                Name = "TestChannelPreview001",
                Address = IPAddress.Parse("0.0.0.0"),
                SubnetPrefixLength = 0
                }
            }
        }
        };
    }

    /// <summary>
    /// Create channel encoding, used in channel creation options. 
    /// </summary>
    /// <returns></returns>
    private static ChannelEncoding CreateChannelEncoding()
    {
        return new ChannelEncoding
        {
        SystemPreset = "Default720p",
        IgnoreCea708ClosedCaptions = false,
        AdMarkerSource = AdMarkerSource.Api,
        // You can only set audio if streaming protocol is set to StreamingProtocol.RTPMPEG2TS.
        AudioStreams = new List<AudioStream> { new AudioStream { Index = 103, Language = "eng" } }.AsReadOnly()
        };
    }

    /// <summary>
    /// Create an asset and configure asset delivery policies.
    /// </summary>
    /// <returns></returns>
    public static IAsset CreateAndConfigureAsset()
    {
        IAsset asset = _context.Assets.Create(AssetlName, AssetCreationOptions.None);

        IAssetDeliveryPolicy policy =
        _context.AssetDeliveryPolicies.Create("Clear Policy",
        AssetDeliveryPolicyType.NoDynamicEncryption,
        AssetDeliveryProtocol.HLS | AssetDeliveryProtocol.SmoothStreaming | AssetDeliveryProtocol.Dash, null);

        asset.DeliveryPolicies.Add(policy);

        return asset;
    }

    /// <summary>
    /// Create a Program on the Channel. You can have multiple Programs that overlap or are sequential;
    /// however each Program must have a unique name within your Media Services account.
    /// </summary>
    /// <param name="channel"></param>
    /// <param name="asset"></param>
    /// <returns></returns>
    public static IProgram CreateAndStartProgram(IChannel channel, IAsset asset)
    {
        IProgram program = channel.Programs.Create(ProgramlName, TimeSpan.FromHours(3), asset.Id);
        Log("Program created", program.Id);

        Log("Starting program");
        var programStartOperation = program.SendStartOperation();
        TrackOperation(programStartOperation, "Program start");

        return program;
    }

    /// <summary>
    /// Create locators in order to be able to publish and stream the video.
    /// </summary>
    /// <param name="asset"></param>
    /// <param name="ArchiveWindowLength"></param>
    /// <returns></returns>
    public static ILocator CreateLocatorForAsset(IAsset asset, TimeSpan ArchiveWindowLength)
    {
        // You cannot create a streaming locator using an AccessPolicy that includes write or delete permissions.            
        var locator = _context.Locators.CreateLocator
        (
            LocatorType.OnDemandOrigin,
            asset,
            _context.AccessPolicies.Create
            (
                "Live Stream Policy",
                ArchiveWindowLength,
                AccessPermissions.Read
            )
        );

        return locator;
    }

    /// <summary>
    /// Perform operations on slates.
    /// </summary>
    /// <param name="channel"></param>
    public static void StartStopAdsSlates(IChannel channel)
    {
        int cueId = new Random().Next(int.MaxValue);
        var path = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\\..\\SlateJPG\\DefaultAzurePortalSlate.jpg"));

        Log("Creating asset");
        var slateAsset = _context.Assets.Create("Slate test asset " + DateTime.Now.ToString("yyyy-MM-dd HH-mm"), AssetCreationOptions.None);
        Log("Slate asset created", slateAsset.Id);

        Log("Uploading file");
        var assetFile = slateAsset.AssetFiles.Create("DefaultAzurePortalSlate.jpg");
        assetFile.Upload(path);
        assetFile.IsPrimary = true;
        assetFile.Update();

        Log("Showing slate");
        var showSlateOpeartion = channel.SendShowSlateOperation(TimeSpan.FromMinutes(1), slateAsset.Id);
        TrackOperation(showSlateOpeartion, "Show slate");

        Log("Hiding slate");
        var hideSlateOperation = channel.SendHideSlateOperation();
        TrackOperation(hideSlateOperation, "Hide slate");

        Log("Starting ad");
        var startAdOperation = channel.SendStartAdvertisementOperation(TimeSpan.FromMinutes(1), cueId, false);
        TrackOperation(startAdOperation, "Start ad");

        Log("Ending ad");
        var endAdOperation = channel.SendEndAdvertisementOperation(cueId);
        TrackOperation(endAdOperation, "End ad");

        Log("Deleting slate asset");
        slateAsset.Delete();
    }

    /// <summary>
    /// Clean up resources associated with the channel.
    /// </summary>
    /// <param name="channel"></param>
    public static void Cleanup(IChannel channel)
    {
        IAsset asset;
        if (channel != null)
        {
        foreach (var program in channel.Programs)
        {
            asset = _context.Assets.Where(se => se.Id == program.AssetId)
                        .FirstOrDefault();

            Log("Stopping program");
            var programStopOperation = program.SendStopOperation();
            TrackOperation(programStopOperation, "Program stop");

            program.Delete();

            if (asset != null)
            {
            Log("Deleting locators");
            foreach (var l in asset.Locators)
                l.Delete();

            Log("Deleting asset");
            asset.Delete();
            }
        }

        Log("Stopping channel");
        var channelStopOperation = channel.SendStopOperation();
        TrackOperation(channelStopOperation, "Channel stop");

        Log("Deleting channel");
        var channelDeleteOperation = channel.SendDeleteOperation();
        TrackOperation(channelDeleteOperation, "Channel delete");
        }
    }

    /// <summary>
    /// Track long running operations.
    /// </summary>
    /// <param name="operation"></param>
    /// <param name="description"></param>
    /// <returns></returns>
    public static string TrackOperation(IOperation operation, string description)
    {
        string entityId = null;
        bool isCompleted = false;

        Log("starting to track ", null, operation.Id);
        while (isCompleted == false)
        {
        operation = _context.Operations.GetOperation(operation.Id);
        isCompleted = IsCompleted(operation, out entityId);
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(30));
        }
        // If we got here, the operation succeeded.
        Log(description + " in completed", operation.TargetEntityId, operation.Id);

        return entityId;
    }

    /// <summary> 
    /// Checks if the operation has been completed. 
    /// If the operation succeeded, the created entity Id is returned in the out parameter.
    /// </summary> 
    /// <param name="operationId">The operation Id.</param> 
    /// <param name="channel">
    /// If the operation succeeded, 
    /// the entity Id associated with the sucessful operation is returned in the out parameter.</param>
    /// <returns>Returns false if the operation is still in progress; otherwise, true.</returns> 
    private static bool IsCompleted(IOperation operation, out string entityId)
    {
        bool completed = false;

        entityId = null;

        switch (operation.State)
        {
        case OperationState.Failed:
            // Handle the failure. 
            // For example, throw an exception. 
            // Use the following information in the exception: operationId, operation.ErrorMessage.
            Log("operation failed", operation.TargetEntityId, operation.Id);
            break;
        case OperationState.Succeeded:
            completed = true;
            entityId = operation.TargetEntityId;
            break;
        case OperationState.InProgress:
            completed = false;
            Log("operation in progress", operation.TargetEntityId, operation.Id);
            break;
        }
        return completed;
    }

    private static void Log(string action, string entityId = null, string operationId = null)
    {
        Console.WriteLine(
        "{0,-21}{1,-51}{2,-51}{3,-51}",
        DateTime.Now.ToString("yyyy'-'MM'-'dd HH':'mm':'ss"),
        action,
        entityId ?? string.Empty,
        operationId ?? string.Empty);
    }
    }
}

Passaggio successivo

Analizzare i percorsi di apprendimento di Servizi multimediali.

Altre informazioni sui percorsi di apprendimento di Servizi multimediali di Azure:

Fornire commenti e suggerimenti

Usare il forum di suggerimenti degli utenti per fornire commenti e suggerimenti su come migliorare Servizi multimediali di Azure. È anche possibile passare direttamente a una delle categorie seguenti: