diff --git a/NETCore/Live/MediaV3LiveApp/Program.cs b/NETCore/Live/MediaV3LiveApp/Program.cs index f6f2a9e..d478eb0 100644 --- a/NETCore/Live/MediaV3LiveApp/Program.cs +++ b/NETCore/Live/MediaV3LiveApp/Program.cs @@ -17,8 +17,6 @@ namespace LiveSample { class Program { - private static string liveEventName; - public static async Task Main(string[] args) { ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder() @@ -57,11 +55,9 @@ public static async Task Main(string[] args) /// Run the sample async. /// /// The parm is of type ConfigWrapper. This class reads values from local configuration file. - /// - // + /// an async Task private static async Task RunAsync(ConfigWrapper config) { - IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config); // Creating a unique suffix so that we don't have name collisions if you run the sample @@ -284,261 +280,17 @@ private static async Task RunAsync(ConfigWrapper config) await CleanupLiveEventAndOutputAsync(client, config.ResourceGroup, config.AccountName, liveEventName); await CleanupLocatorandAssetAsync(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName); - } - } - - private static async Task CreateLiveEventAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string liveEventName) - { - // Getting the mediaServices account so that we can use the location to create the - // LiveEvent and StreamingEndpoint - MediaService mediaService = await client.Mediaservices.GetAsync(resourceGroup, 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 transcoding LiveEvent, and None to enable a pass-through LiveEvent - encodingType: LiveEventEncodingType.None, - presetName: null - ), - input: new LiveEventInput(LiveEventInputProtocol.RTMP, liveEventInputAccess), - preview: liveEventPreview, - streamOptions: new List() - { - // Set this to Default or Low Latency - // When using Low Latency mode, you must configure the Azure Media Player to use the - // quick start hueristic 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. - return await client.LiveEvents.CreateAsync(resourceGroup, accountName, liveEventName, liveEvent, autoStart: true); - } - - private static async Task CreateAssetAndLocatorAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string assetName, string streamingLocatorName) - { - // Create an Asset for the LiveOutput to use - Console.WriteLine($"Creating an asset named {assetName}"); - Console.WriteLine(); - Asset asset = await client.Assets.CreateOrUpdateAsync(resourceGroup, accountName, assetName, new Asset()); - - // Create the StreamingLocator - Console.WriteLine($"Creating a streaming locator named {streamingLocatorName}"); - Console.WriteLine(); - - StreamingLocator locator = new StreamingLocator(assetName: asset.Name, streamingPolicyName: PredefinedStreamingPolicy.ClearStreamingOnly); - return await client.StreamingLocators.CreateAsync(resourceGroup, accountName, streamingLocatorName, locator); - } - - private static async Task StartStreamingEndpointAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string assetName, string streamingEndpointName) - { - // Get the default Streaming Endpoint on the account - StreamingEndpoint streamingEndpoint = await client.StreamingEndpoints.GetAsync(resourceGroup, 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(resourceGroup, accountName, streamingEndpointName); - - streamingEndpoint = await client.StreamingEndpoints.GetAsync(resourceGroup, accountName, streamingEndpointName); - } - - return streamingEndpoint; - } - - private static async Task RunAsync2(ConfigWrapper config) - { - IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config); - - // 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 streamingLocatorName = "streamingLocator" + uniqueness; - string streamingEndpointName = "default"; - - try - { - Task liveEventTask = CreateLiveEventAsync(client, config.ResourceGroup, config.AccountName, liveEventName); - Task streamingLocatorTask = CreateAssetAndLocatorAsync(client, config.ResourceGroup, config.AccountName, assetName, streamingLocatorName); - Task startStreamingEndpointTask = StartStreamingEndpointAsync(client, config.ResourceGroup, config.AccountName, assetName, streamingEndpointName); - - Task[] tasks = new Task[] - { - liveEventTask, - streamingLocatorTask, - startStreamingEndpointTask - }; - - Task.WaitAll(tasks); - - LiveEvent liveEvent = liveEventTask.Result; - StreamingEndpoint streamingEndpoint = startStreamingEndpointTask.Result; - - // 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("Press enter to continue..."); - Console.Out.Flush(); - var ignoredInput = Console.ReadLine(); - - // Create the LiveOutput - string manifestName = "output"; - Console.WriteLine($"Creating a live output named {liveOutputName}"); - Console.WriteLine(); - - LiveOutput liveOutput = new LiveOutput(assetName: assetName, manifestName: manifestName, archiveWindowLength: TimeSpan.FromMinutes(10)); - liveOutput = await client.LiveOutputs.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName, liveOutputName, liveOutput); - - // Get the url to stream the output - var paths = await client.StreamingLocators.ListPathsAsync(config.ResourceGroup, config.AccountName, streamingLocatorName); - - Console.WriteLine("The urls to stream the output from a client:"); - Console.WriteLine(); - StringBuilder stringBuilder = new StringBuilder(); - string playerPath = string.Empty; - - for (int i = 0; i < paths.StreamingPaths.Count; i++) - { - UriBuilder uriBuilder = new UriBuilder(); - uriBuilder.Scheme = "https"; - uriBuilder.Host = streamingEndpoint.HostName; - - if (paths.StreamingPaths[i].Paths.Count > 0) - { - uriBuilder.Path = paths.StreamingPaths[i].Paths[0]; - stringBuilder.AppendLine($"\t{paths.StreamingPaths[i].StreamingProtocol}-{paths.StreamingPaths[i].EncryptionScheme}"); - stringBuilder.AppendLine($"\t\t{uriBuilder.ToString()}"); - stringBuilder.AppendLine(); - - if (paths.StreamingPaths[i].StreamingProtocol == StreamingPolicyStreamingProtocol.Dash) - { - playerPath = uriBuilder.ToString(); - } - } - } - - if (stringBuilder.Length > 0) - { - Console.WriteLine(stringBuilder.ToString()); - Console.WriteLine("Open the following URL to playback the published,recording LiveOutput in the Azure Media Player"); - Console.WriteLine($"\t https://ampdemo.azureedge.net/?url={playerPath}&heuristicprofile=lowlatency"); - Console.WriteLine(); - - Console.WriteLine("Continue experimenting with the stream until you are ready to finish."); - Console.WriteLine("Press enter to stop the LiveOutput..."); - Console.Out.Flush(); - ignoredInput = Console.ReadLine(); - - 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."); - Console.WriteLine("Press enter to finish cleanup..."); - Console.Out.Flush(); - ignoredInput = Console.ReadLine(); - } - else - { - Console.WriteLine("No Streaming Paths were detected. Has the Stream been started?"); - Console.WriteLine("Cleaning up and Exiting..."); - } - } - 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 CleanupLocatorandAssetAsync(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName); + await CleanupStreamingEndpointAsync(client, config.ResourceGroup, config.AccountName, streamingEndpointName, deleteEndpoint: false); } } - /// /// Create the ServiceClientCredentials object based on the credentials /// supplied in local configuration file. /// /// The parm is of type ConfigWrapper. This class reads values from local configuration file. - /// - // + /// ServiceCredentials that can be used to create an Azure Media Services client instance. private static async Task GetCredentialsAsync(ConfigWrapper config) { // Use UserTokenProvider.LoginWithPromptAsync or UserTokenProvider.LoginSilentAsync to get a token using user authentication @@ -553,15 +305,13 @@ private static async Task GetCredentialsAsync(ConfigWr ClientCredential clientCredential = new ClientCredential(config.AadClientId, config.AadSecret); return await ApplicationTokenProvider.LoginSilentAsync(config.AadTenantId, clientCredential, ActiveDirectoryServiceSettings.Azure); } - // /// /// Creates the AzureMediaServicesClient object based on the credentials /// supplied in local configuration file. /// /// The parm is of type ConfigWrapper. This class reads values from local configuration file. - /// - // + /// an Azure Media Services client instance private static async Task CreateMediaServicesClientAsync(ConfigWrapper config) { var credentials = await GetCredentialsAsync(config); @@ -571,9 +321,17 @@ private static async Task CreateMediaServicesClientAs SubscriptionId = config.SubscriptionId, }; } - // - // + /// + /// Checks to see if the LiveEvent exists and if so it stops it (if running) and then deletes the LiveEvent. The removeOutputsOnStop parameter + /// is set to true so that any LiveOutputs associated with the LiveEvent will also be deleted as part of the cleanup. The method will log any + /// caught exceptions to console output but won't rethrow them. + /// + /// Azure Media Services client instance to use + /// The Resource Group containing the Azure Media Services account. + /// The name of the Azure Media Services account. + /// The name of the LiveEvent to delete. + /// Async task private static async Task CleanupLiveEventAndOutputAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string liveEventName) { try @@ -586,10 +344,15 @@ private static async Task CleanupLiveEventAndOutputAsync(IAzureMediaServicesClie { // If the LiveEvent is running, stop it and have it remove any LiveOutputs await client.LiveEvents.StopAsync(resourceGroup, accountName, liveEventName, removeOutputsOnStop: true); + + Console.WriteLine($"LiveEvent {liveEventName} is stopped."); } // Delete the LiveEvent await client.LiveEvents.DeleteAsync(resourceGroup, accountName, liveEventName); + + Console.WriteLine($"LiveEvent {liveEventName} is deleted."); + Console.WriteLine(); } } catch (ApiErrorException e) @@ -600,9 +363,18 @@ private static async Task CleanupLiveEventAndOutputAsync(IAzureMediaServicesClie Console.WriteLine(); } } - // - - // + + + /// + /// Deletes the StreamingLocator and Asset matching the names provided as parameters. The method will log any + /// caught exceptions to console output but won't rethrow them. + /// + /// Azure Media Services client instance to use + /// The Resource Group containing the Azure Media Services account. + /// The name of the Azure Media Services account. + /// The name of the StreamingLocator to delete. + /// The name of the Asset to delete. + /// Async task private static async Task CleanupLocatorandAssetAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string streamingLocatorName, string assetName) { try @@ -610,8 +382,13 @@ private static async Task CleanupLocatorandAssetAsync(IAzureMediaServicesClient // Delete the Streaming Locator await client.StreamingLocators.DeleteAsync(resourceGroup, accountName, streamingLocatorName); + Console.WriteLine($"StreamingLocator {streamingLocatorName} is deleted."); + // Delete the Archive Asset await client.Assets.DeleteAsync(resourceGroup, accountName, assetName); + + Console.WriteLine($"Asset {assetName} is deleted."); + Console.WriteLine(); } catch (ApiErrorException e) { @@ -621,27 +398,39 @@ private static async Task CleanupLocatorandAssetAsync(IAzureMediaServicesClient Console.WriteLine(); } } - // - private static async Task CleanupStreamingEndpointAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string streamingEndpointName, bool stopEndpoint, bool deleteEndpoint) + /// + /// Stops the StreamingEndpoint with the specified name. If the deleteEndpoint parameter is true, then the StreamingEndpoint + /// will be deleted after it is stopped. + /// + /// Azure Media Services client instance to use + /// The Resource Group containing the Azure Media Services account. + /// The name of the Azure Media Services account. + /// The name of the StreamingEndpoint to stop or delete. + /// A value specifying whether the StreamingEndpoint should be deleted after it is stopped. + /// Async task + private static async Task CleanupStreamingEndpointAsync(IAzureMediaServicesClient client, string resourceGroup, string accountName, string streamingEndpointName, bool deleteEndpoint) { try { - if (stopEndpoint || deleteEndpoint) + StreamingEndpoint s = await client.StreamingEndpoints.GetAsync(resourceGroup, accountName, streamingEndpointName); + + if (s != null && s.ResourceState == StreamingEndpointResourceState.Running) { - StreamingEndpoint s = await client.StreamingEndpoints.GetAsync(resourceGroup, accountName, streamingEndpointName); + // Stop the StreamingEndpoint + await client.StreamingEndpoints.StopAsync(resourceGroup, accountName, streamingEndpointName); - if (s != null && s.ResourceState == StreamingEndpointResourceState.Running) - { - // Stop the StreamingEndpoint - await client.StreamingEndpoints.StopAsync(resourceGroup, accountName, streamingEndpointName); - } + Console.WriteLine($"StreamingEndpoint {streamingEndpointName} is stopped."); + Console.WriteLine(); + } - if (deleteEndpoint) - { - // Delete the StreamingEndpoint - await client.StreamingEndpoints.DeleteAsync(resourceGroup, accountName, streamingEndpointName); - } + if (deleteEndpoint) + { + // Delete the StreamingEndpoint + await client.StreamingEndpoints.DeleteAsync(resourceGroup, accountName, streamingEndpointName); + + Console.WriteLine($"StreamingEndpoint {streamingEndpointName} is deleted."); + Console.WriteLine(); } } catch (ApiErrorException e)