private async Task <List <LiveOutput> > ReturnSelectedLiveOutputsAsync()
        {
            List <LiveOutput> SelectedLiveOutputs = new List <LiveOutput>();

            foreach (DataGridViewRow Row in dataGridViewLiveOutputV.SelectedRows)
            {
                string liveOutputName = string.Empty;
                try
                {
                    string eventName = Row.Cells[dataGridViewLiveOutputV.Columns["LiveEventName"].Index].Value.ToString();
                    liveOutputName = Row.Cells[dataGridViewLiveOutputV.Columns["Name"].Index].Value.ToString();
                    LiveOutput liveOutput = await GetLiveOutputAsync(eventName, liveOutputName);

                    if (liveOutput != null)
                    {
                        SelectedLiveOutputs.Add(liveOutput);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(string.Format("Error getting the live output : '{0}'.", liveOutputName) + Constants.endline + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            SelectedLiveOutputs.Reverse();
            return(SelectedLiveOutputs);
        }
Пример #2
0
 internal static void ValidateLiveOutput(LiveOutput liveOutput, string expectedName, string expectedDescription, string expectedManifestName, LiveOutputResourceState expectedResourceState)
 {
     Assert.Equal(expectedDescription, liveOutput.Description);
     Assert.Equal(expectedName, liveOutput.Name);
     Assert.Equal(expectedDescription, liveOutput.Description);
     Assert.Equal(expectedManifestName, liveOutput.ManifestName);
     Assert.Equal(expectedResourceState, liveOutput.ResourceState);
     Assert.False(string.IsNullOrEmpty(liveOutput.Id));
 }
Пример #3
0
        public LiveOutput CreateLiveOutput(string eventName, string eventOutputName, string eventOutputDescription, string eventOutputAssetStorage,
                                           string eventOutputAssetName, int eventArchiveWindowMinutes)
        {
            CreateAsset(eventOutputAssetStorage, eventOutputAssetName);
            LiveOutput eventOutput = new LiveOutput()
            {
                Description         = eventOutputDescription,
                AssetName           = eventOutputAssetName,
                ArchiveWindowLength = new TimeSpan(0, eventArchiveWindowMinutes, 0)
            };

            return(_media.LiveOutputs.Create(MediaAccount.ResourceGroupName, MediaAccount.Name, eventName, eventOutputName, eventOutput));
        }
Пример #4
0
        public LiveOutput CreateLiveOutput(string eventName, string eventOutputName, string eventOutputDescription,
                                           string eventOutputAssetName, int eventArchiveWindowMinutes)
        {
            CreateAsset(null, eventOutputAssetName);
            CreateLocator(eventOutputAssetName, eventOutputAssetName, PredefinedStreamingPolicy.ClearStreamingOnly, null);
            LiveOutput eventOutput = new LiveOutput()
            {
                Description         = eventOutputDescription,
                AssetName           = eventOutputAssetName,
                ArchiveWindowLength = new TimeSpan(0, eventArchiveWindowMinutes, 0)
            };

            return(_media.LiveOutputs.Create(MediaAccount.ResourceGroupName, MediaAccount.Name, eventName, eventOutputName, eventOutput));
        }
Пример #5
0
        public string GetLiveOutputUrl(LiveEvent liveEvent)
        {
            string         liveOutputUrl  = string.Empty;
            MediaLiveEvent mediaLiveEvent = new MediaLiveEvent(this, liveEvent);

            if (mediaLiveEvent.Outputs.Length > 0)
            {
                LiveOutput liveOutput    = mediaLiveEvent.Outputs[0];
                string[]   streamingUrls = GetStreamingUrls(liveOutput.AssetName);
                if (streamingUrls.Length > 0)
                {
                    liveOutputUrl = streamingUrls[0];
                }
            }
            return(liveOutputUrl);
        }
        private async Task CreateLiveOutputAsync(Asset asset, string liveEventName, string liveOutputName, string manifestName)
        {
            LiveOutput liveOutput = new LiveOutput(
                assetName: asset.Name,
                manifestName: manifestName,
                archiveWindowLength: TimeSpan.FromMinutes(Config.ArchiveWindowLength)
                );

            await Client.LiveOutputs.CreateAsync(
                resourceGroupName : Config.ResourceGroup,
                accountName : Config.AccountName,
                liveEventName : liveEventName,
                liveOutputName : liveOutputName,
                parameters : liveOutput
                );

            LoggerService.Info("Created the live output", LoggerService.Start);
        }
Пример #7
0
        public void RefreshProgram(string liveeventName, LiveOutput program)
        {
            int index = -1;

            if (_MyObservLiveOutputs != null)
            {
                foreach (LiveOutputEntry CE in _MyObservLiveOutputs) // let's search for index
                {
                    if (CE.Name == program.Name)
                    {
                        index = _MyObservLiveOutputs.IndexOf(CE);
                        break;
                    }
                }
            }

            if (index >= 0) // we found it
            {               // we update the observation collection
                _client.RefreshTokenIfNeeded();

                program = _client.AMSclient.LiveOutputs.Get(_client.credentialsEntry.ResourceGroup, _client.credentialsEntry.AccountName, liveeventName, program.Name); //refresh
                if (program != null)
                {
                    try // sometimes, index could be wrong id program has been deleted
                    {
                        _MyObservLiveOutputs[index].State               = program.ResourceState;
                        _MyObservLiveOutputs[index].Description         = program.Description;
                        _MyObservLiveOutputs[index].ArchiveWindowLength = program.ArchiveWindowLength;
                        _MyObservLiveOutputs[index].LastModified        = program.LastModified != null ? (DateTime?)((DateTime)program.LastModified).ToLocalTime() : null;
                        this.Refresh();
                    }
                    catch
                    {
                    }
                }
            }
        }
Пример #8
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
            HttpRequest req, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            dynamic data;

            try
            {
                data = JsonConvert.DeserializeObject(new StreamReader(req.Body).ReadToEnd());
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }


            var generalOutputInfos = new List <GeneralOutputInfo>();

            var liveEventName = (string)data.liveEventName;

            if (liveEventName == null)
            {
                return(IrdetoHelpers.ReturnErrorException(log, "Error - please pass liveEventName in the JSON"));
            }

            // default settings
            var eventInfoFromCosmos = new LiveEventSettingsInfo()
            {
                LiveEventName = liveEventName
            };

            // Load config from Cosmos
            try
            {
                var setting = await CosmosHelpers.ReadSettingsDocument(liveEventName);

                eventInfoFromCosmos = setting ?? eventInfoFromCosmos;

                if (setting == null)
                {
                    log.LogWarning("Settings not read from Cosmos.");
                }
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }

            // Azure region management
            var azureRegions = new List <string>();

            if ((string)data.azureRegion != null)
            {
                azureRegions = ((string)data.azureRegion).Split(',').ToList();
            }
            else
            {
                azureRegions.Add((string)null);
            }

            // init default
            var uniquenessAssets = Guid.NewGuid().ToString().Substring(0, 13);

            var streamingLocatorGuid = Guid.NewGuid(); // same locator for the two ouputs if 2 live event namle created
            var uniquenessLocator    = streamingLocatorGuid.ToString().Substring(0, 13);
            var streamingLocatorName = "locator-" + uniquenessLocator;

            string uniquenessPolicyName = Guid.NewGuid().ToString().Substring(0, 13);

            var manifestName = liveEventName.ToLower();

            var useDRM = data.useDRM != null ? (bool)data.useDRM : true;

            if (data.archiveWindowLength != null)
            {
                eventInfoFromCosmos.ArchiveWindowLength = (int)data.archiveWindowLength;
            }

            if (data.inputProtocol != null && ((string)data.inputProtocol).ToUpper() == "RTMP")
            {
                eventInfoFromCosmos.InputProtocol = LiveEventInputProtocol.RTMP;
            }

            if (data.liveEventAutoStart != null)
            {
                eventInfoFromCosmos.AutoStart = (bool)data.liveEventAutoStart;
            }

            if (data.InputACL != null)
            {
                eventInfoFromCosmos.LiveEventInputACL = (List <string>)data.InputACL;
            }

            if (data.PreviewACL != null)
            {
                eventInfoFromCosmos.LiveEventPreviewACL = (List <string>)data.PreviewACL;
            }

            if (data.lowLatency != null)
            {
                eventInfoFromCosmos.LowLatency = (bool)data.lowLatency;
            }

            if (data.useStaticHostname != null)
            {
                eventInfoFromCosmos.UseStaticHostname = (bool)data.useStaticHostname;
            }

            var cencKey = new StreamingLocatorContentKey();
            var cbcsKey = new StreamingLocatorContentKey();

            if (useDRM)
            {
                try
                {
                    ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder()
                                                             .SetBasePath(Directory.GetCurrentDirectory())
                                                             .AddEnvironmentVariables()
                                                             .Build(),
                                                             null);

                    MediaServicesHelpers.LogInformation(log, "Irdeto call...");

                    cencKey = await IrdetoHelpers.GenerateAndRegisterCENCKeyToIrdeto(liveEventName, config);

                    cbcsKey = await IrdetoHelpers.GenerateAndRegisterCBCSKeyToIrdeto(liveEventName, config);

                    MediaServicesHelpers.LogInformation(log, "Irdeto call done.");
                }
                catch (Exception ex)
                {
                    return(IrdetoHelpers.ReturnErrorException(log, ex, "Irdeto response error"));
                }
            }

            var clientTasks = new List <Task <LiveEventEntry> >();

            foreach (var region in azureRegions)
            {
                var task = Task <LiveEventEntry> .Run(async() =>
                {
                    Asset asset                     = null;
                    LiveEvent liveEvent             = null;
                    LiveOutput liveOutput           = null;
                    StreamingPolicy streamingPolicy = null;
                    string storageName              = null;

                    ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder()
                                                             .SetBasePath(Directory.GetCurrentDirectory())
                                                             .AddEnvironmentVariables()
                                                             .Build(),
                                                             region
                                                             );

                    MediaServicesHelpers.LogInformation(log, "config loaded.", region);
                    MediaServicesHelpers.LogInformation(log, "connecting to AMS account : " + config.AccountName, region);

                    if (eventInfoFromCosmos.BaseStorageName != null)
                    {
                        storageName = eventInfoFromCosmos.BaseStorageName + config.AzureRegionCode;
                    }

                    if (data.storageAccountName != null)
                    {
                        storageName = (string)data.storageAccountName;
                    }

                    var client = await MediaServicesHelpers.CreateMediaServicesClientAsync(config);
                    // 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;

                    // LIVE EVENT CREATION
                    MediaServicesHelpers.LogInformation(log, "Live event creation...", region);

                    // let's check that the channel does not exist already
                    liveEvent = await client.LiveEvents.GetAsync(config.ResourceGroup, config.AccountName, liveEventName);
                    if (liveEvent != null)
                    {
                        throw new Exception("Error : live event already exists !");
                    }

                    // IP ACL for preview URL
                    var ipsPreview = new List <IPRange>();
                    if (eventInfoFromCosmos.LiveEventPreviewACL == null ||
                        eventInfoFromCosmos.LiveEventPreviewACL.Count == 0)
                    {
                        MediaServicesHelpers.LogInformation(log, "preview all", region);
                        var ip = new IPRange
                        {
                            Name = "AllowAll", Address = IPAddress.Parse("0.0.0.0").ToString(), SubnetPrefixLength = 0
                        };
                        ipsPreview.Add(ip);
                    }
                    else
                    {
                        foreach (var ipacl in eventInfoFromCosmos.LiveEventPreviewACL)
                        {
                            var ipaclcomp = ipacl.Split('/'); // notation can be "192.168.0.1" or "192.168.0.1/32"
                            var subnet    = ipaclcomp.Count() > 1 ? Convert.ToInt32(ipaclcomp[1]) : 0;
                            var ip        = new IPRange
                            {
                                Name               = "ip",
                                Address            = IPAddress.Parse(ipaclcomp[0]).ToString(),
                                SubnetPrefixLength = subnet
                            };
                            ipsPreview.Add(ip);
                        }
                    }

                    var liveEventPreview = new LiveEventPreview
                    {
                        AccessControl = new LiveEventPreviewAccessControl(new IPAccessControl(ipsPreview))
                    };

                    // IP ACL for input URL
                    var ipsInput = new List <IPRange>();

                    if (eventInfoFromCosmos.LiveEventInputACL == null || eventInfoFromCosmos.LiveEventInputACL.Count == 0)
                    {
                        MediaServicesHelpers.LogInformation(log, "input all", region);
                        var ip = new IPRange
                        {
                            Name = "AllowAll", Address = IPAddress.Parse("0.0.0.0").ToString(), SubnetPrefixLength = 0
                        };
                        ipsInput.Add(ip);
                    }
                    else
                    {
                        foreach (var ipacl in eventInfoFromCosmos.LiveEventInputACL)
                        {
                            var ipaclcomp = ipacl.Split('/'); // notation can be "192.168.0.1" or "192.168.0.1/32"
                            var subnet    = ipaclcomp.Count() > 1 ? Convert.ToInt32(ipaclcomp[1]) : 0;
                            var ip        = new IPRange
                            {
                                Name               = "ip",
                                Address            = IPAddress.Parse(ipaclcomp[0]).ToString(),
                                SubnetPrefixLength = subnet
                            };
                            ipsInput.Add(ip);
                        }
                    }

                    var liveEventInput = new LiveEventInput(
                        eventInfoFromCosmos.InputProtocol,
                        accessControl: new LiveEventInputAccessControl(new IPAccessControl(ipsInput)),
                        accessToken: config.LiveIngestAccessToken
                        );

                    liveEvent = new LiveEvent(
                        name: liveEventName,
                        location: config.Region,
                        description: "",
                        useStaticHostname: eventInfoFromCosmos.UseStaticHostname,
                        encoding: new LiveEventEncoding {
                        EncodingType = LiveEventEncodingType.None
                    },
                        input: liveEventInput,
                        preview: liveEventPreview,
                        streamOptions: new List <StreamOptionsFlag?>
                    {
                        // Set this to Default or Low Latency
                        eventInfoFromCosmos.LowLatency?StreamOptionsFlag.LowLatency: StreamOptionsFlag.Default
                    }
                        );


                    liveEvent = await client.LiveEvents.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName,
                                                                    liveEvent, eventInfoFromCosmos.AutoStart);
                    MediaServicesHelpers.LogInformation(log, "Live event created.", region);


                    if (useDRM)
                    {
                        MediaServicesHelpers.LogInformation(log, "Trying to read streaming policy from Cosmos.", region);
                        string streamingPolicyName = null;
                        // Load streaming policy info from Cosmos
                        try
                        {
                            var info = await CosmosHelpers.ReadStreamingPolicyDocument(new StreamingPolicyInfo(false)
                            {
                                AMSAccountName = config.AccountName
                            });

                            if (info == null)
                            {
                                log.LogWarning("Streaming policy not read from Cosmos.");
                            }
                            else
                            {
                                streamingPolicyName = info.StreamingPolicyName;
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Error reading Cosmos DB", ex);
                        }


                        // STREAMING POLICY CREATION
                        if (streamingPolicyName == null) // not found in Cosmos let's create a new one
                        {
                            MediaServicesHelpers.LogInformation(log, "Creating streaming policy.", region);
                            try
                            {
                                streamingPolicy = await IrdetoHelpers.CreateStreamingPolicyIrdeto(config, client, uniquenessPolicyName);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Streaming policy creation error", ex);
                            }

                            try
                            {
                                if (!await CosmosHelpers.CreateOrUpdatePolicyDocument(new StreamingPolicyInfo(false)
                                {
                                    AMSAccountName = config.AccountName,
                                    StreamingPolicyName = streamingPolicy.Name
                                }))
                                {
                                    log.LogWarning("Cosmos access not configured or error.");
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Streaming policy write error to Cosmos", ex);
                            }
                        }
                        else
                        {
                            MediaServicesHelpers.LogInformation(log, "Getting streaming policy in AMS.", region);
                            try
                            {
                                streamingPolicy = await client.StreamingPolicies.GetAsync(config.ResourceGroup, config.AccountName, streamingPolicyName);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Error when getting streaming policy " + streamingPolicy, ex);
                            }
                        }
                    }

                    // LIVE OUTPUT CREATION
                    MediaServicesHelpers.LogInformation(log, "Live output creation...", region);

                    try
                    {
                        MediaServicesHelpers.LogInformation(log, "Asset creation...", region);

                        asset = await client.Assets.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName,
                                                                        "asset-" + uniquenessAssets,
                                                                        new Asset(storageAccountName: storageName));

                        Hls hlsParam = null;

                        liveOutput = new LiveOutput(asset.Name, TimeSpan.FromMinutes(eventInfoFromCosmos.ArchiveWindowLength),
                                                    null, "output-" + uniquenessAssets, null, null, manifestName,
                                                    hlsParam); //we put the streaming locator in description
                        MediaServicesHelpers.LogInformation(log, "await task...", region);

                        MediaServicesHelpers.LogInformation(log, "create live output...", region);
                        await client.LiveOutputs.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName,
                                                             liveOutput.Name, liveOutput);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("live output creation error", ex);
                    }


                    try
                    {
                        // let's get the asset
                        // in v3, asset name = asset if in v2 (without prefix)
                        MediaServicesHelpers.LogInformation(log, "Asset configuration.", region);

                        StreamingLocator locator = null;
                        if (useDRM)
                        {
                            locator = await IrdetoHelpers.SetupDRMAndCreateLocatorWithNewKeys(config, streamingPolicy.Name,
                                                                                              streamingLocatorName, client, asset, cencKey, cbcsKey, streamingLocatorGuid, liveEventName);
                        }
                        else // no DRM
                        {
                            locator = await IrdetoHelpers.CreateClearLocator(config, streamingLocatorName, client, asset, streamingLocatorGuid);
                        }

                        MediaServicesHelpers.LogInformation(log, "locator : " + locator.Name, region);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("locator creation error", ex);
                    }

                    // let's build info for the live event and output

                    var generalOutputInfo =
                        GenerateInfoHelpers.GenerateOutputInformation(config, client, new List <LiveEvent> {
                        liveEvent
                    });

                    if (!await CosmosHelpers.CreateOrUpdateGeneralInfoDocument(generalOutputInfo.LiveEvents[0]))
                    {
                        log.LogWarning("Cosmos access not configured.");
                    }

                    return(generalOutputInfo.LiveEvents[0]);
                });

                clientTasks.Add(task);
            }

            try
            {
                Task.WaitAll(clientTasks.ToArray());
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }

            return(new OkObjectResult(
                       JsonConvert.SerializeObject(new GeneralOutputInfo {
                Success = true, LiveEvents = clientTasks.Select(i => i.Result).ToList()
            }, Formatting.Indented)
                       ));
        }
Пример #9
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
            HttpRequest req, ILogger log)
        {
            MediaServicesHelpers.LogInformation(log, "C# HTTP trigger function processed a request.");

            dynamic data;

            try
            {
                data = JsonConvert.DeserializeObject(new StreamReader(req.Body).ReadToEnd());
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }

            var generalOutputInfos = new List <GeneralOutputInfo>();

            var liveEventName = (string)data.liveEventName;

            if (liveEventName == null)
            {
                return(IrdetoHelpers.ReturnErrorException(log, "Error - please pass liveEventName in the JSON"));
            }

            // default settings
            var eventInfoFromCosmos = new LiveEventSettingsInfo
            {
                LiveEventName = liveEventName
            };

            // Load config from Cosmos
            try
            {
                var setting = await CosmosHelpers.ReadSettingsDocument(liveEventName);

                eventInfoFromCosmos = setting ?? eventInfoFromCosmos;

                if (setting == null)
                {
                    log.LogWarning("Settings not read from Cosmos.");
                }
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }

            // Azure region management
            var azureRegions = new List <string>();

            if ((string)data.azureRegion != null)
            {
                azureRegions = ((string)data.azureRegion).Split(',').ToList();
            }
            else
            {
                azureRegions.Add((string)null);
            }

            // init default
            var deleteAsset = (data.deleteAsset != null) ? (bool)data.deleteAsset : true;

            var uniquenessAssets = Guid.NewGuid().ToString().Substring(0, 13);

            string uniquenessPolicyName = Guid.NewGuid().ToString().Substring(0, 13);

            var manifestName = liveEventName.ToLower();

            if (data.archiveWindowLength != null)
            {
                eventInfoFromCosmos.ArchiveWindowLength = (int)data.archiveWindowLength;
            }

            var cencKey = new StreamingLocatorContentKey();
            var cbcsKey = new StreamingLocatorContentKey();

            if (!deleteAsset) // we need to regenerate the keys if the user wants to keep the asset as keys cannot be reused for more than one asset
            {
                try
                {
                    ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder()
                                                             .SetBasePath(Directory.GetCurrentDirectory())
                                                             .AddEnvironmentVariables()
                                                             .Build(),
                                                             null);

                    MediaServicesHelpers.LogInformation(log, "Irdeto call...");

                    cencKey = await IrdetoHelpers.GenerateAndRegisterCENCKeyToIrdeto(liveEventName, config);

                    cbcsKey = await IrdetoHelpers.GenerateAndRegisterCBCSKeyToIrdeto(liveEventName, config);

                    MediaServicesHelpers.LogInformation(log, "Irdeto call done.");
                }
                catch (Exception ex)
                {
                    return(IrdetoHelpers.ReturnErrorException(log, ex, "Irdeto response error"));
                }
            }

            var clientTasks = new List <Task <LiveEventEntry> >();

            // list of locators guid for the new locators
            var locatorGuids = new List <Guid>();

            for (int i = 0; i < 10; i++)
            {
                locatorGuids.Add(Guid.NewGuid());
            }

            foreach (var region in azureRegions)
            {
                var task = Task <LiveEventEntry> .Run(async() =>
                {
                    Asset asset           = null;
                    LiveEvent liveEvent   = null;
                    LiveOutput liveOutput = null;

                    bool reuseKeys                = false;
                    string storageAccountName     = null;
                    var streamingLocatorsPolicies = new Dictionary <string, string>(); // locator name, policy name


                    ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder()
                                                             .SetBasePath(Directory.GetCurrentDirectory())
                                                             .AddEnvironmentVariables()
                                                             .Build(),
                                                             region
                                                             );

                    MediaServicesHelpers.LogInformation(log, "config loaded.", region);
                    MediaServicesHelpers.LogInformation(log, "connecting to AMS account : " + config.AccountName, region);

                    var client = await MediaServicesHelpers.CreateMediaServicesClientAsync(config);
                    // 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;

                    // let's check that the channel exists
                    liveEvent = await client.LiveEvents.GetAsync(config.ResourceGroup, config.AccountName, liveEventName);
                    if (liveEvent == null)
                    {
                        throw new Exception("Error : live event does not exist !");
                    }

                    if (liveEvent.ResourceState != LiveEventResourceState.Running && liveEvent.ResourceState != LiveEventResourceState.Stopped)
                    {
                        throw new Exception("Error : live event should be in Running or Stopped state !");
                    }

                    // get live output(s) - it should be one
                    var myLiveOutputs = client.LiveOutputs.List(config.ResourceGroup, config.AccountName, liveEventName);

                    // get the names of the streaming policies. If not possible, recreate it
                    if (myLiveOutputs.FirstOrDefault() != null)
                    {
                        asset = client.Assets.Get(config.ResourceGroup, config.AccountName,
                                                  myLiveOutputs.First().AssetName);

                        var streamingLocatorsNames = client.Assets.ListStreamingLocators(config.ResourceGroup, config.AccountName, asset.Name).StreamingLocators.Select(l => l.Name);
                        foreach (var locatorName in streamingLocatorsNames)
                        {
                            var locator =
                                client.StreamingLocators.Get(config.ResourceGroup, config.AccountName, locatorName);
                            if (locator != null)
                            {
                                streamingLocatorsPolicies.Add(locatorName, locator.StreamingPolicyName);
                                if (locator.StreamingPolicyName != PredefinedStreamingPolicy.ClearStreamingOnly) // let's backup the keys to reuse them
                                {
                                    if (deleteAsset)                                                             // we reuse the keys. Only possible if the previous asset is deleted
                                    {
                                        reuseKeys = true;
                                        var keys  = client.StreamingLocators.ListContentKeys(config.ResourceGroup, config.AccountName, locatorName).ContentKeys;
                                        cencKey   = keys.Where(k => k.LabelReferenceInStreamingPolicy == IrdetoHelpers.labelCenc).FirstOrDefault();
                                        cbcsKey   = keys.Where(k => k.LabelReferenceInStreamingPolicy == IrdetoHelpers.labelCbcs).FirstOrDefault();
                                    }
                                }
                            }
                        }

                        if (streamingLocatorsPolicies.Count == 0) // no way to get the streaming policy, let's read Cosmos or create a new one
                        {
                            MediaServicesHelpers.LogInformation(log, "Trying to read streaming policy from Cosmos.", region);
                            string streamingPolicyName = null;
                            // Load streaming policy info from Cosmos
                            try
                            {
                                var info = await CosmosHelpers.ReadStreamingPolicyDocument(new StreamingPolicyInfo(false)
                                {
                                    AMSAccountName = config.AccountName
                                });

                                if (info == null)
                                {
                                    log.LogWarning("Streaming policy not read from Cosmos.");
                                }
                                else
                                {
                                    streamingPolicyName = info.StreamingPolicyName;
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Error reading Cosmos DB", ex);
                            }


                            // STREAMING POLICY CREATION
                            if (streamingPolicyName == null) // not found in Cosmos let's create a new one
                            {
                                StreamingPolicy streamingPolicy;
                                MediaServicesHelpers.LogInformation(log, "Creating streaming policy.", region);
                                try
                                {
                                    streamingPolicy = await IrdetoHelpers.CreateStreamingPolicyIrdeto(config, client, uniquenessPolicyName);
                                    streamingLocatorsPolicies.Add("", streamingPolicy.Name);
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception("Streaming policy creation error", ex);
                                }

                                try
                                {
                                    if (!await CosmosHelpers.CreateOrUpdatePolicyDocument(new StreamingPolicyInfo(false)
                                    {
                                        AMSAccountName = config.AccountName,
                                        StreamingPolicyName = streamingPolicy.Name
                                    }))
                                    {
                                        log.LogWarning("Cosmos access not configured or error.");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception("Streaming policy write error to Cosmos", ex);
                                }
                            }
                            else
                            {
                                streamingLocatorsPolicies.Add("", streamingPolicyName);
                            }
                        }
                    }

                    // let's purge all live output for now
                    foreach (var p in myLiveOutputs)
                    {
                        var assetName = p.AssetName;

                        var thisAsset = client.Assets.Get(config.ResourceGroup, config.AccountName, p.AssetName);
                        if (thisAsset != null)
                        {
                            storageAccountName = thisAsset.StorageAccountName; // let's backup storage account name to create the new asset here
                        }
                        MediaServicesHelpers.LogInformation(log, "deleting live output : " + p.Name, region);
                        await client.LiveOutputs.DeleteAsync(config.ResourceGroup, config.AccountName, liveEvent.Name, p.Name);
                        if (deleteAsset)
                        {
                            MediaServicesHelpers.LogInformation(log, "deleting asset : " + assetName, region);
                            await client.Assets.DeleteAsync(config.ResourceGroup, config.AccountName, assetName);
                        }
                    }

                    if (liveEvent.ResourceState == LiveEventResourceState.Running)
                    {
                        MediaServicesHelpers.LogInformation(log, "reseting live event : " + liveEvent.Name, region);
                        await client.LiveEvents.ResetAsync(config.ResourceGroup, config.AccountName, liveEvent.Name);
                    }
                    else
                    {
                        MediaServicesHelpers.LogInformation(log, "Skipping the reset of live event " + liveEvent.Name + " as it is stopped.", region);
                    }

                    // LIVE OUTPUT CREATION
                    MediaServicesHelpers.LogInformation(log, "Live output creation...", region);

                    try
                    {
                        MediaServicesHelpers.LogInformation(log, "Asset creation...", region);
                        asset = await client.Assets.CreateOrUpdateAsync(config.ResourceGroup, config.AccountName,
                                                                        "asset-" + uniquenessAssets, new Asset(storageAccountName: storageAccountName));

                        Hls hlsParam = null;
                        liveOutput   = new LiveOutput(asset.Name, TimeSpan.FromMinutes(eventInfoFromCosmos.ArchiveWindowLength),
                                                      null, "output-" + uniquenessAssets, null, null, manifestName,
                                                      hlsParam);

                        MediaServicesHelpers.LogInformation(log, "create live output...", region);
                        await client.LiveOutputs.CreateAsync(config.ResourceGroup, config.AccountName, liveEventName, liveOutput.Name, liveOutput);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("live output creation error", ex);
                    }

                    try
                    {
                        // streaming locator(s) creation
                        MediaServicesHelpers.LogInformation(log, "Locator creation...", region);
                        int i = 0;
                        foreach (var entryLocPol in streamingLocatorsPolicies)
                        {
                            StreamingLocator locator = null;
                            var streamingLocatorGuid = locatorGuids[i]; // same locator for the two ouputs if 2 live event namle created
                            var uniquenessLocator    = streamingLocatorGuid.ToString().Substring(0, 13);
                            var streamingLocatorName = "locator-" + uniquenessLocator;

                            if (entryLocPol.Value == PredefinedStreamingPolicy.ClearStreamingOnly)
                            {
                                locator = await IrdetoHelpers.CreateClearLocator(config, streamingLocatorName, client, asset, streamingLocatorGuid);
                            }
                            else // DRM content
                            {
                                MediaServicesHelpers.LogInformation(log, "creating DRM locator : " + streamingLocatorName, region);

                                if (!reuseKeys)
                                {
                                    MediaServicesHelpers.LogInformation(log, "using new keys.", region);

                                    locator = await IrdetoHelpers.SetupDRMAndCreateLocatorWithNewKeys(
                                        config, entryLocPol.Value, streamingLocatorName, client, asset, cencKey, cbcsKey, streamingLocatorGuid);
                                }
                                else // no need to create new keys, let's use the exiting one
                                {
                                    MediaServicesHelpers.LogInformation(log, "using existing keys.", region);

                                    locator = await IrdetoHelpers.SetupDRMAndCreateLocatorWithExistingKeys(
                                        config, entryLocPol.Value, streamingLocatorName, client, asset, cencKey, cbcsKey, streamingLocatorGuid);
                                }
                            }
                            MediaServicesHelpers.LogInformation(log, "locator : " + streamingLocatorName, region);
                            i++;
                        }

                        await client.Assets.UpdateAsync(config.ResourceGroup, config.AccountName, asset.Name, asset);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("locator creation error", ex);
                    }

                    var generalOutputInfo =
                        GenerateInfoHelpers.GenerateOutputInformation(config, client, new List <LiveEvent> {
                        liveEvent
                    });

                    if (!await CosmosHelpers.CreateOrUpdateGeneralInfoDocument(generalOutputInfo.LiveEvents[0]))
                    {
                        MediaServicesHelpers.LogWarning(log, "Cosmos access not configured.", region);
                    }

                    return(generalOutputInfo.LiveEvents[0]);
                });

                clientTasks.Add(task);
            }

            try
            {
                Task.WaitAll(clientTasks.ToArray());
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }

            return(new OkObjectResult(
                       JsonConvert.SerializeObject(new GeneralOutputInfo {
                Success = true, LiveEvents = clientTasks.Select(i => i.Result).ToList()
            }, Formatting.Indented)
                       ));
        }
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
            HttpRequest req, ILogger log)
        {
            MediaServicesHelpers.LogInformation(log, "C# HTTP trigger function processed a request.");

            dynamic data;

            try
            {
                data = JsonConvert.DeserializeObject(new StreamReader(req.Body).ReadToEnd());
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }


            var liveEventName = (string)data.liveEventName;

            if (liveEventName == null)
            {
                return(IrdetoHelpers.ReturnErrorException(log, "Error - please pass liveEventName in the JSON"));
            }

            // Azure region management
            var azureRegions = new List <string>();

            if ((string)data.azureRegion != null)
            {
                azureRegions = ((string)data.azureRegion).Split(',').ToList();
            }
            else
            {
                azureRegions.Add((string)null);
            }

            // init default
            var uniquenessAssets = Guid.NewGuid().ToString().Substring(0, 13);

            var streamingLocatorGuid = Guid.NewGuid(); // same locator for the two ouputs if 2 live event namle created
            var uniquenessLocator    = streamingLocatorGuid.ToString().Substring(0, 13);
            var streamingLocatorName = "locator-" + uniquenessLocator;

            var manifestName = liveEventName.ToLower();

            var clientTasks = new List <Task <LiveEventEntry> >();

            foreach (var region in azureRegions)
            {
                var task = Task <LiveEventEntry> .Run(async() =>
                {
                    Asset asset           = null;
                    LiveOutput liveOutput = null;

                    ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder()
                                                             .SetBasePath(Directory.GetCurrentDirectory())
                                                             .AddEnvironmentVariables()
                                                             .Build(),
                                                             region
                                                             );

                    MediaServicesHelpers.LogInformation(log, "config loaded.", region);
                    MediaServicesHelpers.LogInformation(log, "connecting to AMS account : " + config.AccountName, region);

                    var client = await MediaServicesHelpers.CreateMediaServicesClientAsync(config);
                    // 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;

                    // let's check that the channel exists
                    var liveEvent = await client.LiveEvents.GetAsync(config.ResourceGroup, config.AccountName, liveEventName);
                    if (liveEvent == null)
                    {
                        throw new Exception($"Live event {liveEventName} does not exist.");
                    }

                    var outputs =
                        await client.LiveOutputs.ListAsync(config.ResourceGroup, config.AccountName, liveEventName);

                    if (outputs.FirstOrDefault() != null)
                    {
                        liveOutput = outputs.FirstOrDefault();
                        asset      = await client.Assets.GetAsync(config.ResourceGroup, config.AccountName,
                                                                  liveOutput.AssetName);
                    }

                    if (asset == null)
                    {
                        throw new Exception("Error - asset not found");
                    }

                    try
                    {
                        // streaming locator creation
                        MediaServicesHelpers.LogInformation(log, "Locator creation...", region);

                        var locator = await IrdetoHelpers.CreateClearLocator(config, streamingLocatorName, client, asset, streamingLocatorGuid);

                        MediaServicesHelpers.LogInformation(log, "locator : " + locator.Name, region);

                        if (liveOutput != null)
                        {
                            await client.Assets.UpdateAsync(config.ResourceGroup, config.AccountName, asset.Name, asset);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("locator creation error", ex);
                    }

                    // object to store the output of the function
                    var generalOutputInfo = new GeneralOutputInfo();

                    // let's build info for the live event and output

                    generalOutputInfo =
                        GenerateInfoHelpers.GenerateOutputInformation(config, client, new List <LiveEvent> {
                        liveEvent
                    });

                    if (!await CosmosHelpers.CreateOrUpdateGeneralInfoDocument(generalOutputInfo.LiveEvents[0]))
                    {
                        MediaServicesHelpers.LogWarning(log, "Cosmos access not configured.", region);
                    }

                    return(generalOutputInfo.LiveEvents[0]);
                });

                clientTasks.Add(task);
            }

            try
            {
                Task.WaitAll(clientTasks.ToArray());
            }
            catch (Exception ex)
            {
                return(IrdetoHelpers.ReturnErrorException(log, ex));
            }

            return(new OkObjectResult(
                       JsonConvert.SerializeObject(new GeneralOutputInfo {
                Success = true, LiveEvents = clientTasks.Select(i => i.Result).ToList()
            }, Formatting.Indented)
                       ));
        }
Пример #11
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.");
                }
            }
        }
Пример #12
0
        public void LiveOutputComboTest()
        {
            using (MockContext context = this.StartMockContextAndInitializeClients(this.GetType()))
            {
                try
                {
                    CreateMediaServicesAccount();

                    // Create the LiveEvent
                    string    eventName       = TestUtilities.GenerateName("liveevent");
                    LiveEvent eventParameters = new LiveEvent(location: Helpers.MediaManagementTestUtilities.DefaultLocation, input: new LiveEventInput(LiveEventInputProtocol.FragmentedMP4));
                    LiveEvent liveEvent       = MediaClient.LiveEvents.Create(ResourceGroup, AccountName, eventName, eventParameters, autoStart: true);

                    // List liveOutputs, which should be empty
                    var liveOutputs = MediaClient.LiveOutputs.List(ResourceGroup, AccountName, eventName);
                    Assert.Empty(liveOutputs);

                    string liveOutputName        = TestUtilities.GenerateName("liveOutput");
                    string liveOutputDescription = "A test liveOutput";

                    // Try to get the live output, which should not exist
                    Assert.Equal(System.Net.HttpStatusCode.NotFound, Assert.Throws <ErrorResponseException>(() => MediaClient.LiveOutputs.Get(ResourceGroup, AccountName, eventName, liveOutputName)).Response.StatusCode);

                    // Create an Asset for the LiveOutput to use
                    string assetName = TestUtilities.GenerateName("asset");
                    Asset  asset     = MediaClient.Assets.CreateOrUpdate(ResourceGroup, AccountName, assetName, new Asset());

                    // Create a liveOutput
                    string     manifestName      = "output";
                    LiveOutput parameters        = new LiveOutput(assetName: asset.AssetId.ToString(), description: liveOutputDescription, manifestName: manifestName, archiveWindowLength: TimeSpan.FromMinutes(10));
                    LiveOutput createdLiveOutput = MediaClient.LiveOutputs.Create(ResourceGroup, AccountName, eventName, liveOutputName, parameters);
                    ValidateLiveOutput(createdLiveOutput, liveOutputName, liveOutputDescription, manifestName, LiveOutputResourceState.Running);

                    // List liveOutputs and validate the created liveOutput shows up
                    liveOutputs = MediaClient.LiveOutputs.List(ResourceGroup, AccountName, eventName);
                    Assert.Single(liveOutputs);
                    ValidateLiveOutput(liveOutputs.First(), liveOutputName, liveOutputDescription, manifestName, LiveOutputResourceState.Running);

                    // Get the newly created liveOutput
                    LiveOutput liveOutput = MediaClient.LiveOutputs.Get(ResourceGroup, AccountName, eventName, liveOutputName);
                    Assert.NotNull(liveOutput);
                    ValidateLiveOutput(liveOutput, liveOutputName, liveOutputDescription, manifestName, LiveOutputResourceState.Running);

                    // Delete the liveOutput
                    MediaClient.LiveOutputs.Delete(ResourceGroup, AccountName, eventName, liveOutputName);

                    // List liveOutputs, which should be empty again
                    liveOutputs = MediaClient.LiveOutputs.List(ResourceGroup, AccountName, eventName);
                    Assert.Empty(liveOutputs);

                    // Get the liveOutput, which should not exist
                    Assert.Equal(System.Net.HttpStatusCode.NotFound, Assert.Throws <ErrorResponseException>(() => MediaClient.LiveOutputs.Get(ResourceGroup, AccountName, eventName, liveOutputName)).Response.StatusCode);

                    // Stop the LiveEvent
                    MediaClient.LiveEvents.Stop(ResourceGroup, AccountName, eventName);

                    // Delete the LiveEvent
                    MediaClient.LiveEvents.Delete(ResourceGroup, AccountName, eventName);

                    // Delete the Asset
                    MediaClient.Assets.Delete(ResourceGroup, AccountName, assetName);
                }
                finally
                {
                    DeleteMediaServicesAccount();
                }
            }
        }
Пример #13
0
 /// <summary>
 /// Create Live Output
 /// </summary>
 /// <remarks>
 /// Creates a new live output.
 /// </remarks>
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='resourceGroupName'>
 /// The name of the resource group within the Azure subscription.
 /// </param>
 /// <param name='accountName'>
 /// The Media Services account name.
 /// </param>
 /// <param name='liveEventName'>
 /// The name of the live event, maximum length is 32.
 /// </param>
 /// <param name='liveOutputName'>
 /// The name of the live output.
 /// </param>
 /// <param name='parameters'>
 /// Live Output properties needed for creation.
 /// </param>
 /// <param name='cancellationToken'>
 /// The cancellation token.
 /// </param>
 public static async Task <LiveOutput> BeginCreateAsync(this ILiveOutputsOperations operations, string resourceGroupName, string accountName, string liveEventName, string liveOutputName, LiveOutput parameters, CancellationToken cancellationToken = default(CancellationToken))
 {
     using (var _result = await operations.BeginCreateWithHttpMessagesAsync(resourceGroupName, accountName, liveEventName, liveOutputName, parameters, null, cancellationToken).ConfigureAwait(false))
     {
         return(_result.Body);
     }
 }
Пример #14
0
 /// <summary>
 /// Create Live Output
 /// </summary>
 /// <remarks>
 /// Creates a new live output.
 /// </remarks>
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='resourceGroupName'>
 /// The name of the resource group within the Azure subscription.
 /// </param>
 /// <param name='accountName'>
 /// The Media Services account name.
 /// </param>
 /// <param name='liveEventName'>
 /// The name of the live event, maximum length is 32.
 /// </param>
 /// <param name='liveOutputName'>
 /// The name of the live output.
 /// </param>
 /// <param name='parameters'>
 /// Live Output properties needed for creation.
 /// </param>
 public static LiveOutput BeginCreate(this ILiveOutputsOperations operations, string resourceGroupName, string accountName, string liveEventName, string liveOutputName, LiveOutput parameters)
 {
     return(operations.BeginCreateAsync(resourceGroupName, accountName, liveEventName, liveOutputName, parameters).GetAwaiter().GetResult());
 }
Пример #15
0
        private static void Main(string[] args)
        {
            try
            {
                ConfigWrapper config = new ConfigWrapper();

                IAzureMediaServicesClient client = CreateMediaServicesClient(config);

                CleanupAccount(client, config.ResourceGroup, config.AccountName);

                // Getting the mediaServices account so that we can use the location to create the
                // LiveEvent and StreamingEndpoint
                MediaService mediaService = client.Mediaservices.Get(config.ResourceGroup, config.AccountName);

                // 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;
                Console.WriteLine($"Creating a live event named {liveEventName}");
                Console.WriteLine();

                LiveEventPreview liveEventPreview = new LiveEventPreview
                {
                    AccessControl = new LiveEventPreviewAccessControl
                    {
                        Ip = new IPAccessControl
                        {
                            Allow = new List <IPRange>
                            {
                                new IPRange
                                {
                                    Name               = "AllowAll",
                                    Address            = "0.0.0.0",
                                    SubnetPrefixLength = 0
                                }
                            }
                        }
                    }
                };

                // This can sometimes take awhile. Be patient.
                LiveEvent liveEvent = new LiveEvent(
                    location: mediaService.Location,
                    description: "Sample non-encoding pass-through LiveEvent for testing",
                    vanityUrl: false,
                    encoding: new LiveEventEncoding(encodingType: LiveEventEncodingType.None, presetName: null),
                    input: new LiveEventInput(LiveEventInputProtocol.RTMP),
                    preview: liveEventPreview
                    );

                liveEvent = client.LiveEvents.Create(config.ResourceGroup, config.AccountName, liveEventName, liveEvent, autoStart: true);

                // Start the LiveEvent so  that we can begin sending encoding data to it.
                // client.LiveEvents.Start(config.ResourceGroup,config.AccountName, liveEventName);

                // Get the LiveEvent again to refresh the ingest endpoints after starting.
                //liveEvent = client.LiveEvents.Get(config.ResourceGroup, config.AccountName, liveEventName);

                // 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("Start the live stream now, sending the input to the ingest url and verify that it is arriving with the preview url.");
                Console.WriteLine("Press enter to continue...");
                Console.Out.Flush();
                var ignoredInput = Console.ReadLine();

                // Create an Asset for the LiveOutput to use
                string assetName = "archiveAsset" + uniqueness;
                Console.WriteLine($"Creating an asset named {assetName}");
                Console.WriteLine();
                Asset asset = client.Assets.CreateOrUpdate(config.ResourceGroup, config.AccountName, assetName, new Asset());

                // Create the LiveOutput
                string manifestName   = "output";
                string liveOutputName = "liveOutput" + uniqueness;
                Console.WriteLine($"Creating a lived output named {liveOutputName}");
                Console.WriteLine();

                // TODO: Update this to Asset.Name once the fix is deployed
                LiveOutput liveOutput = new LiveOutput(assetName: asset.AssetId.ToString(), manifestName: manifestName, archiveWindowLength: TimeSpan.FromMinutes(10));
                liveOutput = client.LiveOutputs.Create(config.ResourceGroup, config.AccountName, liveEventName, liveOutputName, liveOutput);

                // Create the StreamingLocator
                string streamingLocatorName = "streamingLocator" + uniqueness;

                Console.WriteLine($"Creating a streaming locator named {streamingLocatorName}");
                Console.WriteLine();

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

                // Create the StreamingEndpoint
                // string endpointName = "endpoint" + uniqueness;

                // Console.WriteLine($"Creating a streaming endpoint named {endpointName}");
                // Console.WriteLine();

                // StreamingEndpoint streamingEndpoint = new StreamingEndpoint(location: mediaService.Location);
                // streamingEndpoint = client.StreamingEndpoints.Create(config.ResourceGroup, config.AccountName, endpointName, streamingEndpoint, autoStart: true);

                // Get the default Streaming Endpoint on the account
                StreamingEndpoint streamingEndpoint = client.StreamingEndpoints.Get(config.ResourceGroup, config.AccountName, "default");

                // If it's not running, Start it.
                if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
                {
                    client.StreamingEndpoints.Start(config.ResourceGroup, config.AccountName, "default");
                }

                // Get the url to stream the output
                var paths = client.StreamingLocators.ListPaths(config.ResourceGroup, config.AccountName, streamingLocatorName);

                Console.WriteLine("The urls to stream the output from a client:");
                Console.WriteLine();
                StringBuilder stringBuilder = new StringBuilder();
                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 (stringBuilder.Length > 0)
                {
                    Console.WriteLine(stringBuilder.ToString());
                }
                else
                {
                    Console.WriteLine("No Streaming Paths were detected.  Has the Stream been started?");
                    Console.WriteLine("Cleaning up and Exiting...");

                    CleanupLiveEventAndOutput(client, config.ResourceGroup, config.AccountName, liveEventName, liveOutputName);
                    CleanupLocatorAssetAndStreamingEndpoint(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName);

                    return;
                }

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

                CleanupLiveEventAndOutput(client, config.ResourceGroup, config.AccountName, liveEventName, liveOutputName);

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

                CleanupLocatorAssetAndStreamingEndpoint(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName);
            }
            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...");
            }
        }
Пример #16
0
        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 <LiveEvent>         liveEventTask              = CreateLiveEventAsync(client, config.ResourceGroup, config.AccountName, liveEventName);
                Task <StreamingLocator>  streamingLocatorTask       = CreateAssetAndLocatorAsync(client, config.ResourceGroup, config.AccountName, assetName, streamingLocatorName);
                Task <StreamingEndpoint> 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);
            }
        }
        public static async Task <(Uri, bool)> GetValidOnDemandSmoothURIAsync(Asset asset, IAzureMediaServicesClient client, string resourceGroup, string accountName, string useThisLocatorName = null, LiveOutput liveOutput = null)
        {
            bool emptyLiveOutput = false; // used to signal the live output is empty (do not use ListPathsAsync)

            IList <AssetStreamingLocator> locators = (await client.Assets.ListStreamingLocatorsAsync(resourceGroup, accountName, asset.Name)).StreamingLocators;

            Microsoft.Rest.Azure.IPage <StreamingEndpoint> ses = await client.StreamingEndpoints.ListAsync(resourceGroup, accountName);

            StreamingEndpoint runningSes = ses.Where(s => s.ResourceState == StreamingEndpointResourceState.Running).FirstOrDefault();

            if (runningSes == null)
            {
                runningSes = ses.FirstOrDefault();
            }

            if (locators.Count > 0 && runningSes != null)
            {
                string locatorName = useThisLocatorName ?? locators.First().Name;
                AssetStreamingLocator locatorToUse = locators.Where(l => l.Name == locatorName).First();

                IList <StreamingPath>       streamingPaths = (await client.StreamingLocators.ListPathsAsync(resourceGroup, accountName, locatorName)).StreamingPaths;
                IEnumerable <StreamingPath> smoothPath     = streamingPaths.Where(p => p.StreamingProtocol == StreamingPolicyStreamingProtocol.SmoothStreaming);
                if (smoothPath.Any(s => s.Paths.Count != 0))
                {
                    UriBuilder uribuilder = new()
                    {
                        Host = runningSes.HostName,
                        Path = smoothPath.FirstOrDefault().Paths.FirstOrDefault()
                    };
                    return(uribuilder.Uri, emptyLiveOutput);
                }
                else if (smoothPath.Any() && liveOutput != null) // A live output with no data in it as live event not started. But we can determine the output URLs
                {
                    UriBuilder uribuilder = new()
                    {
                        Host = runningSes.HostName,
                        Path = locatorToUse.StreamingLocatorId.ToString() + "/" + liveOutput.ManifestName + ".ism/manifest"
                    };
                    emptyLiveOutput = true;
                    return(uribuilder.Uri, emptyLiveOutput);
                }
                else
                {
                    return(null, emptyLiveOutput);
                }
            }
            else
            {
                return(null, emptyLiveOutput);
            }
        }
        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);
        }
Пример #19
0
        private static void Main(string[] args)
        {
            try
            {
                ConfigWrapper config = new ConfigWrapper();

                IAzureMediaServicesClient client = CreateMediaServicesClient(config);
                // 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;

                // CleanupAccount(client, config.ResourceGroup, config.AccountName);

                // Getting the mediaServices account so that we can use the location to create the
                // LiveEvent and StreamingEndpoint
                MediaService mediaService = client.Mediaservices.Get(config.ResourceGroup, config.AccountName);

                // 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);
                liveEventName = "liveevent-" + uniqueness;

                Console.WriteLine($"Creating a live event named {liveEventName}");
                Console.WriteLine();

                LiveEventPreview liveEventPreview = new LiveEventPreview
                {
                    AccessControl = new LiveEventPreviewAccessControl(
                        ip: new IPAccessControl(
                            allow: new IPRange[]
                    {
                        new IPRange(
                            name: "AllowAll",
                            address: "0.0.0.0",
                            subnetPrefixLength: 0
                            )
                    }
                            )
                        )
                };

                // This can sometimes take awhile. Be patient.
                LiveEvent liveEvent = new LiveEvent(
                    location: mediaService.Location,
                    description: "Sample LiveEvent for testing",
                    vanityUrl: false,
                    encoding: new LiveEventEncoding(
                        // Set this to Basic to enable a transcoding LiveEvent, and None to enable a pass-through LiveEvent
                        encodingType: LiveEventEncodingType.None,
                        presetName: null
                        ),
                    input: new LiveEventInput(LiveEventInputProtocol.RTMP),
                    preview: liveEventPreview,
                    streamOptions: new List <StreamOptionsFlag?>()
                {
                    // Set this to Default or Low Latency
                    StreamOptionsFlag.Default
                }
                    );

                Console.WriteLine($"Creating the LiveEvent, be patient this can take time...");
                liveEvent = client.LiveEvents.Create(config.ResourceGroup, config.AccountName, liveEventName, liveEvent, autoStart: true);



                // 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}");
                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 an Asset for the LiveOutput to use
                string assetName = "archiveAsset" + uniqueness;
                Console.WriteLine($"Creating an asset named {assetName}");
                Console.WriteLine();
                Asset asset = client.Assets.CreateOrUpdate(config.ResourceGroup, config.AccountName, assetName, new Asset());

                // Create the LiveOutput
                string manifestName   = "output";
                string liveOutputName = "liveOutput" + uniqueness;
                Console.WriteLine($"Creating a live output named {liveOutputName}");
                Console.WriteLine();

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

                // Create the StreamingLocator
                string streamingLocatorName = "streamingLocator" + uniqueness;

                Console.WriteLine($"Creating a streaming locator named {streamingLocatorName}");
                Console.WriteLine();

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

                // Get the default Streaming Endpoint on the account
                StreamingEndpoint streamingEndpoint = client.StreamingEndpoints.Get(config.ResourceGroup, config.AccountName, "default");

                // If it's not running, Start it.
                if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
                {
                    Console.WriteLine("Streaming Endpoint was Stopped, restarting now..");
                    client.StreamingEndpoints.Start(config.ResourceGroup, config.AccountName, "default");
                }

                // Get the url to stream the output
                var paths = client.StreamingLocators.ListPaths(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}");
                    Console.WriteLine();
                }
                else
                {
                    Console.WriteLine("No Streaming Paths were detected.  Has the Stream been started?");
                    Console.WriteLine("Cleaning up and Exiting...");

                    CleanupLiveEventAndOutput(client, config.ResourceGroup, config.AccountName, liveEventName, liveOutputName);
                    CleanupLocatorAssetAndStreamingEndpoint(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName);

                    return;
                }

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

                CleanupLiveEventAndOutput(client, config.ResourceGroup, config.AccountName, liveEventName, liveOutputName);

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

                CleanupLocatorAssetAndStreamingEndpoint(client, config.ResourceGroup, config.AccountName, streamingLocatorName, assetName);
            }
            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();
            }
        }
Пример #20
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);
            }
        }
Пример #21
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.");
            }
        }