public async Task Stop(string roomId)
        {
            // TODO: track live stream in each room

            if (_startupResult == null)
            {
                return;
            }

            _startupResult = null;

            Console.WriteLine("Stopping live streaming");

            IAzureMediaServicesClient serviceClient = await _mediaServiceClientFactory.CreateMediaServicesClientAsync(_config);

            await CleanupLiveEventAndOutputAsync(serviceClient, _config.ResourceGroup, _config.AccountName, _liveEventName, _liveOutputName);
            await CleanupLocatorandAssetAsync(serviceClient, _config.ResourceGroup, _config.AccountName, _streamingLocatorName, _assetName);

            await serviceClient.StreamingEndpoints.StopAsync(_config.ResourceGroup, _config.AccountName, _streamingEndpointName);

            Console.WriteLine("Live streaming stopped");
        }
        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);
        }