Esempio n. 1
0
        /// <summary>
        /// Run the sample async.
        /// </summary>
        /// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
        /// <returns></returns>
        // <RunAsync>
        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
            // 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
            {
                // 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);

                #region CreateLiveEvent
                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 <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 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.
                liveEvent = await client.LiveEvents.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName, liveEvent, autoStart : true);

                #endregion

                // Get the input endpoint to configure the on premise encoder with
                #region GetIngestUrl
                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();
                #endregion

                // Use the previewEndpoint to preview and verify
                // that the input from the encoder is actually being received
                #region GetPreviewURLs
                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();
                #endregion

                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 an Asset for the LiveOutput to use
                #region CreateAsset
                Console.WriteLine($"Creating an asset named {assetName}");
                Console.WriteLine();
                Asset asset = await client.Assets.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName, assetName, new Asset());

                #endregion

                // Create the LiveOutput
                #region CreateLiveOutput
                string manifestName = "output";
                Console.WriteLine($"Creating a live output named {liveOutputName}");
                Console.WriteLine();

                LiveOutput liveOutput = new LiveOutput(assetName: asset.Name, manifestName: manifestName, archiveWindowLength: TimeSpan.FromMinutes(10));
                liveOutput = await client.LiveOutputs.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName, liveOutputName, liveOutput);

                #endregion

                // Create the StreamingLocator
                #region CreateStreamingLocator
                Console.WriteLine($"Creating a streaming locator named {streamingLocatorName}");
                Console.WriteLine();

                StreamingLocator locator = new StreamingLocator(assetName: asset.Name, streamingPolicyName: PredefinedStreamingPolicy.ClearStreamingOnly);
                locator = await client.StreamingLocators.CreateAsync(config.ResourceGroup, config.AccountName, streamingLocatorName, locator);

                // 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);
                }
                #endregion

                // 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);
            }
        }
Esempio n. 2
0
        private static async Task <LiveEvent> 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 <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 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));
        }
        public async Task <LiveStreamStartedResult> Start(string roomId)
        {
            // TODO: track live stream in each room

            if (_startupResult != null)
            {
                return(_startupResult);
            }

            _startupResult = new LiveStreamStartedResult();
            Console.WriteLine("Starting live streaming");

            IAzureMediaServicesClient serviceClient = await _mediaServiceClientFactory.CreateMediaServicesClientAsync(_config);

            string uniqueness = Guid.NewGuid().ToString().Substring(0, 13); // Create a GUID for uniqueness. You can make this something static if you dont want to change RTMP ingest settings in OBS constantly.

            _liveEventName  = "liveevent-" + uniqueness;                    // WARNING: Be careful not to leak live events using this sample!
            _assetName      = "archiveAsset" + uniqueness;
            _liveOutputName = "liveOutput" + uniqueness;
            string drvStreamingLocatorName     = "streamingLocator" + uniqueness;
            string archiveStreamingLocatorName = "fullLocator-" + uniqueness;
            string drvAssetFilterName          = "filter-" + uniqueness;

            _streamingLocatorName  = "streamingLocator" + uniqueness;
            _streamingEndpointName = "default";             // Change this to your specific streaming endpoint name if not using "default"

            MediaService mediaService = await serviceClient.Mediaservices.GetAsync(_config.ResourceGroup, _config.AccountName);

            IPRange allAllowIPRange = new IPRange(
                name: "AllowAll",
                address: "0.0.0.0",
                subnetPrefixLength: 0
                );
            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
                }
                    )
            };
            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
                }
                        )
                    )
            };
            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.
                    // See https://go.microsoft.com/fwlink/?linkid=2095101 for more information
                    // 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
            }
                //,
                // 5) Optionally enable live transcriptions if desired.
                // 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
                 *              )
                 *      )
                 * }*/
                );

            liveEvent = await serviceClient.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);

            Asset asset = await serviceClient.Assets.CreateOrUpdateAsync(_config.ResourceGroup, _config.AccountName, _assetName, new Asset());

            string     manifestName = "output";
            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 serviceClient.LiveOutputs.CreateAsync(
                _config.ResourceGroup,
                _config.AccountName,
                _liveEventName,
                _liveOutputName,
                liveOutput);

            await serviceClient.LiveEvents.StartAsync(_config.ResourceGroup, _config.AccountName, _liveEventName);

            // Refresh the liveEvent object's settings after starting it...
            liveEvent = await serviceClient.LiveEvents.GetAsync(_config.ResourceGroup, _config.AccountName, _liveEventName);

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

            string previewEndpoint = liveEvent.Preview.Endpoints.First().Url;

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

            AssetFilter drvAssetFilter = new AssetFilter(
                presentationTimeRange: new PresentationTimeRange(
                    forceEndTimestamp: false,
                    // 10 minute (600) seconds sliding window
                    presentationWindowDuration: 6000000000L,
                    // This value defines the latest live position that a client can seek back to 2 seconds, must be smaller than sliding window.
                    liveBackoffDuration: 20000000L)
                );

            drvAssetFilter = await serviceClient.AssetFilters.CreateOrUpdateAsync(_config.ResourceGroup, _config.AccountName,
                                                                                  _assetName, drvAssetFilterName, drvAssetFilter);

            IList <string> filters = new List <string>
            {
                drvAssetFilterName
            };
            StreamingLocator locator = await serviceClient.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 serviceClient.StreamingEndpoints.GetAsync(_config.ResourceGroup, _config.AccountName, _streamingEndpointName);

            // If it's not running, Start it.
            if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
            {
                await serviceClient.StreamingEndpoints.StartAsync(_config.ResourceGroup, _config.AccountName, _streamingEndpointName);
            }

            var           hostname  = streamingEndpoint.HostName;
            var           scheme    = "https";
            List <string> manifests = BuildManifestPaths(scheme, hostname, locator.StreamingLocatorId.ToString(), manifestName);

            Console.WriteLine($"The HLS (MP4) manifest for the Live stream  : {manifests[0]}");
            Console.WriteLine();
            Console.WriteLine($"The DASH manifest for the Live stream is : {manifests[1]}");
            Console.WriteLine();
            Console.WriteLine("Open the following URL to playback the live stream from the LiveOutput in the Azure Media Player");
            Console.WriteLine($"https://ampdemo.azureedge.net/?url={manifests[1]}&heuristicprofile=lowlatency");
            Console.WriteLine();

            _startupResult = new LiveStreamStartedResult
            {
                PreviewUrl    = previewEndpoint,
                LiveOutputUrl = manifests[1],
                IngestUrl     = ingestUrl
            };

            Console.WriteLine("Live streaming started");

            return(_startupResult);
        }
Esempio n. 4
0
        /// <summary>
        /// Run the sample async.
        /// </summary>
        /// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
        /// <returns></returns>
        // <RunAsync>
        private static async Task RunAsync(ConfigWrapper config)
        {
            IAzureMediaServicesClient client;

            try
            {
                client = await Authentication.CreateMediaServicesClientAsync(config, UseInteractiveAuth);
            }
            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;
            }

            // 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); // Create a GUID for uniqueness. You can make this something static if you dont want to change RTMP ingest settings in OBS constantly.
            string             liveEventName               = "liveevent-" + uniqueness;                  // WARNING: Be careful not to leak live events using this sample!
            string             assetName                   = "archiveAsset" + uniqueness;
            string             liveOutputName              = "liveOutput" + uniqueness;
            string             drvStreamingLocatorName     = "streamingLocator" + uniqueness;
            string             archiveStreamingLocatorName = "fullLocator-" + uniqueness;
            string             drvAssetFilterName          = "filter-" + uniqueness;
            string             streamingLocatorName        = "streamingLocator" + uniqueness;
            string             streamingEndpointName       = "default"; // Change this to your specific streaming endpoint name if not using "default"
            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);

                #region CreateLiveEvent
                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 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-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 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.
                        // See https://go.microsoft.com/fwlink/?linkid=2095101 for more information
                        // 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
                }
                    //,
                    // 5) Optionally enable live transcriptions if desired.
                    // 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.
                    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);
                }

                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}");
                #endregion



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

                #endregion


                #region CreateLiveOutput
                // 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();
                #endregion


                Console.WriteLine("Starting the Live Event now... please stand by as this can take time...");
                watch = Stopwatch.StartNew();
                // Start the Live Event - this will take some time...
                await client.LiveEvents.StartAsync(config.ResourceGroup, config.AccountName, liveEventName);

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

                // Refresh the liveEvent object's settings after starting it...
                liveEvent = await client.LiveEvents.GetAsync(config.ResourceGroup, config.AccountName, liveEventName);


                #region GetIngestUrl
                // 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();
                #endregion

                #region GetPreviewURLs
                // 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();
                #endregion

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


                AssetFilter drvAssetFilter = new AssetFilter(
                    presentationTimeRange: new PresentationTimeRange(
                        forceEndTimestamp: false,
                        // 10 minute (600) seconds sliding window
                        presentationWindowDuration: 6000000000L,
                        // This value defines the latest live position that a client can seek back to 2 seconds, must be smaller than sliding window.
                        liveBackoffDuration: 20000000L)
                    );

                drvAssetFilter = await client.AssetFilters.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName,
                                                                               assetName, drvAssetFilterName, drvAssetFilter);


                // Create the Streaming Locator URL for playback of the contents in the Live Output recording
                #region CreateStreamingLocator
                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;
                }
                #endregion

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

                // The next method "bulidManifestPaths" is a helper to list the streaming manifests for HLS and DASH.
                // The paths are only available after the live streaming source has connected.
                // If you wish to get the streaming manifest ahead of time, make sure to set the manifest name in the LiveOutput as done above.
                // This allows you to have a deterministic manifest path. <streaming endpoint hostname>/<streaming locator ID>/manifestName.ism/manifest(<format string>)

                var hostname = streamingEndpoint.HostName;
                var scheme   = "https";
                BuildManifestPaths(scheme, hostname, locator.StreamingLocatorId.ToString(), manifestName);

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

                // 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 on-demand asset, Use the following urls:");
                    BuildManifestPaths(scheme, hostname, archiveLocator.StreamingLocatorId.ToString(), manifestName);
                }
            }
            catch (ErrorResponseException 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
            {
                Console.WriteLine("Cleaning up resources, stopping Live Event billing, and deleting live Event...");
                Console.WriteLine("CRITICAL WARNING ($$$$) DON'T WASTE MONEY!: - Wait here for the All Clear - this takes a few minutes sometimes to clean up. DO NOT STOP DEBUGGER yet or you will leak billable resources!");

                await CleanupLiveEventAndOutputAsync(client, config.ResourceGroup, config.AccountName, liveEventName, liveOutputName);
                await CleanupLocatorandAssetAsync(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName);

                // 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.");
                }

                Console.WriteLine("The LiveOutput and LiveEvent are now deleted.  The event is available as an archive and can still be streamed.");
                Console.WriteLine("All Clear, and all cleaned up. Please double check in the portal to make sure you have not leaked any Live Events, or left any Running still which would result in unwanted billing.");
            }
        }
Esempio n. 5
0
        /// <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.");
                }
            }
        }