public static async Task <AssetEntry> GenerateAssetInformation(ConfigWrapper config, IAzureMediaServicesClient client, Asset asset, VodSemaphore semaphore, string contentId = null, string region = null) { var assetEntry = new AssetEntry() { AMSAccountName = config.AccountName, Region = config.Region, ResourceGroup = config.ResourceGroup, AssetStorageAccountName = asset?.StorageAccountName, AssetName = asset.Name, Urn = semaphore?.Urn, Semaphore = semaphore, StreamingLocators = new List <StreamingLocatorEntry>(), CreatedTime = asset.Created.ToUniversalTime().ToString(AssetEntry.DateFormat), ContentId = contentId, }; var urls = new List <OutputUrl>(); string streamingPolicyName = null; StreamingPolicy streamingPolicy = null; var streamingLocatorsNames = client.Assets.ListStreamingLocators(config.ResourceGroup, config.AccountName, asset.Name).StreamingLocators.Select(l => l.Name); foreach (var locatorName in streamingLocatorsNames) { string cenckeyId = null; string cbcskeyId = null; StreamingLocator streamingLocator = null; if (locatorName != null) { streamingLocator = client.StreamingLocators.Get(config.ResourceGroup, config.AccountName, locatorName); if (streamingLocator != null) { streamingPolicyName = streamingLocator.StreamingPolicyName; if (streamingLocator.ContentKeys .Where(k => k.LabelReferenceInStreamingPolicy == IrdetoHelpers.labelCenc) .FirstOrDefault() != null && streamingLocator.ContentKeys .Where(k => k.LabelReferenceInStreamingPolicy == IrdetoHelpers.labelCbcs) .FirstOrDefault() != null) { cenckeyId = streamingLocator.ContentKeys .Where(k => k.LabelReferenceInStreamingPolicy == IrdetoHelpers.labelCenc) .FirstOrDefault().Id.ToString(); cbcskeyId = streamingLocator.ContentKeys .Where(k => k.LabelReferenceInStreamingPolicy == IrdetoHelpers.labelCbcs) .FirstOrDefault().Id.ToString(); } // let's get the manifest name string manifestName = null; List <IListBlobItem> blobs = new List <IListBlobItem>(); try { ListContainerSasInput input = new ListContainerSasInput() { Permissions = AssetContainerPermission.Read, ExpiryTime = DateTime.Now.AddHours(2).ToUniversalTime() }; var responseListSas = client.Assets.ListContainerSas(config.ResourceGroup, config.AccountName, asset.Name, input.Permissions, input.ExpiryTime); string uploadSasUrl = responseListSas.AssetContainerSasUrls.First(); var sasUri = new Uri(uploadSasUrl); var container = new CloudBlobContainer(sasUri); BlobContinuationToken continuationToken = null; do { var response = await container.ListBlobsSegmentedAsync(continuationToken); continuationToken = response.ContinuationToken; blobs.AddRange(response.Results); }while (continuationToken != null); // let's take the first manifest file. It should exist manifestName = blobs.Where(b => (b.GetType() == typeof(CloudBlockBlob))).Select(b => (CloudBlockBlob)b).Where(b => b.Name.ToLower().EndsWith(".ism")).FirstOrDefault().Name; } catch { } if (manifestName != null) // there is a manifest { urls = MediaServicesHelpers.GetUrls(config, client, streamingLocator, manifestName, true, true, true, true, true); } else // no manifest { urls = MediaServicesHelpers.GetDownloadUrls(config, client, streamingLocator, blobs); } } } if (streamingPolicyName != null) { streamingPolicy = client.StreamingPolicies.Get(config.ResourceGroup, config.AccountName, streamingPolicyName); } var drmlist = new List <Drm>(); if (streamingPolicy != null) { if (streamingPolicy.CommonEncryptionCbcs != null) { var enProt = MediaServicesHelpers.ReturnOutputProtocolsListCbcs(streamingPolicy .CommonEncryptionCbcs.EnabledProtocols); drmlist.Add(new Drm { Type = "FairPlay", LicenseUrl = streamingPolicy?.CommonEncryptionCbcs?.Drm.FairPlay .CustomLicenseAcquisitionUrlTemplate.Replace("{ContentKeyId}", cbcskeyId).Replace("{AlternativeMediaId}", streamingLocator.AlternativeMediaId), Protocols = enProt, CertificateUrl = config.IrdetoFairPlayCertificateUrl }); } if (streamingPolicy.CommonEncryptionCenc != null) { var enProtW = MediaServicesHelpers.ReturnOutputProtocolsListCencWidevine(streamingPolicy .CommonEncryptionCbcs.EnabledProtocols); var enProtP = MediaServicesHelpers.ReturnOutputProtocolsListCencPlayReady(streamingPolicy .CommonEncryptionCbcs.EnabledProtocols); drmlist.Add(new Drm { Type = "PlayReady", LicenseUrl = streamingPolicy?.CommonEncryptionCenc?.Drm.PlayReady .CustomLicenseAcquisitionUrlTemplate.Replace("{AlternativeMediaId}", streamingLocator.AlternativeMediaId), Protocols = enProtP }); drmlist.Add(new Drm { Type = "Widevine", LicenseUrl = streamingPolicy?.CommonEncryptionCenc?.Drm.Widevine .CustomLicenseAcquisitionUrlTemplate.Replace("{AlternativeMediaId}", streamingLocator.AlternativeMediaId), Protocols = enProtW }); } } var StreamingLocatorInfo = new StreamingLocatorEntry { StreamingLocatorName = locatorName, StreamingPolicyName = streamingPolicyName, CencKeyId = cenckeyId, CbcsKeyId = cbcskeyId, Drm = drmlist, Urls = urls.Select(url => new UrlEntry { Protocol = url.Protocol.ToString(), Url = url.Url }) .ToList() }; assetEntry.StreamingLocators.Add(StreamingLocatorInfo); } return(assetEntry); }
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 assetName = (string)data.assetName; if (assetName == null) { return(IrdetoHelpers.ReturnErrorException(log, "Error - please pass assetName 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); } // semaphore file (json structure) VodSemaphore semaphore = null; if (data.semaphore != null) { semaphore = VodSemaphore.FromJson((string)data.semaphore); } // init default 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 locatorPath = string.Empty; string uniquenessPolicyName = Guid.NewGuid().ToString().Substring(0, 13); // Default content id and semaphare value string irdetoContentId = null; if (semaphore != null && semaphore.DrmContentId != null) // semaphore data has higher priority { irdetoContentId = semaphore.DrmContentId; } else if (data.defaultIrdetoContentId != null) { irdetoContentId = (string)data.defaultIrdetoContentId; } var clientTasks = new List <Task <AssetEntry> >(); foreach (var region in azureRegions) { var task = Task <AssetEntry> .Run(async() => { Asset asset = 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 get the asset try { MediaServicesHelpers.LogInformation(log, "Getting asset.", region); asset = await client.Assets.GetAsync(config.ResourceGroup, config.AccountName, assetName); } catch (Exception ex) { throw new Exception("Error when retreving asset by name", ex); } // Locator creation try { StreamingLocator locator = null; locator = await IrdetoHelpers.CreateClearLocator(config, streamingLocatorName, client, asset, streamingLocatorGuid); MediaServicesHelpers.LogInformation(log, "locator : " + locator.Name, region); } catch (Exception ex) { throw new Exception("Error when creating the locator", ex); } // let's build info for the live event and output AssetEntry assetEntry = await GenerateInfoHelpers.GenerateAssetInformation(config, client, asset, semaphore, irdetoContentId, region); var locatorPath2 = assetEntry.StreamingLocators.Where(l => l.StreamingLocatorName == streamingLocatorName).First().Urls.Where(u => u.Protocol == OutputProtocol.SmoothStreaming.ToString()).First().Url; var uriloc = new Uri(locatorPath2); locatorPath = uriloc.Scheme + "://" + uriloc.Host + "/" + uriloc.Segments[1]; if (!await CosmosHelpers.CreateOrUpdateAssetDocument(assetEntry)) { log.LogWarning("Cosmos access not configured."); } return(assetEntry); }); clientTasks.Add(task); } try { Task.WaitAll(clientTasks.ToArray()); } catch (Exception ex) { return(IrdetoHelpers.ReturnErrorException(log, ex)); } return(new OkObjectResult( JsonConvert.SerializeObject(new VodAssetInfoSimple { CreatedLocatorName = streamingLocatorName, CreatedLocatorPath = locatorPath, Success = true, Asset = clientTasks.Select(i => i.Result).First() }, Formatting.Indented) )); }
public async static Task <ManifestGenerated> LoadAndUpdateManifestTemplateUsingSemaphore(VodSemaphore semaphore, Microsoft.Azure.WebJobs.ExecutionContext execContext, string manifestFileName = null) { var mediaAssetFiles = semaphore.Files.Where(f => f.ContainsVideo || f.ContainsAudio); var audioFiles = semaphore.Files.Where(f => f.ContainsAudio); var videoFiles = semaphore.Files.Where(f => f.ContainsVideo); if (mediaAssetFiles.Count() != 0) { // let's load the manifest template string manifestPath = Path.Combine(System.IO.Directory.GetParent(execContext.FunctionDirectory).FullName, "ManifestTemplate", "manifest.ism"); XDocument doc = XDocument.Load(manifestPath); XNamespace ns = "http://www.w3.org/2001/SMIL20/Language"; var bodyxml = doc.Element(ns + "smil"); var body2 = bodyxml.Element(ns + "body"); var switchxml = body2.Element(ns + "switch"); // audio tracks foreach (var file in audioFiles) { var myListAt = new List <XAttribute>() { new XAttribute("src", file.FileName) }; if (file.AudioLanguage != null) { myListAt.Add(new XAttribute("systemLanguage", file.AudioLanguage)); } /* * // No need as it is recommended to use trackName for audio * if (file.AudioTitle != null) * myListAt.Add(new XAttribute("title", file.AudioTitle)); */ var myListElem = new List <XElement>(); if (file.AudioTrackName != null) { myListElem.Add(new XElement(ns + "param", new List <XAttribute> { new XAttribute("name", "trackName"), new XAttribute("value", file.AudioTrackName) })); } if (file.AudioAccessibility != null) { myListElem.Add(new XElement(ns + "param", new List <XAttribute> { new XAttribute("name", "accessibility"), new XAttribute("value", file.AudioAccessibility) })); } if (file.AudioRole != null) { myListElem.Add(new XElement(ns + "param", new List <XAttribute> { new XAttribute("name", "role"), new XAttribute("value", file.AudioRole) })); } var audioElement = new XElement(ns + "audio", myListAt); audioElement.Add(myListElem); switchxml.Add(audioElement); } // video tracks foreach (var file in videoFiles) { switchxml.Add(new XElement(ns + "video", new XAttribute("src", file.FileName))); } // manifest filename if (manifestFileName == null) { manifestFileName = CommonPrefix(mediaAssetFiles.Select(f => Path.GetFileNameWithoutExtension(f.FileName)).ToArray()); if (string.IsNullOrEmpty(manifestFileName)) { manifestFileName = "manifest"; } else if (manifestFileName.EndsWith("_") && manifestFileName.Length > 1) // i string ends with "_", let's remove it { manifestFileName = manifestFileName.Substring(0, manifestFileName.Length - 1); } manifestFileName = manifestFileName + ".ism"; } else if (!manifestFileName.ToLower().EndsWith(".ism")) { manifestFileName = manifestFileName + ".ism"; } return(new ManifestGenerated() { Content = doc.Declaration.ToString() + Environment.NewLine + doc.ToString(), FileName = manifestFileName }); } else { return(new ManifestGenerated() { Content = null, FileName = string.Empty }); // no mp4 in asset } }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log, Microsoft.Azure.WebJobs.ExecutionContext execContext) { 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 assetName = (string)data.assetName; if (assetName == null) { return(IrdetoHelpers.ReturnErrorException(log, "Error - please pass assetName in the JSON")); } var fileName = (string)data.fileName; ManifestGenerated smildata = null; VodSemaphore semaphore = null; if (data.semaphore != null) { semaphore = VodSemaphore.FromJson((string)data.semaphore); } // Azure region management var azureRegions = new List <string>(); if ((string)data.azureRegion != null) { azureRegions = ((string)data.azureRegion).Split(',').ToList(); } else { azureRegions.Add((string)null); } foreach (var region in azureRegions) { 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; MediaServicesHelpers.LogInformation(log, "asset name : " + assetName, region); var asset = client.Assets.Get(config.ResourceGroup, config.AccountName, assetName); if (asset == null) { throw new Exception($"Asset {asset} does not exist."); } // Access to container ListContainerSasInput input = new ListContainerSasInput() { Permissions = AssetContainerPermission.ReadWriteDelete, ExpiryTime = DateTime.Now.AddHours(2).ToUniversalTime() }; var responseListSas = await client.Assets.ListContainerSasAsync(config.ResourceGroup, config.AccountName, asset.Name, input.Permissions, input.ExpiryTime); string uploadSasUrl = responseListSas.AssetContainerSasUrls.First(); var sasUri = new Uri(uploadSasUrl); var container = new CloudBlobContainer(sasUri); // Manifest generate smildata = (semaphore == null) ? await ManifestHelpers.LoadAndUpdateManifestTemplate(asset, container, execContext, fileName) : await ManifestHelpers.LoadAndUpdateManifestTemplateUsingSemaphore(semaphore, execContext, fileName); // if not file name passed, then we use the one generated based on mp4 files names if (fileName == null) { fileName = smildata.FileName; } var blob = container.GetBlockBlobReference(fileName); using (Stream s = ManifestHelpers.GenerateStreamFromString(smildata.Content)) { await blob.UploadFromStreamAsync(s); } } var response = new JObject { { "success", true }, { "fileName", fileName }, { "manifestContent", smildata.Content }, { "operationsVersion", AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString() } }; return(new OkObjectResult( response )); }
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 assetName = (string)data.assetName; if (assetName == null) { return(IrdetoHelpers.ReturnErrorException(log, "Error - please pass assetName 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); } // semaphore file (json structure) VodSemaphore semaphore = null; if (data.semaphore != null) { semaphore = VodSemaphore.FromJson((string)data.semaphore); } // init default 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); // useDRM init var useDRM = true; if (data.useDRM != null) { useDRM = (bool)data.useDRM; } else if (semaphore != null & semaphore.ClearStream != null) { useDRM = !(bool)semaphore.ClearStream; } // Default content id and semaphare value string irdetoContentId = null; if (semaphore != null && semaphore.DrmContentId != null) // semaphore data has higher priority { irdetoContentId = semaphore.DrmContentId; } else if (data.defaultIrdetoContentId != null) { irdetoContentId = (string)data.defaultIrdetoContentId; } DateTime?startTime = null; DateTime?endTime = null; try { if (semaphore != null && semaphore.StartTime != null) { startTime = DateTime.ParseExact(semaphore.StartTime, AssetEntry.DateFormat, System.Globalization.CultureInfo.InvariantCulture); } if (semaphore != null && semaphore.EndTime != null) { endTime = DateTime.ParseExact(semaphore.EndTime, AssetEntry.DateFormat, System.Globalization.CultureInfo.InvariantCulture); } } catch (Exception ex) { return(IrdetoHelpers.ReturnErrorException(log, ex)); } 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(irdetoContentId, config); cbcsKey = await IrdetoHelpers.GenerateAndRegisterCBCSKeyToIrdeto(irdetoContentId, config); MediaServicesHelpers.LogInformation(log, "Irdeto call done."); } catch (Exception ex) { return(IrdetoHelpers.ReturnErrorException(log, ex, "Irdeto response error")); } } var clientTasks = new List <Task <AssetEntry> >(); foreach (var region in azureRegions) { var task = Task <AssetEntry> .Run(async() => { Asset asset = null; StreamingPolicy streamingPolicy = 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; 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(true) { 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(true) { 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); } } } // let's get the asset try { MediaServicesHelpers.LogInformation(log, "Getting asset.", region); asset = await client.Assets.GetAsync(config.ResourceGroup, config.AccountName, assetName); } catch (Exception ex) { throw new Exception("Error when retreving asset by name", ex); } // Locator creation try { StreamingLocator locator = null; if (useDRM) { locator = await IrdetoHelpers.SetupDRMAndCreateLocatorWithNewKeys(config, streamingPolicy.Name, streamingLocatorName, client, asset, cencKey, cbcsKey, streamingLocatorGuid, irdetoContentId, startTime, endTime); } else // no DRM { locator = await IrdetoHelpers.CreateClearLocator(config, streamingLocatorName, client, asset, streamingLocatorGuid, startTime, endTime); } MediaServicesHelpers.LogInformation(log, "locator : " + locator.Name, region); } catch (Exception ex) { throw new Exception("Error when creating the locator", ex); } // let's build info for the live event and output AssetEntry assetEntry = await GenerateInfoHelpers.GenerateAssetInformation(config, client, asset, semaphore, irdetoContentId, region); if (!await CosmosHelpers.CreateOrUpdateAssetDocument(assetEntry)) { log.LogWarning("Cosmos access not configured."); } return(assetEntry); }); clientTasks.Add(task); } try { Task.WaitAll(clientTasks.ToArray()); } catch (Exception ex) { return(IrdetoHelpers.ReturnErrorException(log, ex)); } return(new OkObjectResult( JsonConvert.SerializeObject(new VodAssetInfo { Success = true, Assets = clientTasks.Select(i => i.Result).ToList() }, Formatting.Indented) )); }