/// <summary> /// Run the sample async. /// </summary> /// <param name="config">The param is of type ConfigWrapper. This class reads values from local configuration file.</param> /// <returns></returns> private static async Task RunAsync(ConfigWrapper config) { IAzureMediaServicesClient client; try { client = await CreateMediaServicesClientAsync(config); } catch (Exception e) { Console.Error.WriteLine("TIP: Make sure that you have filled out the appsettings.json file before running this sample."); Console.Error.WriteLine($"{e.Message}"); return; } // Set the polling interval for long running operations to 2 seconds. // The default value is 30 seconds for the .NET client SDK client.LongRunningOperationRetryTimeout = 2; // Creating a unique suffix so that we don't have name collisions if you run the sample // multiple times without cleaning up. string uniqueness = Guid.NewGuid().ToString().Substring(0, 13); string liveEventName = "liveevent-" + uniqueness; string assetName = "archiveAsset" + uniqueness; string liveOutputName = "liveOutput" + uniqueness; string drvStreamingLocatorName = "streamingLocator" + uniqueness; string archiveStreamingLocatorName = "fullLocator-" + uniqueness; string drvAssetFilterName = "filter-" + uniqueness; string streamingEndpointName = "default"; // Change this to your Endpoint name. EventProcessorHost eventProcessorHost = null; bool stopEndpoint = false; try { // Getting the mediaServices account so that we can use the location to create the // LiveEvent and StreamingEndpoint MediaService mediaService = await client.Mediaservices.GetAsync(config.ResourceGroup, config.AccountName); Console.WriteLine($"Creating a live event named {liveEventName}"); Console.WriteLine(); // 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. LiveEventInputAccessControl liveEventInputAccess = new LiveEventInputAccessControl { Ip = new IPAccessControl( allow: new IPRange[] { allAllowIPRange } ) }; // Create the LiveEvent Preview IP access control LiveEventPreview liveEventPreview = new LiveEventPreview { AccessControl = new LiveEventPreviewAccessControl( ip: new IPAccessControl( allow: new IPRange[] { allAllowIPRange } ) ) }; // To get the same ingest URL for the same LiveEvent name: // 1. Set vanityUrl to true so you have ingest like: // rtmps://liveevent-hevc12-eventgridmediaservice-usw22.channel.media.azure.net:2935/live/522f9b27dd2d4b26aeb9ef8ab96c5c77 // 2. Set accessToken to a desired GUID string (with or without hyphen) LiveEvent liveEvent = new LiveEvent( location: mediaService.Location, description: "Sample LiveEvent for testing", vanityUrl: false, encoding: new LiveEventEncoding( // Set this to Standard to enable a trans-coding LiveEvent, and None to enable a pass-through LiveEvent encodingType: LiveEventEncodingType.Premium1080p, presetName: null ), input: new LiveEventInput(LiveEventInputProtocol.RTMP, liveEventInputAccess), preview: liveEventPreview, 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 } ); Console.WriteLine($"Creating the LiveEvent, be patient this can take time..."); // 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. liveEvent = await client.LiveEvents.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName, liveEvent, autoStart : true); // Start monitoring LiveEvent events. 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}", 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); } // Get the input endpoint to configure the on premise encoder with string ingestUrl = liveEvent.Input.Endpoints.First().Url; Console.WriteLine($"The ingest url to configure the on premise encoder with is:"); Console.WriteLine($"\t{ingestUrl}"); Console.WriteLine(); // Use the previewEndpoint to preview and verify // that the input from the encoder is actually being received 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(); Console.WriteLine("Start the live stream now, sending the input to the ingest url and verify that it is arriving with the preview url."); Console.WriteLine("IMPORTANT TIP!: Make ABSOLUTLEY CERTAIN that the video is flowing to the Preview URL before continuing!"); Console.WriteLine("******************************"); Console.WriteLine("* Press ENTER to continue... *"); Console.WriteLine("******************************"); Console.WriteLine(); Console.Out.Flush(); var ignoredInput = Console.ReadLine(); // Create an Asset for the LiveOutput to use Console.WriteLine($"Creating an asset named {assetName}"); Console.WriteLine(); Asset asset = await client.Assets.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName, assetName, new Asset()); //AssetFilter drvAssetFilter = new AssetFilter( // presentationTimeRange: new PresentationTimeRange( // forceEndTimestamp:false, // // 300 seconds sliding window // presentationWindowDuration: 3000000000L, // // This value defines the latest live position that a client can seek back to 10 seconds, must be smaller than sliding window. // liveBackoffDuration: 100000000L) //); //drvAssetFilter = await client.AssetFilters.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName, // assetName, drvAssetFilterName, drvAssetFilter); // Create the LiveOutput string manifestName = "output"; Console.WriteLine($"Creating a live output named {liveOutputName}"); Console.WriteLine(); // withArchiveWindowLength: 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. LiveOutput liveOutput = new LiveOutput(assetName: asset.Name, manifestName: manifestName, archiveWindowLength: TimeSpan.FromHours(2)); liveOutput = await client.LiveOutputs.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName, liveOutputName, liveOutput); // Create the StreamingLocator Console.WriteLine($"Creating a streaming locator named {drvStreamingLocatorName}"); 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 filters with StreamingLocator. // }); // Set a token signing key that you want to use TokenSigningKey = Convert.FromBase64String(config.SymmetricKeyPR); // Create the content key policy that configures how the content key is delivered to end clients // via the Key Delivery component of Azure Media Services. // We are using the ContentKeyIdentifierClaim in the ContentKeyPolicy which means that the token presented // to the Key Delivery Component must have the identifier of the content key in it. ContentKeyPolicy policy = await GetOrCreateContentKeyPolicyAsync(client, config.ResourceGroup, config.AccountName, ContentKeyPolicyName, TokenSigningKey); // Sets StreamingLocator.StreamingPolicyName to "Predefined_MultiDrmCencStreaming" policy. StreamingLocator locator = await CreateStreamingLocatorAsync(client, config.ResourceGroup, config.AccountName, asset.Name, drvStreamingLocatorName, ContentKeyPolicyName); // In this example, we want to play the PlayReady (CENC) encrypted stream. // We need to get the key identifier of the content key where its type is CommonEncryptionCenc. string keyIdentifier = locator.ContentKeys.Where(k => k.Type == StreamingLocatorContentKeyType.CommonEncryptionCenc).First().Id.ToString(); Console.WriteLine($"KeyIdentifier = {keyIdentifier}"); // In order to generate our test token we must get the ContentKeyId to put in the ContentKeyIdentifierClaim claim. string token = GetTokenAsync(Issuer, Audience, keyIdentifier, TokenSigningKey); StreamingEndpoint streamingEndpoint = await client.StreamingEndpoints.GetAsync(config.ResourceGroup, config.AccountName, DefaultStreamingEndpointName); // 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; } string dashPath = await GetDASHStreamingUrlAsync(client, config.ResourceGroup, config.AccountName, locator.Name, streamingEndpoint); if (dashPath.Length > 0) { Console.WriteLine("---Encrypted URL for IE or Edge---"); Console.WriteLine($"\thttps://ampdemo.azureedge.net/?url={dashPath}&playready=true&token=Bearer%3D{token}"); Console.WriteLine("---Encrypted URL for Chrome or Firefox---"); Console.WriteLine($"\thttps://ampdemo.azureedge.net/?url={dashPath}&widevine=true&token=Bearer%3D{token}"); Console.WriteLine("-------------------"); Console.WriteLine("If you see an error in Azure Media Player, wait a few moments and try again."); Console.WriteLine("Continue experimenting with the stream until you are ready to finish."); Console.WriteLine(); Console.WriteLine("***********************************************"); Console.WriteLine("* Press ENTER anytime to stop the LiveEvent. *"); Console.WriteLine("***********************************************"); Console.WriteLine(); Console.Out.Flush(); ignoredInput = Console.ReadLine(); Console.WriteLine("Cleaning up LiveEvent and output..."); await CleanupLiveEventAndOutputAsync(client, config.ResourceGroup, config.AccountName, liveEventName); Console.WriteLine("The LiveOutput and LiveEvent are now deleted. The event is available as an archive and can still be streamed."); // If we started the endpoint, we'll stop it. Otherwise, we'll keep the endpoint running and print urls // that can be played even after this sample ends. if (!stopEndpoint) { StreamingLocator archiveLocator = await client.StreamingLocators.CreateAsync(config.ResourceGroup, config.AccountName, archiveStreamingLocatorName, new StreamingLocator { AssetName = assetName, StreamingPolicyName = PredefinedStreamingPolicy.ClearStreamingOnly }); Console.WriteLine("To playback the archived files, Use the following urls:"); await PrintPaths(client, config.ResourceGroup, config.AccountName, archiveStreamingLocatorName, streamingEndpoint); } } } catch (ApiErrorException e) { Console.WriteLine("Hit ApiErrorException"); Console.WriteLine($"\tCode: {e.Body.Error.Code}"); Console.WriteLine($"\tCode: {e.Body.Error.Message}"); Console.WriteLine(); Console.WriteLine("Exiting, cleanup may be necessary..."); Console.ReadLine(); } finally { await CleanupLiveEventAndOutputAsync(client, config.ResourceGroup, config.AccountName, liveEventName); await CleanupLocatorAsync(client, config.ResourceGroup, config.AccountName, drvStreamingLocatorName); // Stop event monitoring. if (eventProcessorHost != null) { await eventProcessorHost.UnregisterEventProcessorAsync(); } if (stopEndpoint) { // Because we started the endpoint, we'll stop it. await client.StreamingEndpoints.StopAsync(config.ResourceGroup, config.AccountName, streamingEndpointName); } else { // We will keep the endpoint running because it was not started by us. There are costs to keep it running. // Please refer https://azure.microsoft.com/en-us/pricing/details/media-services/ for pricing. Console.WriteLine($"The endpoint {streamingEndpointName} is running. To halt further billing on the endpoint, please stop it in azure portal or AMS Explorer."); } } }
/// <summary> /// Creates the AzureMediaServicesClient object based on the credentials /// supplied in local configuration file. /// </summary> /// <param name="config">The param is of type ConfigWrapper. This class reads values from local configuration file.</param> /// <returns></returns> private static async Task <IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config) { var credentials = await GetCredentialsAsync(config); return(new AzureMediaServicesClient(config.ArmEndpoint, credentials) { SubscriptionId = config.SubscriptionId, }); }